mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
206 Commits
v0.11.0-beta.1
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 497d84828a | |||
| 0e489901a5 | |||
| 88063e756f | |||
| c8436e2b80 | |||
| 613748ec16 | |||
| 2fbd266da6 | |||
| c79b5e9cdb | |||
| 2ace892613 | |||
| c1ffd4e790 | |||
| d9c139b55f | |||
| 0c4206c97d | |||
| a985dc7a37 | |||
| e7fa8d75bb | |||
| c10718ed2f | |||
| ec2ce3620f | |||
| 71757003c7 | |||
| 6bb7f09927 | |||
| e67ae7f0de | |||
| 8c1535c27b | |||
| fd245daa6f | |||
| 7fb7e8685f | |||
| 93617eef4e | |||
| 171cdf1758 | |||
| 3f8f016daa | |||
| 86d0c9bddb | |||
| 86d63ef27b | |||
| e33bcf7938 | |||
| c80a97b526 | |||
| 3c40cfe199 | |||
| e9efb73125 | |||
| 386c6d8ed8 | |||
| 8f086bf837 | |||
| ad167612dc | |||
| 3366f47d40 | |||
| 0b5ef91f44 | |||
| a162b0ceca | |||
| 8e6d652a21 | |||
| 1be9e6ce2d | |||
| b1f99aa852 | |||
| 4c5465229e | |||
| 9b24bb2319 | |||
| e1701b6b56 | |||
| 77d7d5d6bd | |||
| ee9232eda1 | |||
| da526595bb | |||
| 39a7d3b862 | |||
| 0245d4ce6b | |||
| a7f105c698 | |||
| d2a8670bef | |||
| e9de70a030 | |||
| aee647c6c0 | |||
| 2e4184e7e4 | |||
| 12b24b6c5b | |||
| 1b500b7d47 | |||
| ce4fc80e18 | |||
| 121971f54e | |||
| 6835537e3b | |||
| a2728928cf | |||
| 676f3a6983 | |||
| 0beaac228c | |||
| 9a7f75ad6b | |||
| cd56f92a7f | |||
| 1bd1359f43 | |||
| feec72bcbd | |||
| 0611906c6a | |||
| 72ac247dca | |||
| f2fd010c5f | |||
| 0619f264de | |||
| ddd44bdd36 | |||
| 1152519074 | |||
| 3a2fd1ec59 | |||
| a4c8b20697 | |||
| 6e353d6c9f | |||
| 247208edb1 | |||
| e08768cc5e | |||
| 5b38af9746 | |||
| 54907f80c5 | |||
| ae512f2b49 | |||
| 53c159b6cb | |||
| 2beca6ebe1 | |||
| 09da7a41e5 | |||
| bad20374ad | |||
| 40b507c3ec | |||
| 537cc995f6 | |||
| 5d27cb91b2 | |||
| c70a636ca9 | |||
| 13ff0ca798 | |||
| 44f49e35d6 | |||
| e1ee4058a6 | |||
| f06d0020ea | |||
| d399559d30 | |||
| 30735d5ff1 | |||
| 75c23e5853 | |||
| 347856b806 | |||
| 774a63bece | |||
| c481c87eac | |||
| 85b280a9d6 | |||
| db7b782d3c | |||
| 5f38445558 | |||
| df0a44d405 | |||
| f0806a6d62 | |||
| 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 |
@@ -0,0 +1,68 @@
|
|||||||
|
name: Documentation (dev)
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [dev]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
# Allow only one concurrent deployment
|
||||||
|
concurrency:
|
||||||
|
group: pages
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build Documentation
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@main
|
||||||
|
- uses: dtolnay/rust-toolchain@nightly
|
||||||
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
|
||||||
|
- name: Build documentation
|
||||||
|
env:
|
||||||
|
RUSTDOCFLAGS: "--cfg docsrs"
|
||||||
|
run: |
|
||||||
|
cargo +nightly doc --no-deps \
|
||||||
|
--features "lua55,vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||||
|
|
||||||
|
- name: Create index redirect
|
||||||
|
run: |
|
||||||
|
echo '<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Redirecting to mlua documentation</title>
|
||||||
|
<meta http-equiv="refresh" content="0; URL=mlua/index.html">
|
||||||
|
<link rel="canonical" href="mlua/index.html">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>Redirecting to <a href="mlua/index.html">mlua documentation</a>...</p>
|
||||||
|
</body>
|
||||||
|
</html>' > target/doc/index.html
|
||||||
|
|
||||||
|
- name: Setup Pages
|
||||||
|
uses: actions/configure-pages@v5
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-pages-artifact@v4
|
||||||
|
with:
|
||||||
|
path: target/doc
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
name: Deploy to GitHub Pages
|
||||||
|
environment:
|
||||||
|
name: github-pages
|
||||||
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
steps:
|
||||||
|
- name: Deploy to GitHub Pages
|
||||||
|
id: deployment
|
||||||
|
uses: actions/deploy-pages@v4
|
||||||
+63
-44
@@ -9,12 +9,12 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
rust: [stable]
|
rust: [stable]
|
||||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
|
lua: [lua55, lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
|
||||||
include:
|
include:
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
target: x86_64-unknown-linux-gnu
|
target: x86_64-unknown-linux-gnu
|
||||||
- os: macos-latest
|
- os: macos-latest
|
||||||
target: x86_64-apple-darwin
|
target: aarch64-apple-darwin
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
target: x86_64-pc-windows-msvc
|
target: x86_64-pc-windows-msvc
|
||||||
steps:
|
steps:
|
||||||
@@ -27,39 +27,23 @@ jobs:
|
|||||||
- name: Build ${{ matrix.lua }} vendored
|
- name: Build ${{ matrix.lua }} vendored
|
||||||
run: |
|
run: |
|
||||||
cargo build --features "${{ matrix.lua }},vendored"
|
cargo build --features "${{ matrix.lua }},vendored"
|
||||||
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers"
|
cargo build --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow,userdata-wrappers"
|
||||||
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers,send"
|
cargo build --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow,userdata-wrappers,send"
|
||||||
shell: bash
|
shell: bash
|
||||||
- name: Build ${{ matrix.lua }} pkg-config
|
- name: Build ${{ matrix.lua }} pkg-config
|
||||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
if: ${{ matrix.os == 'ubuntu-latest' && matrix.lua != 'lua55' }}
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
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
|
sudo apt-get install -y --no-install-recommends liblua5.4-dev liblua5.3-dev liblua5.2-dev liblua5.1-0-dev libluajit-5.1-dev
|
||||||
cargo build --features "${{ matrix.lua }}"
|
cargo build --features "${{ matrix.lua }}"
|
||||||
|
|
||||||
build_aarch64_cross_macos:
|
|
||||||
name: Cross-compile to aarch64-apple-darwin
|
|
||||||
runs-on: macos-latest
|
|
||||||
needs: build
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
|
||||||
steps:
|
|
||||||
- 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,anyhow,userdata-wrappers"
|
|
||||||
|
|
||||||
build_aarch64_cross_ubuntu:
|
build_aarch64_cross_ubuntu:
|
||||||
name: Cross-compile to aarch64-unknown-linux-gnu
|
name: Cross-compile to aarch64-unknown-linux-gnu
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build
|
needs: build
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
lua: [lua55, lua54, lua53, lua52, lua51, luajit]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@main
|
- uses: actions/checkout@main
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
@@ -72,7 +56,7 @@ jobs:
|
|||||||
sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross
|
sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross
|
||||||
shell: bash
|
shell: bash
|
||||||
- name: Cross-compile
|
- name: Cross-compile
|
||||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
build_armv7_cross_ubuntu:
|
build_armv7_cross_ubuntu:
|
||||||
@@ -81,7 +65,7 @@ jobs:
|
|||||||
needs: build
|
needs: build
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
lua: [lua54, lua53, lua52, lua51]
|
lua: [lua55, lua54, lua53, lua52, lua51]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@main
|
- uses: actions/checkout@main
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
@@ -94,7 +78,7 @@ jobs:
|
|||||||
sudo apt-get install -y --no-install-recommends gcc-arm-linux-gnueabihf libc-dev-armhf-cross
|
sudo apt-get install -y --no-install-recommends gcc-arm-linux-gnueabihf libc-dev-armhf-cross
|
||||||
shell: bash
|
shell: bash
|
||||||
- name: Cross-compile
|
- name: Cross-compile
|
||||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
test:
|
test:
|
||||||
@@ -105,12 +89,12 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
rust: [stable, nightly]
|
rust: [stable, nightly]
|
||||||
lua: [lua54, lua53, lua52, lua51, luajit, luajit52, luau, luau-jit, luau-vector4]
|
lua: [lua55, lua54, lua53, lua52, lua51, luajit, luajit52, luau, luau-jit, luau-vector4]
|
||||||
include:
|
include:
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
target: x86_64-unknown-linux-gnu
|
target: x86_64-unknown-linux-gnu
|
||||||
- os: macos-latest
|
- os: macos-latest
|
||||||
target: x86_64-apple-darwin
|
target: aarch64-apple-darwin
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
target: x86_64-pc-windows-msvc
|
target: x86_64-pc-windows-msvc
|
||||||
steps:
|
steps:
|
||||||
@@ -123,14 +107,14 @@ jobs:
|
|||||||
- name: Run ${{ matrix.lua }} tests
|
- name: Run ${{ matrix.lua }} tests
|
||||||
run: |
|
run: |
|
||||||
cargo test --features "${{ matrix.lua }},vendored"
|
cargo test --features "${{ matrix.lua }},vendored"
|
||||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers"
|
cargo test --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow,userdata-wrappers"
|
||||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers,send"
|
cargo test --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow,userdata-wrappers,send"
|
||||||
shell: bash
|
shell: bash
|
||||||
- name: Run compile tests (macos lua54)
|
- name: Run compile tests (macos lua55)
|
||||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua54' }}
|
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua55' }}
|
||||||
run: |
|
run: |
|
||||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" -- --ignored
|
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" --tests -- --ignored
|
||||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros" -- --ignored
|
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serde,macros" --tests -- --ignored
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
test_with_sanitizer:
|
test_with_sanitizer:
|
||||||
@@ -141,7 +125,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest]
|
os: [ubuntu-latest]
|
||||||
rust: [nightly]
|
rust: [nightly]
|
||||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
|
lua: [lua55, lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
|
||||||
include:
|
include:
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
target: x86_64-unknown-linux-gnu
|
target: x86_64-unknown-linux-gnu
|
||||||
@@ -154,8 +138,8 @@ jobs:
|
|||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- name: Run ${{ matrix.lua }} tests with address sanitizer
|
- name: Run ${{ matrix.lua }} tests with address sanitizer
|
||||||
run: |
|
run: |
|
||||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow" --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,serialize,macros,anyhow,userdata-wrappers,send" --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
|
shell: bash
|
||||||
env:
|
env:
|
||||||
RUSTFLAGS: -Z sanitizer=address
|
RUSTFLAGS: -Z sanitizer=address
|
||||||
@@ -168,7 +152,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest]
|
os: [ubuntu-latest]
|
||||||
rust: [nightly]
|
rust: [nightly]
|
||||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
|
lua: [lua55, lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
|
||||||
include:
|
include:
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
target: x86_64-unknown-linux-gnu
|
target: x86_64-unknown-linux-gnu
|
||||||
@@ -181,7 +165,7 @@ jobs:
|
|||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- name: Run ${{ matrix.lua }} tests with forced memory limit
|
- name: Run ${{ matrix.lua }} tests with forced memory limit
|
||||||
run: |
|
run: |
|
||||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
RUSTFLAGS: --cfg=force_memory_limit
|
RUSTFLAGS: --cfg=force_memory_limit
|
||||||
@@ -194,12 +178,12 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest]
|
os: [ubuntu-latest, macos-latest]
|
||||||
rust: [stable]
|
rust: [stable]
|
||||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
lua: [lua55, lua54, lua53, lua52, lua51, luajit]
|
||||||
include:
|
include:
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
target: x86_64-unknown-linux-gnu
|
target: x86_64-unknown-linux-gnu
|
||||||
- os: macos-latest
|
- os: macos-latest
|
||||||
target: x86_64-apple-darwin
|
target: aarch64-apple-darwin
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@main
|
- uses: actions/checkout@main
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
@@ -240,7 +224,7 @@ jobs:
|
|||||||
needs: build
|
needs: build
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
lua: [lua54, lua53, lua52, lua51, luau]
|
lua: [lua55, lua54, lua53, lua52, lua51, luau]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@main
|
- uses: actions/checkout@main
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
@@ -254,7 +238,42 @@ jobs:
|
|||||||
- name: Run ${{ matrix.lua }} tests
|
- name: Run ${{ matrix.lua }} tests
|
||||||
run: |
|
run: |
|
||||||
cargo test --tests --features "${{ matrix.lua }},vendored"
|
cargo test --tests --features "${{ matrix.lua }},vendored"
|
||||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers"
|
cargo test --tests --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow,userdata-wrappers"
|
||||||
|
|
||||||
|
test_wasm32_wasip2:
|
||||||
|
name: Test on wasm32-wasip2
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
lua: [lua55, lua54, lua53, lua52, lua51]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@main
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
toolchain: nightly-2025-10-02
|
||||||
|
target: wasm32-wasip2
|
||||||
|
- name: Install wasi-sdk/Wasmtime
|
||||||
|
working-directory: ${{ runner.tool_cache }}
|
||||||
|
run: |
|
||||||
|
wasi_sdk=29
|
||||||
|
wasmtime=v40.0.1
|
||||||
|
|
||||||
|
curl -LO https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-$wasi_sdk/wasi-sdk-$wasi_sdk.0-x86_64-linux.tar.gz
|
||||||
|
tar xf wasi-sdk-$wasi_sdk.0-x86_64-linux.tar.gz
|
||||||
|
WASI_SDK_PATH=`pwd`/wasi-sdk-$wasi_sdk.0-x86_64-linux
|
||||||
|
echo "WASI_SDK_PATH=$WASI_SDK_PATH" >> $GITHUB_ENV
|
||||||
|
echo "CC_wasm32_wasip2=$WASI_SDK_PATH/bin/clang" >> $GITHUB_ENV
|
||||||
|
echo "CARGO_TARGET_WASM32_WASIP2_LINKER=$WASI_SDK_PATH/bin/clang" >> $GITHUB_ENV
|
||||||
|
echo "CARGO_TARGET_WASM32_WASIP2_RUSTFLAGS=-Clink-arg=-Wl,--export=cabi_realloc" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
curl -LO https://github.com/bytecodealliance/wasmtime/releases/download/$wasmtime/wasmtime-$wasmtime-x86_64-linux.tar.xz
|
||||||
|
tar xf wasmtime-$wasmtime-x86_64-linux.tar.xz
|
||||||
|
echo "CARGO_TARGET_WASM32_WASIP2_RUNNER=`pwd`/wasmtime-$wasmtime-x86_64-linux/wasmtime -W exceptions" >> $GITHUB_ENV
|
||||||
|
- name: Run ${{ matrix.lua }} tests
|
||||||
|
run: |
|
||||||
|
cargo test --target wasm32-wasip2 --tests --features "${{ matrix.lua }},vendored"
|
||||||
|
cargo test --target wasm32-wasip2 --tests --features "${{ matrix.lua }},vendored,serde,macros,anyhow,userdata-wrappers"
|
||||||
|
|
||||||
rustfmt:
|
rustfmt:
|
||||||
name: Rustfmt
|
name: Rustfmt
|
||||||
@@ -271,7 +290,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
|
lua: [lua55, lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@main
|
- uses: actions/checkout@main
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
@@ -281,4 +300,4 @@ jobs:
|
|||||||
- uses: giraffate/clippy-action@v1
|
- uses: giraffate/clippy-action@v1
|
||||||
with:
|
with:
|
||||||
reporter: 'github-pr-review'
|
reporter: 'github-pr-review'
|
||||||
clippy_flags: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
clippy_flags: --features "${{ matrix.lua }},vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||||
|
|||||||
@@ -1,17 +1,22 @@
|
|||||||
name: Typos Check
|
name: Spelling Check
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
env:
|
||||||
|
CLICOLOR: 1
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
run:
|
spelling:
|
||||||
name: Spell Check with Typos
|
name: Spell Check with Typos
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Actions Repository
|
- name: Checkout Actions Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@main
|
||||||
|
|
||||||
- name: Check spelling
|
- name: Check spelling
|
||||||
uses: crate-ci/typos@master
|
uses: crate-ci/typos@v1.42.1
|
||||||
with:
|
with:
|
||||||
config: ./typos.toml
|
config: ./typos.toml
|
||||||
|
|||||||
@@ -1,3 +1,81 @@
|
|||||||
|
## v0.11.6 (Jan 27, 2026)
|
||||||
|
|
||||||
|
- Added Lua 5.5 support (`lua55` feature flag)
|
||||||
|
- Luau updated to 0.705+
|
||||||
|
- Added `AnyUserData::is_proxy` method to check if userdata is a proxy
|
||||||
|
- Added `num_params`, `num_upvalues`, `is_vararg` to `FunctionInfo`
|
||||||
|
|
||||||
|
## v0.11.5 (Nov 22, 2025)
|
||||||
|
|
||||||
|
- Luau updated to 0.701
|
||||||
|
- Added `Lua::set_memory_category` and `Lua::heap_dump` functions to profile (Luau) memory
|
||||||
|
- Added `Lua::type_metatable` helper to get metatable of a primitive type
|
||||||
|
- Added `Lua::traceback` function to generate stack traces at different levels
|
||||||
|
- Added `add_method_once` /`add_async_method_once` UserData methods (experimental)
|
||||||
|
- Make `AnyUserData::type_name` public
|
||||||
|
- impl `IntoLuaMulti` for `&MultiValue`
|
||||||
|
- Bugfixes and async perf improvements
|
||||||
|
|
||||||
|
## v0.11.4 (Sep 29, 2025)
|
||||||
|
|
||||||
|
- Make `Value::to_serializable` public
|
||||||
|
- Add new serde option `detect_mixed_tables` (to encode mixed array+map tables)
|
||||||
|
- Add `ObjectLike::get_path` helper (for tables and userdata)
|
||||||
|
|
||||||
|
## v0.11.3 (Aug 30, 2025)
|
||||||
|
|
||||||
|
- Add `Lua::yield_with` to use as `coroutine.yield` functional replacement in async functions for any Lua
|
||||||
|
- Do not try to yield at non-yielable points in Luau interrupt (#632)
|
||||||
|
- Add `Buffer::cursor` method (Luau)
|
||||||
|
- Add `Lua::create_buffer_with_capacity` method (Luau)
|
||||||
|
- Make Lua reference values cheap to clone (only increments ref count)
|
||||||
|
- Fix panic on large (>67M entries) table creation
|
||||||
|
|
||||||
|
## 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)
|
## v0.11.0-beta.1 (May 7th, 2025)
|
||||||
|
|
||||||
- New "require-by-string" for Luau (with `Require` trait and async support)
|
- New "require-by-string" for Luau (with `Require` trait and async support)
|
||||||
|
|||||||
+28
-24
@@ -1,9 +1,9 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mlua"
|
name = "mlua"
|
||||||
version = "0.11.0-beta.1" # remember to update mlua_derive
|
version = "0.12.0-dev.1" # remember to update mlua_derive
|
||||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@kyju.org>"]
|
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@kyju.org>"]
|
||||||
rust-version = "1.79.0"
|
rust-version = "1.88"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
repository = "https://github.com/mlua-rs/mlua"
|
repository = "https://github.com/mlua-rs/mlua"
|
||||||
documentation = "https://docs.rs/mlua"
|
documentation = "https://docs.rs/mlua"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@@ -11,12 +11,12 @@ keywords = ["lua", "luajit", "luau", "async", "scripting"]
|
|||||||
categories = ["api-bindings", "asynchronous"]
|
categories = ["api-bindings", "asynchronous"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = """
|
description = """
|
||||||
High level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Luau
|
High level bindings to Lua 5.5/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.
|
with async/await features and support of writing native Lua modules in Rust.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = ["lua54", "vendored", "async", "send", "serialize", "macros"]
|
features = ["lua55", "vendored", "async", "send", "serde", "macros"]
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
@@ -26,6 +26,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
lua55 = ["ffi/lua55"]
|
||||||
lua54 = ["ffi/lua54"]
|
lua54 = ["ffi/lua54"]
|
||||||
lua53 = ["ffi/lua53"]
|
lua53 = ["ffi/lua53"]
|
||||||
lua52 = ["ffi/lua52"]
|
lua52 = ["ffi/lua52"]
|
||||||
@@ -36,17 +37,20 @@ luau = ["ffi/luau"]
|
|||||||
luau-jit = ["luau", "ffi/luau-codegen"]
|
luau-jit = ["luau", "ffi/luau-codegen"]
|
||||||
luau-vector4 = ["luau", "ffi/luau-vector4"]
|
luau-vector4 = ["luau", "ffi/luau-vector4"]
|
||||||
vendored = ["ffi/vendored"]
|
vendored = ["ffi/vendored"]
|
||||||
module = ["dep:mlua_derive", "ffi/module"]
|
module = ["mlua_derive", "ffi/module"]
|
||||||
async = ["dep:futures-util"]
|
async = ["dep:futures-util"]
|
||||||
send = ["parking_lot/send_guard", "error-send"]
|
send = ["error-send"]
|
||||||
error-send = []
|
error-send = []
|
||||||
serialize = ["dep:serde", "dep:erased-serde", "dep:serde-value", "bstr/serde"]
|
serde = ["dep:serde", "dep:erased-serde", "dep:serde-value", "bstr/serde"]
|
||||||
macros = ["mlua_derive/macros"]
|
macros = ["mlua_derive/macros"]
|
||||||
anyhow = ["dep:anyhow", "error-send"]
|
anyhow = ["dep:anyhow", "error-send"]
|
||||||
userdata-wrappers = []
|
userdata-wrappers = ["parking_lot/send_guard"]
|
||||||
|
|
||||||
|
# deprecated features
|
||||||
|
serialize = ["serde"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
mlua_derive = { version = "=0.11.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 }
|
bstr = { version = "1.0", features = ["std"], default-features = false }
|
||||||
either = "1.0"
|
either = "1.0"
|
||||||
num-traits = { version = "0.2.14" }
|
num-traits = { version = "0.2.14" }
|
||||||
@@ -57,26 +61,26 @@ erased-serde = { version = "0.4", optional = true }
|
|||||||
serde-value = { version = "0.7", optional = true }
|
serde-value = { version = "0.7", optional = true }
|
||||||
parking_lot = { version = "0.12", features = ["arc_lock"] }
|
parking_lot = { version = "0.12", features = ["arc_lock"] }
|
||||||
anyhow = { version = "1.0", optional = true }
|
anyhow = { version = "1.0", optional = true }
|
||||||
rustversion = "1.0"
|
libc = "0.2"
|
||||||
|
|
||||||
ffi = { package = "mlua-sys", version = "0.7.0", path = "mlua-sys" }
|
ffi = { package = "mlua-sys", version = "0.10.0", path = "mlua-sys" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
trybuild = "1.0"
|
trybuild = "1.0"
|
||||||
hyper = { version = "1.2", features = ["full"] }
|
|
||||||
hyper-util = { version = "0.1.3", features = ["full"] }
|
|
||||||
http-body-util = "0.1.1"
|
|
||||||
reqwest = { version = "0.12", features = ["json"] }
|
|
||||||
tokio = { version = "1.0", features = ["macros", "rt", "time"] }
|
tokio = { version = "1.0", features = ["macros", "rt", "time"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = { version = "1.0", features = ["arbitrary_precision"] }
|
serde_json = { version = "1.0", features = ["arbitrary_precision"] }
|
||||||
maplit = "1.0"
|
maplit = "1.0"
|
||||||
tempfile = "3"
|
|
||||||
static_assertions = "1.0"
|
static_assertions = "1.0"
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
[target.'cfg(not(target_family = "wasm"))'.dev-dependencies]
|
||||||
criterion = { version = "0.5", features = ["async_tokio"] }
|
hyper = { version = "1.2", features = ["full"] }
|
||||||
rustyline = "15.0"
|
hyper-util = { version = "0.1.3", features = ["full"] }
|
||||||
|
http-body-util = "0.1.1"
|
||||||
|
reqwest = { version = "0.12", features = ["json"] }
|
||||||
|
tempfile = "3"
|
||||||
|
criterion = { version = "0.7", features = ["async_tokio"] }
|
||||||
|
rustyline = "17.0"
|
||||||
tokio = { version = "1.0", features = ["full"] }
|
tokio = { version = "1.0", features = ["full"] }
|
||||||
|
|
||||||
[lints.rust]
|
[lints.rust]
|
||||||
@@ -90,7 +94,7 @@ required-features = ["async"]
|
|||||||
[[bench]]
|
[[bench]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
harness = false
|
harness = false
|
||||||
required-features = ["serialize"]
|
required-features = ["serde"]
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "async_http_client"
|
name = "async_http_client"
|
||||||
@@ -98,7 +102,7 @@ required-features = ["async", "macros"]
|
|||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "async_http_reqwest"
|
name = "async_http_reqwest"
|
||||||
required-features = ["async", "serialize", "macros"]
|
required-features = ["async", "serde", "macros"]
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "async_http_server"
|
name = "async_http_server"
|
||||||
@@ -113,8 +117,8 @@ name = "guided_tour"
|
|||||||
required-features = ["macros"]
|
required-features = ["macros"]
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "serialize"
|
name = "serde"
|
||||||
required-features = ["serialize"]
|
required-features = ["serde"]
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "userdata"
|
name = "userdata"
|
||||||
|
|||||||
@@ -17,20 +17,14 @@
|
|||||||
[Benchmarks]: https://github.com/khvzak/script-bench-rs
|
[Benchmarks]: https://github.com/khvzak/script-bench-rs
|
||||||
[FAQ]: FAQ.md
|
[FAQ]: FAQ.md
|
||||||
|
|
||||||
# The main branch is the development version of `mlua`. Please see the [v0.10](https://github.com/mlua-rs/mlua/tree/v0.10) 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 of providing a
|
||||||
|
_safe_ (as much as possible), high level, easy to use, practical and flexible API.
|
||||||
|
|
||||||
> **Note**
|
Started as an `rlua` fork, `mlua` supports Lua 5.5, 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.
|
||||||
>
|
|
||||||
> See v0.10 [release notes](https://github.com/mlua-rs/mlua/blob/main/docs/release_notes/v0.10.md).
|
|
||||||
|
|
||||||
`mlua` is bindings to [Lua](https://www.lua.org) programming language for Rust with a goal to provide
|
`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).
|
||||||
_safe_ (as far as it's possible), high level, easy to use, practical and flexible API.
|
|
||||||
|
|
||||||
Started as `rlua` fork, `mlua` supports Lua 5.4, 5.3, 5.2, 5.1 (including LuaJIT) and [Luau] and allows to write native Lua modules in Rust as well as use Lua in a standalone mode.
|
WebAssembly (WASM) is supported through the `wasm32-unknown-emscripten` target for all Lua/Luau versions excluding JIT.
|
||||||
|
|
||||||
`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.
|
|
||||||
|
|
||||||
[GitHub Actions]: https://github.com/mlua-rs/mlua/actions
|
[GitHub Actions]: https://github.com/mlua-rs/mlua/actions
|
||||||
[Luau]: https://luau.org
|
[Luau]: https://luau.org
|
||||||
@@ -39,9 +33,10 @@ WebAssembly (WASM) is supported through `wasm32-unknown-emscripten` target for a
|
|||||||
|
|
||||||
### Feature flags
|
### 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 number of dependencies and compiled code, and allow choosing only the required set of features.
|
||||||
Below is a list of the available feature flags. By default `mlua` does not enable any features.
|
Below is a list of the available feature flags. By default `mlua` does not enable any features.
|
||||||
|
|
||||||
|
* `lua55`: enable Lua [5.5] support
|
||||||
* `lua54`: enable Lua [5.4] support
|
* `lua54`: enable Lua [5.4] support
|
||||||
* `lua53`: enable Lua [5.3] support
|
* `lua53`: enable Lua [5.3] support
|
||||||
* `lua52`: enable Lua [5.2] support
|
* `lua52`: enable Lua [5.2] support
|
||||||
@@ -51,16 +46,17 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
|
|||||||
* `luau`: enable [Luau] support (auto vendored mode)
|
* `luau`: enable [Luau] support (auto vendored mode)
|
||||||
* `luau-jit`: enable [Luau] support with JIT backend.
|
* `luau-jit`: enable [Luau] support with JIT backend.
|
||||||
* `luau-vector4`: enable [Luau] support with 4-dimensional vector.
|
* `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)
|
* `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])
|
* `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`)
|
* `send`: make `mlua::Lua: Send + Sync` (adds [`Send`] requirement to `mlua::Function` and `mlua::UserData`)
|
||||||
* `error-send`: make `mlua:Error: Send + Sync`
|
* `error-send`: make `mlua:Error: Send + Sync`
|
||||||
* `serialize`: add serialization and deserialization support to `mlua` types using [serde] framework
|
* `serde`: add serialization and deserialization support to `mlua` types using [serde]
|
||||||
* `macros`: enable procedural macros (such as `chunk!`)
|
* `macros`: enable procedural macros (such as `chunk!`)
|
||||||
* `anyhow`: enable `anyhow::Error` conversion into Lua
|
* `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`
|
* `userdata-wrappers`: opt into `impl UserData` for `Rc<T>`/`Arc<T>`/`Rc<RefCell<T>>`/`Arc<Mutex<T>>` where `T: UserData`
|
||||||
|
|
||||||
|
[5.5]: https://www.lua.org/manual/5.5/manual.html
|
||||||
[5.4]: https://www.lua.org/manual/5.4/manual.html
|
[5.4]: https://www.lua.org/manual/5.4/manual.html
|
||||||
[5.3]: https://www.lua.org/manual/5.3/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.2]: https://www.lua.org/manual/5.2/manual.html
|
||||||
@@ -78,7 +74,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.
|
`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**:
|
**Examples**:
|
||||||
- [HTTP Client](examples/async_http_client.rs)
|
- [HTTP Client](examples/async_http_client.rs)
|
||||||
@@ -93,18 +89,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
|
cargo run --example async_http_client --features=lua54,async,macros
|
||||||
|
|
||||||
# async http client (reqwest)
|
# 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
|
# async http server
|
||||||
cargo run --example async_http_server --features=lua54,async,macros,send
|
cargo run --example async_http_server --features=lua54,async,macros,send
|
||||||
curl -v http://localhost:3000
|
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::Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html
|
||||||
[`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
|
[`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
|
||||||
@@ -114,28 +110,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.
|
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.
|
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 could be more preferable to use a custom lua library.
|
In most cases it works as desired, although sometimes it may be preferable to use a custom Lua library.
|
||||||
To achieve this, mlua supports `LUA_LIB`, `LUA_LIB_NAME` and `LUA_LINK` environment variables.
|
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).
|
`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
|
``` sh
|
||||||
my_project $ LUA_LIB=$HOME/tmp/lua-5.2.4/src LUA_LIB_NAME=lua LUA_LINK=static cargo build
|
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).
|
[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
|
### 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
|
``` toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
mlua = { version = "0.10", features = ["lua54", "vendored"] }
|
mlua = { version = "0.11", features = ["lua54", "vendored"] }
|
||||||
```
|
```
|
||||||
|
|
||||||
`main.rs`
|
`main.rs`
|
||||||
@@ -159,21 +155,21 @@ fn main() -> LuaResult<()> {
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Module mode
|
### Module mode
|
||||||
In a module mode `mlua` allows to create a compiled Lua module that can be loaded from Lua code using [`require`](https://www.lua.org/manual/5.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)
|
[Example](examples/module)
|
||||||
|
|
||||||
Add to `Cargo.toml` :
|
Add to `Cargo.toml`:
|
||||||
|
|
||||||
``` toml
|
``` toml
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
mlua = { version = "0.10", features = ["lua54", "module"] }
|
mlua = { version = "0.11", features = ["lua54", "module"] }
|
||||||
```
|
```
|
||||||
|
|
||||||
`lib.rs` :
|
`lib.rs`:
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
use mlua::prelude::*;
|
use mlua::prelude::*;
|
||||||
@@ -216,14 +212,14 @@ rustflags = [
|
|||||||
```
|
```
|
||||||
On Linux you can build modules normally with `cargo build --release`.
|
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.
|
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
|
### 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:
|
Modules written in Rust and published to luarocks:
|
||||||
- [`decasify`](https://github.com/alerque/decasify)
|
- [`decasify`](https://github.com/alerque/decasify)
|
||||||
@@ -236,10 +232,10 @@ Modules written in Rust and published to luarocks:
|
|||||||
|
|
||||||
## Safety
|
## Safety
|
||||||
|
|
||||||
One of the `mlua` goals is to provide *safe* API between Rust and Lua.
|
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 in any way is protected by `lua_pcall`,
|
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,
|
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.
|
There is overhead associated with this safety.
|
||||||
|
|
||||||
Unfortunately, `mlua` does not provide absolute safety even without using `unsafe` .
|
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!
|
This library contains a huge amount of unsafe code. There are almost certainly bugs still lurking in this library!
|
||||||
@@ -247,8 +243,8 @@ It is surprisingly, fiendishly difficult to use the Lua C API without the potent
|
|||||||
|
|
||||||
## Panic handling
|
## Panic handling
|
||||||
|
|
||||||
`mlua` wraps panics that are generated inside Rust callbacks in a regular Lua error. Panics could be
|
`mlua` wraps panics that are generated inside Rust callbacks in a regular Lua error. Panics can then be
|
||||||
resumed then by returning or propagating the Lua error to Rust code.
|
resumed by returning or propagating the Lua error to Rust code.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
``` rust
|
``` rust
|
||||||
@@ -267,16 +263,16 @@ let _ = lua.load(r#"
|
|||||||
unreachable!()
|
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`
|
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
|
`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
|
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.
|
using panics for general error handling.
|
||||||
|
|
||||||
Below is a list of `mlua` behaviors that should be considered a bug.
|
Below is a list of `mlua` behaviors that should be considered bugs.
|
||||||
If you encounter them, a bug report would be very welcome:
|
If you encounter them, a bug report would be very welcome:
|
||||||
|
|
||||||
+ If you can cause UB with `mlua` without typing the word "unsafe", this is a bug.
|
+ If you can cause UB with `mlua` without typing the word "unsafe", this is a bug.
|
||||||
@@ -289,12 +285,12 @@ If you encounter them, a bug report would be very welcome:
|
|||||||
|
|
||||||
## Sandboxing
|
## 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.org/sandbox
|
[Luau Sandboxing]: https://luau.org/sandbox
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is licensed under the [MIT license](LICENSE)
|
This project is licensed under the [MIT license](LICENSE).
|
||||||
|
|||||||
+55
-1
@@ -1,7 +1,7 @@
|
|||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
|
|
||||||
@@ -128,6 +128,22 @@ fn table_traversal_sequence(c: &mut Criterion) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn table_ref_clone(c: &mut Criterion) {
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
let t = lua.create_table().unwrap();
|
||||||
|
|
||||||
|
c.bench_function("table [ref clone]", |b| {
|
||||||
|
b.iter_batched(
|
||||||
|
|| collect_gc_twice(&lua),
|
||||||
|
|_| {
|
||||||
|
let _t2 = t.clone();
|
||||||
|
},
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn function_create(c: &mut Criterion) {
|
fn function_create(c: &mut Criterion) {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|
||||||
@@ -350,6 +366,42 @@ fn userdata_call_method(c: &mut Criterion) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A userdata method call that goes through an implicit `__index` function
|
||||||
|
fn userdata_call_method_complex(c: &mut Criterion) {
|
||||||
|
struct UserData(u64);
|
||||||
|
impl LuaUserData for UserData {
|
||||||
|
fn register(registry: &mut LuaUserDataRegistry<Self>) {
|
||||||
|
registry.add_field_method_get("val", |_, this| Ok(this.0));
|
||||||
|
registry.add_method_mut("inc_by", |_, this, by: u64| {
|
||||||
|
this.0 += by;
|
||||||
|
Ok(this.0)
|
||||||
|
});
|
||||||
|
|
||||||
|
#[cfg(feature = "luau")]
|
||||||
|
registry.enable_namecall();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let lua = Lua::new();
|
||||||
|
let ud = lua.create_userdata(UserData(0)).unwrap();
|
||||||
|
let inc_by = lua
|
||||||
|
.load("function(ud, s) return ud:inc_by(s) end")
|
||||||
|
.eval::<LuaFunction>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
c.bench_function("userdata [call method complex]", |b| {
|
||||||
|
b.iter_batched(
|
||||||
|
|| {
|
||||||
|
collect_gc_twice(&lua);
|
||||||
|
},
|
||||||
|
|_| {
|
||||||
|
inc_by.call::<()>((&ud, 1)).unwrap();
|
||||||
|
},
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn userdata_async_call_method(c: &mut Criterion) {
|
fn userdata_async_call_method(c: &mut Criterion) {
|
||||||
struct UserData(i64);
|
struct UserData(i64);
|
||||||
impl LuaUserData for UserData {
|
impl LuaUserData for UserData {
|
||||||
@@ -399,6 +451,7 @@ criterion_group! {
|
|||||||
table_traversal_pairs,
|
table_traversal_pairs,
|
||||||
table_traversal_for_each,
|
table_traversal_for_each,
|
||||||
table_traversal_sequence,
|
table_traversal_sequence,
|
||||||
|
table_ref_clone,
|
||||||
|
|
||||||
function_create,
|
function_create,
|
||||||
function_call_sum,
|
function_call_sum,
|
||||||
@@ -413,6 +466,7 @@ criterion_group! {
|
|||||||
userdata_create,
|
userdata_create,
|
||||||
userdata_call_index,
|
userdata_call_index,
|
||||||
userdata_call_method,
|
userdata_call_method,
|
||||||
|
userdata_call_method_complex,
|
||||||
userdata_async_call_method,
|
userdata_async_call_method,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
|
||||||
|
|
||||||
use mlua::prelude::*;
|
use mlua::prelude::*;
|
||||||
|
|
||||||
|
|||||||
@@ -336,7 +336,7 @@ In previous mlua versions, building a Lua module for Windows requires having Lua
|
|||||||
In contrast, on Linux and macOS, modules can be built without any external dependencies using the `-undefined=dynamic_lookup` linker flag.
|
In contrast, on Linux and macOS, modules can be built without any external dependencies using the `-undefined=dynamic_lookup` linker flag.
|
||||||
|
|
||||||
With Rust 1.71+ it's now possible to lift this restriction for Windows as well. You can build modules normally and they will be linked with
|
With Rust 1.71+ it's now possible to lift this restriction for Windows as well. You can build modules normally and they will be linked with
|
||||||
`lua54.dll`/`lua53.dll`/`lua52.dll`/`lua51.dll` depending on the enabled Lua version.
|
`lua5x.dll` depending on the enabled Lua version.
|
||||||
|
|
||||||
You still need to have the dll although, linked to application where the module will be loaded.
|
You still need to have the dll although, linked to application where the module will be loaded.
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use hyper::body::Incoming;
|
|||||||
use hyper_util::client::legacy::Client as HyperClient;
|
use hyper_util::client::legacy::Client as HyperClient;
|
||||||
use hyper_util::rt::TokioExecutor;
|
use hyper_util::rt::TokioExecutor;
|
||||||
|
|
||||||
use mlua::{chunk, ExternalResult, Lua, Result, UserData, UserDataMethods};
|
use mlua::{ExternalResult, Lua, Result, UserData, UserDataMethods, chunk};
|
||||||
|
|
||||||
struct BodyReader(Incoming);
|
struct BodyReader(Incoming);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use mlua::{chunk, ExternalResult, Lua, LuaSerdeExt, Result, Value};
|
use mlua::{ExternalResult, Lua, LuaSerdeExt, Result, Value, chunk};
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use hyper::{Request, Response};
|
|||||||
use hyper_util::rt::TokioIo;
|
use hyper_util::rt::TokioIo;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
|
|
||||||
use mlua::{chunk, Error as LuaError, Function, Lua, String as LuaString, Table, UserData, UserDataMethods};
|
use mlua::{Error as LuaError, Function, Lua, String as LuaString, Table, UserData, UserDataMethods, chunk};
|
||||||
|
|
||||||
/// Wrapper around incoming request that implements UserData
|
/// Wrapper around incoming request that implements UserData
|
||||||
struct LuaRequest(SocketAddr, Request<Incoming>);
|
struct LuaRequest(SocketAddr, Request<Incoming>);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::net::SocketAddr;
|
|||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
use tokio::net::{TcpListener, TcpStream};
|
use tokio::net::{TcpListener, TcpStream};
|
||||||
|
|
||||||
use mlua::{chunk, BString, Function, Lua, UserData, UserDataMethods};
|
use mlua::{BString, Function, Lua, UserData, UserDataMethods, chunk};
|
||||||
|
|
||||||
struct LuaTcpStream(TcpStream);
|
struct LuaTcpStream(TcpStream);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::f32;
|
use std::f32;
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
|
|
||||||
use mlua::{chunk, FromLua, Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Value, Variadic};
|
use mlua::{FromLua, Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Value, Variadic, chunk};
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
// You can create a new Lua state with `Lua::new()`. This loads the default Lua std library
|
// You can create a new Lua state with `Lua::new()`. This loads the default Lua std library
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ crate-type = ["cdylib"]
|
|||||||
[workspace]
|
[workspace]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
lua55 = ["mlua/lua55"]
|
||||||
lua54 = ["mlua/lua54"]
|
lua54 = ["mlua/lua54"]
|
||||||
lua53 = ["mlua/lua53"]
|
lua53 = ["mlua/lua53"]
|
||||||
lua52 = ["mlua/lua52"]
|
lua52 = ["mlua/lua52"]
|
||||||
|
|||||||
+10
-8
@@ -20,14 +20,16 @@ fn main() {
|
|||||||
match lua.load(&line).eval::<MultiValue>() {
|
match lua.load(&line).eval::<MultiValue>() {
|
||||||
Ok(values) => {
|
Ok(values) => {
|
||||||
editor.add_history_entry(line).unwrap();
|
editor.add_history_entry(line).unwrap();
|
||||||
println!(
|
if values.len() > 0 {
|
||||||
"{}",
|
println!(
|
||||||
values
|
"{}",
|
||||||
.iter()
|
values
|
||||||
.map(|value| format!("{:#?}", value))
|
.iter()
|
||||||
.collect::<Vec<_>>()
|
.map(|value| format!("{:#?}", value))
|
||||||
.join("\t")
|
.collect::<Vec<_>>()
|
||||||
);
|
.join("\t")
|
||||||
|
);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Err(Error::SyntaxError {
|
Err(Error::SyntaxError {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use mlua::{chunk, Lua, MetaMethod, Result, UserData};
|
use mlua::{Lua, MetaMethod, Result, UserData, chunk};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct Rectangle {
|
struct Rectangle {
|
||||||
|
|||||||
+10
-7
@@ -1,9 +1,9 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mlua-sys"
|
name = "mlua-sys"
|
||||||
version = "0.7.0"
|
version = "0.10.0"
|
||||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||||
rust-version = "1.71"
|
rust-version = "1.88"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
repository = "https://github.com/mlua-rs/mlua"
|
repository = "https://github.com/mlua-rs/mlua"
|
||||||
documentation = "https://docs.rs/mlua-sys"
|
documentation = "https://docs.rs/mlua-sys"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@@ -12,14 +12,15 @@ license = "MIT"
|
|||||||
links = "lua"
|
links = "lua"
|
||||||
build = "build/main.rs"
|
build = "build/main.rs"
|
||||||
description = """
|
description = """
|
||||||
Low level (FFI) bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Luau
|
Low level (FFI) bindings to Lua 5.5/5.4/5.3/5.2/5.1 (including LuaJIT) and Luau
|
||||||
"""
|
"""
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = ["lua54", "vendored"]
|
features = ["lua55", "vendored"]
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
lua55 = []
|
||||||
lua54 = []
|
lua54 = []
|
||||||
lua53 = []
|
lua53 = []
|
||||||
lua52 = []
|
lua52 = []
|
||||||
@@ -30,17 +31,19 @@ luau = ["luau0-src"]
|
|||||||
luau-codegen = ["luau"]
|
luau-codegen = ["luau"]
|
||||||
luau-vector4 = ["luau"]
|
luau-vector4 = ["luau"]
|
||||||
vendored = ["lua-src", "luajit-src"]
|
vendored = ["lua-src", "luajit-src"]
|
||||||
|
external = []
|
||||||
module = []
|
module = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
libc = "0.2"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cc = "1.0"
|
cc = "1.0"
|
||||||
cfg-if = "1.0"
|
cfg-if = "1.0"
|
||||||
pkg-config = "0.3.17"
|
pkg-config = "0.3.17"
|
||||||
lua-src = { version = ">= 547.1.0, < 547.2.0", optional = true }
|
lua-src = { version = ">= 550.0.0, < 550.1.0", optional = true }
|
||||||
luajit-src = { version = ">= 210.6.0, < 210.7.0", optional = true }
|
luajit-src = { version = ">= 210.6.0, < 210.7.0", optional = true }
|
||||||
luau0-src = { version = "0.14.2", optional = true }
|
luau0-src = { version = "0.18.0", optional = true }
|
||||||
|
|
||||||
[lints.rust]
|
[lints.rust]
|
||||||
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(raw_dylib)'] }
|
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(raw_dylib)'] }
|
||||||
|
|||||||
+2
-1
@@ -1,8 +1,9 @@
|
|||||||
# mlua-sys
|
# mlua-sys
|
||||||
|
|
||||||
Low level (FFI) bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and [Luau].
|
Low level (FFI) bindings to Lua 5.5/5.4/5.3/5.2/5.1 (including [LuaJIT]) and [Luau].
|
||||||
|
|
||||||
Intended to be consumed by the [mlua] crate.
|
Intended to be consumed by the [mlua] crate.
|
||||||
|
|
||||||
|
[LuaJIT]: https://github.com/LuaJIT/LuaJIT
|
||||||
[Luau]: https://github.com/luau-lang/luau
|
[Luau]: https://github.com/luau-lang/luau
|
||||||
[mlua]: https://crates.io/crates/mlua
|
[mlua]: https://crates.io/crates/mlua
|
||||||
|
|||||||
@@ -31,18 +31,16 @@ pub fn probe_lua() {
|
|||||||
|
|
||||||
// Find using `pkg-config`
|
// Find using `pkg-config`
|
||||||
|
|
||||||
|
#[cfg(feature = "lua55")]
|
||||||
|
let (incl_bound, excl_bound, alt_probe, ver) = ("5.5", "5.6", ["lua5.5", "lua-5.5", "lua55"], "5.5");
|
||||||
#[cfg(feature = "lua54")]
|
#[cfg(feature = "lua54")]
|
||||||
let (incl_bound, excl_bound, alt_probe, ver) =
|
let (incl_bound, excl_bound, alt_probe, ver) = ("5.4", "5.5", ["lua5.4", "lua-5.4", "lua54"], "5.4");
|
||||||
("5.4", "5.5", ["lua5.4", "lua-5.4", "lua54"], "5.4");
|
|
||||||
#[cfg(feature = "lua53")]
|
#[cfg(feature = "lua53")]
|
||||||
let (incl_bound, excl_bound, alt_probe, ver) =
|
let (incl_bound, excl_bound, alt_probe, ver) = ("5.3", "5.4", ["lua5.3", "lua-5.3", "lua53"], "5.3");
|
||||||
("5.3", "5.4", ["lua5.3", "lua-5.3", "lua53"], "5.3");
|
|
||||||
#[cfg(feature = "lua52")]
|
#[cfg(feature = "lua52")]
|
||||||
let (incl_bound, excl_bound, alt_probe, ver) =
|
let (incl_bound, excl_bound, alt_probe, ver) = ("5.2", "5.3", ["lua5.2", "lua-5.2", "lua52"], "5.2");
|
||||||
("5.2", "5.3", ["lua5.2", "lua-5.2", "lua52"], "5.2");
|
|
||||||
#[cfg(feature = "lua51")]
|
#[cfg(feature = "lua51")]
|
||||||
let (incl_bound, excl_bound, alt_probe, ver) =
|
let (incl_bound, excl_bound, alt_probe, ver) = ("5.1", "5.2", ["lua5.1", "lua-5.1", "lua51"], "5.1");
|
||||||
("5.1", "5.2", ["lua5.1", "lua-5.1", "lua51"], "5.1");
|
|
||||||
#[cfg(feature = "luajit")]
|
#[cfg(feature = "luajit")]
|
||||||
let (incl_bound, excl_bound, alt_probe, ver) = ("2.0.4", "2.2", [], "JIT");
|
let (incl_bound, excl_bound, alt_probe, ver) = ("2.0.4", "2.2", [], "JIT");
|
||||||
|
|
||||||
@@ -54,9 +52,7 @@ pub fn probe_lua() {
|
|||||||
|
|
||||||
if lua.is_err() {
|
if lua.is_err() {
|
||||||
for pkg in alt_probe {
|
for pkg in alt_probe {
|
||||||
lua = pkg_config::Config::new()
|
lua = pkg_config::Config::new().cargo_metadata(true).probe(pkg);
|
||||||
.cargo_metadata(true)
|
|
||||||
.probe(pkg);
|
|
||||||
|
|
||||||
if lua.is_ok() {
|
if lua.is_ok() {
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
pub fn probe_lua() {
|
pub fn probe_lua() {
|
||||||
|
#[cfg(feature = "lua55")]
|
||||||
|
let artifacts = lua_src::Build::new().build(lua_src::Lua55);
|
||||||
|
|
||||||
#[cfg(feature = "lua54")]
|
#[cfg(feature = "lua54")]
|
||||||
let artifacts = lua_src::Build::new().build(lua_src::Lua54);
|
let artifacts = lua_src::Build::new().build(lua_src::Lua54);
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,21 @@
|
|||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(all(feature = "lua54", not(any(feature = "lua53", feature = "lua52", feature = "lua51", feature = "luajit", feature = "luau"))))] {
|
if #[cfg(all(feature = "lua55", not(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "lua51", feature = "luajit", feature = "luau"))))] {
|
||||||
include!("main_inner.rs");
|
include!("main_inner.rs");
|
||||||
} else if #[cfg(all(feature = "lua53", not(any(feature = "lua54", feature = "lua52", feature = "lua51", feature = "luajit", feature = "luau"))))] {
|
} else if #[cfg(all(feature = "lua54", not(any(feature = "lua55", feature = "lua53", feature = "lua52", feature = "lua51", feature = "luajit", feature = "luau"))))] {
|
||||||
include!("main_inner.rs");
|
include!("main_inner.rs");
|
||||||
} else if #[cfg(all(feature = "lua52", not(any(feature = "lua54", feature = "lua53", feature = "lua51", feature = "luajit", feature = "luau"))))] {
|
} else if #[cfg(all(feature = "lua53", not(any(feature = "lua55", feature = "lua54", feature = "lua52", feature = "lua51", feature = "luajit", feature = "luau"))))] {
|
||||||
include!("main_inner.rs");
|
include!("main_inner.rs");
|
||||||
} else if #[cfg(all(feature = "lua51", not(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit", feature = "luau"))))] {
|
} else if #[cfg(all(feature = "lua52", not(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua51", feature = "luajit", feature = "luau"))))] {
|
||||||
include!("main_inner.rs");
|
include!("main_inner.rs");
|
||||||
} else if #[cfg(all(feature = "luajit", not(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "lua51", feature = "luau"))))] {
|
} else if #[cfg(all(feature = "lua51", not(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit", feature = "luau"))))] {
|
||||||
include!("main_inner.rs");
|
include!("main_inner.rs");
|
||||||
} else if #[cfg(all(feature = "luau", not(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "lua51", feature = "luajit"))))] {
|
} else if #[cfg(all(feature = "luajit", not(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52", feature = "lua51", feature = "luau"))))] {
|
||||||
|
include!("main_inner.rs");
|
||||||
|
} else if #[cfg(all(feature = "luau", not(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52", feature = "lua51", feature = "luajit"))))] {
|
||||||
include!("main_inner.rs");
|
include!("main_inner.rs");
|
||||||
} else {
|
} else {
|
||||||
fn main() {
|
fn main() {
|
||||||
compile_error!("You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau");
|
compile_error!("You can enable only one of the features: lua55, lua54, lua53, lua52, lua51, luajit, luajit52, luau");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,22 +14,29 @@ fn main() {
|
|||||||
#[cfg(all(feature = "luau", feature = "module", windows))]
|
#[cfg(all(feature = "luau", feature = "module", windows))]
|
||||||
compile_error!("Luau does not support `module` mode on Windows");
|
compile_error!("Luau does not support `module` mode on Windows");
|
||||||
|
|
||||||
#[cfg(all(feature = "module", feature = "vendored"))]
|
#[cfg(any(
|
||||||
compile_error!("`vendored` and `module` features are mutually exclusive");
|
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");
|
println!("cargo:rerun-if-changed=build");
|
||||||
|
|
||||||
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
|
// Check if compilation and linking is handled by external crate
|
||||||
if target_os == "windows" && cfg!(feature = "module") {
|
if cfg!(not(feature = "external")) {
|
||||||
if !std::env::var("LUA_LIB_NAME").unwrap_or_default().is_empty() {
|
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||||
// Don't use raw-dylib linking
|
if target_os == "windows" && cfg!(feature = "module") {
|
||||||
find::probe_lua();
|
if !std::env::var("LUA_LIB_NAME").unwrap_or_default().is_empty() {
|
||||||
return;
|
// 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();
|
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-22
@@ -1,4 +1,4 @@
|
|||||||
//! Low level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Luau.
|
//! Low level bindings to Lua 5.5/5.4/5.3/5.2/5.1 (including LuaJIT) and Luau.
|
||||||
|
|
||||||
#![allow(non_camel_case_types, non_snake_case)]
|
#![allow(non_camel_case_types, non_snake_case)]
|
||||||
#![allow(clippy::missing_safety_doc)]
|
#![allow(clippy::missing_safety_doc)]
|
||||||
@@ -8,6 +8,9 @@
|
|||||||
|
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
|
#[cfg(any(feature = "lua55", doc))]
|
||||||
|
pub use lua55::*;
|
||||||
|
|
||||||
#[cfg(any(feature = "lua54", doc))]
|
#[cfg(any(feature = "lua54", doc))]
|
||||||
pub use lua54::*;
|
pub use lua54::*;
|
||||||
|
|
||||||
@@ -23,7 +26,7 @@ pub use lua51::*;
|
|||||||
#[cfg(any(feature = "luau", doc))]
|
#[cfg(any(feature = "luau", doc))]
|
||||||
pub use luau::*;
|
pub use luau::*;
|
||||||
|
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub const LUA_MAX_UPVALUES: c_int = 255;
|
pub const LUA_MAX_UPVALUES: c_int = 255;
|
||||||
|
|
||||||
@@ -40,14 +43,22 @@ pub const LUA_MAX_UPVALUES: c_int = 200;
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub const LUA_TRACEBACK_STACK: c_int = 11;
|
pub const LUA_TRACEBACK_STACK: c_int = 11;
|
||||||
|
|
||||||
// Copied from https://github.com/rust-lang/rust/blob/master/library/std/src/sys/pal/common/alloc.rs
|
// The minimum alignment guaranteed by the architecture.
|
||||||
// The minimum alignment guaranteed by the architecture. This value is used to
|
// Copied from https://github.com/rust-lang/rust/blob/main/library/std/src/sys/alloc/mod.rs
|
||||||
// add fast paths for low alignment values.
|
#[doc(hidden)]
|
||||||
#[cfg(any(
|
#[rustfmt::skip]
|
||||||
|
pub const SYS_MIN_ALIGN: usize = if cfg!(any(
|
||||||
|
all(target_arch = "riscv32", any(target_os = "espidf", target_os = "zkvm")),
|
||||||
|
all(target_arch = "xtensa", target_os = "espidf"),
|
||||||
|
)) {
|
||||||
|
// The allocator on the esp-idf and zkvm platforms guarantees 4 byte alignment.
|
||||||
|
4
|
||||||
|
} else if cfg!(any(
|
||||||
target_arch = "x86",
|
target_arch = "x86",
|
||||||
target_arch = "arm",
|
target_arch = "arm",
|
||||||
target_arch = "m68k",
|
target_arch = "m68k",
|
||||||
target_arch = "csky",
|
target_arch = "csky",
|
||||||
|
target_arch = "loongarch32",
|
||||||
target_arch = "mips",
|
target_arch = "mips",
|
||||||
target_arch = "mips32r6",
|
target_arch = "mips32r6",
|
||||||
target_arch = "powerpc",
|
target_arch = "powerpc",
|
||||||
@@ -55,12 +66,11 @@ pub const LUA_TRACEBACK_STACK: c_int = 11;
|
|||||||
target_arch = "sparc",
|
target_arch = "sparc",
|
||||||
target_arch = "wasm32",
|
target_arch = "wasm32",
|
||||||
target_arch = "hexagon",
|
target_arch = "hexagon",
|
||||||
all(target_arch = "riscv32", not(any(target_os = "espidf", target_os = "zkvm"))),
|
target_arch = "riscv32",
|
||||||
all(target_arch = "xtensa", not(target_os = "espidf")),
|
target_arch = "xtensa",
|
||||||
))]
|
)) {
|
||||||
#[doc(hidden)]
|
8
|
||||||
pub const SYS_MIN_ALIGN: usize = 8;
|
} else if cfg!(any(
|
||||||
#[cfg(any(
|
|
||||||
target_arch = "x86_64",
|
target_arch = "x86_64",
|
||||||
target_arch = "aarch64",
|
target_arch = "aarch64",
|
||||||
target_arch = "arm64ec",
|
target_arch = "arm64ec",
|
||||||
@@ -71,20 +81,19 @@ pub const SYS_MIN_ALIGN: usize = 8;
|
|||||||
target_arch = "sparc64",
|
target_arch = "sparc64",
|
||||||
target_arch = "riscv64",
|
target_arch = "riscv64",
|
||||||
target_arch = "wasm64",
|
target_arch = "wasm64",
|
||||||
))]
|
)) {
|
||||||
#[doc(hidden)]
|
16
|
||||||
pub const SYS_MIN_ALIGN: usize = 16;
|
} else {
|
||||||
// The allocator on the esp-idf and zkvm platforms guarantee 4 byte alignment.
|
panic!("no value for SYS_MIN_ALIGN")
|
||||||
#[cfg(any(
|
};
|
||||||
all(target_arch = "riscv32", any(target_os = "espidf", target_os = "zkvm")),
|
|
||||||
all(target_arch = "xtensa", target_os = "espidf"),
|
|
||||||
))]
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub const SYS_MIN_ALIGN: usize = 4;
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
|
|
||||||
|
#[cfg(any(feature = "lua55", doc))]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "lua55")))]
|
||||||
|
pub mod lua55;
|
||||||
|
|
||||||
#[cfg(any(feature = "lua54", doc))]
|
#[cfg(any(feature = "lua54", doc))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
||||||
pub mod lua54;
|
pub mod lua54;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
//!
|
//!
|
||||||
//! Based on github.com/keplerproject/lua-compat-5.3
|
//! Based on github.com/keplerproject/lua-compat-5.3
|
||||||
|
|
||||||
|
use std::ffi::CStr;
|
||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
use std::{mem, ptr};
|
use std::{mem, ptr};
|
||||||
|
|
||||||
@@ -20,8 +21,8 @@ unsafe fn compat53_reverse(L: *mut lua_State, mut a: c_int, mut b: c_int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const COMPAT53_LEVELS1: c_int = 12; // size of the first part of the stack
|
const COMPAT53_LEVELS1: c_int = 10; // size of the first part of the stack
|
||||||
const COMPAT53_LEVELS2: c_int = 10; // size of the second part of the stack
|
const COMPAT53_LEVELS2: c_int = 11; // size of the second part of the stack
|
||||||
|
|
||||||
unsafe fn compat53_countlevels(L: *mut lua_State) -> c_int {
|
unsafe fn compat53_countlevels(L: *mut lua_State) -> c_int {
|
||||||
let mut ar: lua_Debug = mem::zeroed();
|
let mut ar: lua_Debug = mem::zeroed();
|
||||||
@@ -54,11 +55,7 @@ unsafe fn compat53_checkmode(
|
|||||||
while *st != 0 && *st != c {
|
while *st != 0 && *st != c {
|
||||||
st = st.offset(1);
|
st = st.offset(1);
|
||||||
}
|
}
|
||||||
if *st == c {
|
if *st == c { st } else { ptr::null() }
|
||||||
st
|
|
||||||
} else {
|
|
||||||
ptr::null()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !mode.is_null() && strchr(mode, *modename).is_null() {
|
if !mode.is_null() && strchr(mode, *modename).is_null() {
|
||||||
@@ -88,11 +85,10 @@ unsafe fn compat53_findfield(L: *mut lua_State, objidx: c_int, level: c_int) ->
|
|||||||
lua_pop(L, 1); // remove value (but keep name)
|
lua_pop(L, 1); // remove value (but keep name)
|
||||||
return 1;
|
return 1;
|
||||||
} else if compat53_findfield(L, objidx, level - 1) != 0 {
|
} else if compat53_findfield(L, objidx, level - 1) != 0 {
|
||||||
// try recursively
|
// stack: lib_name, lib_table, field_name (top)
|
||||||
lua_remove(L, -2); // remove table (but keep name)
|
lua_pushliteral(L, c"."); // place '.' between the two names
|
||||||
lua_pushliteral(L, c".");
|
lua_replace(L, -3); // (in the slot occupied by table)
|
||||||
lua_insert(L, -2); // place '.' between the two names
|
lua_concat(L, 3); // lib_name.field_name
|
||||||
lua_concat(L, 3);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,13 +97,20 @@ unsafe fn compat53_findfield(L: *mut lua_State, objidx: c_int, level: c_int) ->
|
|||||||
0 // not found
|
0 // not found
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn compat53_pushglobalfuncname(L: *mut lua_State, ar: *mut lua_Debug) -> c_int {
|
unsafe fn compat53_pushglobalfuncname(L: *mut lua_State, L1: *mut lua_State, ar: *mut lua_Debug) -> c_int {
|
||||||
let top = lua_gettop(L);
|
let top = lua_gettop(L);
|
||||||
lua_getinfo(L, cstr!("f"), ar); // push function
|
lua_getinfo(L1, cstr!("f"), ar); // push function
|
||||||
|
lua_xmove(L1, L, 1); // and move onto L
|
||||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||||
|
luaL_checkstack(L, 6, cstr!("not enough stack")); // slots for 'findfield'
|
||||||
if compat53_findfield(L, top + 1, 2) != 0 {
|
if compat53_findfield(L, top + 1, 2) != 0 {
|
||||||
|
let name = lua_tostring(L, -1);
|
||||||
|
if CStr::from_ptr(name).to_bytes().starts_with(b"_G.") {
|
||||||
|
lua_pushstring(L, name.add(3)); // push name without prefix
|
||||||
|
lua_remove(L, -2); // remove original name
|
||||||
|
}
|
||||||
lua_copy(L, -1, top + 1); // move name to proper place
|
lua_copy(L, -1, top + 1); // move name to proper place
|
||||||
lua_pop(L, 2); // remove pushed values
|
lua_settop(L, top + 1); // remove pushed values
|
||||||
1
|
1
|
||||||
} else {
|
} else {
|
||||||
lua_settop(L, top); // remove function and global table
|
lua_settop(L, top); // remove function and global table
|
||||||
@@ -115,27 +118,23 @@ unsafe fn compat53_pushglobalfuncname(L: *mut lua_State, ar: *mut lua_Debug) ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn compat53_pushfuncname(L: *mut lua_State, ar: *mut lua_Debug) {
|
unsafe fn compat53_pushfuncname(L: *mut lua_State, L1: *mut lua_State, ar: *mut lua_Debug) {
|
||||||
if *(*ar).namewhat != b'\0' as c_char {
|
// try first a global name
|
||||||
// is there a name?
|
if compat53_pushglobalfuncname(L, L1, ar) != 0 {
|
||||||
lua_pushfstring(L, cstr!("function '%s'"), (*ar).name);
|
lua_pushfstring(L, cstr!("function '%s'"), lua_tostring(L, -1));
|
||||||
|
lua_remove(L, -2); // remove name
|
||||||
|
} else if *(*ar).namewhat != b'\0' as c_char {
|
||||||
|
// use name from code
|
||||||
|
lua_pushfstring(L, cstr!("%s '%s'"), (*ar).namewhat, (*ar).name);
|
||||||
} else if *(*ar).what == b'm' as c_char {
|
} else if *(*ar).what == b'm' as c_char {
|
||||||
// main?
|
// main?
|
||||||
lua_pushliteral(L, c"main chunk");
|
lua_pushliteral(L, c"main chunk");
|
||||||
} else if *(*ar).what == b'C' as c_char {
|
} else if *(*ar).what != b'C' as c_char {
|
||||||
if compat53_pushglobalfuncname(L, ar) != 0 {
|
// for Lua functions, use <file:line>
|
||||||
lua_pushfstring(L, cstr!("function '%s'"), lua_tostring(L, -1));
|
let short_src = (*ar).short_src.as_ptr();
|
||||||
lua_remove(L, -2); // remove name
|
lua_pushfstring(L, cstr!("function <%s:%d>"), short_src, (*ar).linedefined);
|
||||||
} else {
|
|
||||||
lua_pushliteral(L, c"?");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
lua_pushfstring(
|
lua_pushliteral(L, c"?");
|
||||||
L,
|
|
||||||
cstr!("function <%s:%d>"),
|
|
||||||
(*ar).short_src.as_ptr(),
|
|
||||||
(*ar).linedefined,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +185,8 @@ pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
|
|||||||
if lua_type(L, idx) == LUA_TNUMBER {
|
if lua_type(L, idx) == LUA_TNUMBER {
|
||||||
let n = lua_tonumber(L, idx);
|
let n = lua_tonumber(L, idx);
|
||||||
let i = lua_tointeger(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;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -458,32 +458,36 @@ pub unsafe fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const
|
|||||||
let mut ar: lua_Debug = mem::zeroed();
|
let mut ar: lua_Debug = mem::zeroed();
|
||||||
let top = lua_gettop(L);
|
let top = lua_gettop(L);
|
||||||
let numlevels = compat53_countlevels(L1);
|
let numlevels = compat53_countlevels(L1);
|
||||||
let mark = if numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2 {
|
#[rustfmt::skip]
|
||||||
COMPAT53_LEVELS1
|
let mut limit = if numlevels - level > COMPAT53_LEVELS1 + COMPAT53_LEVELS2 { COMPAT53_LEVELS1 } else { -1 };
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
if !msg.is_null() {
|
if !msg.is_null() {
|
||||||
lua_pushfstring(L, cstr!("%s\n"), msg);
|
lua_pushfstring(L, cstr!("%s\n"), msg);
|
||||||
}
|
}
|
||||||
lua_pushliteral(L, c"stack traceback:");
|
lua_pushliteral(L, c"stack traceback:");
|
||||||
while lua_getstack(L1, level, &mut ar) != 0 {
|
while lua_getstack(L1, level, &mut ar) != 0 {
|
||||||
level += 1;
|
if limit == 0 {
|
||||||
if level == mark {
|
|
||||||
// too many levels?
|
// too many levels?
|
||||||
lua_pushliteral(L, c"\n\t..."); // add a '...'
|
let n = numlevels - level - COMPAT53_LEVELS2;
|
||||||
level = numlevels - COMPAT53_LEVELS2; // and skip to last ones
|
// add warning about skip ("n + 1" because we skip current level too)
|
||||||
|
lua_pushfstring(L, cstr!("\n\t...\t(skipping %d levels)"), n + 1); // add warning about skip
|
||||||
|
level += n; // and skip to last levels
|
||||||
} else {
|
} else {
|
||||||
lua_getinfo(L1, cstr!("Slnt"), &mut ar);
|
lua_getinfo(L1, cstr!("Sln"), &mut ar);
|
||||||
lua_pushfstring(L, cstr!("\n\t%s:"), ar.short_src.as_ptr());
|
if *ar.what != b't' as c_char {
|
||||||
if ar.currentline > 0 {
|
if ar.currentline <= 0 {
|
||||||
lua_pushfstring(L, cstr!("%d:"), ar.currentline);
|
lua_pushfstring(L, cstr!("\n\t%s: in "), ar.short_src.as_ptr());
|
||||||
|
} else {
|
||||||
|
lua_pushfstring(L, cstr!("\n\t%s:%d: in "), ar.short_src.as_ptr(), ar.currentline);
|
||||||
|
}
|
||||||
|
compat53_pushfuncname(L, L1, &mut ar);
|
||||||
|
lua_concat(L, lua_gettop(L) - top);
|
||||||
|
} else {
|
||||||
|
lua_pushstring(L, cstr!("\n\t(...tail calls...)"));
|
||||||
}
|
}
|
||||||
lua_pushliteral(L, c" in ");
|
|
||||||
compat53_pushfuncname(L, &mut ar);
|
|
||||||
lua_concat(L, lua_gettop(L) - top);
|
|
||||||
}
|
}
|
||||||
|
level += 1;
|
||||||
|
limit -= 1;
|
||||||
}
|
}
|
||||||
lua_concat(L, lua_gettop(L) - top);
|
lua_concat(L, lua_gettop(L) - top);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,8 +107,6 @@ pub unsafe fn luaL_optstring(L: *mut lua_State, n: c_int, d: *const c_char) -> *
|
|||||||
luaL_optlstring(L, n, d, ptr::null_mut())
|
luaL_optlstring(L, n, d, ptr::null_mut())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated from 5.3: luaL_checkint, luaL_optint, luaL_checklong, luaL_optlong
|
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn luaL_typename(L: *mut lua_State, i: c_int) -> *const c_char {
|
pub unsafe fn luaL_typename(L: *mut lua_State, i: c_int) -> *const c_char {
|
||||||
lua::lua_typename(L, lua::lua_type(L, i))
|
lua::lua_typename(L, lua::lua_type(L, i))
|
||||||
@@ -138,8 +136,62 @@ pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) {
|
|||||||
lua::lua_getfield_(L, lua::LUA_REGISTRYINDEX, n);
|
lua::lua_getfield_(L, lua::LUA_REGISTRYINDEX, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: luaL_opt
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_opt<T>(
|
||||||
|
L: *mut lua_State,
|
||||||
|
f: unsafe extern "C-unwind" fn(*mut lua_State, c_int) -> T,
|
||||||
|
n: c_int,
|
||||||
|
d: T,
|
||||||
|
) -> T {
|
||||||
|
if lua::lua_isnoneornil(L, n) != 0 {
|
||||||
|
d
|
||||||
|
} else {
|
||||||
|
f(L, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// TODO: Generic Buffer Manipulation
|
// Generic Buffer Manipulation
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
const BUFSIZ: usize = 1024; // WASI libc's BUFSIZ is 1024
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
const BUFSIZ: usize = libc::BUFSIZ as usize;
|
||||||
|
|
||||||
|
// The buffer size used by the lauxlib buffer system.
|
||||||
|
// The "16384" workaround is taken from the LuaJIT source code.
|
||||||
|
pub const LUAL_BUFFERSIZE: usize = if BUFSIZ > 16384 { 8192 } else { BUFSIZ };
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct luaL_Buffer {
|
||||||
|
pub p: *mut c_char, // current position in buffer
|
||||||
|
pub lvl: c_int, // number of strings in the stack
|
||||||
|
pub L: *mut lua_State,
|
||||||
|
pub buffer: [c_char; LUAL_BUFFERSIZE],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
|
||||||
|
unsafe extern "C-unwind" {
|
||||||
|
pub fn luaL_buffinit(L: *mut lua_State, B: *mut luaL_Buffer);
|
||||||
|
pub fn luaL_prepbuffer(B: *mut luaL_Buffer) -> *mut c_char;
|
||||||
|
pub fn luaL_addlstring(B: *mut luaL_Buffer, s: *const c_char, l: usize);
|
||||||
|
pub fn luaL_addstring(B: *mut luaL_Buffer, s: *const c_char);
|
||||||
|
pub fn luaL_addvalue(B: *mut luaL_Buffer);
|
||||||
|
pub fn luaL_pushresult(B: *mut luaL_Buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_addchar(B: *mut luaL_Buffer, c: c_char) {
|
||||||
|
let buffer_end = (*B).buffer.as_mut_ptr().add(LUAL_BUFFERSIZE);
|
||||||
|
if (*B).p >= buffer_end {
|
||||||
|
luaL_prepbuffer(B);
|
||||||
|
}
|
||||||
|
*(*B).p = c;
|
||||||
|
(*B).p = (*B).p.add(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_addsize(B: *mut luaL_Buffer, n: usize) {
|
||||||
|
(*B).p = (*B).p.add(n);
|
||||||
|
}
|
||||||
|
|||||||
@@ -270,7 +270,10 @@ pub unsafe fn lua_pushcfunction(L: *mut lua_State, f: lua_CFunction) {
|
|||||||
lua_pushcclosure(L, f, 0)
|
lua_pushcclosure(L, f, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: lua_strlen
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_strlen(L: *mut lua_State, i: c_int) -> usize {
|
||||||
|
lua_objlen(L, i)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn lua_isfunction(L: *mut lua_State, n: c_int) -> c_int {
|
pub unsafe fn lua_isfunction(L: *mut lua_State, n: c_int) -> 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 {
|
if lua_type(L, idx) == LUA_TNUMBER {
|
||||||
let n = lua_tonumber(L, idx);
|
let n = lua_tonumber(L, idx);
|
||||||
let i = lua_tointeger(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;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,7 +125,7 @@ pub unsafe fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int {
|
pub unsafe fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int {
|
||||||
let n = n.try_into().expect("cannot convert index to lua_Integer");
|
let n = n.try_into().expect("cannot convert index to c_int");
|
||||||
lua_rawgeti_(L, idx, n);
|
lua_rawgeti_(L, idx, n);
|
||||||
lua_type(L, -1)
|
lua_type(L, -1)
|
||||||
}
|
}
|
||||||
@@ -152,7 +153,7 @@ pub unsafe fn lua_seti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer) {
|
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");
|
let n = n.try_into().expect("cannot convert index to c_int");
|
||||||
lua_rawseti_(L, idx, n)
|
lua_rawseti_(L, idx, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ unsafe extern "C-unwind" {
|
|||||||
pub fn luaL_argerror(L: *mut lua_State, arg: c_int, extramsg: *const c_char) -> c_int;
|
pub fn luaL_argerror(L: *mut lua_State, arg: c_int, extramsg: *const c_char) -> c_int;
|
||||||
pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char;
|
pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char;
|
||||||
pub fn luaL_optlstring(L: *mut lua_State, arg: c_int, def: *const c_char, l: *mut usize)
|
pub fn luaL_optlstring(L: *mut lua_State, arg: c_int, def: *const c_char, l: *mut usize)
|
||||||
-> *const c_char;
|
-> *const c_char;
|
||||||
pub fn luaL_checknumber(L: *mut lua_State, arg: c_int) -> lua_Number;
|
pub fn luaL_checknumber(L: *mut lua_State, arg: c_int) -> lua_Number;
|
||||||
pub fn luaL_optnumber(L: *mut lua_State, arg: c_int, def: lua_Number) -> lua_Number;
|
pub fn luaL_optnumber(L: *mut lua_State, arg: c_int, def: lua_Number) -> lua_Number;
|
||||||
pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer;
|
pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer;
|
||||||
@@ -166,13 +166,76 @@ pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) {
|
|||||||
lua::lua_getfield_(L, lua::LUA_REGISTRYINDEX, n);
|
lua::lua_getfield_(L, lua::LUA_REGISTRYINDEX, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
// luaL_opt would be implemented here but it is undocumented, so it's omitted
|
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn luaL_loadbuffer(L: *mut lua_State, s: *const c_char, sz: usize, n: *const c_char) -> c_int {
|
pub unsafe fn luaL_loadbuffer(L: *mut lua_State, s: *const c_char, sz: usize, n: *const c_char) -> c_int {
|
||||||
luaL_loadbufferx(L, s, sz, n, ptr::null())
|
luaL_loadbufferx(L, s, sz, n, ptr::null())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_opt<T>(
|
||||||
|
L: *mut lua_State,
|
||||||
|
f: unsafe extern "C-unwind" fn(*mut lua_State, c_int) -> T,
|
||||||
|
n: c_int,
|
||||||
|
d: T,
|
||||||
|
) -> T {
|
||||||
|
if lua::lua_isnoneornil(L, n) != 0 {
|
||||||
|
d
|
||||||
|
} else {
|
||||||
|
f(L, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// TODO: Generic Buffer Manipulation
|
// Generic Buffer Manipulation
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
const BUFSIZ: usize = 1024; // WASI libc's BUFSIZ is 1024
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
const BUFSIZ: usize = libc::BUFSIZ as usize;
|
||||||
|
|
||||||
|
// The buffer size used by the lauxlib buffer system.
|
||||||
|
// The "16384" workaround is taken from the LuaJIT source code.
|
||||||
|
pub const LUAL_BUFFERSIZE: usize = if BUFSIZ > 16384 { 8192 } else { BUFSIZ };
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct luaL_Buffer {
|
||||||
|
pub b: *mut c_char, // buffer address
|
||||||
|
pub size: usize, // buffer size
|
||||||
|
pub n: usize, // number of characters in buffer
|
||||||
|
pub L: *mut lua_State,
|
||||||
|
pub initb: [c_char; LUAL_BUFFERSIZE], // initial buffer space
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
|
||||||
|
unsafe extern "C-unwind" {
|
||||||
|
pub fn luaL_buffinit(L: *mut lua_State, B: *mut luaL_Buffer);
|
||||||
|
pub fn luaL_prepbuffsize(B: *mut luaL_Buffer, sz: usize) -> *mut c_char;
|
||||||
|
pub fn luaL_addlstring(B: *mut luaL_Buffer, s: *const c_char, l: usize);
|
||||||
|
pub fn luaL_addstring(B: *mut luaL_Buffer, s: *const c_char);
|
||||||
|
pub fn luaL_addvalue(B: *mut luaL_Buffer);
|
||||||
|
pub fn luaL_pushresult(B: *mut luaL_Buffer);
|
||||||
|
pub fn luaL_pushresultsize(B: *mut luaL_Buffer, sz: usize);
|
||||||
|
pub fn luaL_buffinitsize(L: *mut lua_State, B: *mut luaL_Buffer, sz: usize) -> *mut c_char;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Macro implementations as inline functions
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_prepbuffer(B: *mut luaL_Buffer) -> *mut c_char {
|
||||||
|
luaL_prepbuffsize(B, LUAL_BUFFERSIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_addchar(B: *mut luaL_Buffer, c: c_char) {
|
||||||
|
if (*B).n >= (*B).size {
|
||||||
|
luaL_prepbuffsize(B, 1);
|
||||||
|
}
|
||||||
|
*(*B).b.add((*B).n) = c;
|
||||||
|
(*B).n += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_addsize(B: *mut luaL_Buffer, n: usize) {
|
||||||
|
(*B).n += n;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! Contains definitions from `lauxlib.h`.
|
//! Contains definitions from `lauxlib.h`.
|
||||||
|
|
||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
use std::ptr;
|
use std::{mem, ptr};
|
||||||
|
|
||||||
use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State};
|
use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State};
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ unsafe extern "C-unwind" {
|
|||||||
pub fn luaL_argerror(L: *mut lua_State, arg: c_int, extramsg: *const c_char) -> c_int;
|
pub fn luaL_argerror(L: *mut lua_State, arg: c_int, extramsg: *const c_char) -> c_int;
|
||||||
pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char;
|
pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char;
|
||||||
pub fn luaL_optlstring(L: *mut lua_State, arg: c_int, def: *const c_char, l: *mut usize)
|
pub fn luaL_optlstring(L: *mut lua_State, arg: c_int, def: *const c_char, l: *mut usize)
|
||||||
-> *const c_char;
|
-> *const c_char;
|
||||||
pub fn luaL_checknumber(L: *mut lua_State, arg: c_int) -> lua_Number;
|
pub fn luaL_checknumber(L: *mut lua_State, arg: c_int) -> lua_Number;
|
||||||
pub fn luaL_optnumber(L: *mut lua_State, arg: c_int, def: lua_Number) -> lua_Number;
|
pub fn luaL_optnumber(L: *mut lua_State, arg: c_int, def: lua_Number) -> lua_Number;
|
||||||
pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer;
|
pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer;
|
||||||
@@ -166,13 +166,72 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
|||||||
luaL_tolstring_(L, lua::lua_absindex(L, idx), len)
|
luaL_tolstring_(L, lua::lua_absindex(L, idx), len)
|
||||||
}
|
}
|
||||||
|
|
||||||
// luaL_opt would be implemented here but it is undocumented, so it's omitted
|
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn luaL_loadbuffer(L: *mut lua_State, s: *const c_char, sz: usize, n: *const c_char) -> c_int {
|
pub unsafe fn luaL_loadbuffer(L: *mut lua_State, s: *const c_char, sz: usize, n: *const c_char) -> c_int {
|
||||||
luaL_loadbufferx(L, s, sz, n, ptr::null())
|
luaL_loadbufferx(L, s, sz, n, ptr::null())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_opt<T>(
|
||||||
|
L: *mut lua_State,
|
||||||
|
f: unsafe extern "C-unwind" fn(*mut lua_State, c_int) -> T,
|
||||||
|
n: c_int,
|
||||||
|
d: T,
|
||||||
|
) -> T {
|
||||||
|
if lua::lua_isnoneornil(L, n) != 0 {
|
||||||
|
d
|
||||||
|
} else {
|
||||||
|
f(L, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// TODO: Generic Buffer Manipulation
|
// Generic Buffer Manipulation
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// The buffer size used by the lauxlib buffer system.
|
||||||
|
// In Lua 5.3: LUAL_BUFFERSIZE = (int)(0x80 * sizeof(void*) * sizeof(lua_Integer))
|
||||||
|
#[rustfmt::skip]
|
||||||
|
pub const LUAL_BUFFERSIZE: usize = 0x80 * mem::size_of::<*const ()>() * mem::size_of::<lua_Integer>();
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct luaL_Buffer {
|
||||||
|
pub b: *mut c_char, // buffer address
|
||||||
|
pub size: usize, // buffer size
|
||||||
|
pub n: usize, // number of characters in buffer
|
||||||
|
pub L: *mut lua_State,
|
||||||
|
pub initb: [c_char; LUAL_BUFFERSIZE], // initial buffer space
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||||
|
unsafe extern "C-unwind" {
|
||||||
|
pub fn luaL_buffinit(L: *mut lua_State, B: *mut luaL_Buffer);
|
||||||
|
pub fn luaL_prepbuffsize(B: *mut luaL_Buffer, sz: usize) -> *mut c_char;
|
||||||
|
pub fn luaL_addlstring(B: *mut luaL_Buffer, s: *const c_char, l: usize);
|
||||||
|
pub fn luaL_addstring(B: *mut luaL_Buffer, s: *const c_char);
|
||||||
|
pub fn luaL_addvalue(B: *mut luaL_Buffer);
|
||||||
|
pub fn luaL_pushresult(B: *mut luaL_Buffer);
|
||||||
|
pub fn luaL_pushresultsize(B: *mut luaL_Buffer, sz: usize);
|
||||||
|
pub fn luaL_buffinitsize(L: *mut lua_State, B: *mut luaL_Buffer, sz: usize) -> *mut c_char;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Macro implementations as inline functions
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_prepbuffer(B: *mut luaL_Buffer) -> *mut c_char {
|
||||||
|
luaL_prepbuffsize(B, LUAL_BUFFERSIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_addchar(B: *mut luaL_Buffer, c: c_char) {
|
||||||
|
if (*B).n >= (*B).size {
|
||||||
|
luaL_prepbuffsize(B, 1);
|
||||||
|
}
|
||||||
|
*(*B).b.add((*B).n) = c;
|
||||||
|
(*B).n += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_addsize(B: *mut luaL_Buffer, n: usize) {
|
||||||
|
(*B).n += n;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! Contains definitions from `lauxlib.h`.
|
//! Contains definitions from `lauxlib.h`.
|
||||||
|
|
||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_double, c_int, c_long, c_void};
|
||||||
use std::ptr;
|
use std::{mem, ptr};
|
||||||
|
|
||||||
use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State};
|
use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State};
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ unsafe extern "C-unwind" {
|
|||||||
pub fn luaL_argerror(L: *mut lua_State, arg: c_int, extramsg: *const c_char) -> c_int;
|
pub fn luaL_argerror(L: *mut lua_State, arg: c_int, extramsg: *const c_char) -> c_int;
|
||||||
pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char;
|
pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char;
|
||||||
pub fn luaL_optlstring(L: *mut lua_State, arg: c_int, def: *const c_char, l: *mut usize)
|
pub fn luaL_optlstring(L: *mut lua_State, arg: c_int, def: *const c_char, l: *mut usize)
|
||||||
-> *const c_char;
|
-> *const c_char;
|
||||||
pub fn luaL_checknumber(L: *mut lua_State, arg: c_int) -> lua_Number;
|
pub fn luaL_checknumber(L: *mut lua_State, arg: c_int) -> lua_Number;
|
||||||
pub fn luaL_optnumber(L: *mut lua_State, arg: c_int, def: lua_Number) -> lua_Number;
|
pub fn luaL_optnumber(L: *mut lua_State, arg: c_int, def: lua_Number) -> lua_Number;
|
||||||
pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer;
|
pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer;
|
||||||
@@ -91,7 +91,7 @@ unsafe extern "C-unwind" {
|
|||||||
|
|
||||||
pub fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer;
|
pub fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer;
|
||||||
|
|
||||||
// TODO: luaL_addgsub
|
pub fn luaL_addgsub(B: *mut luaL_Buffer, s: *const c_char, p: *const c_char, r: *const c_char);
|
||||||
|
|
||||||
pub fn luaL_gsub(
|
pub fn luaL_gsub(
|
||||||
L: *mut lua_State,
|
L: *mut lua_State,
|
||||||
@@ -162,8 +162,6 @@ pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) {
|
|||||||
lua::lua_getfield(L, lua::LUA_REGISTRYINDEX, n);
|
lua::lua_getfield(L, lua::LUA_REGISTRYINDEX, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
// luaL_opt would be implemented here but it is undocumented, so it's omitted
|
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn luaL_loadbuffer(L: *mut lua_State, s: *const c_char, sz: usize, n: *const c_char) -> c_int {
|
pub unsafe fn luaL_loadbuffer(L: *mut lua_State, s: *const c_char, sz: usize, n: *const c_char) -> c_int {
|
||||||
luaL_loadbufferx(L, s, sz, n, ptr::null())
|
luaL_loadbufferx(L, s, sz, n, ptr::null())
|
||||||
@@ -188,6 +186,96 @@ pub unsafe fn luaL_loadbufferenv(
|
|||||||
status
|
status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_opt<T>(
|
||||||
|
L: *mut lua_State,
|
||||||
|
f: unsafe extern "C-unwind" fn(*mut lua_State, c_int) -> T,
|
||||||
|
n: c_int,
|
||||||
|
d: T,
|
||||||
|
) -> T {
|
||||||
|
if lua::lua_isnoneornil(L, n) != 0 {
|
||||||
|
d
|
||||||
|
} else {
|
||||||
|
f(L, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// TODO: Generic Buffer Manipulation
|
// Generic Buffer Manipulation
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// The buffer size used by the lauxlib buffer system.
|
||||||
|
// LUAL_BUFFERSIZE = (int)(16 * sizeof(void*) * sizeof(lua_Number))
|
||||||
|
#[rustfmt::skip]
|
||||||
|
pub const LUAL_BUFFERSIZE: usize = 16 * mem::size_of::<*const ()>() * mem::size_of::<lua_Number>();
|
||||||
|
|
||||||
|
// Union used for the initial buffer with maximum alignment.
|
||||||
|
// This ensures proper alignment for the buffer data.
|
||||||
|
#[repr(C)]
|
||||||
|
pub union luaL_BufferInit {
|
||||||
|
// Alignment matches LUAI_MAXALIGN
|
||||||
|
pub _align_n: lua_Number,
|
||||||
|
pub _align_u: c_double,
|
||||||
|
pub _align_s: *mut c_void,
|
||||||
|
pub _align_i: lua_Integer,
|
||||||
|
pub _align_l: c_long,
|
||||||
|
// Initial buffer space
|
||||||
|
pub b: [c_char; LUAL_BUFFERSIZE],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct luaL_Buffer {
|
||||||
|
pub b: *mut c_char, // buffer address
|
||||||
|
pub size: usize, // buffer size
|
||||||
|
pub n: usize, // number of characters in buffer
|
||||||
|
pub L: *mut lua_State,
|
||||||
|
pub init: luaL_BufferInit, // initial buffer (union with alignment)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||||
|
unsafe extern "C-unwind" {
|
||||||
|
pub fn luaL_buffinit(L: *mut lua_State, B: *mut luaL_Buffer);
|
||||||
|
pub fn luaL_prepbuffsize(B: *mut luaL_Buffer, sz: usize) -> *mut c_char;
|
||||||
|
pub fn luaL_addlstring(B: *mut luaL_Buffer, s: *const c_char, l: usize);
|
||||||
|
pub fn luaL_addstring(B: *mut luaL_Buffer, s: *const c_char);
|
||||||
|
pub fn luaL_addvalue(B: *mut luaL_Buffer);
|
||||||
|
pub fn luaL_pushresult(B: *mut luaL_Buffer);
|
||||||
|
pub fn luaL_pushresultsize(B: *mut luaL_Buffer, sz: usize);
|
||||||
|
pub fn luaL_buffinitsize(L: *mut lua_State, B: *mut luaL_Buffer, sz: usize) -> *mut c_char;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Macro implementations as inline functions
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_prepbuffer(B: *mut luaL_Buffer) -> *mut c_char {
|
||||||
|
luaL_prepbuffsize(B, LUAL_BUFFERSIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_addchar(B: *mut luaL_Buffer, c: c_char) {
|
||||||
|
if (*B).n >= (*B).size {
|
||||||
|
luaL_prepbuffsize(B, 1);
|
||||||
|
}
|
||||||
|
*(*B).b.add((*B).n) = c;
|
||||||
|
(*B).n += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_addsize(B: *mut luaL_Buffer, n: usize) {
|
||||||
|
(*B).n += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_buffsub(B: *mut luaL_Buffer, n: usize) {
|
||||||
|
(*B).n -= n;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_bufflen(B: *mut luaL_Buffer) -> usize {
|
||||||
|
(*B).n
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_buffaddr(B: *mut luaL_Buffer) -> *mut c_char {
|
||||||
|
(*B).b
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,299 @@
|
|||||||
|
//! Contains definitions from `lauxlib.h`.
|
||||||
|
|
||||||
|
use std::os::raw::{c_char, c_double, c_int, c_long, c_uint, c_void};
|
||||||
|
use std::{mem, ptr};
|
||||||
|
|
||||||
|
use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State};
|
||||||
|
|
||||||
|
// Extra error code for 'luaL_loadfilex'
|
||||||
|
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,
|
||||||
|
pub func: lua_CFunction,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(all(windows, raw_dylib), link(name = "lua55", kind = "raw-dylib"))]
|
||||||
|
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;
|
||||||
|
pub fn luaL_callmeta(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
|
||||||
|
pub fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
|
||||||
|
pub fn luaL_argerror(L: *mut lua_State, arg: c_int, extramsg: *const c_char) -> c_int;
|
||||||
|
pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char;
|
||||||
|
pub fn luaL_optlstring(L: *mut lua_State, arg: c_int, def: *const c_char, l: *mut usize)
|
||||||
|
-> *const c_char;
|
||||||
|
pub fn luaL_checknumber(L: *mut lua_State, arg: c_int) -> lua_Number;
|
||||||
|
pub fn luaL_optnumber(L: *mut lua_State, arg: c_int, def: lua_Number) -> lua_Number;
|
||||||
|
pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer;
|
||||||
|
pub fn luaL_optinteger(L: *mut lua_State, arg: c_int, def: lua_Integer) -> lua_Integer;
|
||||||
|
|
||||||
|
pub fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char);
|
||||||
|
pub fn luaL_checktype(L: *mut lua_State, arg: c_int, t: c_int);
|
||||||
|
pub fn luaL_checkany(L: *mut lua_State, arg: c_int);
|
||||||
|
|
||||||
|
pub fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_int;
|
||||||
|
pub fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char);
|
||||||
|
pub fn luaL_testudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
|
||||||
|
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
|
||||||
|
|
||||||
|
pub fn luaL_where(L: *mut lua_State, lvl: c_int);
|
||||||
|
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> c_int;
|
||||||
|
|
||||||
|
pub fn luaL_checkoption(
|
||||||
|
L: *mut lua_State,
|
||||||
|
arg: c_int,
|
||||||
|
def: *const c_char,
|
||||||
|
lst: *const *const c_char,
|
||||||
|
) -> c_int;
|
||||||
|
|
||||||
|
pub fn luaL_fileresult(L: *mut lua_State, stat: c_int, fname: *const c_char) -> c_int;
|
||||||
|
pub fn luaL_execresult(L: *mut lua_State, stat: c_int) -> c_int;
|
||||||
|
pub fn luaL_alloc(L: *mut lua_State, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-defined references
|
||||||
|
pub const LUA_NOREF: c_int = -2;
|
||||||
|
pub const LUA_REFNIL: c_int = -1;
|
||||||
|
|
||||||
|
#[cfg_attr(all(windows, raw_dylib), link(name = "lua55", kind = "raw-dylib"))]
|
||||||
|
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);
|
||||||
|
|
||||||
|
pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char) -> c_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_loadfile(L: *mut lua_State, f: *const c_char) -> c_int {
|
||||||
|
luaL_loadfilex(L, f, ptr::null())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(all(windows, raw_dylib), link(name = "lua55", kind = "raw-dylib"))]
|
||||||
|
unsafe extern "C-unwind" {
|
||||||
|
pub fn luaL_loadbufferx(
|
||||||
|
L: *mut lua_State,
|
||||||
|
buff: *const c_char,
|
||||||
|
sz: usize,
|
||||||
|
name: *const c_char,
|
||||||
|
mode: *const c_char,
|
||||||
|
) -> c_int;
|
||||||
|
pub fn luaL_loadstring(L: *mut lua_State, s: *const c_char) -> c_int;
|
||||||
|
|
||||||
|
pub fn luaL_newstate() -> *mut lua_State;
|
||||||
|
|
||||||
|
#[link_name = "luaL_makeseed"]
|
||||||
|
pub fn luaL_makeseed_(L: *mut lua_State) -> c_uint;
|
||||||
|
|
||||||
|
pub fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer;
|
||||||
|
|
||||||
|
pub fn luaL_addgsub(B: *mut luaL_Buffer, s: *const c_char, p: *const c_char, r: *const c_char);
|
||||||
|
|
||||||
|
pub fn luaL_gsub(
|
||||||
|
L: *mut lua_State,
|
||||||
|
s: *const c_char,
|
||||||
|
p: *const c_char,
|
||||||
|
r: *const c_char,
|
||||||
|
) -> *const c_char;
|
||||||
|
|
||||||
|
pub fn luaL_setfuncs(L: *mut lua_State, l: *const luaL_Reg, nup: c_int);
|
||||||
|
|
||||||
|
pub fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int;
|
||||||
|
|
||||||
|
pub fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, level: c_int);
|
||||||
|
|
||||||
|
pub fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Some useful macros (implemented as Rust functions)
|
||||||
|
//
|
||||||
|
|
||||||
|
// TODO: luaL_newlibtable, luaL_newlib
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_argcheck(L: *mut lua_State, cond: c_int, arg: c_int, extramsg: *const c_char) {
|
||||||
|
if cond == 0 {
|
||||||
|
luaL_argerror(L, arg, extramsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_checkstring(L: *mut lua_State, n: c_int) -> *const c_char {
|
||||||
|
luaL_checklstring(L, n, ptr::null_mut())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_optstring(L: *mut lua_State, n: c_int, d: *const c_char) -> *const c_char {
|
||||||
|
luaL_optlstring(L, n, d, ptr::null_mut())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_typename(L: *mut lua_State, i: c_int) -> *const c_char {
|
||||||
|
lua::lua_typename(L, lua::lua_type(L, i))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_dofile(L: *mut lua_State, filename: *const c_char) -> c_int {
|
||||||
|
let status = luaL_loadfile(L, filename);
|
||||||
|
if status == 0 {
|
||||||
|
lua::lua_pcall(L, 0, lua::LUA_MULTRET, 0)
|
||||||
|
} else {
|
||||||
|
status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_dostring(L: *mut lua_State, s: *const c_char) -> c_int {
|
||||||
|
let status = luaL_loadstring(L, s);
|
||||||
|
if status == 0 {
|
||||||
|
lua::lua_pcall(L, 0, lua::LUA_MULTRET, 0)
|
||||||
|
} else {
|
||||||
|
status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) {
|
||||||
|
lua::lua_getfield(L, lua::LUA_REGISTRYINDEX, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_loadbuffer(L: *mut lua_State, s: *const c_char, sz: usize, n: *const c_char) -> c_int {
|
||||||
|
luaL_loadbufferx(L, s, sz, n, ptr::null())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn luaL_loadbufferenv(
|
||||||
|
L: *mut lua_State,
|
||||||
|
data: *const c_char,
|
||||||
|
size: usize,
|
||||||
|
name: *const c_char,
|
||||||
|
mode: *const c_char,
|
||||||
|
mut env: c_int,
|
||||||
|
) -> c_int {
|
||||||
|
if env != 0 {
|
||||||
|
env = lua::lua_absindex(L, env);
|
||||||
|
}
|
||||||
|
let status = luaL_loadbufferx(L, data, size, name, mode);
|
||||||
|
if status == lua::LUA_OK && env != 0 {
|
||||||
|
lua::lua_pushvalue(L, env);
|
||||||
|
lua::lua_setupvalue(L, -2, 1);
|
||||||
|
}
|
||||||
|
status
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn luaL_makeseed(L: *mut lua_State) -> c_uint {
|
||||||
|
#[cfg(macos)]
|
||||||
|
return libc::arc4random();
|
||||||
|
#[cfg(linux)]
|
||||||
|
{
|
||||||
|
let mut seed = 0u32;
|
||||||
|
let buf = &mut seed as *mut _ as *mut c_void;
|
||||||
|
if libc::getrandom(buf, 4, libc::GRND_NONBLOCK) == 4 {
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
luaL_makeseed_(L)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_opt<T>(
|
||||||
|
L: *mut lua_State,
|
||||||
|
f: unsafe extern "C-unwind" fn(*mut lua_State, c_int) -> T,
|
||||||
|
n: c_int,
|
||||||
|
d: T,
|
||||||
|
) -> T {
|
||||||
|
if lua::lua_isnoneornil(L, n) != 0 {
|
||||||
|
d
|
||||||
|
} else {
|
||||||
|
f(L, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Generic Buffer Manipulation
|
||||||
|
//
|
||||||
|
|
||||||
|
// The buffer size used by the lauxlib buffer system.
|
||||||
|
// LUAL_BUFFERSIZE = (int)(16 * sizeof(void*) * sizeof(lua_Number))
|
||||||
|
#[rustfmt::skip]
|
||||||
|
pub const LUAL_BUFFERSIZE: usize = 16 * mem::size_of::<*const ()>() * mem::size_of::<lua_Number>();
|
||||||
|
|
||||||
|
// Union used for the initial buffer with maximum alignment.
|
||||||
|
// This ensures proper alignment for the buffer data.
|
||||||
|
#[repr(C)]
|
||||||
|
pub union luaL_BufferInit {
|
||||||
|
// Alignment matches LUAI_MAXALIGN
|
||||||
|
pub _align_n: lua_Number,
|
||||||
|
pub _align_u: c_double,
|
||||||
|
pub _align_s: *mut c_void,
|
||||||
|
pub _align_i: lua_Integer,
|
||||||
|
pub _align_l: c_long,
|
||||||
|
// Initial buffer space
|
||||||
|
pub b: [c_char; LUAL_BUFFERSIZE],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct luaL_Buffer {
|
||||||
|
pub b: *mut c_char, // buffer address
|
||||||
|
pub size: usize, // buffer size
|
||||||
|
pub n: usize, // number of characters in buffer
|
||||||
|
pub L: *mut lua_State,
|
||||||
|
pub init: luaL_BufferInit, // initial buffer (union with alignment)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(all(windows, raw_dylib), link(name = "lua55", kind = "raw-dylib"))]
|
||||||
|
unsafe extern "C-unwind" {
|
||||||
|
pub fn luaL_buffinit(L: *mut lua_State, B: *mut luaL_Buffer);
|
||||||
|
pub fn luaL_prepbuffsize(B: *mut luaL_Buffer, sz: usize) -> *mut c_char;
|
||||||
|
pub fn luaL_addlstring(B: *mut luaL_Buffer, s: *const c_char, l: usize);
|
||||||
|
pub fn luaL_addstring(B: *mut luaL_Buffer, s: *const c_char);
|
||||||
|
pub fn luaL_addvalue(B: *mut luaL_Buffer);
|
||||||
|
pub fn luaL_pushresult(B: *mut luaL_Buffer);
|
||||||
|
pub fn luaL_pushresultsize(B: *mut luaL_Buffer, sz: usize);
|
||||||
|
pub fn luaL_buffinitsize(L: *mut lua_State, B: *mut luaL_Buffer, sz: usize) -> *mut c_char;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Macro implementations as inline functions
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_prepbuffer(B: *mut luaL_Buffer) -> *mut c_char {
|
||||||
|
luaL_prepbuffsize(B, LUAL_BUFFERSIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_addchar(B: *mut luaL_Buffer, c: c_char) {
|
||||||
|
if (*B).n >= (*B).size {
|
||||||
|
luaL_prepbuffsize(B, 1);
|
||||||
|
}
|
||||||
|
*(*B).b.add((*B).n) = c;
|
||||||
|
(*B).n += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_addsize(B: *mut luaL_Buffer, n: usize) {
|
||||||
|
(*B).n += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_buffsub(B: *mut luaL_Buffer, n: usize) {
|
||||||
|
(*B).n -= n;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_bufflen(B: *mut luaL_Buffer) -> usize {
|
||||||
|
(*B).n
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_buffaddr(B: *mut luaL_Buffer) -> *mut c_char {
|
||||||
|
(*B).b
|
||||||
|
}
|
||||||
@@ -0,0 +1,578 @@
|
|||||||
|
//! 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::{mem, ptr};
|
||||||
|
|
||||||
|
// Mark for precompiled code (`<esc>Lua`)
|
||||||
|
pub const LUA_SIGNATURE: &[u8] = b"\x1bLua";
|
||||||
|
|
||||||
|
// Option for multiple returns in 'lua_pcall' and 'lua_call'
|
||||||
|
pub const LUA_MULTRET: c_int = -1;
|
||||||
|
|
||||||
|
// Size of the Lua stack
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub const LUAI_MAXSTACK: c_int = c_int::MAX;
|
||||||
|
|
||||||
|
// Size of a raw memory area associated with a Lua state with very fast access.
|
||||||
|
pub const LUA_EXTRASPACE: usize = mem::size_of::<*const ()>();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Pseudo-indices
|
||||||
|
//
|
||||||
|
pub const LUA_REGISTRYINDEX: c_int = -(c_int::MAX / 2 + 1000);
|
||||||
|
|
||||||
|
pub const fn lua_upvalueindex(i: c_int) -> c_int {
|
||||||
|
LUA_REGISTRYINDEX - i
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Thread status
|
||||||
|
//
|
||||||
|
pub const LUA_OK: c_int = 0;
|
||||||
|
pub const LUA_YIELD: c_int = 1;
|
||||||
|
pub const LUA_ERRRUN: c_int = 2;
|
||||||
|
pub const LUA_ERRSYNTAX: c_int = 3;
|
||||||
|
pub const LUA_ERRMEM: c_int = 4;
|
||||||
|
pub const LUA_ERRERR: c_int = 5;
|
||||||
|
|
||||||
|
/// A raw Lua state associated with a thread.
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct lua_State {
|
||||||
|
_data: [u8; 0],
|
||||||
|
_marker: PhantomData<(*mut u8, PhantomPinned)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Basic types
|
||||||
|
//
|
||||||
|
pub const LUA_TNONE: c_int = -1;
|
||||||
|
|
||||||
|
pub const LUA_TNIL: c_int = 0;
|
||||||
|
pub const LUA_TBOOLEAN: c_int = 1;
|
||||||
|
pub const LUA_TLIGHTUSERDATA: c_int = 2;
|
||||||
|
pub const LUA_TNUMBER: c_int = 3;
|
||||||
|
pub const LUA_TSTRING: c_int = 4;
|
||||||
|
pub const LUA_TTABLE: c_int = 5;
|
||||||
|
pub const LUA_TFUNCTION: c_int = 6;
|
||||||
|
pub const LUA_TUSERDATA: c_int = 7;
|
||||||
|
pub const LUA_TTHREAD: c_int = 8;
|
||||||
|
|
||||||
|
pub const LUA_NUMTYPES: c_int = 9;
|
||||||
|
|
||||||
|
/// Minimum Lua stack available to a C function
|
||||||
|
pub const LUA_MINSTACK: c_int = 20;
|
||||||
|
|
||||||
|
// Predefined values in the registry
|
||||||
|
// index 1 is reserved for the reference mechanism
|
||||||
|
pub const LUA_RIDX_GLOBALS: lua_Integer = 2;
|
||||||
|
pub const LUA_RIDX_MAINTHREAD: lua_Integer = 3;
|
||||||
|
pub const LUA_RIDX_LAST: lua_Integer = 3;
|
||||||
|
|
||||||
|
/// A Lua number, usually equivalent to `f64`
|
||||||
|
pub type lua_Number = c_double;
|
||||||
|
|
||||||
|
/// A Lua integer, usually equivalent to `i64`
|
||||||
|
pub type lua_Integer = i64;
|
||||||
|
|
||||||
|
/// A Lua unsigned integer, usually equivalent to `u64`
|
||||||
|
pub type lua_Unsigned = u64;
|
||||||
|
|
||||||
|
/// Type for continuation-function contexts
|
||||||
|
pub type lua_KContext = isize;
|
||||||
|
|
||||||
|
/// Type for native C functions that can be passed to Lua
|
||||||
|
pub type lua_CFunction = unsafe extern "C-unwind" fn(L: *mut lua_State) -> c_int;
|
||||||
|
|
||||||
|
/// Type for continuation functions
|
||||||
|
pub type lua_KFunction =
|
||||||
|
unsafe extern "C-unwind" fn(L: *mut lua_State, status: c_int, ctx: lua_KContext) -> c_int;
|
||||||
|
|
||||||
|
// Type for functions that read/write blocks when loading/dumping Lua chunks
|
||||||
|
#[rustfmt::skip]
|
||||||
|
pub type lua_Reader =
|
||||||
|
unsafe extern "C-unwind" fn(L: *mut lua_State, ud: *mut c_void, sz: *mut usize) -> *const c_char;
|
||||||
|
#[rustfmt::skip]
|
||||||
|
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 (no unwinding)
|
||||||
|
#[rustfmt::skip]
|
||||||
|
pub type lua_Alloc =
|
||||||
|
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 = "lua55", kind = "raw-dylib"))]
|
||||||
|
unsafe extern "C-unwind" {
|
||||||
|
//
|
||||||
|
// State manipulation
|
||||||
|
//
|
||||||
|
pub fn lua_newstate(f: lua_Alloc, ud: *mut c_void, seed: c_uint) -> *mut lua_State;
|
||||||
|
pub fn lua_close(L: *mut lua_State);
|
||||||
|
pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State;
|
||||||
|
pub fn lua_closethread(L: *mut lua_State, from: *mut lua_State) -> c_int;
|
||||||
|
|
||||||
|
pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction;
|
||||||
|
|
||||||
|
pub fn lua_version(L: *mut lua_State) -> lua_Number;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Basic stack manipulation
|
||||||
|
//
|
||||||
|
pub fn lua_absindex(L: *mut lua_State, idx: c_int) -> c_int;
|
||||||
|
pub fn lua_gettop(L: *mut lua_State) -> c_int;
|
||||||
|
pub fn lua_settop(L: *mut lua_State, idx: c_int);
|
||||||
|
pub fn lua_pushvalue(L: *mut lua_State, idx: c_int);
|
||||||
|
pub fn lua_rotate(L: *mut lua_State, idx: c_int, n: c_int);
|
||||||
|
pub fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int);
|
||||||
|
pub fn lua_checkstack(L: *mut lua_State, sz: c_int) -> c_int;
|
||||||
|
|
||||||
|
pub fn lua_xmove(from: *mut lua_State, to: *mut lua_State, n: c_int);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Access functions (stack -> C)
|
||||||
|
//
|
||||||
|
pub fn lua_isnumber(L: *mut lua_State, idx: c_int) -> c_int;
|
||||||
|
pub fn lua_isstring(L: *mut lua_State, idx: c_int) -> c_int;
|
||||||
|
pub fn lua_iscfunction(L: *mut lua_State, idx: c_int) -> c_int;
|
||||||
|
pub fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int;
|
||||||
|
pub fn lua_isuserdata(L: *mut lua_State, idx: c_int) -> c_int;
|
||||||
|
pub fn lua_type(L: *mut lua_State, idx: c_int) -> c_int;
|
||||||
|
pub fn lua_typename(L: *mut lua_State, tp: c_int) -> *const c_char;
|
||||||
|
|
||||||
|
pub fn lua_tonumberx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Number;
|
||||||
|
pub fn lua_tointegerx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Integer;
|
||||||
|
pub fn lua_toboolean(L: *mut lua_State, idx: c_int) -> c_int;
|
||||||
|
pub fn lua_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
|
||||||
|
#[link_name = "lua_rawlen"]
|
||||||
|
fn lua_rawlen_(L: *mut lua_State, idx: c_int) -> lua_Unsigned;
|
||||||
|
pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> Option<lua_CFunction>;
|
||||||
|
pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
|
||||||
|
pub fn lua_tothread(L: *mut lua_State, idx: c_int) -> *mut lua_State;
|
||||||
|
pub fn lua_topointer(L: *mut lua_State, idx: c_int) -> *const c_void;
|
||||||
|
}
|
||||||
|
|
||||||
|
// lua_rawlen's return type changed from size_t to lua_Unsigned int in Lua 5.4.
|
||||||
|
// This adapts the crate API to the new Lua ABI.
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize {
|
||||||
|
lua_rawlen_(L, idx) as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Comparison and arithmetic functions
|
||||||
|
//
|
||||||
|
pub const LUA_OPADD: c_int = 0;
|
||||||
|
pub const LUA_OPSUB: c_int = 1;
|
||||||
|
pub const LUA_OPMUL: c_int = 2;
|
||||||
|
pub const LUA_OPMOD: c_int = 3;
|
||||||
|
pub const LUA_OPPOW: c_int = 4;
|
||||||
|
pub const LUA_OPDIV: c_int = 5;
|
||||||
|
pub const LUA_OPIDIV: c_int = 6;
|
||||||
|
pub const LUA_OPBAND: c_int = 7;
|
||||||
|
pub const LUA_OPBOR: c_int = 8;
|
||||||
|
pub const LUA_OPBXOR: c_int = 9;
|
||||||
|
pub const LUA_OPSHL: c_int = 10;
|
||||||
|
pub const LUA_OPSHR: c_int = 11;
|
||||||
|
pub const LUA_OPUNM: c_int = 12;
|
||||||
|
pub const LUA_OPBNOT: c_int = 13;
|
||||||
|
|
||||||
|
pub const LUA_OPEQ: c_int = 0;
|
||||||
|
pub const LUA_OPLT: c_int = 1;
|
||||||
|
pub const LUA_OPLE: c_int = 2;
|
||||||
|
|
||||||
|
#[cfg_attr(all(windows, raw_dylib), link(name = "lua55", kind = "raw-dylib"))]
|
||||||
|
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 = "lua55", kind = "raw-dylib"))]
|
||||||
|
unsafe extern "C-unwind" {
|
||||||
|
//
|
||||||
|
// Push functions (C -> stack)
|
||||||
|
//
|
||||||
|
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);
|
||||||
|
pub fn lua_pushlstring(L: *mut lua_State, s: *const c_char, len: usize) -> *const c_char;
|
||||||
|
pub fn lua_pushexternalstring(
|
||||||
|
L: *mut lua_State,
|
||||||
|
s: *const c_char,
|
||||||
|
len: usize,
|
||||||
|
falloc: Option<lua_Alloc>,
|
||||||
|
ud: *mut c_void,
|
||||||
|
) -> *const c_char;
|
||||||
|
pub fn lua_pushstring(L: *mut lua_State, s: *const c_char) -> *const c_char;
|
||||||
|
// lua_pushvfstring
|
||||||
|
pub fn lua_pushfstring(L: *mut lua_State, fmt: *const c_char, ...) -> *const c_char;
|
||||||
|
pub fn lua_pushcclosure(L: *mut lua_State, f: lua_CFunction, n: c_int);
|
||||||
|
pub fn lua_pushboolean(L: *mut lua_State, b: c_int);
|
||||||
|
pub fn lua_pushlightuserdata(L: *mut lua_State, p: *mut c_void);
|
||||||
|
pub fn lua_pushthread(L: *mut lua_State) -> c_int;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get functions (Lua -> stack)
|
||||||
|
//
|
||||||
|
pub fn lua_getglobal(L: *mut lua_State, name: *const c_char) -> c_int;
|
||||||
|
pub fn lua_gettable(L: *mut lua_State, idx: c_int) -> c_int;
|
||||||
|
pub fn lua_getfield(L: *mut lua_State, idx: c_int, k: *const c_char) -> c_int;
|
||||||
|
pub fn lua_geti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int;
|
||||||
|
pub fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int;
|
||||||
|
pub fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int;
|
||||||
|
pub fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int;
|
||||||
|
|
||||||
|
pub fn lua_createtable(L: *mut lua_State, narr: c_int, nrec: c_int);
|
||||||
|
pub fn lua_newuserdatauv(L: *mut lua_State, sz: usize, nuvalue: c_int) -> *mut c_void;
|
||||||
|
pub fn lua_getmetatable(L: *mut lua_State, objindex: c_int) -> c_int;
|
||||||
|
pub fn lua_getiuservalue(L: *mut lua_State, idx: c_int, n: c_int) -> c_int;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Set functions (stack -> Lua)
|
||||||
|
//
|
||||||
|
pub fn lua_setglobal(L: *mut lua_State, name: *const c_char);
|
||||||
|
pub fn lua_settable(L: *mut lua_State, idx: c_int);
|
||||||
|
pub fn lua_setfield(L: *mut lua_State, idx: c_int, k: *const c_char);
|
||||||
|
pub fn lua_seti(L: *mut lua_State, idx: c_int, n: lua_Integer);
|
||||||
|
pub fn lua_rawset(L: *mut lua_State, idx: c_int);
|
||||||
|
pub fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer);
|
||||||
|
pub fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void);
|
||||||
|
pub fn lua_setmetatable(L: *mut lua_State, objindex: c_int) -> c_int;
|
||||||
|
pub fn lua_setiuservalue(L: *mut lua_State, idx: c_int, n: c_int) -> c_int;
|
||||||
|
|
||||||
|
//
|
||||||
|
// 'load' and 'call' functions (load and run Lua code)
|
||||||
|
//
|
||||||
|
pub fn lua_callk(
|
||||||
|
L: *mut lua_State,
|
||||||
|
nargs: c_int,
|
||||||
|
nresults: c_int,
|
||||||
|
ctx: lua_KContext,
|
||||||
|
k: Option<lua_KFunction>,
|
||||||
|
);
|
||||||
|
pub fn lua_pcallk(
|
||||||
|
L: *mut lua_State,
|
||||||
|
nargs: c_int,
|
||||||
|
nresults: c_int,
|
||||||
|
errfunc: c_int,
|
||||||
|
ctx: lua_KContext,
|
||||||
|
k: Option<lua_KFunction>,
|
||||||
|
) -> c_int;
|
||||||
|
|
||||||
|
pub fn lua_load(
|
||||||
|
L: *mut lua_State,
|
||||||
|
reader: lua_Reader,
|
||||||
|
data: *mut c_void,
|
||||||
|
chunkname: *const c_char,
|
||||||
|
mode: *const c_char,
|
||||||
|
) -> c_int;
|
||||||
|
|
||||||
|
pub fn lua_dump(L: *mut lua_State, writer: lua_Writer, data: *mut c_void, strip: c_int) -> c_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_call(L: *mut lua_State, n: c_int, r: c_int) {
|
||||||
|
lua_callk(L, n, r, 0, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_pcall(L: *mut lua_State, n: c_int, r: c_int, f: c_int) -> c_int {
|
||||||
|
lua_pcallk(L, n, r, f, 0, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(all(windows, raw_dylib), link(name = "lua55", kind = "raw-dylib"))]
|
||||||
|
unsafe extern "C-unwind" {
|
||||||
|
//
|
||||||
|
// Coroutine functions
|
||||||
|
//
|
||||||
|
pub fn lua_yieldk(
|
||||||
|
L: *mut lua_State,
|
||||||
|
nresults: c_int,
|
||||||
|
ctx: lua_KContext,
|
||||||
|
k: Option<lua_KFunction>,
|
||||||
|
) -> c_int;
|
||||||
|
pub fn lua_resume(L: *mut lua_State, from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int;
|
||||||
|
pub fn lua_status(L: *mut lua_State) -> c_int;
|
||||||
|
pub fn lua_isyieldable(L: *mut lua_State) -> c_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_yield(L: *mut lua_State, n: c_int) -> c_int {
|
||||||
|
lua_yieldk(L, n, 0, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Warning-related functions
|
||||||
|
//
|
||||||
|
#[cfg_attr(all(windows, raw_dylib), link(name = "lua55", kind = "raw-dylib"))]
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Garbage-collection options
|
||||||
|
//
|
||||||
|
pub const LUA_GCSTOP: c_int = 0;
|
||||||
|
pub const LUA_GCRESTART: c_int = 1;
|
||||||
|
pub const LUA_GCCOLLECT: c_int = 2;
|
||||||
|
pub const LUA_GCCOUNT: c_int = 3;
|
||||||
|
pub const LUA_GCCOUNTB: c_int = 4;
|
||||||
|
pub const LUA_GCSTEP: c_int = 5;
|
||||||
|
pub const LUA_GCISRUNNING: c_int = 6;
|
||||||
|
pub const LUA_GCGEN: c_int = 7;
|
||||||
|
pub const LUA_GCINC: c_int = 8;
|
||||||
|
pub const LUA_GCPARAM: c_int = 9;
|
||||||
|
|
||||||
|
// Parameters for GC generational mode
|
||||||
|
pub const LUA_GCPMINORMUL: c_int = 0; // control minor collections
|
||||||
|
pub const LUA_GCPMAJORMINOR: c_int = 1; // control shift major->minor
|
||||||
|
pub const LUA_GCPMINORMAJOR: c_int = 2; // control shift minor->major
|
||||||
|
|
||||||
|
// Parameters for GC incremental mode
|
||||||
|
pub const LUA_GCPPAUSE: c_int = 3; // size of pause between successive GCs
|
||||||
|
pub const LUA_GCPSTEPMUL: c_int = 4; // GC "speed"
|
||||||
|
pub const LUA_GCPSTEPSIZE: c_int = 5; // GC granularity
|
||||||
|
|
||||||
|
pub const LUA_GCPNUM: c_int = 6; // number of parameters
|
||||||
|
|
||||||
|
#[cfg_attr(all(windows, raw_dylib), link(name = "lua55", kind = "raw-dylib"))]
|
||||||
|
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 = "lua55", kind = "raw-dylib"))]
|
||||||
|
unsafe extern "C-unwind" {
|
||||||
|
//
|
||||||
|
// Miscellaneous functions
|
||||||
|
//
|
||||||
|
#[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;
|
||||||
|
pub fn lua_concat(L: *mut lua_State, n: c_int);
|
||||||
|
pub fn lua_len(L: *mut lua_State, idx: c_int);
|
||||||
|
pub fn lua_numbertocstring(L: *mut lua_State, idx: c_int, buff: *mut c_char) -> c_uint;
|
||||||
|
pub fn lua_stringtonumber(L: *mut lua_State, s: *const c_char) -> usize;
|
||||||
|
pub fn lua_getallocf(L: *mut lua_State, ud: *mut *mut c_void) -> lua_Alloc;
|
||||||
|
pub fn lua_setallocf(L: *mut lua_State, f: lua_Alloc, ud: *mut c_void);
|
||||||
|
|
||||||
|
pub fn lua_toclose(L: *mut lua_State, idx: c_int);
|
||||||
|
pub fn lua_closeslot(L: *mut lua_State, idx: c_int);
|
||||||
|
}
|
||||||
|
|
||||||
|
// lua_error does not return but is declared to return int, and Rust translates
|
||||||
|
// ! to void which can cause link-time errors if the platform linker is aware
|
||||||
|
// of return types and requires they match (for example: wasm does this).
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_error(L: *mut lua_State) -> ! {
|
||||||
|
lua_error_(L);
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Some useful macros (implemented as Rust functions)
|
||||||
|
//
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_getextraspace(L: *mut lua_State) -> *mut c_void {
|
||||||
|
(L as *mut c_char).sub(LUA_EXTRASPACE) as *mut c_void
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_tonumber(L: *mut lua_State, i: c_int) -> lua_Number {
|
||||||
|
lua_tonumberx(L, i, 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())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_pop(L: *mut lua_State, n: c_int) {
|
||||||
|
lua_settop(L, -n - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_newtable(L: *mut lua_State) {
|
||||||
|
lua_createtable(L, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_register(L: *mut lua_State, n: *const c_char, f: lua_CFunction) {
|
||||||
|
lua_pushcfunction(L, f);
|
||||||
|
lua_setglobal(L, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_pushcfunction(L: *mut lua_State, f: lua_CFunction) {
|
||||||
|
lua_pushcclosure(L, f, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_isfunction(L: *mut lua_State, n: c_int) -> c_int {
|
||||||
|
(lua_type(L, n) == LUA_TFUNCTION) as c_int
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_istable(L: *mut lua_State, n: c_int) -> c_int {
|
||||||
|
(lua_type(L, n) == LUA_TTABLE) as c_int
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_islightuserdata(L: *mut lua_State, n: c_int) -> c_int {
|
||||||
|
(lua_type(L, n) == LUA_TLIGHTUSERDATA) as c_int
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_isnil(L: *mut lua_State, n: c_int) -> c_int {
|
||||||
|
(lua_type(L, n) == LUA_TNIL) as c_int
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_isboolean(L: *mut lua_State, n: c_int) -> c_int {
|
||||||
|
(lua_type(L, n) == LUA_TBOOLEAN) as c_int
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_isthread(L: *mut lua_State, n: c_int) -> c_int {
|
||||||
|
(lua_type(L, n) == LUA_TTHREAD) as c_int
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_isnone(L: *mut lua_State, n: c_int) -> c_int {
|
||||||
|
(lua_type(L, n) == LUA_TNONE) as c_int
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_isnoneornil(L: *mut lua_State, n: c_int) -> c_int {
|
||||||
|
(lua_type(L, n) <= 0) as c_int
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static CStr) {
|
||||||
|
lua_pushstring(L, s.as_ptr());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_pushglobaltable(L: *mut lua_State) -> c_int {
|
||||||
|
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void {
|
||||||
|
if lua_islightuserdata(L, idx) != 0 {
|
||||||
|
return lua_touserdata(L, idx);
|
||||||
|
}
|
||||||
|
ptr::null_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
|
||||||
|
lua_tolstring(L, i, ptr::null_mut())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_insert(L: *mut lua_State, idx: c_int) {
|
||||||
|
lua_rotate(L, idx, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_remove(L: *mut lua_State, idx: c_int) {
|
||||||
|
lua_rotate(L, idx, -1);
|
||||||
|
lua_pop(L, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_replace(L: *mut lua_State, idx: c_int) {
|
||||||
|
lua_copy(L, -1, idx);
|
||||||
|
lua_pop(L, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_xpush(from: *mut lua_State, to: *mut lua_State, idx: c_int) {
|
||||||
|
lua_pushvalue(from, idx);
|
||||||
|
lua_xmove(from, to, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void {
|
||||||
|
lua_newuserdatauv(L, sz, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int {
|
||||||
|
lua_getiuservalue(L, idx, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_setuservalue(L: *mut lua_State, idx: c_int) -> c_int {
|
||||||
|
lua_setiuservalue(L, idx, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Debug API
|
||||||
|
//
|
||||||
|
|
||||||
|
// Maximum size for the description of the source of a function in debug information.
|
||||||
|
const LUA_IDSIZE: usize = 60;
|
||||||
|
|
||||||
|
// Event codes
|
||||||
|
pub const LUA_HOOKCALL: c_int = 0;
|
||||||
|
pub const LUA_HOOKRET: c_int = 1;
|
||||||
|
pub const LUA_HOOKLINE: c_int = 2;
|
||||||
|
pub const LUA_HOOKCOUNT: c_int = 3;
|
||||||
|
pub const LUA_HOOKTAILCALL: c_int = 4;
|
||||||
|
|
||||||
|
// Event masks
|
||||||
|
pub const LUA_MASKCALL: c_int = 1 << (LUA_HOOKCALL as usize);
|
||||||
|
pub const LUA_MASKRET: c_int = 1 << (LUA_HOOKRET as usize);
|
||||||
|
pub const LUA_MASKLINE: c_int = 1 << (LUA_HOOKLINE as usize);
|
||||||
|
pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize);
|
||||||
|
|
||||||
|
/// Type for functions to be called on debug events.
|
||||||
|
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 = "lua55", kind = "raw-dylib"))]
|
||||||
|
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;
|
||||||
|
pub fn lua_setlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
|
||||||
|
pub fn lua_getupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
|
||||||
|
pub fn lua_setupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
|
||||||
|
|
||||||
|
pub fn lua_upvalueid(L: *mut lua_State, fidx: c_int, n: c_int) -> *mut c_void;
|
||||||
|
pub fn lua_upvaluejoin(L: *mut lua_State, fidx1: c_int, n1: c_int, fidx2: c_int, n2: c_int);
|
||||||
|
|
||||||
|
pub fn lua_sethook(L: *mut lua_State, func: Option<lua_Hook>, mask: c_int, count: c_int);
|
||||||
|
pub fn lua_gethook(L: *mut lua_State) -> Option<lua_Hook>;
|
||||||
|
pub fn lua_gethookmask(L: *mut lua_State) -> c_int;
|
||||||
|
pub fn lua_gethookcount(L: *mut lua_State) -> c_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct lua_Debug {
|
||||||
|
pub event: c_int,
|
||||||
|
pub name: *const c_char, // (n)
|
||||||
|
pub namewhat: *const c_char, // (n) 'global', 'local', 'field', 'method'
|
||||||
|
pub what: *const c_char, // (S) 'Lua', 'C', 'main', 'tail'
|
||||||
|
pub source: *const c_char, // (S)
|
||||||
|
pub srclen: usize, // (S)
|
||||||
|
pub currentline: c_int, // (l)
|
||||||
|
pub linedefined: c_int, // (S)
|
||||||
|
pub lastlinedefined: c_int, // (S)
|
||||||
|
pub nups: c_uchar, // (u) number of upvalues
|
||||||
|
pub nparams: c_uchar, // (u) number of parameters
|
||||||
|
pub isvararg: c_char, // (u)
|
||||||
|
pub extraargs: c_uchar, // (t) number of extra arguments
|
||||||
|
pub istailcall: c_char, // (t)
|
||||||
|
pub ftransfer: c_int, // (r) index of first value transferred
|
||||||
|
pub ntransfer: c_int, // (r) number of transferred values
|
||||||
|
pub short_src: [c_char; LUA_IDSIZE], // (S)
|
||||||
|
// lua.h mentions this is for private use
|
||||||
|
i_ci: *mut c_void,
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
//! Contains definitions from `lualib.h`.
|
||||||
|
|
||||||
|
use std::os::raw::{c_char, c_int};
|
||||||
|
|
||||||
|
use super::lua::lua_State;
|
||||||
|
|
||||||
|
pub const LUA_GLIBK: c_int = 1;
|
||||||
|
|
||||||
|
pub const LUA_LOADLIBNAME: *const c_char = cstr!("package");
|
||||||
|
pub const LUA_LOADLIBK: c_int = LUA_GLIBK << 1;
|
||||||
|
|
||||||
|
pub const LUA_COLIBNAME: *const c_char = cstr!("coroutine");
|
||||||
|
pub const LUA_COLIBK: c_int = LUA_GLIBK << 2;
|
||||||
|
|
||||||
|
pub const LUA_DBLIBNAME: *const c_char = cstr!("debug");
|
||||||
|
pub const LUA_DBLIBK: c_int = LUA_GLIBK << 3;
|
||||||
|
|
||||||
|
pub const LUA_IOLIBNAME: *const c_char = cstr!("io");
|
||||||
|
pub const LUA_IOLIBK: c_int = LUA_GLIBK << 4;
|
||||||
|
|
||||||
|
pub const LUA_MATHLIBNAME: *const c_char = cstr!("math");
|
||||||
|
pub const LUA_MATHLIBK: c_int = LUA_GLIBK << 5;
|
||||||
|
|
||||||
|
pub const LUA_OSLIBNAME: *const c_char = cstr!("os");
|
||||||
|
pub const LUA_OSLIBK: c_int = LUA_GLIBK << 6;
|
||||||
|
|
||||||
|
pub const LUA_STRLIBNAME: *const c_char = cstr!("string");
|
||||||
|
pub const LUA_STRLIBK: c_int = LUA_GLIBK << 7;
|
||||||
|
|
||||||
|
pub const LUA_TABLIBNAME: *const c_char = cstr!("table");
|
||||||
|
pub const LUA_TABLIBK: c_int = LUA_GLIBK << 8;
|
||||||
|
|
||||||
|
pub const LUA_UTF8LIBNAME: *const c_char = cstr!("utf8");
|
||||||
|
pub const LUA_UTF8LIBK: c_int = LUA_GLIBK << 9;
|
||||||
|
|
||||||
|
#[cfg_attr(all(windows, raw_dylib), link(name = "lua55", kind = "raw-dylib"))]
|
||||||
|
unsafe extern "C-unwind" {
|
||||||
|
pub fn luaopen_base(L: *mut lua_State) -> c_int;
|
||||||
|
pub fn luaopen_package(L: *mut lua_State) -> c_int;
|
||||||
|
pub fn luaopen_coroutine(L: *mut lua_State) -> c_int;
|
||||||
|
pub fn luaopen_debug(L: *mut lua_State) -> c_int;
|
||||||
|
pub fn luaopen_io(L: *mut lua_State) -> c_int;
|
||||||
|
pub fn luaopen_math(L: *mut lua_State) -> c_int;
|
||||||
|
pub fn luaopen_os(L: *mut lua_State) -> c_int;
|
||||||
|
pub fn luaopen_string(L: *mut lua_State) -> c_int;
|
||||||
|
pub fn luaopen_table(L: *mut lua_State) -> c_int;
|
||||||
|
pub fn luaopen_utf8(L: *mut lua_State) -> c_int;
|
||||||
|
|
||||||
|
// open all builtin libraries
|
||||||
|
pub fn luaL_openselectedlibs(L: *mut lua_State, load: c_int, preload: c_int);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn luaL_openlibs(L: *mut lua_State) {
|
||||||
|
luaL_openselectedlibs(L, !0, 0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
//! Low level bindings to Lua 5.5.
|
||||||
|
|
||||||
|
pub use lauxlib::*;
|
||||||
|
pub use lua::*;
|
||||||
|
pub use lualib::*;
|
||||||
|
|
||||||
|
pub mod lauxlib;
|
||||||
|
pub mod lua;
|
||||||
|
pub mod lualib;
|
||||||
+68
-47
@@ -23,8 +23,8 @@ unsafe fn compat53_reverse(L: *mut lua_State, mut a: c_int, mut b: c_int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const COMPAT53_LEVELS1: c_int = 12; // size of the first part of the stack
|
const COMPAT53_LEVELS1: c_int = 10; // size of the first part of the stack
|
||||||
const COMPAT53_LEVELS2: c_int = 10; // size of the second part of the stack
|
const COMPAT53_LEVELS2: c_int = 11; // size of the second part of the stack
|
||||||
|
|
||||||
unsafe fn compat53_findfield(L: *mut lua_State, objidx: c_int, level: c_int) -> c_int {
|
unsafe fn compat53_findfield(L: *mut lua_State, objidx: c_int, level: c_int) -> c_int {
|
||||||
if level == 0 || lua_istable(L, -1) == 0 {
|
if level == 0 || lua_istable(L, -1) == 0 {
|
||||||
@@ -41,11 +41,10 @@ unsafe fn compat53_findfield(L: *mut lua_State, objidx: c_int, level: c_int) ->
|
|||||||
lua_pop(L, 1); // remove value (but keep name)
|
lua_pop(L, 1); // remove value (but keep name)
|
||||||
return 1;
|
return 1;
|
||||||
} else if compat53_findfield(L, objidx, level - 1) != 0 {
|
} else if compat53_findfield(L, objidx, level - 1) != 0 {
|
||||||
// try recursively
|
// stack: lib_name, lib_table, field_name (top)
|
||||||
lua_remove(L, -2); // remove table (but keep name)
|
lua_pushliteral(L, c"."); // place '.' between the two names
|
||||||
lua_pushliteral(L, c".");
|
lua_replace(L, -3); // (in the slot occupied by table)
|
||||||
lua_insert(L, -2); // place '.' between the two names
|
lua_concat(L, 3); // lib_name.field_name
|
||||||
lua_concat(L, 3);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,14 +53,25 @@ unsafe fn compat53_findfield(L: *mut lua_State, objidx: c_int, level: c_int) ->
|
|||||||
0 // not found
|
0 // not found
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn compat53_pushglobalfuncname(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int {
|
unsafe fn compat53_pushglobalfuncname(
|
||||||
|
L: *mut lua_State,
|
||||||
|
L1: *mut lua_State,
|
||||||
|
level: c_int,
|
||||||
|
ar: *mut lua_Debug,
|
||||||
|
) -> c_int {
|
||||||
let top = lua_gettop(L);
|
let top = lua_gettop(L);
|
||||||
// push function
|
lua_getinfo(L1, level, cstr!("f"), ar); // push function
|
||||||
lua_getinfo(L, level, cstr!("f"), ar);
|
lua_xmove(L1, L, 1); // and move onto L
|
||||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||||
|
luaL_checkstack(L, 6, cstr!("not enough stack")); // slots for 'findfield'
|
||||||
if compat53_findfield(L, top + 1, 2) != 0 {
|
if compat53_findfield(L, top + 1, 2) != 0 {
|
||||||
|
let name = lua_tostring(L, -1);
|
||||||
|
if CStr::from_ptr(name).to_bytes().starts_with(b"_G.") {
|
||||||
|
lua_pushstring(L, name.add(3)); // push name without prefix
|
||||||
|
lua_remove(L, -2); // remove original name
|
||||||
|
}
|
||||||
lua_copy(L, -1, top + 1); // move name to proper place
|
lua_copy(L, -1, top + 1); // move name to proper place
|
||||||
lua_pop(L, 2); // remove pushed values
|
lua_settop(L, top + 1); // remove pushed values
|
||||||
1
|
1
|
||||||
} else {
|
} else {
|
||||||
lua_settop(L, top); // remove function and global table
|
lua_settop(L, top); // remove function and global table
|
||||||
@@ -69,13 +79,16 @@ unsafe fn compat53_pushglobalfuncname(L: *mut lua_State, level: c_int, ar: *mut
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn compat53_pushfuncname(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) {
|
unsafe fn compat53_pushfuncname(L: *mut lua_State, L1: *mut lua_State, level: c_int, ar: *mut lua_Debug) {
|
||||||
if !(*ar).name.is_null() {
|
if !(*ar).name.is_null() {
|
||||||
// is there a name?
|
// is there a name?
|
||||||
lua_pushfstring(L, cstr!("function '%s'"), (*ar).name);
|
lua_pushfstring(L, cstr!("function '%s'"), (*ar).name);
|
||||||
} else if compat53_pushglobalfuncname(L, level, ar) != 0 {
|
} else if compat53_pushglobalfuncname(L, L1, level, ar) != 0 {
|
||||||
lua_pushfstring(L, cstr!("function '%s'"), lua_tostring(L, -1));
|
lua_pushfstring(L, cstr!("function '%s'"), lua_tostring(L, -1));
|
||||||
lua_remove(L, -2); // remove name
|
lua_remove(L, -2); // remove name
|
||||||
|
} else if *(*ar).what != b'C' as c_char {
|
||||||
|
// for Lua functions, use <file:line>
|
||||||
|
lua_pushfstring(L, cstr!("function <%s:%d>"), (*ar).short_src, (*ar).linedefined);
|
||||||
} else {
|
} else {
|
||||||
lua_pushliteral(L, c"?");
|
lua_pushliteral(L, c"?");
|
||||||
}
|
}
|
||||||
@@ -120,7 +133,8 @@ pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
|
|||||||
if lua_type(L, idx) == LUA_TNUMBER {
|
if lua_type(L, idx) == LUA_TNUMBER {
|
||||||
let n = lua_tonumber(L, idx);
|
let n = lua_tonumber(L, idx);
|
||||||
let i = lua_tointeger(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;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,9 +203,7 @@ pub unsafe fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_in
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int {
|
pub unsafe fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int {
|
||||||
let abs_i = lua_absindex(L, idx);
|
lua_rawgetptagged(L, idx, p, 0)
|
||||||
lua_pushlightuserdata(L, p as *mut c_void);
|
|
||||||
lua_rawget(L, abs_i)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -225,11 +237,7 @@ pub unsafe fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer) {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void) {
|
pub unsafe fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void) {
|
||||||
let abs_i = lua_absindex(L, idx);
|
lua_rawsetptagged(L, idx, p, 0)
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -388,7 +396,7 @@ pub unsafe fn luaL_loadbufferenv(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if chunk_is_text {
|
let status = if chunk_is_text {
|
||||||
if env < 0 {
|
if env < 0 {
|
||||||
env -= 1;
|
env -= 1;
|
||||||
}
|
}
|
||||||
@@ -397,14 +405,21 @@ pub unsafe fn luaL_loadbufferenv(
|
|||||||
ptr::write(data_ud, data);
|
ptr::write(data_ud, data);
|
||||||
// By deferring the `free(data)` to the userdata destructor, we ensure that
|
// By deferring the `free(data)` to the userdata destructor, we ensure that
|
||||||
// even if `luau_load` throws an error, the `data` is still released.
|
// even if `luau_load` throws an error, the `data` is still released.
|
||||||
let ok = luau_load(L, name, data, size, env) == 0;
|
let status = luau_load(L, name, data, size, env);
|
||||||
lua_replace(L, -2); // replace data with the result
|
lua_replace(L, -2); // replace data with the result
|
||||||
if !ok {
|
status
|
||||||
return LUA_ERRSYNTAX;
|
} 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;
|
return LUA_ERRSYNTAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
LUA_OK
|
LUA_OK
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,36 +459,42 @@ pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
|
|||||||
|
|
||||||
pub unsafe fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, mut level: c_int) {
|
pub unsafe fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, mut level: c_int) {
|
||||||
let mut ar: lua_Debug = mem::zeroed();
|
let mut ar: lua_Debug = mem::zeroed();
|
||||||
let top = lua_gettop(L);
|
|
||||||
let numlevels = lua_stackdepth(L);
|
let numlevels = lua_stackdepth(L);
|
||||||
let mark = if numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2 {
|
#[rustfmt::skip]
|
||||||
COMPAT53_LEVELS1
|
let mut limit = if numlevels - level > COMPAT53_LEVELS1 + COMPAT53_LEVELS2 { COMPAT53_LEVELS1 } else { -1 };
|
||||||
} else {
|
|
||||||
0
|
let mut buf: luaL_Strbuf = mem::zeroed();
|
||||||
};
|
luaL_buffinit(L, &mut buf);
|
||||||
|
|
||||||
if !msg.is_null() {
|
if !msg.is_null() {
|
||||||
lua_pushfstring(L, cstr!("%s\n"), msg);
|
luaL_addstring(&mut buf, msg);
|
||||||
|
luaL_addstring(&mut buf, cstr!("\n"));
|
||||||
}
|
}
|
||||||
lua_pushliteral(L, c"stack traceback:");
|
luaL_addstring(&mut buf, cstr!("stack traceback:"));
|
||||||
while lua_getinfo(L1, level, cstr!(""), &mut ar) != 0 {
|
while lua_getinfo(L1, level, cstr!("sln"), &mut ar) != 0 {
|
||||||
if level + 1 == mark {
|
if limit == 0 {
|
||||||
// too many levels?
|
// too many levels?
|
||||||
lua_pushliteral(L, c"\n\t..."); // add a '...'
|
let n = numlevels - level - COMPAT53_LEVELS2;
|
||||||
level = numlevels - COMPAT53_LEVELS2; // and skip to last ones
|
// add warning about skip ("n + 1" because we skip current level too)
|
||||||
|
lua_pushfstring(L, cstr!("\n\t...\t(skipping %d levels)"), n + 1);
|
||||||
|
luaL_addvalue(&mut buf);
|
||||||
|
level += n; // and skip to last levels
|
||||||
} else {
|
} else {
|
||||||
lua_getinfo(L1, level, cstr!("sln"), &mut ar);
|
luaL_addstring(&mut buf, cstr!("\n\t"));
|
||||||
lua_pushfstring(L, cstr!("\n\t%s:"), ar.short_src);
|
luaL_addstring(&mut buf, ar.short_src);
|
||||||
|
luaL_addstring(&mut buf, cstr!(":"));
|
||||||
if ar.currentline > 0 {
|
if ar.currentline > 0 {
|
||||||
lua_pushfstring(L, cstr!("%d:"), ar.currentline);
|
luaL_addunsigned(&mut buf, ar.currentline as _);
|
||||||
|
luaL_addstring(&mut buf, cstr!(":"));
|
||||||
}
|
}
|
||||||
lua_pushliteral(L, c" in ");
|
luaL_addstring(&mut buf, cstr!(" in "));
|
||||||
compat53_pushfuncname(L, level, &mut ar);
|
compat53_pushfuncname(L, L1, level, &mut ar);
|
||||||
lua_concat(L, lua_gettop(L) - top);
|
luaL_addvalue(&mut buf);
|
||||||
}
|
}
|
||||||
level += 1;
|
level += 1;
|
||||||
|
limit -= 1;
|
||||||
}
|
}
|
||||||
lua_concat(L, lua_gettop(L) - top);
|
luaL_pushresult(&mut buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize) -> *const c_char {
|
pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize) -> *const c_char {
|
||||||
@@ -536,7 +557,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) {
|
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_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 {
|
if lua_getfield(L, -1, modname) == LUA_TNIL {
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
lua_pushcfunction(L, openf);
|
lua_pushcfunction(L, openf);
|
||||||
|
|||||||
@@ -3,7 +3,10 @@
|
|||||||
use std::os::raw::{c_char, c_float, c_int, c_void};
|
use std::os::raw::{c_char, c_float, c_int, c_void};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
use super::lua::{self, lua_CFunction, lua_Number, lua_State, lua_Unsigned, LUA_REGISTRYINDEX};
|
use super::lua::{self, LUA_REGISTRYINDEX, lua_CFunction, lua_Number, lua_State, lua_Unsigned};
|
||||||
|
|
||||||
|
// Key, in the registry, for table of loaded modules
|
||||||
|
pub const LUA_LOADED_TABLE: *const c_char = cstr!("_LOADED");
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct luaL_Reg {
|
pub struct luaL_Reg {
|
||||||
@@ -82,6 +85,9 @@ unsafe extern "C-unwind" {
|
|||||||
|
|
||||||
pub fn luaL_callyieldable(L: *mut lua_State, nargs: c_int, nresults: c_int) -> c_int;
|
pub fn luaL_callyieldable(L: *mut lua_State, nargs: c_int, nresults: c_int) -> c_int;
|
||||||
|
|
||||||
|
#[link_name = "luaL_traceback"]
|
||||||
|
pub fn luaL_traceback_(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, level: c_int);
|
||||||
|
|
||||||
// sandbox libraries and globals
|
// sandbox libraries and globals
|
||||||
#[link_name = "luaL_sandbox"]
|
#[link_name = "luaL_sandbox"]
|
||||||
pub fn luaL_sandbox_(L: *mut lua_State);
|
pub fn luaL_sandbox_(L: *mut lua_State);
|
||||||
@@ -116,7 +122,19 @@ pub unsafe fn luaL_optstring(L: *mut lua_State, n: c_int, d: *const c_char) -> *
|
|||||||
luaL_optlstring(L, n, d, ptr::null_mut())
|
luaL_optlstring(L, n, d, ptr::null_mut())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: luaL_opt
|
#[inline(always)]
|
||||||
|
pub unsafe fn luaL_opt<T>(
|
||||||
|
L: *mut lua_State,
|
||||||
|
f: unsafe extern "C-unwind" fn(*mut lua_State, c_int) -> T,
|
||||||
|
n: c_int,
|
||||||
|
d: T,
|
||||||
|
) -> T {
|
||||||
|
if lua::lua_isnoneornil(L, n) != 0 {
|
||||||
|
d
|
||||||
|
} else {
|
||||||
|
f(L, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) -> c_int {
|
pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) -> c_int {
|
||||||
@@ -209,3 +227,18 @@ pub unsafe fn luaL_addstring(B: *mut luaL_Strbuf, s: *const c_char) {
|
|||||||
}
|
}
|
||||||
luaL_addlstring(B, s, len);
|
luaL_addlstring(B, s, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub unsafe fn luaL_addunsigned(B: *mut luaL_Strbuf, mut n: lua_Unsigned) {
|
||||||
|
let mut buf: [c_char; 32] = [0; 32];
|
||||||
|
let mut i = 32;
|
||||||
|
loop {
|
||||||
|
i -= 1;
|
||||||
|
let digit = (n % 10) as u8;
|
||||||
|
buf[i] = (b'0' + digit) as c_char;
|
||||||
|
n /= 10;
|
||||||
|
if n == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
luaL_addlstring(B, buf.as_ptr().add(i), 32 - i);
|
||||||
|
}
|
||||||
|
|||||||
@@ -37,6 +37,16 @@ pub const LUA_ERRRUN: c_int = 2;
|
|||||||
pub const LUA_ERRSYNTAX: c_int = 3;
|
pub const LUA_ERRSYNTAX: c_int = 3;
|
||||||
pub const LUA_ERRMEM: c_int = 4;
|
pub const LUA_ERRMEM: c_int = 4;
|
||||||
pub const LUA_ERRERR: c_int = 5;
|
pub const LUA_ERRERR: c_int = 5;
|
||||||
|
pub const LUA_BREAK: c_int = 6; // yielded for a debug breakpoint
|
||||||
|
|
||||||
|
//
|
||||||
|
// Coroutine status
|
||||||
|
//
|
||||||
|
pub const LUA_CORUN: c_int = 0; // running
|
||||||
|
pub const LUA_COSUS: c_int = 1; // suspended
|
||||||
|
pub const LUA_CONOR: c_int = 2; // 'normal' (it resumed another coroutine)
|
||||||
|
pub const LUA_COFIN: c_int = 3; // finished
|
||||||
|
pub const LUA_COERR: c_int = 4; // finished with error
|
||||||
|
|
||||||
/// A raw Lua state associated with a thread.
|
/// A raw Lua state associated with a thread.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@@ -145,8 +155,15 @@ unsafe extern "C-unwind" {
|
|||||||
pub fn lua_toboolean(L: *mut lua_State, idx: c_int) -> c_int;
|
pub fn lua_toboolean(L: *mut lua_State, idx: c_int) -> c_int;
|
||||||
pub fn lua_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
|
pub fn lua_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
|
||||||
pub fn lua_tostringatom(L: *mut lua_State, idx: c_int, atom: *mut c_int) -> *const c_char;
|
pub fn lua_tostringatom(L: *mut lua_State, idx: c_int, atom: *mut c_int) -> *const c_char;
|
||||||
|
pub fn lua_tolstringatom(
|
||||||
|
L: *mut lua_State,
|
||||||
|
idx: c_int,
|
||||||
|
len: *mut usize,
|
||||||
|
atom: *mut c_int,
|
||||||
|
) -> *const c_char;
|
||||||
pub fn lua_namecallatom(L: *mut lua_State, atom: *mut c_int) -> *const c_char;
|
pub fn lua_namecallatom(L: *mut lua_State, atom: *mut c_int) -> *const c_char;
|
||||||
pub fn lua_objlen(L: *mut lua_State, idx: c_int) -> usize;
|
#[link_name = "lua_objlen"]
|
||||||
|
pub fn lua_objlen_(L: *mut lua_State, idx: c_int) -> c_int;
|
||||||
pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> Option<lua_CFunction>;
|
pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> Option<lua_CFunction>;
|
||||||
pub fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
|
pub fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
|
||||||
pub fn lua_tolightuserdatatagged(L: *mut lua_State, idx: c_int, tag: c_int) -> *mut c_void;
|
pub fn lua_tolightuserdatatagged(L: *mut lua_State, idx: c_int, tag: c_int) -> *mut c_void;
|
||||||
@@ -203,6 +220,7 @@ unsafe extern "C-unwind" {
|
|||||||
pub fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int;
|
pub fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int;
|
||||||
#[link_name = "lua_rawgeti"]
|
#[link_name = "lua_rawgeti"]
|
||||||
pub fn lua_rawgeti_(L: *mut lua_State, idx: c_int, n: c_int) -> c_int;
|
pub fn lua_rawgeti_(L: *mut lua_State, idx: c_int, n: c_int) -> c_int;
|
||||||
|
pub fn lua_rawgetptagged(L: *mut lua_State, idx: c_int, p: *const c_void, tag: c_int) -> c_int;
|
||||||
pub fn lua_createtable(L: *mut lua_State, narr: c_int, nrec: c_int);
|
pub fn lua_createtable(L: *mut lua_State, narr: c_int, nrec: c_int);
|
||||||
|
|
||||||
pub fn lua_setreadonly(L: *mut lua_State, idx: c_int, enabled: c_int);
|
pub fn lua_setreadonly(L: *mut lua_State, idx: c_int, enabled: c_int);
|
||||||
@@ -217,9 +235,11 @@ unsafe extern "C-unwind" {
|
|||||||
//
|
//
|
||||||
pub fn lua_settable(L: *mut lua_State, idx: c_int);
|
pub fn lua_settable(L: *mut lua_State, idx: c_int);
|
||||||
pub fn lua_setfield(L: *mut lua_State, idx: c_int, k: *const c_char);
|
pub fn lua_setfield(L: *mut lua_State, idx: c_int, k: *const c_char);
|
||||||
|
pub fn lua_rawsetfield(L: *mut lua_State, idx: c_int, k: *const c_char);
|
||||||
pub fn lua_rawset(L: *mut lua_State, idx: c_int);
|
pub fn lua_rawset(L: *mut lua_State, idx: c_int);
|
||||||
#[link_name = "lua_rawseti"]
|
#[link_name = "lua_rawseti"]
|
||||||
pub fn lua_rawseti_(L: *mut lua_State, idx: c_int, n: c_int);
|
pub fn lua_rawseti_(L: *mut lua_State, idx: c_int, n: c_int);
|
||||||
|
pub fn lua_rawsetptagged(L: *mut lua_State, idx: c_int, p: *const c_void, tag: c_int);
|
||||||
pub fn lua_setmetatable(L: *mut lua_State, objindex: c_int) -> c_int;
|
pub fn lua_setmetatable(L: *mut lua_State, objindex: c_int) -> c_int;
|
||||||
pub fn lua_setfenv(L: *mut lua_State, idx: c_int) -> c_int;
|
pub fn lua_setfenv(L: *mut lua_State, idx: c_int) -> c_int;
|
||||||
|
|
||||||
@@ -235,6 +255,7 @@ unsafe extern "C-unwind" {
|
|||||||
) -> c_int;
|
) -> c_int;
|
||||||
pub fn lua_call(L: *mut lua_State, nargs: c_int, nresults: c_int);
|
pub fn lua_call(L: *mut lua_State, nargs: c_int, nresults: c_int);
|
||||||
pub fn lua_pcall(L: *mut lua_State, nargs: c_int, nresults: c_int, errfunc: c_int) -> c_int;
|
pub fn lua_pcall(L: *mut lua_State, nargs: c_int, nresults: c_int, errfunc: c_int) -> c_int;
|
||||||
|
pub fn lua_cpcall(L: *mut lua_State, f: lua_CFunction, ud: *mut c_void) -> c_int;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Coroutine functions
|
// Coroutine functions
|
||||||
@@ -248,6 +269,12 @@ unsafe extern "C-unwind" {
|
|||||||
pub fn lua_isyieldable(L: *mut lua_State) -> c_int;
|
pub fn lua_isyieldable(L: *mut lua_State) -> c_int;
|
||||||
pub fn lua_getthreaddata(L: *mut lua_State) -> *mut c_void;
|
pub fn lua_getthreaddata(L: *mut lua_State) -> *mut c_void;
|
||||||
pub fn lua_setthreaddata(L: *mut lua_State, data: *mut c_void);
|
pub fn lua_setthreaddata(L: *mut lua_State, data: *mut c_void);
|
||||||
|
pub fn lua_costatus(L: *mut lua_State, co: *mut lua_State) -> c_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_objlen(L: *mut lua_State, idx: c_int) -> usize {
|
||||||
|
lua_objlen_(L, idx) as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -284,7 +311,7 @@ unsafe extern "C-unwind" {
|
|||||||
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
|
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
|
||||||
pub fn lua_rawiter(L: *mut lua_State, idx: c_int, iter: c_int) -> c_int;
|
pub fn lua_rawiter(L: *mut lua_State, idx: c_int, iter: c_int) -> c_int;
|
||||||
pub fn lua_concat(L: *mut lua_State, n: c_int);
|
pub fn lua_concat(L: *mut lua_State, n: c_int);
|
||||||
// TODO: lua_encodepointer
|
pub fn lua_encodepointer(L: *mut lua_State, p: usize) -> usize;
|
||||||
pub fn lua_clock() -> c_double;
|
pub fn lua_clock() -> c_double;
|
||||||
pub fn lua_setuserdatatag(L: *mut lua_State, idx: c_int, tag: c_int);
|
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_setuserdatadtor(L: *mut lua_State, tag: c_int, dtor: Option<lua_Destructor>);
|
||||||
@@ -295,6 +322,7 @@ unsafe extern "C-unwind" {
|
|||||||
pub fn lua_getlightuserdataname(L: *mut lua_State, tag: c_int) -> *const c_char;
|
pub fn lua_getlightuserdataname(L: *mut lua_State, tag: c_int) -> *const c_char;
|
||||||
pub fn lua_clonefunction(L: *mut lua_State, idx: c_int);
|
pub fn lua_clonefunction(L: *mut lua_State, idx: c_int);
|
||||||
pub fn lua_cleartable(L: *mut lua_State, idx: c_int);
|
pub fn lua_cleartable(L: *mut lua_State, idx: c_int);
|
||||||
|
pub fn lua_clonetable(L: *mut lua_State, idx: c_int);
|
||||||
pub fn lua_getallocf(L: *mut lua_State, ud: *mut *mut c_void) -> lua_Alloc;
|
pub fn lua_getallocf(L: *mut lua_State, ud: *mut *mut c_void) -> lua_Alloc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,7 +382,10 @@ pub unsafe fn lua_newuserdata_t<T>(L: *mut lua_State, data: T) -> *mut T {
|
|||||||
ud_ptr
|
ud_ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: lua_strlen
|
#[inline(always)]
|
||||||
|
pub unsafe fn lua_strlen(L: *mut lua_State, i: c_int) -> usize {
|
||||||
|
lua_objlen(L, i)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn lua_isfunction(L: *mut lua_State, n: c_int) -> c_int {
|
pub unsafe fn lua_isfunction(L: *mut lua_State, n: c_int) -> c_int {
|
||||||
@@ -544,4 +575,9 @@ unsafe extern "C" {
|
|||||||
unsafe extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn luau_setfflag(name: *const c_char, value: c_int) -> c_int;
|
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;
|
pub fn lua_getmetatablepointer(L: *mut lua_State, idx: c_int) -> *const c_void;
|
||||||
|
pub fn lua_gcdump(
|
||||||
|
L: *mut lua_State,
|
||||||
|
file: *mut c_void,
|
||||||
|
category_name: Option<unsafe extern "C" fn(L: *mut lua_State, memcat: u8) -> *const c_char>,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,14 +23,27 @@ pub enum luarequire_WriteResult {
|
|||||||
Failure,
|
Failure,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents whether a configuration file is present, and if so, its syntax.
|
||||||
|
#[repr(C)]
|
||||||
|
pub enum luarequire_ConfigStatus {
|
||||||
|
Absent,
|
||||||
|
// Signals the presence of multiple configuration files
|
||||||
|
Ambiguous,
|
||||||
|
PresentJson,
|
||||||
|
PresentLuau,
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct luarequire_Configuration {
|
pub struct luarequire_Configuration {
|
||||||
// Returns whether requires are permitted from the given chunkname.
|
// Returns whether requires are permitted from the given chunkname.
|
||||||
pub is_require_allowed:
|
pub is_require_allowed: unsafe extern "C-unwind" fn(
|
||||||
unsafe extern "C" fn(L: *mut lua_State, ctx: *mut c_void, requirer_chunkname: *const c_char) -> bool,
|
L: *mut lua_State,
|
||||||
|
ctx: *mut c_void,
|
||||||
|
requirer_chunkname: *const c_char,
|
||||||
|
) -> bool,
|
||||||
|
|
||||||
// Resets the internal state to point at the requirer module.
|
// Resets the internal state to point at the requirer module.
|
||||||
pub reset: unsafe extern "C" fn(
|
pub reset: unsafe extern "C-unwind" fn(
|
||||||
L: *mut lua_State,
|
L: *mut lua_State,
|
||||||
ctx: *mut c_void,
|
ctx: *mut c_void,
|
||||||
requirer_chunkname: *const c_char,
|
requirer_chunkname: *const c_char,
|
||||||
@@ -39,26 +52,50 @@ pub struct luarequire_Configuration {
|
|||||||
// Resets the internal state to point at an aliased module, given its exact path from a configuration
|
// 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
|
// file. This function is only called when an alias's path cannot be resolved relative to its
|
||||||
// configuration file.
|
// configuration file.
|
||||||
pub jump_to_alias: unsafe extern "C" fn(
|
pub jump_to_alias: unsafe extern "C-unwind" fn(
|
||||||
L: *mut lua_State,
|
L: *mut lua_State,
|
||||||
ctx: *mut c_void,
|
ctx: *mut c_void,
|
||||||
path: *const c_char,
|
path: *const c_char,
|
||||||
) -> luarequire_NavigateResult,
|
) -> luarequire_NavigateResult,
|
||||||
|
|
||||||
|
// Provides an initial alias override opportunity prior to searching for configuration files.
|
||||||
|
// If NAVIGATE_SUCCESS is returned, the internal state must be updated to point at the
|
||||||
|
// aliased location.
|
||||||
|
// Can be left undefined.
|
||||||
|
pub to_alias_override: Option<
|
||||||
|
unsafe extern "C-unwind" fn(
|
||||||
|
L: *mut lua_State,
|
||||||
|
ctx: *mut c_void,
|
||||||
|
alias_unprefixed: *const c_char,
|
||||||
|
) -> luarequire_NavigateResult,
|
||||||
|
>,
|
||||||
|
|
||||||
|
// Provides a final override opportunity if an alias cannot be found in configuration files. If
|
||||||
|
// NAVIGATE_SUCCESS is returned, this must update the internal state to point at the aliased module.
|
||||||
|
// Can be left undefined.
|
||||||
|
pub to_alias_fallback: Option<
|
||||||
|
unsafe extern "C-unwind" fn(
|
||||||
|
L: *mut lua_State,
|
||||||
|
ctx: *mut c_void,
|
||||||
|
alias_unprefixed: *const c_char,
|
||||||
|
) -> luarequire_NavigateResult,
|
||||||
|
>,
|
||||||
|
|
||||||
// Navigates through the context by making mutations to the internal state.
|
// Navigates through the context by making mutations to the internal state.
|
||||||
pub to_parent: unsafe extern "C" fn(L: *mut lua_State, ctx: *mut c_void) -> luarequire_NavigateResult,
|
pub to_parent:
|
||||||
pub to_child: unsafe extern "C" fn(
|
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,
|
L: *mut lua_State,
|
||||||
ctx: *mut c_void,
|
ctx: *mut c_void,
|
||||||
name: *const c_char,
|
name: *const c_char,
|
||||||
) -> luarequire_NavigateResult,
|
) -> luarequire_NavigateResult,
|
||||||
|
|
||||||
// Returns whether the context is currently pointing at a module.
|
// Returns whether the context is currently pointing at a module.
|
||||||
pub is_module_present: unsafe extern "C" fn(L: *mut lua_State, ctx: *mut c_void) -> bool,
|
pub is_module_present: unsafe extern "C-unwind" fn(L: *mut lua_State, ctx: *mut c_void) -> bool,
|
||||||
|
|
||||||
// Provides the contents of the current module. This function is only called if is_module_present returns
|
// Provides a chunkname for the current module. This will be accessible through the debug library. This
|
||||||
// true.
|
// function is only called if is_module_present returns true.
|
||||||
pub get_contents: unsafe extern "C" fn(
|
pub get_chunkname: unsafe extern "C-unwind" fn(
|
||||||
L: *mut lua_State,
|
L: *mut lua_State,
|
||||||
ctx: *mut c_void,
|
ctx: *mut c_void,
|
||||||
buffer: *mut c_char,
|
buffer: *mut c_char,
|
||||||
@@ -66,9 +103,9 @@ pub struct luarequire_Configuration {
|
|||||||
size_out: *mut usize,
|
size_out: *mut usize,
|
||||||
) -> luarequire_WriteResult,
|
) -> luarequire_WriteResult,
|
||||||
|
|
||||||
// Provides a chunkname for the current module. This will be accessible through the debug library. This
|
// Provides a loadname that identifies the current module and is passed to load. This function
|
||||||
// function is only called if is_module_present returns true.
|
// is only called if is_module_present returns true.
|
||||||
pub get_chunkname: unsafe extern "C" fn(
|
pub get_loadname: unsafe extern "C-unwind" fn(
|
||||||
L: *mut lua_State,
|
L: *mut lua_State,
|
||||||
ctx: *mut c_void,
|
ctx: *mut c_void,
|
||||||
buffer: *mut c_char,
|
buffer: *mut c_char,
|
||||||
@@ -78,7 +115,7 @@ pub struct luarequire_Configuration {
|
|||||||
|
|
||||||
// Provides a cache key representing the current module. This function is only called if
|
// Provides a cache key representing the current module. This function is only called if
|
||||||
// is_module_present returns true.
|
// is_module_present returns true.
|
||||||
pub get_cache_key: unsafe extern "C" fn(
|
pub get_cache_key: unsafe extern "C-unwind" fn(
|
||||||
L: *mut lua_State,
|
L: *mut lua_State,
|
||||||
ctx: *mut c_void,
|
ctx: *mut c_void,
|
||||||
buffer: *mut c_char,
|
buffer: *mut c_char,
|
||||||
@@ -86,20 +123,48 @@ pub struct luarequire_Configuration {
|
|||||||
size_out: *mut usize,
|
size_out: *mut usize,
|
||||||
) -> luarequire_WriteResult,
|
) -> luarequire_WriteResult,
|
||||||
|
|
||||||
// Returns whether a configuration file is present in the current context.
|
// Returns whether a configuration file is present in the current context, and if so, its syntax.
|
||||||
// If not, require-by-string will call to_parent until either a configuration file is present or
|
// If not present, require-by-string will call to_parent until either a configuration file is present or
|
||||||
// NAVIGATE_FAILURE is returned (at root).
|
// NAVIGATE_FAILURE is returned (at root).
|
||||||
pub is_config_present: unsafe extern "C" fn(L: *mut lua_State, ctx: *mut c_void) -> bool,
|
pub get_config_status:
|
||||||
|
unsafe extern "C-unwind" fn(L: *mut lua_State, ctx: *mut c_void) -> luarequire_ConfigStatus,
|
||||||
|
|
||||||
|
// 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 get_config_status
|
||||||
|
// 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.
|
// Provides the contents of the configuration file in the current context.
|
||||||
// This function is only called if is_config_present returns true.
|
// This function is only called if get_config_status does not return CONFIG_ABSENT. If this function
|
||||||
pub get_config: unsafe extern "C" fn(
|
// pointer is set, get_alias must not be set. Opting in to this function pointer enables parsing
|
||||||
L: *mut lua_State,
|
// configuration files internally.
|
||||||
ctx: *mut c_void,
|
pub get_config: Option<
|
||||||
buffer: *mut c_char,
|
unsafe extern "C-unwind" fn(
|
||||||
buffer_size: usize,
|
L: *mut lua_State,
|
||||||
size_out: *mut usize,
|
ctx: *mut c_void,
|
||||||
) -> luarequire_WriteResult,
|
buffer: *mut c_char,
|
||||||
|
buffer_size: usize,
|
||||||
|
size_out: *mut usize,
|
||||||
|
) -> luarequire_WriteResult,
|
||||||
|
>,
|
||||||
|
|
||||||
|
// Returns the maximum number of milliseconds to allow for executing a given Luau-syntax configuration
|
||||||
|
// file. This function is only called if get_config_status returns CONFIG_PRESENT_LUAU and can be left
|
||||||
|
// undefined if support for Luau-syntax configuration files is not needed. A default value of 2000ms is
|
||||||
|
// used. Negative values are treated as infinite.
|
||||||
|
pub get_luau_config_timeout:
|
||||||
|
Option<unsafe extern "C-unwind" fn(L: *mut lua_State, ctx: *mut c_void) -> c_int>,
|
||||||
|
|
||||||
// Executes the module and places the result on the stack. Returns the number of results placed on the
|
// Executes the module and places the result on the stack. Returns the number of results placed on the
|
||||||
// stack.
|
// stack.
|
||||||
@@ -110,12 +175,12 @@ pub struct luarequire_Configuration {
|
|||||||
ctx: *mut c_void,
|
ctx: *mut c_void,
|
||||||
path: *const c_char,
|
path: *const c_char,
|
||||||
chunkname: *const c_char,
|
chunkname: *const c_char,
|
||||||
contents: *const c_char,
|
loadname: *const c_char,
|
||||||
) -> c_int,
|
) -> c_int,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populates function pointers in the given luarequire_Configuration.
|
// Populates function pointers in the given luarequire_Configuration.
|
||||||
pub type luarequire_Configuration_init = unsafe extern "C" fn(config: *mut luarequire_Configuration);
|
pub type luarequire_Configuration_init = unsafe extern "C-unwind" fn(config: *mut luarequire_Configuration);
|
||||||
|
|
||||||
unsafe extern "C-unwind" {
|
unsafe extern "C-unwind" {
|
||||||
// Initializes and pushes the require closure onto the stack without registration.
|
// Initializes and pushes the require closure onto the stack without registration.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mlua_derive"
|
name = "mlua_derive"
|
||||||
version = "0.11.0-beta.1"
|
version = "0.11.0"
|
||||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Procedural macros for the mlua crate."
|
description = "Procedural macros for the mlua crate."
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
|||||||
Some(ChunkMode::Text)
|
Some(ChunkMode::Text)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn source<'a>(self) -> IoResult<Cow<'a, [u8]>> {
|
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>> {
|
||||||
Ok(Cow::Borrowed((#source).as_bytes()))
|
Ok(Cow::Borrowed((#source).as_bytes()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,13 +129,13 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
|||||||
let globals = lua.globals();
|
let globals = lua.globals();
|
||||||
let env = lua.create_table()?;
|
let env = lua.create_table()?;
|
||||||
let meta = lua.create_table()?;
|
let meta = lua.create_table()?;
|
||||||
meta.raw_set("__index", globals.clone())?;
|
meta.raw_set("__index", &globals)?;
|
||||||
meta.raw_set("__newindex", globals)?;
|
meta.raw_set("__newindex", &globals)?;
|
||||||
|
|
||||||
// Add captured variables
|
// Add captured variables
|
||||||
#(#caps)*
|
#(#caps)*
|
||||||
|
|
||||||
env.set_metatable(Some(meta));
|
env.set_metatable(Some(meta))?;
|
||||||
Ok(env)
|
Ok(env)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+98
-17
@@ -1,6 +1,9 @@
|
|||||||
#[cfg(feature = "serialize")]
|
use std::io;
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
use serde::ser::{Serialize, Serializer};
|
use serde::ser::{Serialize, Serializer};
|
||||||
|
|
||||||
|
use crate::state::RawLua;
|
||||||
use crate::types::ValueRef;
|
use crate::types::ValueRef;
|
||||||
|
|
||||||
/// A Luau buffer type.
|
/// A Luau buffer type.
|
||||||
@@ -16,16 +19,17 @@ pub struct Buffer(pub(crate) ValueRef);
|
|||||||
impl Buffer {
|
impl Buffer {
|
||||||
/// Copies the buffer data into a new `Vec<u8>`.
|
/// Copies the buffer data into a new `Vec<u8>`.
|
||||||
pub fn to_vec(&self) -> Vec<u8> {
|
pub fn to_vec(&self) -> Vec<u8> {
|
||||||
unsafe { self.as_slice().to_vec() }
|
let lua = self.0.lua.lock();
|
||||||
|
self.as_slice(&lua).to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the length of the buffer.
|
/// Returns the length of the buffer.
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
unsafe { self.as_slice().len() }
|
let lua = self.0.lua.lock();
|
||||||
|
self.as_slice(&lua).len()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the buffer is empty.
|
/// Returns `true` if the buffer is empty.
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.len() == 0
|
self.len() == 0
|
||||||
}
|
}
|
||||||
@@ -35,7 +39,8 @@ impl Buffer {
|
|||||||
/// Offset is 0-based.
|
/// Offset is 0-based.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn read_bytes<const N: usize>(&self, offset: usize) -> [u8; N] {
|
pub fn read_bytes<const N: usize>(&self, offset: usize) -> [u8; N] {
|
||||||
let data = unsafe { self.as_slice() };
|
let lua = self.0.lua.lock();
|
||||||
|
let data = self.as_slice(&lua);
|
||||||
let mut bytes = [0u8; N];
|
let mut bytes = [0u8; N];
|
||||||
bytes.copy_from_slice(&data[offset..offset + N]);
|
bytes.copy_from_slice(&data[offset..offset + N]);
|
||||||
bytes
|
bytes
|
||||||
@@ -46,21 +51,36 @@ impl Buffer {
|
|||||||
/// Offset is 0-based.
|
/// Offset is 0-based.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn write_bytes(&self, offset: usize, bytes: &[u8]) {
|
pub fn write_bytes(&self, offset: usize, bytes: &[u8]) {
|
||||||
let data = unsafe {
|
let lua = self.0.lua.lock();
|
||||||
let (buf, size) = self.as_raw_parts();
|
let data = self.as_slice_mut(&lua);
|
||||||
std::slice::from_raw_parts_mut(buf, size)
|
|
||||||
};
|
|
||||||
data[offset..offset + bytes.len()].copy_from_slice(bytes);
|
data[offset..offset + bytes.len()].copy_from_slice(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn as_slice(&self) -> &[u8] {
|
/// Returns an adaptor implementing [`io::Read`], [`io::Write`] and [`io::Seek`] over the
|
||||||
let (buf, size) = self.as_raw_parts();
|
/// buffer.
|
||||||
std::slice::from_raw_parts(buf, size)
|
///
|
||||||
|
/// Buffer operations are infallible, none of the read/write functions will return a Err.
|
||||||
|
pub fn cursor(self) -> impl io::Read + io::Write + io::Seek {
|
||||||
|
BufferCursor(self, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::mut_from_ref)]
|
||||||
|
fn as_slice_mut(&self, lua: &RawLua) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
let (buf, size) = self.as_raw_parts(lua);
|
||||||
|
std::slice::from_raw_parts_mut(buf, size)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
unsafe fn as_raw_parts(&self) -> (*mut u8, usize) {
|
unsafe fn as_raw_parts(&self, lua: &RawLua) -> (*mut u8, usize) {
|
||||||
let lua = self.0.lua.lock();
|
|
||||||
let mut size = 0usize;
|
let mut size = 0usize;
|
||||||
let buf = ffi::lua_tobuffer(lua.ref_thread(), self.0.index, &mut size);
|
let buf = ffi::lua_tobuffer(lua.ref_thread(), self.0.index, &mut size);
|
||||||
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
|
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
|
||||||
@@ -68,15 +88,76 @@ impl Buffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
unsafe fn as_raw_parts(&self) -> (*mut u8, usize) {
|
unsafe fn as_raw_parts(&self, lua: &RawLua) -> (*mut u8, usize) {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
struct BufferCursor(Buffer, usize);
|
||||||
|
|
||||||
|
impl io::Read for BufferCursor {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
let lua = self.0.0.lua.lock();
|
||||||
|
let data = self.0.as_slice(&lua);
|
||||||
|
if self.1 == data.len() {
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
let len = buf.len().min(data.len() - self.1);
|
||||||
|
buf[..len].copy_from_slice(&data[self.1..self.1 + len]);
|
||||||
|
self.1 += len;
|
||||||
|
Ok(len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl io::Write for BufferCursor {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
let lua = self.0.0.lua.lock();
|
||||||
|
let data = self.0.as_slice_mut(&lua);
|
||||||
|
if self.1 == data.len() {
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
let len = buf.len().min(data.len() - self.1);
|
||||||
|
data[self.1..self.1 + len].copy_from_slice(&buf[..len]);
|
||||||
|
self.1 += len;
|
||||||
|
Ok(len)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl io::Seek for BufferCursor {
|
||||||
|
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
|
||||||
|
let lua = self.0.0.lua.lock();
|
||||||
|
let data = self.0.as_slice(&lua);
|
||||||
|
let new_offset = match pos {
|
||||||
|
io::SeekFrom::Start(offset) => offset as i64,
|
||||||
|
io::SeekFrom::End(offset) => data.len() as i64 + offset,
|
||||||
|
io::SeekFrom::Current(offset) => self.1 as i64 + offset,
|
||||||
|
};
|
||||||
|
if new_offset < 0 {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"invalid seek to a negative position",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if new_offset as usize > data.len() {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"invalid seek to a position beyond the end of the buffer",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
self.1 = new_offset as usize;
|
||||||
|
Ok(self.1 as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
impl Serialize for Buffer {
|
impl Serialize for Buffer {
|
||||||
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
|
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
|
||||||
serializer.serialize_bytes(unsafe { self.as_slice() })
|
let lua = self.0.lua.lock();
|
||||||
|
serializer.serialize_bytes(self.as_slice(&lua))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+161
-91
@@ -4,7 +4,6 @@ use std::ffi::CString;
|
|||||||
use std::io::Result as IoResult;
|
use std::io::Result as IoResult;
|
||||||
use std::panic::Location;
|
use std::panic::Location;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::string::String as StdString;
|
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::function::Function;
|
use crate::function::Function;
|
||||||
@@ -20,7 +19,7 @@ pub trait AsChunk {
|
|||||||
/// Returns optional chunk name
|
/// Returns optional chunk name
|
||||||
///
|
///
|
||||||
/// See [`Chunk::set_name`] for possible name prefixes.
|
/// See [`Chunk::set_name`] for possible name prefixes.
|
||||||
fn name(&self) -> Option<StdString> {
|
fn name(&self) -> Option<String> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,28 +37,28 @@ pub trait AsChunk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns chunk data (can be text or binary)
|
/// Returns chunk data (can be text or binary)
|
||||||
fn source<'a>(self) -> IoResult<Cow<'a, [u8]>>
|
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>>
|
||||||
where
|
where
|
||||||
Self: 'a;
|
Self: 'a;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsChunk for &str {
|
impl AsChunk for &str {
|
||||||
fn source<'a>(self) -> IoResult<Cow<'a, [u8]>>
|
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>>
|
||||||
where
|
where
|
||||||
Self: 'a,
|
Self: 'a,
|
||||||
{
|
{
|
||||||
Ok(Cow::Borrowed(self.as_ref()))
|
Ok(Cow::Borrowed(self.as_bytes()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsChunk for StdString {
|
impl AsChunk for String {
|
||||||
fn source<'a>(self) -> IoResult<Cow<'a, [u8]>> {
|
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>> {
|
||||||
Ok(Cow::Owned(self.into_bytes()))
|
Ok(Cow::Owned(self.clone().into_bytes()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsChunk for &StdString {
|
impl AsChunk for &String {
|
||||||
fn source<'a>(self) -> IoResult<Cow<'a, [u8]>>
|
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>>
|
||||||
where
|
where
|
||||||
Self: 'a,
|
Self: 'a,
|
||||||
{
|
{
|
||||||
@@ -68,7 +67,7 @@ impl AsChunk for &StdString {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AsChunk for &[u8] {
|
impl AsChunk for &[u8] {
|
||||||
fn source<'a>(self) -> IoResult<Cow<'a, [u8]>>
|
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>>
|
||||||
where
|
where
|
||||||
Self: 'a,
|
Self: 'a,
|
||||||
{
|
{
|
||||||
@@ -77,13 +76,13 @@ impl AsChunk for &[u8] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AsChunk for Vec<u8> {
|
impl AsChunk for Vec<u8> {
|
||||||
fn source<'a>(self) -> IoResult<Cow<'a, [u8]>> {
|
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>> {
|
||||||
Ok(Cow::Owned(self))
|
Ok(Cow::Owned(self.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsChunk for &Vec<u8> {
|
impl AsChunk for &Vec<u8> {
|
||||||
fn source<'a>(self) -> IoResult<Cow<'a, [u8]>>
|
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>>
|
||||||
where
|
where
|
||||||
Self: 'a,
|
Self: 'a,
|
||||||
{
|
{
|
||||||
@@ -92,30 +91,51 @@ impl AsChunk for &Vec<u8> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AsChunk for &Path {
|
impl AsChunk for &Path {
|
||||||
fn name(&self) -> Option<StdString> {
|
fn name(&self) -> Option<String> {
|
||||||
Some(format!("@{}", self.display()))
|
Some(format!("@{}", self.display()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn source<'a>(self) -> IoResult<Cow<'a, [u8]>> {
|
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>> {
|
||||||
std::fs::read(self).map(Cow::Owned)
|
std::fs::read(self).map(Cow::Owned)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsChunk for PathBuf {
|
impl AsChunk for PathBuf {
|
||||||
fn name(&self) -> Option<StdString> {
|
fn name(&self) -> Option<String> {
|
||||||
Some(format!("@{}", self.display()))
|
Some(format!("@{}", self.display()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn source<'a>(self) -> IoResult<Cow<'a, [u8]>> {
|
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>> {
|
||||||
std::fs::read(self).map(Cow::Owned)
|
std::fs::read(self).map(Cow::Owned)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<C: AsChunk + ?Sized> AsChunk for Box<C> {
|
||||||
|
fn name(&self) -> Option<String> {
|
||||||
|
(**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.
|
/// Returned from [`Lua::load`] and is used to finalize loading and executing Lua main chunks.
|
||||||
#[must_use = "`Chunk`s do nothing unless one of `exec`, `eval`, `call`, or `into_function` are called on them"]
|
#[must_use = "`Chunk`s do nothing unless one of `exec`, `eval`, `call`, or `into_function` are called on them"]
|
||||||
pub struct Chunk<'a> {
|
pub struct Chunk<'a> {
|
||||||
pub(crate) lua: WeakLua,
|
pub(crate) lua: WeakLua,
|
||||||
pub(crate) name: StdString,
|
pub(crate) name: String,
|
||||||
pub(crate) env: Result<Option<Table>>,
|
pub(crate) env: Result<Option<Table>>,
|
||||||
pub(crate) mode: Option<ChunkMode>,
|
pub(crate) mode: Option<ChunkMode>,
|
||||||
pub(crate) source: IoResult<Cow<'a, [u8]>>,
|
pub(crate) source: IoResult<Cow<'a, [u8]>>,
|
||||||
@@ -142,15 +162,36 @@ pub enum CompileConstant {
|
|||||||
String(String),
|
String(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(any(feature = "luau", doc))]
|
||||||
impl From<&'static str> for CompileConstant {
|
impl From<bool> for CompileConstant {
|
||||||
fn from(s: &'static str) -> Self {
|
fn from(b: bool) -> Self {
|
||||||
CompileConstant::String(s.to_string())
|
CompileConstant::Boolean(b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "luau", doc))]
|
#[cfg(any(feature = "luau", doc))]
|
||||||
type LibraryMemberConstantMap = std::sync::Arc<HashMap<(String, String), CompileConstant>>;
|
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<(String, String), CompileConstant>;
|
||||||
|
|
||||||
/// Luau compiler
|
/// Luau compiler
|
||||||
#[cfg(any(feature = "luau", doc))]
|
#[cfg(any(feature = "luau", doc))]
|
||||||
@@ -246,20 +287,23 @@ impl Compiler {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
/// Sets alternative global builtin to construct vectors, in addition to default builtin
|
||||||
#[must_use]
|
/// `vector.create`.
|
||||||
pub fn set_vector_lib(mut self, lib: impl Into<String>) -> Self {
|
///
|
||||||
self.vector_lib = Some(lib.into());
|
/// To set the library and method name, use the `lib.ctor` format.
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn set_vector_ctor(mut self, ctor: impl Into<String>) -> Self {
|
pub fn set_vector_ctor(mut self, ctor: impl Into<String>) -> Self {
|
||||||
self.vector_ctor = Some(ctor.into());
|
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
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets alternative vector type name for type tables, in addition to default type `vector`.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[must_use]
|
#[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<String>) -> Self {
|
||||||
@@ -267,49 +311,75 @@ impl Compiler {
|
|||||||
self
|
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<String>) -> Self {
|
||||||
|
self.mutable_globals.push(global.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets a list of globals that are mutable.
|
/// Sets a list of globals that are mutable.
|
||||||
///
|
///
|
||||||
/// It disables the import optimization for fields accessed through these.
|
/// It disables the import optimization for fields accessed through these.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn set_mutable_globals<S: Into<String>>(mut self, globals: Vec<S>) -> Self {
|
pub fn set_mutable_globals<S: Into<String>>(mut self, globals: impl IntoIterator<Item = S>) -> Self {
|
||||||
self.mutable_globals = globals.into_iter().map(|s| s.into()).collect();
|
self.mutable_globals = globals.into_iter().map(|s| s.into()).collect();
|
||||||
self
|
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<String>) -> Self {
|
||||||
|
self.userdata_types.push(r#type.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets a list of userdata types that will be included in the type information.
|
/// Sets a list of userdata types that will be included in the type information.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn set_userdata_types<S: Into<String>>(mut self, types: Vec<S>) -> Self {
|
pub fn set_userdata_types<S: Into<String>>(mut self, types: impl IntoIterator<Item = S>) -> Self {
|
||||||
self.userdata_types = types.into_iter().map(|s| s.into()).collect();
|
self.userdata_types = types.into_iter().map(|s| s.into()).collect();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets constants for known library members.
|
/// Adds a constant for a known library member.
|
||||||
///
|
///
|
||||||
/// The constants are used by the compiler to optimize the generated bytecode.
|
/// 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.
|
/// Optimization level must be at least 2 for this to have any effect.
|
||||||
///
|
///
|
||||||
/// The first element of the tuple is the library name,the second is the member name, and the
|
/// The `name` is a string in the format `lib.member`, where `lib` is the library name
|
||||||
/// third is the constant value.
|
/// and `member` is the member (constant) name.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn set_library_constants<L, M>(mut self, constants: Vec<(L, M, CompileConstant)>) -> Self
|
pub fn add_library_constant(
|
||||||
where
|
mut self,
|
||||||
L: Into<String>,
|
name: impl AsRef<str>,
|
||||||
M: Into<String>,
|
r#const: impl Into<CompileConstant>,
|
||||||
{
|
) -> Self {
|
||||||
let map = constants
|
let Some((lib, member)) = name.as_ref().split_once('.') else {
|
||||||
.into_iter()
|
return self;
|
||||||
.map(|(lib, member, cons)| ((lib.into(), member.into()), cons))
|
};
|
||||||
.collect::<HashMap<_, _>>();
|
let (lib, member) = (lib.to_owned(), member.to_owned());
|
||||||
self.library_constants = Some(std::sync::Arc::new(map));
|
|
||||||
self.libraries_with_known_members = (self.library_constants.clone())
|
if !self.libraries_with_known_members.contains(&lib) {
|
||||||
.map(|map| map.keys().map(|(lib, _)| lib.clone()).collect())
|
self.libraries_with_known_members.push(lib.clone());
|
||||||
.unwrap_or_default();
|
}
|
||||||
|
self.library_constants
|
||||||
|
.get_or_insert_default()
|
||||||
|
.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<String>) -> Self {
|
||||||
|
self.disabled_builtins.push(builtin.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a list of builtins that should be disabled.
|
/// Sets a list of builtins that should be disabled.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn set_disabled_builtins<S: Into<String>>(mut self, builtins: Vec<S>) -> Self {
|
pub fn set_disabled_builtins<S: Into<String>>(mut self, builtins: impl IntoIterator<Item = S>) -> Self {
|
||||||
self.disabled_builtins = builtins.into_iter().map(|s| s.into()).collect();
|
self.disabled_builtins = builtins.into_iter().map(|s| s.into()).collect();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -403,11 +473,11 @@ impl Compiler {
|
|||||||
options.mutableGlobals = mutable_globals_ptr;
|
options.mutableGlobals = mutable_globals_ptr;
|
||||||
options.userdataTypes = userdata_types_ptr;
|
options.userdataTypes = userdata_types_ptr;
|
||||||
options.librariesWithKnownMembers = libraries_with_known_members_ptr;
|
options.librariesWithKnownMembers = libraries_with_known_members_ptr;
|
||||||
if let Some(map) = self.library_constants.as_ref() {
|
if let Some(map) = self.library_constants.as_ref()
|
||||||
if !self.libraries_with_known_members.is_empty() {
|
&& !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);
|
LIBRARY_MEMBER_CONSTANT_MAP.with_borrow_mut(|gmap| *gmap = map.clone());
|
||||||
}
|
options.libraryMemberConstantCallback = Some(library_member_constant_callback);
|
||||||
}
|
}
|
||||||
options.disabledBuiltins = disabled_builtins_ptr;
|
options.disabledBuiltins = disabled_builtins_ptr;
|
||||||
ffi::luau_compile(source.as_ref(), options)
|
ffi::luau_compile(source.as_ref(), options)
|
||||||
@@ -416,7 +486,7 @@ impl Compiler {
|
|||||||
if bytecode.first() == Some(&0) {
|
if bytecode.first() == Some(&0) {
|
||||||
// The rest of the bytecode is the error message starting with `:`
|
// 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
|
// 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 = String::from_utf8_lossy(&bytecode[2..]).into_owned();
|
||||||
return Err(Error::SyntaxError {
|
return Err(Error::SyntaxError {
|
||||||
incomplete_input: message.ends_with("<eof>"),
|
incomplete_input: message.ends_with("<eof>"),
|
||||||
message,
|
message,
|
||||||
@@ -588,19 +658,19 @@ impl Chunk<'_> {
|
|||||||
///
|
///
|
||||||
/// It does nothing if the chunk is already binary or invalid.
|
/// It does nothing if the chunk is already binary or invalid.
|
||||||
fn compile(&mut self) {
|
fn compile(&mut self) {
|
||||||
if let Ok(ref source) = self.source {
|
if let Ok(ref source) = self.source
|
||||||
if self.detect_mode() == ChunkMode::Text {
|
&& self.detect_mode() == ChunkMode::Text
|
||||||
#[cfg(feature = "luau")]
|
{
|
||||||
if let Ok(data) = self.compiler.get_or_insert_with(Default::default).compile(source) {
|
#[cfg(feature = "luau")]
|
||||||
self.source = Ok(Cow::Owned(data));
|
if let Ok(data) = self.compiler.get_or_insert_default().compile(source) {
|
||||||
self.mode = Some(ChunkMode::Binary);
|
self.source = Ok(Cow::Owned(data));
|
||||||
}
|
self.mode = Some(ChunkMode::Binary);
|
||||||
#[cfg(not(feature = "luau"))]
|
}
|
||||||
if let Ok(func) = self.lua.lock().load_chunk(None, None, None, source.as_ref()) {
|
#[cfg(not(feature = "luau"))]
|
||||||
let data = func.dump(false);
|
if let Ok(func) = self.lua.lock().load_chunk(None, None, None, source.as_ref()) {
|
||||||
self.source = Ok(Cow::Owned(data));
|
let data = func.dump(false);
|
||||||
self.mode = Some(ChunkMode::Binary);
|
self.source = Ok(Cow::Owned(data));
|
||||||
}
|
self.mode = Some(ChunkMode::Binary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -613,33 +683,33 @@ impl Chunk<'_> {
|
|||||||
|
|
||||||
// Try to fetch compiled chunk from cache
|
// Try to fetch compiled chunk from cache
|
||||||
let mut text_source = None;
|
let mut text_source = None;
|
||||||
if let Ok(ref source) = self.source {
|
if let Ok(ref source) = self.source
|
||||||
if self.detect_mode() == ChunkMode::Text {
|
&& self.detect_mode() == ChunkMode::Text
|
||||||
let lua = self.lua.lock();
|
{
|
||||||
if let Some(cache) = lua.app_data_ref_unguarded::<ChunksCache>() {
|
let lua = self.lua.lock();
|
||||||
if let Some(data) = cache.0.get(source.as_ref()) {
|
if let Some(cache) = lua.priv_app_data_ref::<ChunksCache>()
|
||||||
self.source = Ok(Cow::Owned(data.clone()));
|
&& let Some(data) = cache.0.get(source.as_ref())
|
||||||
self.mode = Some(ChunkMode::Binary);
|
{
|
||||||
return self;
|
self.source = Ok(Cow::Owned(data.clone()));
|
||||||
}
|
self.mode = Some(ChunkMode::Binary);
|
||||||
}
|
return self;
|
||||||
text_source = Some(source.as_ref().to_vec());
|
|
||||||
}
|
}
|
||||||
|
text_source = Some(source.as_ref().to_vec());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile and cache the chunk
|
// Compile and cache the chunk
|
||||||
if let Some(text_source) = text_source {
|
if let Some(text_source) = text_source {
|
||||||
self.compile();
|
self.compile();
|
||||||
if let Ok(ref binary_source) = self.source {
|
if let Ok(ref binary_source) = self.source
|
||||||
if self.detect_mode() == ChunkMode::Binary {
|
&& self.detect_mode() == ChunkMode::Binary
|
||||||
let lua = self.lua.lock();
|
{
|
||||||
if let Some(mut cache) = lua.app_data_mut_unguarded::<ChunksCache>() {
|
let lua = self.lua.lock();
|
||||||
cache.0.insert(text_source, binary_source.to_vec());
|
if let Some(mut cache) = lua.priv_app_data_mut::<ChunksCache>() {
|
||||||
} else {
|
cache.0.insert(text_source, binary_source.to_vec());
|
||||||
let mut cache = ChunksCache(HashMap::new());
|
} else {
|
||||||
cache.0.insert(text_source, binary_source.to_vec());
|
let mut cache = ChunksCache(HashMap::new());
|
||||||
let _ = lua.try_set_app_data(cache);
|
cache.0.insert(text_source, binary_source.to_vec());
|
||||||
};
|
lua.set_priv_app_data(cache);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+139
-196
@@ -4,16 +4,15 @@ use std::ffi::{CStr, CString, OsStr, OsString};
|
|||||||
use std::hash::{BuildHasher, Hash};
|
use std::hash::{BuildHasher, Hash};
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::string::String as StdString;
|
|
||||||
use std::{mem, slice, str};
|
use std::{mem, slice, str};
|
||||||
|
|
||||||
use bstr::{BStr, BString, ByteSlice, ByteVec};
|
use bstr::{BStr, BString, ByteVec};
|
||||||
use num_traits::cast;
|
use num_traits::cast;
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::function::Function;
|
use crate::function::Function;
|
||||||
use crate::state::{Lua, RawLua};
|
use crate::state::{Lua, RawLua};
|
||||||
use crate::string::{BorrowedBytes, BorrowedStr, String};
|
use crate::string::{BorrowedBytes, BorrowedStr, LuaString};
|
||||||
use crate::table::Table;
|
use crate::table::Table;
|
||||||
use crate::thread::Thread;
|
use crate::thread::Thread;
|
||||||
use crate::traits::{FromLua, IntoLua, ShortTypeName as _};
|
use crate::traits::{FromLua, IntoLua, ShortTypeName as _};
|
||||||
@@ -47,14 +46,14 @@ impl FromLua for Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoLua for String {
|
impl IntoLua for LuaString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||||
Ok(Value::String(self))
|
Ok(Value::String(self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoLua for &String {
|
impl IntoLua for &LuaString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||||
Ok(Value::String(self.clone()))
|
Ok(Value::String(self.clone()))
|
||||||
@@ -67,16 +66,12 @@ impl IntoLua for &String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromLua for String {
|
impl FromLua for LuaString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_lua(value: Value, lua: &Lua) -> Result<String> {
|
fn from_lua(value: Value, lua: &Lua) -> Result<LuaString> {
|
||||||
let ty = value.type_name();
|
let ty = value.type_name();
|
||||||
lua.coerce_string(value)?
|
lua.coerce_string(value)?
|
||||||
.ok_or_else(|| Error::FromLuaConversionError {
|
.ok_or_else(|| Error::from_lua_conversion(ty, "string", "expected string or number".to_string()))
|
||||||
from: ty,
|
|
||||||
to: "string".to_string(),
|
|
||||||
message: Some("expected string or number".to_string()),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||||
@@ -84,7 +79,7 @@ impl FromLua for String {
|
|||||||
let type_id = ffi::lua_type(state, idx);
|
let type_id = ffi::lua_type(state, idx);
|
||||||
if type_id == ffi::LUA_TSTRING {
|
if type_id == ffi::LUA_TSTRING {
|
||||||
ffi::lua_xpush(state, lua.ref_thread(), idx);
|
ffi::lua_xpush(state, lua.ref_thread(), idx);
|
||||||
return Ok(String(lua.pop_ref_thread()));
|
return Ok(LuaString(lua.pop_ref_thread()));
|
||||||
}
|
}
|
||||||
// Fallback to default
|
// Fallback to default
|
||||||
Self::from_lua(lua.stack_value(idx, Some(type_id)), lua.lua())
|
Self::from_lua(lua.stack_value(idx, Some(type_id)), lua.lua())
|
||||||
@@ -119,7 +114,7 @@ impl IntoLua for &BorrowedStr<'_> {
|
|||||||
|
|
||||||
impl FromLua for BorrowedStr<'_> {
|
impl FromLua for BorrowedStr<'_> {
|
||||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||||
let s = String::from_lua(value, lua)?;
|
let s = LuaString::from_lua(value, lua)?;
|
||||||
let BorrowedStr { buf, _lua, .. } = BorrowedStr::try_from(&s)?;
|
let BorrowedStr { buf, _lua, .. } = BorrowedStr::try_from(&s)?;
|
||||||
let buf = unsafe { mem::transmute::<&str, &'static str>(buf) };
|
let buf = unsafe { mem::transmute::<&str, &'static str>(buf) };
|
||||||
let borrow = Cow::Owned(s);
|
let borrow = Cow::Owned(s);
|
||||||
@@ -127,7 +122,7 @@ impl FromLua for BorrowedStr<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||||
let s = String::from_stack(idx, lua)?;
|
let s = LuaString::from_stack(idx, lua)?;
|
||||||
let BorrowedStr { buf, _lua, .. } = BorrowedStr::try_from(&s)?;
|
let BorrowedStr { buf, _lua, .. } = BorrowedStr::try_from(&s)?;
|
||||||
let buf = unsafe { mem::transmute::<&str, &'static str>(buf) };
|
let buf = unsafe { mem::transmute::<&str, &'static str>(buf) };
|
||||||
let borrow = Cow::Owned(s);
|
let borrow = Cow::Owned(s);
|
||||||
@@ -163,7 +158,7 @@ impl IntoLua for &BorrowedBytes<'_> {
|
|||||||
|
|
||||||
impl FromLua for BorrowedBytes<'_> {
|
impl FromLua for BorrowedBytes<'_> {
|
||||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||||
let s = String::from_lua(value, lua)?;
|
let s = LuaString::from_lua(value, lua)?;
|
||||||
let BorrowedBytes { buf, _lua, .. } = BorrowedBytes::from(&s);
|
let BorrowedBytes { buf, _lua, .. } = BorrowedBytes::from(&s);
|
||||||
let buf = unsafe { mem::transmute::<&[u8], &'static [u8]>(buf) };
|
let buf = unsafe { mem::transmute::<&[u8], &'static [u8]>(buf) };
|
||||||
let borrow = Cow::Owned(s);
|
let borrow = Cow::Owned(s);
|
||||||
@@ -171,7 +166,7 @@ impl FromLua for BorrowedBytes<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||||
let s = String::from_stack(idx, lua)?;
|
let s = LuaString::from_stack(idx, lua)?;
|
||||||
let BorrowedBytes { buf, _lua, .. } = BorrowedBytes::from(&s);
|
let BorrowedBytes { buf, _lua, .. } = BorrowedBytes::from(&s);
|
||||||
let buf = unsafe { mem::transmute::<&[u8], &'static [u8]>(buf) };
|
let buf = unsafe { mem::transmute::<&[u8], &'static [u8]>(buf) };
|
||||||
let borrow = Cow::Owned(s);
|
let borrow = Cow::Owned(s);
|
||||||
@@ -204,11 +199,7 @@ impl FromLua for Table {
|
|||||||
fn from_lua(value: Value, _: &Lua) -> Result<Table> {
|
fn from_lua(value: Value, _: &Lua) -> Result<Table> {
|
||||||
match value {
|
match value {
|
||||||
Value::Table(table) => Ok(table),
|
Value::Table(table) => Ok(table),
|
||||||
_ => Err(Error::FromLuaConversionError {
|
_ => Err(Error::from_lua_conversion(value.type_name(), "table", None)),
|
||||||
from: value.type_name(),
|
|
||||||
to: "table".to_string(),
|
|
||||||
message: None,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,11 +229,7 @@ impl FromLua for Function {
|
|||||||
fn from_lua(value: Value, _: &Lua) -> Result<Function> {
|
fn from_lua(value: Value, _: &Lua) -> Result<Function> {
|
||||||
match value {
|
match value {
|
||||||
Value::Function(table) => Ok(table),
|
Value::Function(table) => Ok(table),
|
||||||
_ => Err(Error::FromLuaConversionError {
|
_ => Err(Error::from_lua_conversion(value.type_name(), "function", None)),
|
||||||
from: value.type_name(),
|
|
||||||
to: "function".to_string(),
|
|
||||||
message: None,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -272,11 +259,7 @@ impl FromLua for Thread {
|
|||||||
fn from_lua(value: Value, _: &Lua) -> Result<Thread> {
|
fn from_lua(value: Value, _: &Lua) -> Result<Thread> {
|
||||||
match value {
|
match value {
|
||||||
Value::Thread(t) => Ok(t),
|
Value::Thread(t) => Ok(t),
|
||||||
_ => Err(Error::FromLuaConversionError {
|
_ => Err(Error::from_lua_conversion(value.type_name(), "thread", None)),
|
||||||
from: value.type_name(),
|
|
||||||
to: "thread".to_string(),
|
|
||||||
message: None,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -306,11 +289,7 @@ impl FromLua for AnyUserData {
|
|||||||
fn from_lua(value: Value, _: &Lua) -> Result<AnyUserData> {
|
fn from_lua(value: Value, _: &Lua) -> Result<AnyUserData> {
|
||||||
match value {
|
match value {
|
||||||
Value::UserData(ud) => Ok(ud),
|
Value::UserData(ud) => Ok(ud),
|
||||||
_ => Err(Error::FromLuaConversionError {
|
_ => Err(Error::from_lua_conversion(value.type_name(), "userdata", None)),
|
||||||
from: value.type_name(),
|
|
||||||
to: "userdata".to_string(),
|
|
||||||
message: None,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -428,11 +407,11 @@ impl FromLua for LightUserData {
|
|||||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||||
match value {
|
match value {
|
||||||
Value::LightUserData(ud) => Ok(ud),
|
Value::LightUserData(ud) => Ok(ud),
|
||||||
_ => Err(Error::FromLuaConversionError {
|
_ => Err(Error::from_lua_conversion(
|
||||||
from: value.type_name(),
|
value.type_name(),
|
||||||
to: "lightuserdata".to_string(),
|
"lightuserdata",
|
||||||
message: None,
|
None,
|
||||||
}),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -451,11 +430,7 @@ impl FromLua for crate::Vector {
|
|||||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||||
match value {
|
match value {
|
||||||
Value::Vector(v) => Ok(v),
|
Value::Vector(v) => Ok(v),
|
||||||
_ => Err(Error::FromLuaConversionError {
|
_ => Err(Error::from_lua_conversion(value.type_name(), "vector", None)),
|
||||||
from: value.type_name(),
|
|
||||||
to: "vector".to_string(),
|
|
||||||
message: None,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -488,37 +463,41 @@ impl FromLua for crate::Buffer {
|
|||||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||||
match value {
|
match value {
|
||||||
Value::Buffer(buf) => Ok(buf),
|
Value::Buffer(buf) => Ok(buf),
|
||||||
_ => Err(Error::FromLuaConversionError {
|
_ => Err(Error::from_lua_conversion(value.type_name(), "buffer", None)),
|
||||||
from: value.type_name(),
|
|
||||||
to: "buffer".to_string(),
|
|
||||||
message: None,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoLua for StdString {
|
impl IntoLua for String {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||||
|
#[cfg(feature = "lua55")]
|
||||||
|
if true {
|
||||||
|
return Ok(Value::String(lua.create_external_string(self)?));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Value::String(lua.create_string(self)?))
|
Ok(Value::String(lua.create_string(self)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||||
|
#[cfg(feature = "lua55")]
|
||||||
|
if lua.unlikely_memory_error() {
|
||||||
|
return crate::util::push_external_string(lua.state(), self.into(), false);
|
||||||
|
}
|
||||||
|
|
||||||
push_bytes_into_stack(self, lua)
|
push_bytes_into_stack(self, lua)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromLua for StdString {
|
impl FromLua for String {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||||
let ty = value.type_name();
|
let ty = value.type_name();
|
||||||
Ok(lua
|
Ok(lua
|
||||||
.coerce_string(value)?
|
.coerce_string(value)?
|
||||||
.ok_or_else(|| Error::FromLuaConversionError {
|
.ok_or_else(|| {
|
||||||
from: ty,
|
Error::from_lua_conversion(ty, Self::type_name(), "expected string or number".to_string())
|
||||||
to: Self::type_name(),
|
|
||||||
message: Some("expected string or number".to_string()),
|
|
||||||
})?
|
})?
|
||||||
.to_str()?
|
.to_str()?
|
||||||
.to_owned())
|
.to_owned())
|
||||||
@@ -534,11 +513,7 @@ impl FromLua for StdString {
|
|||||||
let bytes = slice::from_raw_parts(data as *const u8, size);
|
let bytes = slice::from_raw_parts(data as *const u8, size);
|
||||||
return str::from_utf8(bytes)
|
return str::from_utf8(bytes)
|
||||||
.map(|s| s.to_owned())
|
.map(|s| s.to_owned())
|
||||||
.map_err(|e| Error::FromLuaConversionError {
|
.map_err(|e| Error::from_lua_conversion("string", Self::type_name(), e.to_string()));
|
||||||
from: "string",
|
|
||||||
to: Self::type_name(),
|
|
||||||
message: Some(e.to_string()),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
// Fallback to default
|
// Fallback to default
|
||||||
Self::from_lua(lua.stack_value(idx, Some(type_id)), lua.lua())
|
Self::from_lua(lua.stack_value(idx, Some(type_id)), lua.lua())
|
||||||
@@ -577,10 +552,8 @@ impl FromLua for Box<str> {
|
|||||||
let ty = value.type_name();
|
let ty = value.type_name();
|
||||||
Ok(lua
|
Ok(lua
|
||||||
.coerce_string(value)?
|
.coerce_string(value)?
|
||||||
.ok_or_else(|| Error::FromLuaConversionError {
|
.ok_or_else(|| {
|
||||||
from: ty,
|
Error::from_lua_conversion(ty, Self::type_name(), "expected string or number".to_string())
|
||||||
to: Self::type_name(),
|
|
||||||
message: Some("expected string or number".to_string()),
|
|
||||||
})?
|
})?
|
||||||
.to_str()?
|
.to_str()?
|
||||||
.to_owned()
|
.to_owned()
|
||||||
@@ -591,6 +564,11 @@ impl FromLua for Box<str> {
|
|||||||
impl IntoLua for CString {
|
impl IntoLua for CString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||||
|
#[cfg(feature = "lua55")]
|
||||||
|
if true {
|
||||||
|
return Ok(Value::String(lua.create_external_string(self)?));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Value::String(lua.create_string(self.as_bytes())?))
|
Ok(Value::String(lua.create_string(self.as_bytes())?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -599,21 +577,12 @@ impl FromLua for CString {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||||
let ty = value.type_name();
|
let ty = value.type_name();
|
||||||
let string = lua
|
let string = lua.coerce_string(value)?.ok_or_else(|| {
|
||||||
.coerce_string(value)?
|
Error::from_lua_conversion(ty, Self::type_name(), "expected string or number".to_string())
|
||||||
.ok_or_else(|| Error::FromLuaConversionError {
|
})?;
|
||||||
from: ty,
|
|
||||||
to: Self::type_name(),
|
|
||||||
message: Some("expected string or number".to_string()),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
match CStr::from_bytes_with_nul(&string.as_bytes_with_nul()) {
|
match CStr::from_bytes_with_nul(&string.as_bytes_with_nul()) {
|
||||||
Ok(s) => Ok(s.into()),
|
Ok(s) => Ok(s.into()),
|
||||||
Err(_) => Err(Error::FromLuaConversionError {
|
Err(err) => Err(Error::from_lua_conversion(ty, Self::type_name(), err.to_string())),
|
||||||
from: ty,
|
|
||||||
to: Self::type_name(),
|
|
||||||
message: Some("invalid C-style string".to_string()),
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -635,6 +604,11 @@ impl IntoLua for Cow<'_, CStr> {
|
|||||||
impl IntoLua for BString {
|
impl IntoLua for BString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||||
|
#[cfg(feature = "lua55")]
|
||||||
|
if true {
|
||||||
|
return Ok(Value::String(lua.create_external_string(self)?));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Value::String(lua.create_string(self)?))
|
Ok(Value::String(lua.create_string(self)?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -645,13 +619,11 @@ impl FromLua for BString {
|
|||||||
match value {
|
match value {
|
||||||
Value::String(s) => Ok((*s.as_bytes()).into()),
|
Value::String(s) => Ok((*s.as_bytes()).into()),
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
Value::Buffer(buf) => unsafe { Ok(buf.as_slice().into()) },
|
Value::Buffer(buf) => Ok(buf.to_vec().into()),
|
||||||
_ => Ok((*lua
|
_ => Ok((*lua
|
||||||
.coerce_string(value)?
|
.coerce_string(value)?
|
||||||
.ok_or_else(|| Error::FromLuaConversionError {
|
.ok_or_else(|| {
|
||||||
from: ty,
|
Error::from_lua_conversion(ty, Self::type_name(), "expected string or number".to_string())
|
||||||
to: Self::type_name(),
|
|
||||||
message: Some("expected string or number".to_string()),
|
|
||||||
})?
|
})?
|
||||||
.as_bytes())
|
.as_bytes())
|
||||||
.into()),
|
.into()),
|
||||||
@@ -702,23 +674,22 @@ impl FromLua for OsString {
|
|||||||
let bs = BString::from_lua(value, lua)?;
|
let bs = BString::from_lua(value, lua)?;
|
||||||
Vec::from(bs)
|
Vec::from(bs)
|
||||||
.into_os_string()
|
.into_os_string()
|
||||||
.map_err(|err| Error::FromLuaConversionError {
|
.map_err(|err| Error::from_lua_conversion(ty, "OsString", err.to_string()))
|
||||||
from: ty,
|
|
||||||
to: "OsString".into(),
|
|
||||||
message: Some(err.to_string()),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoLua for &OsStr {
|
impl IntoLua for &OsStr {
|
||||||
|
#[cfg(unix)]
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||||
let s = <[u8]>::from_os_str(self).ok_or_else(|| Error::ToLuaConversionError {
|
use std::os::unix::ffi::OsStrExt;
|
||||||
from: "OsStr".into(),
|
Ok(Value::String(lua.create_string(self.as_bytes())?))
|
||||||
to: "string",
|
}
|
||||||
message: Some("invalid utf-8 encoding".into()),
|
|
||||||
})?;
|
#[cfg(not(unix))]
|
||||||
Ok(Value::String(lua.create_string(s)?))
|
#[inline]
|
||||||
|
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||||
|
self.display().to_string().into_lua(lua)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -756,34 +727,25 @@ impl FromLua for char {
|
|||||||
fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
|
fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
|
||||||
let ty = value.type_name();
|
let ty = value.type_name();
|
||||||
match value {
|
match value {
|
||||||
Value::Integer(i) => {
|
Value::Integer(i) => cast(i).and_then(char::from_u32).ok_or_else(|| {
|
||||||
cast(i)
|
let msg = "integer out of range when converting to char";
|
||||||
.and_then(char::from_u32)
|
Error::from_lua_conversion(ty, "char", msg.to_string())
|
||||||
.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) => {
|
Value::String(s) => {
|
||||||
let str = s.to_str()?;
|
let str = s.to_str()?;
|
||||||
let mut str_iter = str.chars();
|
let mut str_iter = str.chars();
|
||||||
match (str_iter.next(), str_iter.next()) {
|
match (str_iter.next(), str_iter.next()) {
|
||||||
(Some(char), None) => Ok(char),
|
(Some(char), None) => Ok(char),
|
||||||
_ => Err(Error::FromLuaConversionError {
|
_ => {
|
||||||
from: ty,
|
let msg = "expected string to have exactly one char when converting to char";
|
||||||
to: "char".to_string(),
|
Err(Error::from_lua_conversion(ty, "char", msg.to_string()))
|
||||||
message: Some(
|
}
|
||||||
"expected string to have exactly one char when converting to char".to_string(),
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Err(Error::FromLuaConversionError {
|
_ => {
|
||||||
from: ty,
|
let msg = "expected string or integer";
|
||||||
to: Self::type_name(),
|
Err(Error::from_lua_conversion(ty, Self::type_name(), msg.to_string()))
|
||||||
message: Some("expected string or integer".to_string()),
|
}
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -834,24 +796,14 @@ macro_rules! lua_convert_int {
|
|||||||
if let Some(i) = lua.coerce_integer(value.clone())? {
|
if let Some(i) = lua.coerce_integer(value.clone())? {
|
||||||
cast(i)
|
cast(i)
|
||||||
} else {
|
} else {
|
||||||
cast(
|
cast(lua.coerce_number(value)?.ok_or_else(|| {
|
||||||
lua.coerce_number(value)?
|
let msg = "expected number or string coercible to number";
|
||||||
.ok_or_else(|| Error::FromLuaConversionError {
|
Error::from_lua_conversion(ty, stringify!($x), msg.to_string())
|
||||||
from: ty,
|
})?)
|
||||||
to: stringify!($x).to_string(),
|
|
||||||
message: Some(
|
|
||||||
"expected number or string coercible to number".to_string(),
|
|
||||||
),
|
|
||||||
})?,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.ok_or_else(|| Error::FromLuaConversionError {
|
.ok_or_else(|| Error::from_lua_conversion(ty, stringify!($x), "out of range".to_string()))
|
||||||
from: ty,
|
|
||||||
to: stringify!($x).to_string(),
|
|
||||||
message: Some("out of range".to_owned()),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||||
@@ -861,10 +813,8 @@ macro_rules! lua_convert_int {
|
|||||||
let mut ok = 0;
|
let mut ok = 0;
|
||||||
let i = ffi::lua_tointegerx(state, idx, &mut ok);
|
let i = ffi::lua_tointegerx(state, idx, &mut ok);
|
||||||
if ok != 0 {
|
if ok != 0 {
|
||||||
return cast(i).ok_or_else(|| Error::FromLuaConversionError {
|
return cast(i).ok_or_else(|| {
|
||||||
from: "integer",
|
Error::from_lua_conversion("integer", stringify!($x), "out of range".to_string())
|
||||||
to: stringify!($x).to_string(),
|
|
||||||
message: Some("out of range".to_owned()),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -901,13 +851,10 @@ macro_rules! lua_convert_float {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||||
let ty = value.type_name();
|
let ty = value.type_name();
|
||||||
lua.coerce_number(value)?
|
lua.coerce_number(value)?.map(|n| n as $x).ok_or_else(|| {
|
||||||
.map(|n| n as $x)
|
let msg = "expected number or string coercible to number";
|
||||||
.ok_or_else(|| Error::FromLuaConversionError {
|
Error::from_lua_conversion(ty, stringify!($x), msg.to_string())
|
||||||
from: ty,
|
})
|
||||||
to: stringify!($x).to_string(),
|
|
||||||
message: Some("expected number or string coercible to number".to_string()),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||||
@@ -967,18 +914,16 @@ where
|
|||||||
},
|
},
|
||||||
Value::Table(table) => {
|
Value::Table(table) => {
|
||||||
let vec = table.sequence_values().collect::<Result<Vec<_>>>()?;
|
let vec = table.sequence_values().collect::<Result<Vec<_>>>()?;
|
||||||
vec.try_into()
|
vec.try_into().map_err(|vec: Vec<T>| {
|
||||||
.map_err(|vec: Vec<T>| Error::FromLuaConversionError {
|
let msg = format!("expected table of length {N}, got {}", vec.len());
|
||||||
from: "table",
|
Error::from_lua_conversion("table", Self::type_name(), msg)
|
||||||
to: Self::type_name(),
|
})
|
||||||
message: Some(format!("expected table of length {N}, got {}", vec.len())),
|
}
|
||||||
})
|
_ => {
|
||||||
|
let msg = format!("expected table of length {N}");
|
||||||
|
let err = Error::from_lua_conversion(value.type_name(), Self::type_name(), msg.to_string());
|
||||||
|
Err(err)
|
||||||
}
|
}
|
||||||
_ => Err(Error::FromLuaConversionError {
|
|
||||||
from: value.type_name(),
|
|
||||||
to: Self::type_name(),
|
|
||||||
message: Some("expected table".to_string()),
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1009,11 +954,11 @@ impl<T: FromLua> FromLua for Vec<T> {
|
|||||||
fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
|
fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
|
||||||
match value {
|
match value {
|
||||||
Value::Table(table) => table.sequence_values().collect(),
|
Value::Table(table) => table.sequence_values().collect(),
|
||||||
_ => Err(Error::FromLuaConversionError {
|
_ => Err(Error::from_lua_conversion(
|
||||||
from: value.type_name(),
|
value.type_name(),
|
||||||
to: Self::type_name(),
|
Self::type_name(),
|
||||||
message: Some("expected table".to_string()),
|
"expected table".to_string(),
|
||||||
}),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1028,14 +973,13 @@ impl<K: Eq + Hash + IntoLua, V: IntoLua, S: BuildHasher> IntoLua for HashMap<K,
|
|||||||
impl<K: Eq + Hash + FromLua, V: FromLua, S: BuildHasher + Default> FromLua for HashMap<K, V, S> {
|
impl<K: Eq + Hash + FromLua, V: FromLua, S: BuildHasher + Default> FromLua for HashMap<K, V, S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||||
if let Value::Table(table) = value {
|
match value {
|
||||||
table.pairs().collect()
|
Value::Table(table) => table.pairs().collect(),
|
||||||
} else {
|
_ => Err(Error::from_lua_conversion(
|
||||||
Err(Error::FromLuaConversionError {
|
value.type_name(),
|
||||||
from: value.type_name(),
|
Self::type_name(),
|
||||||
to: Self::type_name(),
|
"expected table".to_string(),
|
||||||
message: Some("expected table".to_string()),
|
)),
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1050,14 +994,13 @@ impl<K: Ord + IntoLua, V: IntoLua> IntoLua for BTreeMap<K, V> {
|
|||||||
impl<K: Ord + FromLua, V: FromLua> FromLua for BTreeMap<K, V> {
|
impl<K: Ord + FromLua, V: FromLua> FromLua for BTreeMap<K, V> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||||
if let Value::Table(table) = value {
|
match value {
|
||||||
table.pairs().collect()
|
Value::Table(table) => table.pairs().collect(),
|
||||||
} else {
|
_ => Err(Error::from_lua_conversion(
|
||||||
Err(Error::FromLuaConversionError {
|
value.type_name(),
|
||||||
from: value.type_name(),
|
Self::type_name(),
|
||||||
to: Self::type_name(),
|
"expected table".to_string(),
|
||||||
message: Some("expected table".to_string()),
|
)),
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1077,11 +1020,11 @@ impl<T: Eq + Hash + FromLua, S: BuildHasher + Default> FromLua for HashSet<T, S>
|
|||||||
match value {
|
match value {
|
||||||
Value::Table(table) if table.raw_len() > 0 => table.sequence_values().collect(),
|
Value::Table(table) if table.raw_len() > 0 => table.sequence_values().collect(),
|
||||||
Value::Table(table) => table.pairs::<T, Value>().map(|res| res.map(|(k, _)| k)).collect(),
|
Value::Table(table) => table.pairs::<T, Value>().map(|res| res.map(|(k, _)| k)).collect(),
|
||||||
_ => Err(Error::FromLuaConversionError {
|
_ => Err(Error::from_lua_conversion(
|
||||||
from: value.type_name(),
|
value.type_name(),
|
||||||
to: Self::type_name(),
|
Self::type_name(),
|
||||||
message: Some("expected table".to_string()),
|
"expected table".to_string(),
|
||||||
}),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1101,11 +1044,11 @@ impl<T: Ord + FromLua> FromLua for BTreeSet<T> {
|
|||||||
match value {
|
match value {
|
||||||
Value::Table(table) if table.raw_len() > 0 => table.sequence_values().collect(),
|
Value::Table(table) if table.raw_len() > 0 => table.sequence_values().collect(),
|
||||||
Value::Table(table) => table.pairs::<T, Value>().map(|res| res.map(|(k, _)| k)).collect(),
|
Value::Table(table) => table.pairs::<T, Value>().map(|res| res.map(|(k, _)| k)).collect(),
|
||||||
_ => Err(Error::FromLuaConversionError {
|
_ => Err(Error::from_lua_conversion(
|
||||||
from: value.type_name(),
|
value.type_name(),
|
||||||
to: Self::type_name(),
|
Self::type_name(),
|
||||||
message: Some("expected table".to_string()),
|
"expected table".to_string(),
|
||||||
}),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1175,11 +1118,11 @@ impl<L: FromLua, R: FromLua> FromLua for Either<L, R> {
|
|||||||
// Try the right type
|
// Try the right type
|
||||||
Err(_) => match R::from_lua(value, lua).map(Either::Right) {
|
Err(_) => match R::from_lua(value, lua).map(Either::Right) {
|
||||||
Ok(r) => Ok(r),
|
Ok(r) => Ok(r),
|
||||||
Err(_) => Err(Error::FromLuaConversionError {
|
Err(_) => Err(Error::from_lua_conversion(
|
||||||
from: value_type_name,
|
value_type_name,
|
||||||
to: Self::type_name(),
|
Self::type_name(),
|
||||||
message: None,
|
None,
|
||||||
}),
|
)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1191,12 +1134,12 @@ impl<L: FromLua, R: FromLua> FromLua for Either<L, R> {
|
|||||||
Err(_) => match R::from_stack(idx, lua).map(Either::Right) {
|
Err(_) => match R::from_stack(idx, lua).map(Either::Right) {
|
||||||
Ok(r) => Ok(r),
|
Ok(r) => Ok(r),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let value_type_name = CStr::from_ptr(ffi::luaL_typename(lua.state(), idx));
|
let state = lua.state();
|
||||||
Err(Error::FromLuaConversionError {
|
let from_type_name = CStr::from_ptr(ffi::lua_typename(state, ffi::lua_type(state, idx)))
|
||||||
from: value_type_name.to_str().unwrap(),
|
.to_str()
|
||||||
to: Self::type_name(),
|
.unwrap_or("unknown");
|
||||||
message: None,
|
let err = Error::from_lua_conversion(from_type_name, Self::type_name(), None);
|
||||||
})
|
Err(err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
+109
-119
@@ -1,66 +1,39 @@
|
|||||||
|
//! Lua debugging interface.
|
||||||
|
//!
|
||||||
|
//! This module provides access to the Lua debug interface, allowing inspection of the call stack,
|
||||||
|
//! and function information. The main types are [`Debug`] for accessing debug information and
|
||||||
|
//! [`HookTriggers`] for configuring debug hooks.
|
||||||
|
|
||||||
use std::borrow::Cow;
|
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 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::state::RawLua;
|
||||||
use crate::types::ReentrantMutexGuard;
|
use crate::util::{StackGuard, assert_stack, linenumber_to_usize, ptr_to_lossy_str, ptr_to_str};
|
||||||
use crate::util::{linenumber_to_usize, ptr_to_lossy_str, ptr_to_str};
|
|
||||||
|
|
||||||
/// Contains information about currently executing Lua code.
|
/// Contains information about currently executing Lua code.
|
||||||
///
|
///
|
||||||
/// The `Debug` structure is provided as a parameter to the hook function set with
|
/// You may call the methods on this structure to retrieve information about the Lua code executing
|
||||||
/// [`Lua::set_hook`]. You may call the methods on this structure to retrieve information about the
|
/// at the specific level. Further information can be found in the Lua [documentation].
|
||||||
/// Lua code executing at the time that the hook function was called. Further information can be
|
|
||||||
/// found in the Lua [documentation].
|
|
||||||
///
|
///
|
||||||
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#lua_Debug
|
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#lua_Debug
|
||||||
/// [`Lua::set_hook`]: crate::Lua::set_hook
|
|
||||||
pub struct Debug<'a> {
|
pub struct Debug<'a> {
|
||||||
lua: EitherLua<'a>,
|
state: *mut lua_State,
|
||||||
ar: ActivationRecord,
|
lua: &'a RawLua,
|
||||||
#[cfg(feature = "luau")]
|
#[cfg_attr(not(feature = "luau"), allow(unused))]
|
||||||
level: c_int,
|
level: c_int,
|
||||||
}
|
ar: *mut lua_Debug,
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Debug<'a> {
|
impl<'a> Debug<'a> {
|
||||||
// We assume the lock is held when this function is called.
|
pub(crate) fn new(lua: &'a RawLua, level: c_int, ar: *mut lua_Debug) -> Self {
|
||||||
#[cfg(not(feature = "luau"))]
|
|
||||||
pub(crate) fn new(lua: &'a RawLua, ar: *mut lua_Debug) -> Self {
|
|
||||||
Debug {
|
Debug {
|
||||||
lua: EitherLua::Borrowed(lua),
|
state: lua.state(),
|
||||||
ar: ActivationRecord::Borrowed(ar),
|
lua,
|
||||||
}
|
ar,
|
||||||
}
|
level,
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +47,7 @@ impl<'a> Debug<'a> {
|
|||||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||||
pub fn event(&self) -> DebugEvent {
|
pub fn event(&self) -> DebugEvent {
|
||||||
unsafe {
|
unsafe {
|
||||||
match (*self.ar.get()).event {
|
match (*self.ar).event {
|
||||||
ffi::LUA_HOOKCALL => DebugEvent::Call,
|
ffi::LUA_HOOKCALL => DebugEvent::Call,
|
||||||
ffi::LUA_HOOKRET => DebugEvent::Ret,
|
ffi::LUA_HOOKRET => DebugEvent::Ret,
|
||||||
ffi::LUA_HOOKTAILCALL => DebugEvent::TailCall,
|
ffi::LUA_HOOKTAILCALL => DebugEvent::TailCall,
|
||||||
@@ -85,24 +58,48 @@ impl<'a> Debug<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Corresponds to the `n` what mask.
|
/// Returns the function that is running at the given level.
|
||||||
pub fn names(&self) -> DebugNames {
|
///
|
||||||
|
/// 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 {
|
unsafe {
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
mlua_assert!(
|
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`"
|
"lua_getinfo failed with `n`"
|
||||||
);
|
);
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
mlua_assert!(
|
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`"
|
"lua_getinfo failed with `n`"
|
||||||
);
|
);
|
||||||
|
|
||||||
DebugNames {
|
DebugNames {
|
||||||
name: ptr_to_lossy_str((*self.ar.get()).name),
|
name: ptr_to_lossy_str((*self.ar).name),
|
||||||
#[cfg(not(feature = "luau"))]
|
#[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,
|
Some("") => None,
|
||||||
val => val,
|
val => val,
|
||||||
},
|
},
|
||||||
@@ -112,119 +109,107 @@ impl<'a> Debug<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Corresponds to the `S` what mask.
|
/// Corresponds to the `S` "what" mask.
|
||||||
pub fn source(&self) -> DebugSource {
|
pub fn source(&self) -> DebugSource<'_> {
|
||||||
unsafe {
|
unsafe {
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
mlua_assert!(
|
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`"
|
"lua_getinfo failed with `S`"
|
||||||
);
|
);
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
mlua_assert!(
|
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`"
|
"lua_getinfo failed with `s`"
|
||||||
);
|
);
|
||||||
|
|
||||||
DebugSource {
|
DebugSource {
|
||||||
source: ptr_to_lossy_str((*self.ar.get()).source),
|
source: ptr_to_lossy_str((*self.ar).source),
|
||||||
#[cfg(not(feature = "luau"))]
|
#[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")]
|
#[cfg(feature = "luau")]
|
||||||
short_src: ptr_to_lossy_str((*self.ar.get()).short_src),
|
short_src: ptr_to_lossy_str((*self.ar).short_src),
|
||||||
line_defined: linenumber_to_usize((*self.ar.get()).linedefined),
|
line_defined: linenumber_to_usize((*self.ar).linedefined),
|
||||||
#[cfg(not(feature = "luau"))]
|
#[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")]
|
#[cfg(feature = "luau")]
|
||||||
last_line_defined: None,
|
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.
|
/// Corresponds to the `l` "what" mask. Returns the current line.
|
||||||
pub fn curr_line(&self) -> i32 {
|
pub fn current_line(&self) -> Option<usize> {
|
||||||
unsafe {
|
unsafe {
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
mlua_assert!(
|
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`"
|
"lua_getinfo failed with `l`"
|
||||||
);
|
);
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
mlua_assert!(
|
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`"
|
"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
|
/// Corresponds to the `t` "what" mask. Returns true if the hook is in a function tail call,
|
||||||
/// otherwise.
|
/// false otherwise.
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
#[cfg_attr(
|
||||||
|
docsrs,
|
||||||
|
doc(cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52")))
|
||||||
|
)]
|
||||||
pub fn is_tail_call(&self) -> bool {
|
pub fn is_tail_call(&self) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
mlua_assert!(
|
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`"
|
"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 {
|
pub fn stack(&self) -> DebugStack {
|
||||||
unsafe {
|
unsafe {
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
mlua_assert!(
|
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`"
|
"lua_getinfo failed with `u`"
|
||||||
);
|
);
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
mlua_assert!(
|
mlua_assert!(
|
||||||
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("au"), self.ar.get()) != 0,
|
ffi::lua_getinfo(self.state, self.level, cstr!("au"), self.ar) != 0,
|
||||||
"lua_getinfo failed with `au`"
|
"lua_getinfo failed with `au`"
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
let stack = DebugStack {
|
let stack = DebugStack {
|
||||||
num_ups: (*self.ar.get()).nups as _,
|
num_upvalues: (*self.ar).nups as _,
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
#[cfg(not(any(feature = "lua51", feature = "luajit")))]
|
||||||
num_params: (*self.ar.get()).nparams as _,
|
num_params: (*self.ar).nparams as _,
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
#[cfg(not(any(feature = "lua51", feature = "luajit")))]
|
||||||
is_vararg: (*self.ar.get()).isvararg != 0,
|
is_vararg: (*self.ar).isvararg != 0,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
let stack = DebugStack {
|
let stack = DebugStack {
|
||||||
num_ups: (*self.ar.get()).nupvals,
|
num_upvalues: (*self.ar).nupvals,
|
||||||
num_params: (*self.ar.get()).nparams,
|
num_params: (*self.ar).nparams,
|
||||||
is_vararg: (*self.ar.get()).isvararg != 0,
|
is_vararg: (*self.ar).isvararg != 0,
|
||||||
};
|
};
|
||||||
stack
|
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.
|
/// Represents a specific event that triggered the hook.
|
||||||
|
#[cfg(not(feature = "luau"))]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum DebugEvent {
|
pub enum DebugEvent {
|
||||||
Call,
|
Call,
|
||||||
@@ -235,6 +220,9 @@ pub enum DebugEvent {
|
|||||||
Unknown(c_int),
|
Unknown(c_int),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Contains the name information of a function in the call stack.
|
||||||
|
///
|
||||||
|
/// Returned by the [`Debug::names`] method.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct DebugNames<'a> {
|
pub struct DebugNames<'a> {
|
||||||
/// A (reasonable) name of the function (`None` if the name cannot be found).
|
/// A (reasonable) name of the function (`None` if the name cannot be found).
|
||||||
@@ -245,6 +233,9 @@ pub struct DebugNames<'a> {
|
|||||||
pub name_what: Option<&'static str>,
|
pub name_what: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Contains the source information of a function in the call stack.
|
||||||
|
///
|
||||||
|
/// Returned by the [`Debug::source`] method.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct DebugSource<'a> {
|
pub struct DebugSource<'a> {
|
||||||
/// Source of the chunk that created the function.
|
/// Source of the chunk that created the function.
|
||||||
@@ -260,23 +251,20 @@ pub struct DebugSource<'a> {
|
|||||||
pub what: &'static str,
|
pub what: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Contains stack information about a function in the call stack.
|
||||||
|
///
|
||||||
|
/// Returned by the [`Debug::stack`] method.
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct DebugStack {
|
pub struct DebugStack {
|
||||||
/// Number of upvalues.
|
/// The number of upvalues of the function.
|
||||||
pub num_ups: u8,
|
pub num_upvalues: u8,
|
||||||
/// Number of parameters.
|
/// The number of parameters of the function (always 0 for C).
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
#[cfg(any(not(any(feature = "lua51", feature = "luajit")), doc))]
|
||||||
#[cfg_attr(
|
#[cfg_attr(docsrs, doc(cfg(not(any(feature = "lua51", feature = "luajit")))))]
|
||||||
docsrs,
|
|
||||||
doc(cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau")))
|
|
||||||
)]
|
|
||||||
pub num_params: u8,
|
pub num_params: u8,
|
||||||
/// Whether the function is a vararg function.
|
/// Whether the function is a variadic function (always true for C).
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
#[cfg(any(not(any(feature = "lua51", feature = "luajit")), doc))]
|
||||||
#[cfg_attr(
|
#[cfg_attr(docsrs, doc(cfg(not(any(feature = "lua51", feature = "luajit")))))]
|
||||||
docsrs,
|
|
||||||
doc(cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau")))
|
|
||||||
)]
|
|
||||||
pub is_vararg: bool,
|
pub is_vararg: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,6 +342,7 @@ impl HookTriggers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compute the mask to pass to `lua_sethook`.
|
// Compute the mask to pass to `lua_sethook`.
|
||||||
|
#[cfg(not(feature = "luau"))]
|
||||||
pub(crate) const fn mask(&self) -> c_int {
|
pub(crate) const fn mask(&self) -> c_int {
|
||||||
let mut mask: c_int = 0;
|
let mut mask: c_int = 0;
|
||||||
if self.on_calls {
|
if self.on_calls {
|
||||||
@@ -373,6 +362,7 @@ impl HookTriggers {
|
|||||||
|
|
||||||
// Returns the `count` parameter to pass to `lua_sethook`, if applicable. Otherwise, zero is
|
// Returns the `count` parameter to pass to `lua_sethook`, if applicable. Otherwise, zero is
|
||||||
// returned.
|
// returned.
|
||||||
|
#[cfg(not(feature = "luau"))]
|
||||||
pub(crate) const fn count(&self) -> c_int {
|
pub(crate) const fn count(&self) -> c_int {
|
||||||
match self.every_nth_instruction {
|
match self.every_nth_instruction {
|
||||||
Some(n) => n as c_int,
|
Some(n) => n as c_int,
|
||||||
@@ -382,7 +372,7 @@ impl HookTriggers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
impl BitOr for HookTriggers {
|
impl std::ops::BitOr for HookTriggers {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn bitor(mut self, rhs: Self) -> Self::Output {
|
fn bitor(mut self, rhs: Self) -> Self::Output {
|
||||||
@@ -397,7 +387,7 @@ impl BitOr for HookTriggers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
impl BitOrAssign for HookTriggers {
|
impl std::ops::BitOrAssign for HookTriggers {
|
||||||
fn bitor_assign(&mut self, rhs: Self) {
|
fn bitor_assign(&mut self, rhs: Self) {
|
||||||
*self = *self | rhs;
|
*self = *self | rhs;
|
||||||
}
|
}
|
||||||
+38
-49
@@ -4,7 +4,6 @@ use std::io::Error as IoError;
|
|||||||
use std::net::AddrParseError;
|
use std::net::AddrParseError;
|
||||||
use std::result::Result as StdResult;
|
use std::result::Result as StdResult;
|
||||||
use std::str::Utf8Error;
|
use std::str::Utf8Error;
|
||||||
use std::string::String as StdString;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::private::Sealed;
|
use crate::private::Sealed;
|
||||||
@@ -22,7 +21,7 @@ pub enum Error {
|
|||||||
/// Syntax error while parsing Lua source code.
|
/// Syntax error while parsing Lua source code.
|
||||||
SyntaxError {
|
SyntaxError {
|
||||||
/// The error message as returned by Lua.
|
/// The error message as returned by Lua.
|
||||||
message: StdString,
|
message: String,
|
||||||
/// `true` if the error can likely be fixed by appending more input to the source code.
|
/// `true` if the error can likely be fixed by appending more input to the source code.
|
||||||
///
|
///
|
||||||
/// This is useful for implementing REPLs as they can query the user for more input if this
|
/// This is useful for implementing REPLs as they can query the user for more input if this
|
||||||
@@ -34,20 +33,20 @@ pub enum Error {
|
|||||||
/// The Lua VM returns this error when a builtin operation is performed on incompatible types.
|
/// The Lua VM returns this error when a builtin operation is performed on incompatible types.
|
||||||
/// Among other things, this includes invoking operators on wrong types (such as calling or
|
/// Among other things, this includes invoking operators on wrong types (such as calling or
|
||||||
/// indexing a `nil` value).
|
/// indexing a `nil` value).
|
||||||
RuntimeError(StdString),
|
RuntimeError(String),
|
||||||
/// Lua memory error, aka `LUA_ERRMEM`
|
/// Lua memory error, aka `LUA_ERRMEM`
|
||||||
///
|
///
|
||||||
/// The Lua VM returns this error when the allocator does not return the requested memory, aka
|
/// The Lua VM returns this error when the allocator does not return the requested memory, aka
|
||||||
/// it is an out-of-memory error.
|
/// it is an out-of-memory error.
|
||||||
MemoryError(StdString),
|
MemoryError(String),
|
||||||
/// Lua garbage collector error, aka `LUA_ERRGCMM`.
|
/// Lua garbage collector error, aka `LUA_ERRGCMM`.
|
||||||
///
|
///
|
||||||
/// The Lua VM returns this error when there is an error running a `__gc` metamethod.
|
/// The Lua VM returns this error when there is an error running a `__gc` metamethod.
|
||||||
#[cfg(any(feature = "lua53", feature = "lua52", doc))]
|
#[cfg(any(feature = "lua53", feature = "lua52", doc))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua53", feature = "lua52"))))]
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua53", feature = "lua52"))))]
|
||||||
GarbageCollectorError(StdString),
|
GarbageCollectorError(String),
|
||||||
/// Potentially unsafe action in safe mode.
|
/// Potentially unsafe action in safe mode.
|
||||||
SafetyError(StdString),
|
SafetyError(String),
|
||||||
/// Memory control 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
|
/// This error can only happen when Lua state was not created by us and does not have the
|
||||||
@@ -80,23 +79,14 @@ pub enum Error {
|
|||||||
/// (which is stored in the corresponding field).
|
/// (which is stored in the corresponding field).
|
||||||
BadArgument {
|
BadArgument {
|
||||||
/// Function that was called.
|
/// Function that was called.
|
||||||
to: Option<StdString>,
|
to: Option<String>,
|
||||||
/// Argument position (usually starts from 1).
|
/// Argument position (usually starts from 1).
|
||||||
pos: usize,
|
pos: usize,
|
||||||
/// Argument name.
|
/// Argument name.
|
||||||
name: Option<StdString>,
|
name: Option<String>,
|
||||||
/// Underlying error returned when converting argument to a Lua value.
|
/// Underlying error returned when converting argument to a Lua value.
|
||||||
cause: Arc<Error>,
|
cause: Arc<Error>,
|
||||||
},
|
},
|
||||||
/// A Rust value could not be converted to a Lua value.
|
|
||||||
ToLuaConversionError {
|
|
||||||
/// Name of the Rust type that could not be converted.
|
|
||||||
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.
|
|
||||||
message: Option<StdString>,
|
|
||||||
},
|
|
||||||
/// A Lua value could not be converted to the expected Rust type.
|
/// A Lua value could not be converted to the expected Rust type.
|
||||||
FromLuaConversionError {
|
FromLuaConversionError {
|
||||||
/// Name of the Lua type that could not be converted.
|
/// Name of the Lua type that could not be converted.
|
||||||
@@ -104,7 +94,7 @@ pub enum Error {
|
|||||||
/// Name of the Rust type that could not be created.
|
/// Name of the Rust type that could not be created.
|
||||||
to: String,
|
to: String,
|
||||||
/// A string containing more detailed error information.
|
/// A string containing more detailed error information.
|
||||||
message: Option<StdString>,
|
message: Option<String>,
|
||||||
},
|
},
|
||||||
/// [`Thread::resume`] was called on an unresumable coroutine.
|
/// [`Thread::resume`] was called on an unresumable coroutine.
|
||||||
///
|
///
|
||||||
@@ -154,17 +144,17 @@ pub enum Error {
|
|||||||
/// A [`MetaMethod`] operation is restricted (typically for `__gc` or `__metatable`).
|
/// A [`MetaMethod`] operation is restricted (typically for `__gc` or `__metatable`).
|
||||||
///
|
///
|
||||||
/// [`MetaMethod`]: crate::MetaMethod
|
/// [`MetaMethod`]: crate::MetaMethod
|
||||||
MetaMethodRestricted(StdString),
|
MetaMethodRestricted(String),
|
||||||
/// A [`MetaMethod`] (eg. `__index` or `__newindex`) has invalid type.
|
/// A [`MetaMethod`] (eg. `__index` or `__newindex`) has invalid type.
|
||||||
///
|
///
|
||||||
/// [`MetaMethod`]: crate::MetaMethod
|
/// [`MetaMethod`]: crate::MetaMethod
|
||||||
MetaMethodTypeError {
|
MetaMethodTypeError {
|
||||||
/// Name of the metamethod.
|
/// Name of the metamethod.
|
||||||
method: StdString,
|
method: String,
|
||||||
/// Passed value type.
|
/// Passed value type.
|
||||||
type_name: &'static str,
|
type_name: &'static str,
|
||||||
/// A string containing more detailed error information.
|
/// A string containing more detailed error information.
|
||||||
message: Option<StdString>,
|
message: Option<String>,
|
||||||
},
|
},
|
||||||
/// A [`RegistryKey`] produced from a different Lua state was used.
|
/// A [`RegistryKey`] produced from a different Lua state was used.
|
||||||
///
|
///
|
||||||
@@ -173,7 +163,7 @@ pub enum Error {
|
|||||||
/// A Rust callback returned `Err`, raising the contained `Error` as a Lua error.
|
/// A Rust callback returned `Err`, raising the contained `Error` as a Lua error.
|
||||||
CallbackError {
|
CallbackError {
|
||||||
/// Lua call stack backtrace.
|
/// Lua call stack backtrace.
|
||||||
traceback: StdString,
|
traceback: String,
|
||||||
/// Original error returned by the Rust code.
|
/// Original error returned by the Rust code.
|
||||||
cause: Arc<Error>,
|
cause: Arc<Error>,
|
||||||
},
|
},
|
||||||
@@ -183,13 +173,13 @@ pub enum Error {
|
|||||||
/// and returned again.
|
/// and returned again.
|
||||||
PreviouslyResumedPanic,
|
PreviouslyResumedPanic,
|
||||||
/// Serialization error.
|
/// Serialization error.
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||||
SerializeError(StdString),
|
SerializeError(String),
|
||||||
/// Deserialization error.
|
/// Deserialization error.
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||||
DeserializeError(StdString),
|
DeserializeError(String),
|
||||||
/// A custom error.
|
/// A custom error.
|
||||||
///
|
///
|
||||||
/// This can be used for returning user-defined errors from callbacks.
|
/// This can be used for returning user-defined errors from callbacks.
|
||||||
@@ -201,7 +191,7 @@ pub enum Error {
|
|||||||
/// An error with additional context.
|
/// An error with additional context.
|
||||||
WithContext {
|
WithContext {
|
||||||
/// A string containing additional context.
|
/// A string containing additional context.
|
||||||
context: StdString,
|
context: String,
|
||||||
/// Underlying error.
|
/// Underlying error.
|
||||||
cause: Arc<Error>,
|
cause: Arc<Error>,
|
||||||
},
|
},
|
||||||
@@ -225,7 +215,7 @@ impl fmt::Display for Error {
|
|||||||
}
|
}
|
||||||
Error::SafetyError(msg) => {
|
Error::SafetyError(msg) => {
|
||||||
write!(fmt, "safety error: {msg}")
|
write!(fmt, "safety error: {msg}")
|
||||||
},
|
}
|
||||||
Error::MemoryControlNotAvailable => {
|
Error::MemoryControlNotAvailable => {
|
||||||
write!(fmt, "memory control is not available")
|
write!(fmt, "memory control is not available")
|
||||||
}
|
}
|
||||||
@@ -238,10 +228,7 @@ impl fmt::Display for Error {
|
|||||||
fmt,
|
fmt,
|
||||||
"out of Lua stack, too many arguments to a Lua function or too many return values from a callback"
|
"out of Lua stack, too many arguments to a Lua function or too many return values from a callback"
|
||||||
),
|
),
|
||||||
Error::BindError => write!(
|
Error::BindError => write!(fmt, "too many arguments to Function::bind"),
|
||||||
fmt,
|
|
||||||
"too many arguments to Function::bind"
|
|
||||||
),
|
|
||||||
Error::BadArgument { to, pos, name, cause } => {
|
Error::BadArgument { to, pos, name, cause } => {
|
||||||
if let Some(name) = name {
|
if let Some(name) = name {
|
||||||
write!(fmt, "bad argument `{name}`")?;
|
write!(fmt, "bad argument `{name}`")?;
|
||||||
@@ -252,13 +239,6 @@ impl fmt::Display for Error {
|
|||||||
write!(fmt, " to `{to}`")?;
|
write!(fmt, " to `{to}`")?;
|
||||||
}
|
}
|
||||||
write!(fmt, ": {cause}")
|
write!(fmt, ": {cause}")
|
||||||
},
|
|
||||||
Error::ToLuaConversionError { from, to, message } => {
|
|
||||||
write!(fmt, "error converting {from} to Lua {to}")?;
|
|
||||||
match message {
|
|
||||||
None => Ok(()),
|
|
||||||
Some(message) => write!(fmt, " ({message})"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Error::FromLuaConversionError { from, to, message } => {
|
Error::FromLuaConversionError { from, to, message } => {
|
||||||
write!(fmt, "error converting Lua {from} to {to}")?;
|
write!(fmt, "error converting Lua {from} to {to}")?;
|
||||||
@@ -273,7 +253,11 @@ impl fmt::Display for Error {
|
|||||||
Error::UserDataBorrowError => write!(fmt, "error borrowing userdata"),
|
Error::UserDataBorrowError => write!(fmt, "error borrowing userdata"),
|
||||||
Error::UserDataBorrowMutError => write!(fmt, "error mutably borrowing userdata"),
|
Error::UserDataBorrowMutError => write!(fmt, "error mutably borrowing userdata"),
|
||||||
Error::MetaMethodRestricted(method) => write!(fmt, "metamethod {method} is restricted"),
|
Error::MetaMethodRestricted(method) => write!(fmt, "metamethod {method} is restricted"),
|
||||||
Error::MetaMethodTypeError { method, type_name, message } => {
|
Error::MetaMethodTypeError {
|
||||||
|
method,
|
||||||
|
type_name,
|
||||||
|
message,
|
||||||
|
} => {
|
||||||
write!(fmt, "metamethod {method} has unsupported type {type_name}")?;
|
write!(fmt, "metamethod {method} has unsupported type {type_name}")?;
|
||||||
match message {
|
match message {
|
||||||
None => Ok(()),
|
None => Ok(()),
|
||||||
@@ -286,7 +270,11 @@ impl fmt::Display for Error {
|
|||||||
Error::CallbackError { cause, traceback } => {
|
Error::CallbackError { cause, traceback } => {
|
||||||
// Trace errors down to the root
|
// Trace errors down to the root
|
||||||
let (mut cause, mut full_traceback) = (cause, None);
|
let (mut cause, mut full_traceback) = (cause, None);
|
||||||
while let Error::CallbackError { cause: cause2, traceback: traceback2 } = &**cause {
|
while let Error::CallbackError {
|
||||||
|
cause: cause2,
|
||||||
|
traceback: traceback2,
|
||||||
|
} = &**cause
|
||||||
|
{
|
||||||
cause = cause2;
|
cause = cause2;
|
||||||
full_traceback = Some(traceback2);
|
full_traceback = Some(traceback2);
|
||||||
}
|
}
|
||||||
@@ -309,14 +297,14 @@ impl fmt::Display for Error {
|
|||||||
Error::PreviouslyResumedPanic => {
|
Error::PreviouslyResumedPanic => {
|
||||||
write!(fmt, "previously resumed panic returned again")
|
write!(fmt, "previously resumed panic returned again")
|
||||||
}
|
}
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
Error::SerializeError(err) => {
|
Error::SerializeError(err) => {
|
||||||
write!(fmt, "serialize error: {err}")
|
write!(fmt, "serialize error: {err}")
|
||||||
},
|
}
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
Error::DeserializeError(err) => {
|
Error::DeserializeError(err) => {
|
||||||
write!(fmt, "deserialize error: {err}")
|
write!(fmt, "deserialize error: {err}")
|
||||||
},
|
}
|
||||||
Error::ExternalError(err) => err.fmt(fmt),
|
Error::ExternalError(err) => err.fmt(fmt),
|
||||||
Error::WithContext { context, cause } => {
|
Error::WithContext { context, cause } => {
|
||||||
writeln!(fmt, "{context}")?;
|
writeln!(fmt, "{context}")?;
|
||||||
@@ -394,6 +382,7 @@ impl Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub(crate) fn from_lua_conversion(
|
pub(crate) fn from_lua_conversion(
|
||||||
from: &'static str,
|
from: &'static str,
|
||||||
to: impl ToString,
|
to: impl ToString,
|
||||||
@@ -494,14 +483,14 @@ impl From<Utf8Error> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
impl serde::ser::Error for Error {
|
impl serde::ser::Error for Error {
|
||||||
fn custom<T: fmt::Display>(msg: T) -> Self {
|
fn custom<T: fmt::Display>(msg: T) -> Self {
|
||||||
Self::SerializeError(msg.to_string())
|
Self::SerializeError(msg.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
impl serde::de::Error for Error {
|
impl serde::de::Error for Error {
|
||||||
fn custom<T: fmt::Display>(msg: T) -> Self {
|
fn custom<T: fmt::Display>(msg: T) -> Self {
|
||||||
Self::DeserializeError(msg.to_string())
|
Self::DeserializeError(msg.to_string())
|
||||||
|
|||||||
+160
-34
@@ -1,3 +1,84 @@
|
|||||||
|
//! Lua function handling.
|
||||||
|
//!
|
||||||
|
//! This module provides types for working with Lua functions from Rust, including
|
||||||
|
//! both Lua-defined functions and native Rust callbacks.
|
||||||
|
//!
|
||||||
|
//! # Main Types
|
||||||
|
//!
|
||||||
|
//! - [`Function`] - A handle to a Lua function that can be called from Rust.
|
||||||
|
//! - [`FunctionInfo`] - Debug information about a function (name, source, line numbers, etc.).
|
||||||
|
//! - [`CoverageInfo`] - Code coverage data for Luau functions (requires `luau` feature).
|
||||||
|
//!
|
||||||
|
//! # Calling Functions
|
||||||
|
//!
|
||||||
|
//! Use [`Function::call`] to invoke a Lua function synchronously:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use mlua::{Function, Lua, Result};
|
||||||
|
//! # fn main() -> Result<()> {
|
||||||
|
//! let lua = Lua::new();
|
||||||
|
//!
|
||||||
|
//! // Get a built-in function
|
||||||
|
//! let print: Function = lua.globals().get("print")?;
|
||||||
|
//! print.call::<()>("Hello from Rust!")?;
|
||||||
|
//!
|
||||||
|
//! // Call a function that returns values
|
||||||
|
//! let tonumber: Function = lua.globals().get("tonumber")?;
|
||||||
|
//! let n: i32 = tonumber.call("42")?;
|
||||||
|
//! assert_eq!(n, 42);
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! For asynchronous execution, use `Function::call_async` (requires `async` feature):
|
||||||
|
//!
|
||||||
|
//! ```ignore
|
||||||
|
//! let result: String = my_async_func.call_async(args).await?;
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Creating Functions
|
||||||
|
//!
|
||||||
|
//! Functions can be created from Rust closures using [`Lua::create_function`]:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use mlua::{Lua, Result};
|
||||||
|
//! # fn main() -> Result<()> {
|
||||||
|
//! let lua = Lua::new();
|
||||||
|
//!
|
||||||
|
//! let greet = lua.create_function(|_, name: String| {
|
||||||
|
//! Ok(format!("Hello, {}!", name))
|
||||||
|
//! })?;
|
||||||
|
//!
|
||||||
|
//! lua.globals().set("greet", greet)?;
|
||||||
|
//! let result: String = lua.load(r#"greet("World")"#).eval()?;
|
||||||
|
//! assert_eq!(result, "Hello, World!");
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! For simpler cases, use [`Function::wrap`] or [`Function::wrap_raw`] to convert a Rust function
|
||||||
|
//! directly:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use mlua::{Function, Lua, Result};
|
||||||
|
//! # fn main() -> Result<()> {
|
||||||
|
//! let lua = Lua::new();
|
||||||
|
//!
|
||||||
|
//! fn add(a: i32, b: i32) -> i32 { a + b }
|
||||||
|
//!
|
||||||
|
//! lua.globals().set("add", Function::wrap_raw(add))?;
|
||||||
|
//! let sum: i32 = lua.load("add(2, 3)").eval()?;
|
||||||
|
//! assert_eq!(sum, 5);
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Function Environments
|
||||||
|
//!
|
||||||
|
//! Lua functions have an associated environment table that determines how global
|
||||||
|
//! variables are resolved. Use [`Function::environment`] and [`Function::set_environment`]
|
||||||
|
//! to inspect or modify this environment.
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::os::raw::{c_int, c_void};
|
use std::os::raw::{c_int, c_void};
|
||||||
use std::{mem, ptr, slice};
|
use std::{mem, ptr, slice};
|
||||||
@@ -8,7 +89,7 @@ use crate::table::Table;
|
|||||||
use crate::traits::{FromLuaMulti, IntoLua, IntoLuaMulti, LuaNativeFn, LuaNativeFnMut};
|
use crate::traits::{FromLuaMulti, IntoLua, IntoLuaMulti, LuaNativeFn, LuaNativeFnMut};
|
||||||
use crate::types::{Callback, LuaType, MaybeSend, ValueRef};
|
use crate::types::{Callback, LuaType, MaybeSend, ValueRef};
|
||||||
use crate::util::{
|
use crate::util::{
|
||||||
assert_stack, check_stack, linenumber_to_usize, pop_error, ptr_to_lossy_str, ptr_to_str, StackGuard,
|
StackGuard, assert_stack, check_stack, linenumber_to_usize, pop_error, ptr_to_lossy_str, ptr_to_str,
|
||||||
};
|
};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
|
|
||||||
@@ -18,7 +99,7 @@ use {
|
|||||||
crate::traits::LuaNativeAsyncFn,
|
crate::traits::LuaNativeAsyncFn,
|
||||||
crate::types::AsyncCallback,
|
crate::types::AsyncCallback,
|
||||||
std::future::{self, Future},
|
std::future::{self, Future},
|
||||||
std::pin::Pin,
|
std::pin::{Pin, pin},
|
||||||
std::task::{Context, Poll},
|
std::task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32,6 +113,7 @@ pub struct Function(pub(crate) ValueRef);
|
|||||||
///
|
///
|
||||||
/// [`Lua Debug Interface`]: https://www.lua.org/manual/5.4/manual.html#4.7
|
/// [`Lua Debug Interface`]: https://www.lua.org/manual/5.4/manual.html#4.7
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub struct FunctionInfo {
|
pub struct FunctionInfo {
|
||||||
/// A (reasonable) name of the function (`None` if the name cannot be found).
|
/// A (reasonable) name of the function (`None` if the name cannot be found).
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
@@ -50,6 +132,16 @@ pub struct FunctionInfo {
|
|||||||
pub line_defined: Option<usize>,
|
pub line_defined: Option<usize>,
|
||||||
/// The line number where the definition of the function ends (not set by Luau).
|
/// The line number where the definition of the function ends (not set by Luau).
|
||||||
pub last_line_defined: Option<usize>,
|
pub last_line_defined: Option<usize>,
|
||||||
|
/// The number of upvalues of the function.
|
||||||
|
pub num_upvalues: u8,
|
||||||
|
/// The number of parameters of the function (always 0 for C).
|
||||||
|
#[cfg(any(not(any(feature = "lua51", feature = "luajit")), doc))]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(not(any(feature = "lua51", feature = "luajit")))))]
|
||||||
|
pub num_params: u8,
|
||||||
|
/// Whether the function is a variadic function (always true for C).
|
||||||
|
#[cfg(any(not(any(feature = "lua51", feature = "luajit")), doc))]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(not(any(feature = "lua51", feature = "luajit")))))]
|
||||||
|
pub is_vararg: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Luau function coverage snapshot.
|
/// Luau function coverage snapshot.
|
||||||
@@ -253,7 +345,7 @@ impl Function {
|
|||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.try_cache()
|
.try_cache()
|
||||||
.set_name("__mlua_bind")
|
.set_name("=__mlua_bind")
|
||||||
.call((self, args_wrapper))
|
.call((self, args_wrapper))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,7 +368,7 @@ impl Function {
|
|||||||
|
|
||||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||||
ffi::lua_getfenv(state, -1);
|
ffi::lua_getfenv(state, -1);
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||||
for i in 1..=255 {
|
for i in 1..=255 {
|
||||||
// Traverse upvalues until we find the _ENV one
|
// Traverse upvalues until we find the _ENV one
|
||||||
match ffi::lua_getupvalue(state, -1, i) {
|
match ffi::lua_getupvalue(state, -1, i) {
|
||||||
@@ -316,7 +408,7 @@ impl Function {
|
|||||||
lua.push_ref(&env.0);
|
lua.push_ref(&env.0);
|
||||||
ffi::lua_setfenv(state, -2);
|
ffi::lua_setfenv(state, -2);
|
||||||
}
|
}
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||||
for i in 1..=255 {
|
for i in 1..=255 {
|
||||||
match ffi::lua_getupvalue(state, -1, i) {
|
match ffi::lua_getupvalue(state, -1, i) {
|
||||||
s if s.is_null() => return Ok(false),
|
s if s.is_null() => return Ok(false),
|
||||||
@@ -343,7 +435,8 @@ impl Function {
|
|||||||
|
|
||||||
/// Returns information about the function.
|
/// Returns information about the function.
|
||||||
///
|
///
|
||||||
/// Corresponds to the `>Sn` what mask for [`lua_getinfo`] when applied to the function.
|
/// Corresponds to the `>Snu` (`>Sn` for Luau) what mask for
|
||||||
|
/// [`lua_getinfo`] when applied to the function.
|
||||||
///
|
///
|
||||||
/// [`lua_getinfo`]: https://www.lua.org/manual/5.4/manual.html#lua_getinfo
|
/// [`lua_getinfo`]: https://www.lua.org/manual/5.4/manual.html#lua_getinfo
|
||||||
pub fn info(&self) -> FunctionInfo {
|
pub fn info(&self) -> FunctionInfo {
|
||||||
@@ -355,11 +448,16 @@ impl Function {
|
|||||||
|
|
||||||
let mut ar: ffi::lua_Debug = mem::zeroed();
|
let mut ar: ffi::lua_Debug = mem::zeroed();
|
||||||
lua.push_ref(&self.0);
|
lua.push_ref(&self.0);
|
||||||
|
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
let res = ffi::lua_getinfo(state, cstr!(">Sn"), &mut ar);
|
let res = ffi::lua_getinfo(state, cstr!(">Snu"), &mut ar);
|
||||||
|
#[cfg(not(feature = "luau"))]
|
||||||
|
mlua_assert!(res != 0, "lua_getinfo failed with `>Snu`");
|
||||||
|
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
let res = ffi::lua_getinfo(state, -1, cstr!("sn"), &mut ar);
|
let res = ffi::lua_getinfo(state, -1, cstr!("snau"), &mut ar);
|
||||||
mlua_assert!(res != 0, "lua_getinfo failed with `>Sn`");
|
#[cfg(feature = "luau")]
|
||||||
|
mlua_assert!(res != 0, "lua_getinfo failed with `snau`");
|
||||||
|
|
||||||
FunctionInfo {
|
FunctionInfo {
|
||||||
name: ptr_to_lossy_str(ar.name).map(|s| s.into_owned()),
|
name: ptr_to_lossy_str(ar.name).map(|s| s.into_owned()),
|
||||||
@@ -381,6 +479,14 @@ impl Function {
|
|||||||
last_line_defined: linenumber_to_usize(ar.lastlinedefined),
|
last_line_defined: linenumber_to_usize(ar.lastlinedefined),
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
last_line_defined: None,
|
last_line_defined: None,
|
||||||
|
#[cfg(not(feature = "luau"))]
|
||||||
|
num_upvalues: ar.nups as _,
|
||||||
|
#[cfg(feature = "luau")]
|
||||||
|
num_upvalues: ar.nupvals,
|
||||||
|
#[cfg(not(any(feature = "lua51", feature = "luajit")))]
|
||||||
|
num_params: ar.nparams,
|
||||||
|
#[cfg(not(any(feature = "lua51", feature = "luajit")))]
|
||||||
|
is_vararg: ar.isvararg != 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -400,11 +506,14 @@ impl Function {
|
|||||||
_state: *mut ffi::lua_State,
|
_state: *mut ffi::lua_State,
|
||||||
buf: *const c_void,
|
buf: *const c_void,
|
||||||
buf_len: usize,
|
buf_len: usize,
|
||||||
data: *mut c_void,
|
data_ptr: *mut c_void,
|
||||||
) -> c_int {
|
) -> c_int {
|
||||||
let data = &mut *(data as *mut Vec<u8>);
|
// If `data` is null, then it's a signal that write is finished.
|
||||||
let buf = slice::from_raw_parts(buf as *const u8, buf_len);
|
if !data_ptr.is_null() && buf_len > 0 {
|
||||||
data.extend_from_slice(buf);
|
let data = &mut *(data_ptr as *mut Vec<u8>);
|
||||||
|
let buf = slice::from_raw_parts(buf as *const u8, buf_len);
|
||||||
|
data.extend_from_slice(buf);
|
||||||
|
}
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -434,7 +543,7 @@ impl Function {
|
|||||||
/// [`Compiler::set_coverage_level`]: crate::chunk::Compiler::set_coverage_level
|
/// [`Compiler::set_coverage_level`]: crate::chunk::Compiler::set_coverage_level
|
||||||
#[cfg(any(feature = "luau", doc))]
|
#[cfg(any(feature = "luau", doc))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||||
pub fn coverage<F>(&self, mut func: F)
|
pub fn coverage<F>(&self, func: F)
|
||||||
where
|
where
|
||||||
F: FnMut(CoverageInfo),
|
F: FnMut(CoverageInfo),
|
||||||
{
|
{
|
||||||
@@ -454,13 +563,16 @@ impl Function {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let rust_callback = &mut *(data as *mut F);
|
let rust_callback = &*(data as *const RefCell<F>);
|
||||||
rust_callback(CoverageInfo {
|
if let Ok(mut rust_callback) = rust_callback.try_borrow_mut() {
|
||||||
function,
|
// Call the Rust callback with CoverageInfo
|
||||||
line_defined,
|
rust_callback(CoverageInfo {
|
||||||
depth,
|
function,
|
||||||
hits: slice::from_raw_parts(hits, size).to_vec(),
|
line_defined,
|
||||||
});
|
depth,
|
||||||
|
hits: slice::from_raw_parts(hits, size).to_vec(),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let lua = self.0.lua.lock();
|
let lua = self.0.lua.lock();
|
||||||
@@ -470,7 +582,8 @@ impl Function {
|
|||||||
assert_stack(state, 1);
|
assert_stack(state, 1);
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
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>);
|
ffi::lua_getcoverage(state, -1, func_ptr, callback::<F>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -492,16 +605,24 @@ impl Function {
|
|||||||
/// This function returns shallow clone (same handle) for Rust/C functions.
|
/// This function returns shallow clone (same handle) for Rust/C functions.
|
||||||
#[cfg(any(feature = "luau", doc))]
|
#[cfg(any(feature = "luau", doc))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
#[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 lua = self.0.lua.lock();
|
||||||
let ref_thread = lua.ref_thread();
|
let state = lua.state();
|
||||||
unsafe {
|
unsafe {
|
||||||
if ffi::lua_iscfunction(ref_thread, self.0.index) != 0 {
|
let _sg = StackGuard::new(state);
|
||||||
return self.clone();
|
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);
|
if lua.unlikely_memory_error() {
|
||||||
Function(lua.pop_ref_thread())
|
ffi::lua_clonefunction(state, -1);
|
||||||
|
} else {
|
||||||
|
protect_lua!(state, 1, 1, fn(state) ffi::lua_clonefunction(state, -1))?;
|
||||||
|
}
|
||||||
|
Ok(Function(lua.pop_ref()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -641,22 +762,27 @@ impl LuaType for Function {
|
|||||||
const TYPE_ID: c_int = ffi::LUA_TFUNCTION;
|
const TYPE_ID: c_int = ffi::LUA_TFUNCTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Future for asynchronous function calls.
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||||
pub struct AsyncCallFuture<R: FromLuaMulti>(Result<AsyncThread<R>>);
|
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")]
|
#[cfg(feature = "async")]
|
||||||
impl<R: FromLuaMulti> Future for AsyncCallFuture<R> {
|
impl<R: FromLuaMulti> Future for AsyncCallFuture<R> {
|
||||||
type Output = Result<R>;
|
type Output = Result<R>;
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
// Safety: We're not moving any pinned data
|
let this = self.get_mut();
|
||||||
let this = unsafe { self.get_unchecked_mut() };
|
|
||||||
match &mut this.0 {
|
match &mut this.0 {
|
||||||
Ok(thread) => {
|
Ok(thread) => pin!(thread).poll(cx),
|
||||||
let pinned_thread = unsafe { Pin::new_unchecked(thread) };
|
|
||||||
pinned_thread.poll(cx)
|
|
||||||
}
|
|
||||||
Err(err) => Poll::Ready(Err(err.clone())),
|
Err(err) => Poll::Ready(Err(err.clone())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-18
@@ -35,7 +35,7 @@
|
|||||||
//! The [`Value`] enum and other types implement [`serde::Serialize`] trait to support serializing
|
//! The [`Value`] enum and other types implement [`serde::Serialize`] trait to support serializing
|
||||||
//! Lua values into Rust values.
|
//! Lua values into Rust values.
|
||||||
//!
|
//!
|
||||||
//! Requires `feature = "serialize"`.
|
//! Requires `feature = "serde"`.
|
||||||
//!
|
//!
|
||||||
//! # Async/await support
|
//! # Async/await support
|
||||||
//!
|
//!
|
||||||
@@ -66,7 +66,6 @@
|
|||||||
// warnings at all.
|
// warnings at all.
|
||||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![cfg_attr(not(send), allow(clippy::arc_with_non_send_sync))]
|
#![cfg_attr(not(send), allow(clippy::arc_with_non_send_sync))]
|
||||||
#![allow(clippy::ptr_eq)]
|
|
||||||
#![allow(unsafe_op_in_unsafe_fn)]
|
#![allow(unsafe_op_in_unsafe_fn)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
@@ -76,8 +75,6 @@ mod buffer;
|
|||||||
mod chunk;
|
mod chunk;
|
||||||
mod conversion;
|
mod conversion;
|
||||||
mod error;
|
mod error;
|
||||||
mod function;
|
|
||||||
mod hook;
|
|
||||||
#[cfg(any(feature = "luau", doc))]
|
#[cfg(any(feature = "luau", doc))]
|
||||||
mod luau;
|
mod luau;
|
||||||
mod memory;
|
mod memory;
|
||||||
@@ -86,7 +83,6 @@ mod scope;
|
|||||||
mod state;
|
mod state;
|
||||||
mod stdlib;
|
mod stdlib;
|
||||||
mod string;
|
mod string;
|
||||||
mod table;
|
|
||||||
mod thread;
|
mod thread;
|
||||||
mod traits;
|
mod traits;
|
||||||
mod types;
|
mod types;
|
||||||
@@ -95,21 +91,23 @@ mod util;
|
|||||||
mod value;
|
mod value;
|
||||||
mod vector;
|
mod vector;
|
||||||
|
|
||||||
|
pub mod debug;
|
||||||
|
pub mod function;
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
|
pub mod table;
|
||||||
|
|
||||||
pub use bstr::BString;
|
pub use bstr::BString;
|
||||||
pub use ffi::{self, lua_CFunction, lua_State};
|
pub use ffi::{self, lua_CFunction, lua_State};
|
||||||
|
|
||||||
pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
|
pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
|
||||||
pub use crate::error::{Error, ErrorContext, ExternalError, ExternalResult, Result};
|
pub use crate::error::{Error, ErrorContext, ExternalError, ExternalResult, Result};
|
||||||
pub use crate::function::{Function, FunctionInfo};
|
pub use crate::function::Function;
|
||||||
pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack};
|
|
||||||
pub use crate::multi::{MultiValue, Variadic};
|
pub use crate::multi::{MultiValue, Variadic};
|
||||||
pub use crate::scope::Scope;
|
pub use crate::scope::Scope;
|
||||||
pub use crate::state::{GCMode, Lua, LuaOptions, WeakLua};
|
pub use crate::state::{GCMode, Lua, LuaOptions, WeakLua};
|
||||||
pub use crate::stdlib::StdLib;
|
pub use crate::stdlib::StdLib;
|
||||||
pub use crate::string::{BorrowedBytes, BorrowedStr, String};
|
pub use crate::string::{BorrowedBytes, BorrowedStr, LuaString, LuaString as String};
|
||||||
pub use crate::table::{Table, TablePairs, TableSequence};
|
pub use crate::table::Table;
|
||||||
pub use crate::thread::{Thread, ThreadStatus};
|
pub use crate::thread::{Thread, ThreadStatus};
|
||||||
pub use crate::traits::{
|
pub use crate::traits::{
|
||||||
FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, LuaNativeFn, LuaNativeFnMut, ObjectLike,
|
FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, LuaNativeFn, LuaNativeFnMut, ObjectLike,
|
||||||
@@ -124,15 +122,14 @@ pub use crate::userdata::{
|
|||||||
pub use crate::value::{Nil, Value};
|
pub use crate::value::{Nil, Value};
|
||||||
|
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
pub use crate::hook::HookTriggers;
|
pub use crate::debug::HookTriggers;
|
||||||
|
|
||||||
#[cfg(any(feature = "luau", doc))]
|
#[cfg(any(feature = "luau", doc))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
buffer::Buffer,
|
buffer::Buffer,
|
||||||
chunk::{CompileConstant, Compiler},
|
chunk::{CompileConstant, Compiler},
|
||||||
function::CoverageInfo,
|
luau::{HeapDump, NavigateError, Require, TextRequirer},
|
||||||
luau::{NavigateError, Require},
|
|
||||||
vector::Vector,
|
vector::Vector,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -140,12 +137,15 @@ pub use crate::{
|
|||||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||||
pub use crate::{thread::AsyncThread, traits::LuaNativeAsyncFn};
|
pub use crate::{thread::AsyncThread, traits::LuaNativeAsyncFn};
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use crate::serde::{de::Options as DeserializeOptions, ser::Options as SerializeOptions, LuaSerdeExt};
|
pub use crate::{
|
||||||
|
serde::{LuaSerdeExt, de::Options as DeserializeOptions, ser::Options as SerializeOptions},
|
||||||
|
value::SerializableValue,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||||
pub mod serde;
|
pub mod serde;
|
||||||
|
|
||||||
#[cfg(feature = "mlua_derive")]
|
#[cfg(feature = "mlua_derive")]
|
||||||
@@ -217,7 +217,7 @@ pub use mlua_derive::FromLua;
|
|||||||
///
|
///
|
||||||
/// You can register multiple entrypoints as required.
|
/// You can register multiple entrypoints as required.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```ignore
|
||||||
/// use mlua::{Lua, Result, Table};
|
/// use mlua::{Lua, Result, Table};
|
||||||
///
|
///
|
||||||
/// #[mlua::lua_module]
|
/// #[mlua::lua_module]
|
||||||
@@ -254,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")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "module")))]
|
||||||
pub use mlua_derive::lua_module;
|
pub use mlua_derive::lua_module;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,178 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::hash::Hash;
|
||||||
|
use std::mem;
|
||||||
|
use std::os::raw::c_char;
|
||||||
|
|
||||||
|
use crate::state::ExtraData;
|
||||||
|
|
||||||
|
use super::json::{self, Json};
|
||||||
|
|
||||||
|
/// Represents a heap dump of a Luau memory state.
|
||||||
|
#[cfg(any(feature = "luau", doc))]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||||
|
pub struct HeapDump {
|
||||||
|
data: Json<'static>, // refers to the contents of `buf`
|
||||||
|
buf: Box<str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HeapDump {
|
||||||
|
/// Dumps the current Lua heap state.
|
||||||
|
pub(crate) unsafe fn new(state: *mut ffi::lua_State) -> Option<Self> {
|
||||||
|
unsafe extern "C" fn category_name(state: *mut ffi::lua_State, cat: u8) -> *const c_char {
|
||||||
|
(&*ExtraData::get(state))
|
||||||
|
.mem_categories
|
||||||
|
.get(cat as usize)
|
||||||
|
.map(|s| s.as_ptr())
|
||||||
|
.unwrap_or(cstr!("unknown"))
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
unsafe {
|
||||||
|
let file = libc::tmpfile();
|
||||||
|
if file.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
ffi::lua_gcdump(state, file as *mut _, Some(category_name));
|
||||||
|
libc::fseek(file, 0, libc::SEEK_END);
|
||||||
|
let len = libc::ftell(file) as usize;
|
||||||
|
libc::rewind(file);
|
||||||
|
if len > 0 {
|
||||||
|
buf.reserve(len);
|
||||||
|
libc::fread(buf.as_mut_ptr() as *mut _, 1, len, file);
|
||||||
|
buf.set_len(len);
|
||||||
|
}
|
||||||
|
libc::fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
let buf = String::from_utf8(buf).ok()?.into_boxed_str();
|
||||||
|
let data = json::parse(unsafe { mem::transmute::<&str, &'static str>(&buf) }).ok()?;
|
||||||
|
Some(HeapDump { data, buf })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the raw JSON representation of the heap dump.
|
||||||
|
///
|
||||||
|
/// The JSON structure is an internal detail and may change in future versions.
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn to_json(&self) -> &str {
|
||||||
|
&self.buf
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the total size of the Lua heap in bytes.
|
||||||
|
pub fn size(&self) -> u64 {
|
||||||
|
self.data["stats"]["size"].as_u64().unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mapping from object type to (count, total size in bytes).
|
||||||
|
///
|
||||||
|
/// If `category` is provided, only objects in that category are considered.
|
||||||
|
pub fn size_by_type<'a>(&'a self, category: Option<&str>) -> HashMap<&'a str, (usize, u64)> {
|
||||||
|
self.size_by_type_inner(category).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size_by_type_inner<'a>(&'a self, category: Option<&str>) -> Option<HashMap<&'a str, (usize, u64)>> {
|
||||||
|
let category_id = match category {
|
||||||
|
// If we cannot find the category, return empty result
|
||||||
|
Some(cat) => Some(self.find_category_id(cat)?),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut size_by_type = HashMap::new();
|
||||||
|
let objects = self.data["objects"].as_object()?;
|
||||||
|
for obj in objects.values() {
|
||||||
|
if let Some(cat_id) = category_id
|
||||||
|
&& obj["cat"].as_i64()? != cat_id
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
update_size(&mut size_by_type, obj["type"].as_str()?, obj["size"].as_u64()?);
|
||||||
|
}
|
||||||
|
Some(size_by_type)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mapping from category name to total size in bytes.
|
||||||
|
pub fn size_by_category(&self) -> HashMap<&str, u64> {
|
||||||
|
let mut size_by_category = HashMap::new();
|
||||||
|
if let Some(categories) = self.data["stats"]["categories"].as_object() {
|
||||||
|
for cat in categories.values() {
|
||||||
|
if let Some(cat_name) = cat["name"].as_str() {
|
||||||
|
size_by_category.insert(cat_name, cat["size"].as_u64().unwrap_or_default());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
size_by_category
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mapping from userdata type to (count, total size in bytes).
|
||||||
|
pub fn size_by_userdata<'a>(&'a self, category: Option<&str>) -> HashMap<&'a str, (usize, u64)> {
|
||||||
|
self.size_by_userdata_inner(category).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size_by_userdata_inner<'a>(
|
||||||
|
&'a self,
|
||||||
|
category: Option<&str>,
|
||||||
|
) -> Option<HashMap<&'a str, (usize, u64)>> {
|
||||||
|
let category_id = match category {
|
||||||
|
// If we cannot find the category, return empty result
|
||||||
|
Some(cat) => Some(self.find_category_id(cat)?),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut size_by_userdata = HashMap::new();
|
||||||
|
let objects = self.data["objects"].as_object()?;
|
||||||
|
for obj in objects.values() {
|
||||||
|
if obj["type"] != "userdata" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let Some(cat_id) = category_id
|
||||||
|
&& obj["cat"].as_i64()? != cat_id
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine userdata type from metatable
|
||||||
|
let mut ud_type = "unknown";
|
||||||
|
if let Some(metatable_addr) = obj["metatable"].as_str()
|
||||||
|
&& let Some(t) = get_key(objects, &objects[metatable_addr], "__type")
|
||||||
|
{
|
||||||
|
ud_type = t;
|
||||||
|
}
|
||||||
|
update_size(&mut size_by_userdata, ud_type, obj["size"].as_u64()?);
|
||||||
|
}
|
||||||
|
Some(size_by_userdata)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds the category ID for a given category name.
|
||||||
|
fn find_category_id(&self, category: &str) -> Option<i64> {
|
||||||
|
let categories = self.data["stats"]["categories"].as_object()?;
|
||||||
|
for (cat_id, cat) in categories {
|
||||||
|
if cat["name"].as_str() == Some(category) {
|
||||||
|
return cat_id.parse().ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates the size mapping for a given key.
|
||||||
|
fn update_size<K: Eq + Hash>(size_type: &mut HashMap<K, (usize, u64)>, key: K, size: u64) {
|
||||||
|
let (count, total_size) = size_type.entry(key).or_insert((0, 0));
|
||||||
|
*count += 1;
|
||||||
|
*total_size += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the value associated with a given `key` from a Lua table `tbl`.
|
||||||
|
fn get_key<'a>(objects: &'a HashMap<&'a str, Json>, tbl: &Json, key: &str) -> Option<&'a str> {
|
||||||
|
let pairs = tbl["pairs"].as_array()?;
|
||||||
|
for kv in pairs.chunks_exact(2) {
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let (Some(key_addr), Some(val_addr)) = (kv[0].as_str(), kv[1].as_str()) else { continue; };
|
||||||
|
if objects[key_addr]["type"] == "string" && objects[key_addr]["data"].as_str() == Some(key) {
|
||||||
|
if objects[val_addr]["type"] == "string" {
|
||||||
|
return objects[val_addr]["data"].as_str();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
@@ -0,0 +1,327 @@
|
|||||||
|
use std::array;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::iter::Peekable;
|
||||||
|
use std::ops::Index;
|
||||||
|
use std::str::CharIndices;
|
||||||
|
|
||||||
|
// A simple JSON parser and representation.
|
||||||
|
// This parser supports only a subset of JSON specification and is intended for Luau's use cases.
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub(crate) enum Json<'a> {
|
||||||
|
Null,
|
||||||
|
Bool(bool),
|
||||||
|
Integer(i64),
|
||||||
|
Number(f64),
|
||||||
|
String(&'a str),
|
||||||
|
Array(Vec<Json<'a>>),
|
||||||
|
Object(HashMap<&'a str, Json<'a>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Index<&str> for Json<'a> {
|
||||||
|
type Output = Json<'a>;
|
||||||
|
|
||||||
|
fn index(&self, key: &str) -> &Self::Output {
|
||||||
|
match self {
|
||||||
|
Json::Object(map) => map.get(key).unwrap_or(&Json::Null),
|
||||||
|
_ => &Json::Null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<&str> for Json<'_> {
|
||||||
|
fn eq(&self, other: &&str) -> bool {
|
||||||
|
matches!(self, Json::String(s) if s == other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Json<'a> {
|
||||||
|
pub(crate) fn as_str(&self) -> Option<&'a str> {
|
||||||
|
match self {
|
||||||
|
Json::String(s) => Some(s),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn as_i64(&self) -> Option<i64> {
|
||||||
|
match self {
|
||||||
|
Json::Integer(i) => Some(*i),
|
||||||
|
Json::Number(n) if n.fract() == 0.0 => Some(*n as i64),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn as_u64(&self) -> Option<u64> {
|
||||||
|
self.as_i64()
|
||||||
|
.and_then(|i| if i >= 0 { Some(i as u64) } else { None })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn as_array(&self) -> Option<&[Json<'a>]> {
|
||||||
|
match self {
|
||||||
|
Json::Array(arr) => Some(arr),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn as_object(&self) -> Option<&HashMap<&'a str, Json<'a>>> {
|
||||||
|
match self {
|
||||||
|
Json::Object(map) => Some(map),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parse<'a>(s: &'a str) -> Result<Json<'a>, &'static str> {
|
||||||
|
let s = s.trim_ascii();
|
||||||
|
let mut chars = s.char_indices().peekable();
|
||||||
|
let value = parse_value(s, &mut chars)?;
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_value<'a>(s: &'a str, chars: &mut Peekable<CharIndices>) -> Result<Json<'a>, &'static str> {
|
||||||
|
skip_whitespace(chars);
|
||||||
|
match chars.peek() {
|
||||||
|
Some((_, '{')) => parse_object(s, chars),
|
||||||
|
Some((_, '[')) => parse_array(s, chars),
|
||||||
|
Some((_, '"')) => parse_string(s, chars).map(Json::String),
|
||||||
|
Some((_, 't' | 'f')) => parse_bool(chars),
|
||||||
|
Some((_, 'n')) => parse_null(chars),
|
||||||
|
Some((_, '-' | '0'..='9')) => parse_number(chars),
|
||||||
|
Some(_) => Err("unexpected character"),
|
||||||
|
None => Err("unexpected end of input"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_object<'a>(s: &'a str, chars: &mut Peekable<CharIndices>) -> Result<Json<'a>, &'static str> {
|
||||||
|
chars.next(); // consume '{'
|
||||||
|
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
skip_whitespace(chars);
|
||||||
|
if matches!(chars.peek(), Some((_, '}'))) {
|
||||||
|
chars.next();
|
||||||
|
return Ok(Json::Object(map));
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
skip_whitespace(chars);
|
||||||
|
let key = parse_string(s, chars)?;
|
||||||
|
skip_whitespace(chars);
|
||||||
|
if !matches!(chars.next(), Some((_, ':'))) {
|
||||||
|
return Err("expected ':'");
|
||||||
|
}
|
||||||
|
let value = parse_value(s, chars)?;
|
||||||
|
map.insert(key, value);
|
||||||
|
skip_whitespace(chars);
|
||||||
|
match chars.next() {
|
||||||
|
Some((_, ',')) => continue,
|
||||||
|
Some((_, '}')) => break,
|
||||||
|
_ => return Err("expected ',' or '}'"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Json::Object(map))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_array<'a>(s: &'a str, chars: &mut Peekable<CharIndices>) -> Result<Json<'a>, &'static str> {
|
||||||
|
chars.next(); // consume '['
|
||||||
|
|
||||||
|
let mut arr = Vec::new();
|
||||||
|
skip_whitespace(chars);
|
||||||
|
if matches!(chars.peek(), Some((_, ']'))) {
|
||||||
|
chars.next();
|
||||||
|
return Ok(Json::Array(arr));
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
skip_whitespace(chars);
|
||||||
|
arr.push(parse_value(s, chars)?);
|
||||||
|
skip_whitespace(chars);
|
||||||
|
match chars.next() {
|
||||||
|
Some((_, ',')) => continue,
|
||||||
|
Some((_, ']')) => return Ok(Json::Array(arr)),
|
||||||
|
_ => return Err("expected ',' or ']'"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_string<'a>(s: &'a str, chars: &mut Peekable<CharIndices>) -> Result<&'a str, &'static str> {
|
||||||
|
if !matches!(chars.next(), Some((_, '"'))) {
|
||||||
|
return Err("expected string starting with '\"'");
|
||||||
|
}
|
||||||
|
let start = chars.peek().map(|(i, _)| *i).unwrap_or(0);
|
||||||
|
for (i, c) in chars {
|
||||||
|
if c == '"' {
|
||||||
|
return Ok(&s[start..i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err("unterminated string")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_number(chars: &mut Peekable<CharIndices>) -> Result<Json<'static>, &'static str> {
|
||||||
|
let mut is_float = false;
|
||||||
|
let mut num = String::new();
|
||||||
|
while let Some((_, c @ ('0'..='9' | '-' | '.' | 'e' | 'E' | '+'))) = chars.peek() {
|
||||||
|
num.push(*c);
|
||||||
|
is_float = is_float || matches!(c, '.' | 'e' | 'E');
|
||||||
|
chars.next();
|
||||||
|
}
|
||||||
|
if !is_float {
|
||||||
|
let i = num.parse::<i64>().map_err(|_| "invalid integer")?;
|
||||||
|
return Ok(Json::Integer(i));
|
||||||
|
}
|
||||||
|
let n = num.parse::<f64>().map_err(|_| "invalid number")?;
|
||||||
|
Ok(Json::Number(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_bool(chars: &mut Peekable<CharIndices>) -> Result<Json<'static>, &'static str> {
|
||||||
|
let bool = next_chars(chars);
|
||||||
|
if bool == [Some('t'), Some('r'), Some('u'), Some('e')] {
|
||||||
|
return Ok(Json::Bool(true));
|
||||||
|
}
|
||||||
|
if bool == [Some('f'), Some('a'), Some('l'), Some('s')] && matches!(chars.next(), Some((_, 'e'))) {
|
||||||
|
return Ok(Json::Bool(false));
|
||||||
|
}
|
||||||
|
Err("invalid boolean literal")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_null(chars: &mut Peekable<CharIndices>) -> Result<Json<'static>, &'static str> {
|
||||||
|
if next_chars(chars) == [Some('n'), Some('u'), Some('l'), Some('l')] {
|
||||||
|
return Ok(Json::Null);
|
||||||
|
}
|
||||||
|
Err("invalid \"null\" literal")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn skip_whitespace(chars: &mut Peekable<CharIndices>) {
|
||||||
|
while let Some((_, ' ' | '\n' | '\r' | '\t')) = chars.peek() {
|
||||||
|
chars.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_chars<const N: usize>(chars: &mut Peekable<CharIndices>) -> [Option<char>; N] {
|
||||||
|
array::from_fn(|_| chars.next().map(|(_, c)| c))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse() {
|
||||||
|
assert_eq!(parse("null").unwrap(), Json::Null);
|
||||||
|
assert_eq!(parse("true").unwrap(), Json::Bool(true));
|
||||||
|
assert_eq!(parse("false").unwrap(), Json::Bool(false));
|
||||||
|
assert_eq!(parse("42").unwrap(), Json::Integer(42));
|
||||||
|
assert_eq!(parse("42.0").unwrap(), Json::Number(42.0));
|
||||||
|
assert_eq!(parse(r#""hello""#).unwrap(), Json::String("hello"));
|
||||||
|
assert_eq!(
|
||||||
|
parse("[1,2.0,3]").unwrap(),
|
||||||
|
Json::Array(vec![Json::Integer(1), Json::Number(2.0), Json::Integer(3)])
|
||||||
|
);
|
||||||
|
let mut obj = HashMap::new();
|
||||||
|
obj.insert("key", Json::String("value"));
|
||||||
|
assert_eq!(parse(r#"{"key":"value"}"#).unwrap(), Json::Object(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_whitespace_handling() {
|
||||||
|
assert_eq!(parse(" null ").unwrap(), Json::Null);
|
||||||
|
assert_eq!(parse(" true ").unwrap(), Json::Bool(true));
|
||||||
|
assert_eq!(
|
||||||
|
parse(" [ 1 , 2.0 , 3 ] ").unwrap(),
|
||||||
|
Json::Array(vec![Json::Integer(1), Json::Number(2.0), Json::Integer(3)])
|
||||||
|
);
|
||||||
|
let mut obj = HashMap::new();
|
||||||
|
obj.insert("key", Json::String("value"));
|
||||||
|
assert_eq!(parse(r#" { "key" : "value" } "#).unwrap(), Json::Object(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_empty_collections() {
|
||||||
|
assert_eq!(parse("[]").unwrap(), Json::Array(vec![]));
|
||||||
|
assert_eq!(parse("{}").unwrap(), Json::Object(HashMap::new()));
|
||||||
|
assert_eq!(parse("[ ]").unwrap(), Json::Array(vec![]));
|
||||||
|
assert_eq!(parse("{ }").unwrap(), Json::Object(HashMap::new()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_nested_structures() {
|
||||||
|
assert_eq!(
|
||||||
|
parse(r#"{"nested":{"inner":"value"}}"#).unwrap(),
|
||||||
|
Json::Object({
|
||||||
|
let mut outer = HashMap::new();
|
||||||
|
let mut inner = HashMap::new();
|
||||||
|
inner.insert("inner", Json::String("value"));
|
||||||
|
outer.insert("nested", Json::Object(inner));
|
||||||
|
outer
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse("[[1,2],[3,4]]").unwrap(),
|
||||||
|
Json::Array(vec![
|
||||||
|
Json::Array(vec![Json::Integer(1), Json::Integer(2)]),
|
||||||
|
Json::Array(vec![Json::Integer(3), Json::Integer(4)])
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_numbers() {
|
||||||
|
assert_eq!(parse("0").unwrap(), Json::Integer(0));
|
||||||
|
assert_eq!(parse("-42").unwrap(), Json::Integer(-42));
|
||||||
|
assert_eq!(parse("3.14").unwrap(), Json::Number(3.14));
|
||||||
|
assert_eq!(parse("-3.14").unwrap(), Json::Number(-3.14));
|
||||||
|
assert_eq!(parse("1e10").unwrap(), Json::Number(1e10));
|
||||||
|
assert_eq!(parse("1E10").unwrap(), Json::Number(1E10));
|
||||||
|
assert_eq!(parse("1e-10").unwrap(), Json::Number(1e-10));
|
||||||
|
assert_eq!(parse("1.5e+10").unwrap(), Json::Number(1.5e+10));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_strings() {
|
||||||
|
assert_eq!(parse(r#""""#).unwrap(), Json::String(""));
|
||||||
|
assert_eq!(parse(r#""hello world""#).unwrap(), Json::String("hello world"));
|
||||||
|
assert_eq!(
|
||||||
|
parse(r#""with spaces and 123""#).unwrap(),
|
||||||
|
Json::String("with spaces and 123")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mixed_array() {
|
||||||
|
assert_eq!(
|
||||||
|
parse(r#"[null, true, false, 35.1, 42, "text", [], {}]"#).unwrap(),
|
||||||
|
Json::Array(vec![
|
||||||
|
Json::Null,
|
||||||
|
Json::Bool(true),
|
||||||
|
Json::Bool(false),
|
||||||
|
Json::Number(35.1),
|
||||||
|
Json::Integer(42),
|
||||||
|
Json::String("text"),
|
||||||
|
Json::Array(vec![]),
|
||||||
|
Json::Object(HashMap::new())
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_object_multiple_keys() {
|
||||||
|
let mut obj = HashMap::new();
|
||||||
|
obj.insert("a", Json::Integer(1));
|
||||||
|
obj.insert("b", Json::Bool(true));
|
||||||
|
obj.insert("c", Json::Null);
|
||||||
|
assert_eq!(parse(r#"{"a":1,"b":true,"c":null}"#).unwrap(), Json::Object(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_cases() {
|
||||||
|
assert!(parse("").is_err());
|
||||||
|
assert!(parse("nul").is_err());
|
||||||
|
assert!(parse("tru").is_err()); // typos:ignore
|
||||||
|
assert!(parse("fals").is_err()); // typos:ignore
|
||||||
|
assert!(parse(r#""unterminated"#).is_err());
|
||||||
|
assert!(parse("[1,2,]").is_err());
|
||||||
|
assert!(parse(r#"{"key""#).is_err());
|
||||||
|
assert!(parse(r#"{"key":"value""#).is_err());
|
||||||
|
assert!(parse(r#"{"key":"value",}"#).is_err());
|
||||||
|
assert!(parse("invalid").is_err());
|
||||||
|
assert!(parse("[1 2]").is_err());
|
||||||
|
assert!(parse(r#"{"key":"value" "key2":"value2"}"#).is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
+85
-10
@@ -1,11 +1,16 @@
|
|||||||
use std::ffi::CStr;
|
use std::ffi::{CStr, CString};
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
use crate::error::Result;
|
use crate::chunk::ChunkMode;
|
||||||
|
use crate::error::{Error, Result};
|
||||||
use crate::function::Function;
|
use crate::function::Function;
|
||||||
use crate::state::Lua;
|
use crate::state::{ExtraData, Lua, callback_error_ext};
|
||||||
|
use crate::traits::{FromLuaMulti, IntoLua};
|
||||||
|
use crate::types::MaybeSend;
|
||||||
|
|
||||||
pub use require::{NavigateError, Require};
|
pub use heap_dump::HeapDump;
|
||||||
|
pub use require::{NavigateError, Require, TextRequirer};
|
||||||
|
|
||||||
// Since Luau has some missing standard functions, we re-implement them here
|
// Since Luau has some missing standard functions, we re-implement them here
|
||||||
|
|
||||||
@@ -14,14 +19,65 @@ impl Lua {
|
|||||||
/// and load modules.
|
/// and load modules.
|
||||||
#[cfg(any(feature = "luau", doc))]
|
#[cfg(any(feature = "luau", doc))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||||
pub fn create_require_function<R: Require + 'static>(&self, require: R) -> Result<Function> {
|
pub fn create_require_function<R: Require + MaybeSend + 'static>(&self, require: R) -> Result<Function> {
|
||||||
require::create_require_function(self, require)
|
require::create_require_function(self, require)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the memory category for subsequent allocations from this Lua state.
|
||||||
|
///
|
||||||
|
/// The category "main" is reserved for the default memory category.
|
||||||
|
/// Maximum of 255 categories can be registered.
|
||||||
|
/// The category is set per Lua thread (state) and affects all allocations made from that
|
||||||
|
/// thread.
|
||||||
|
///
|
||||||
|
/// Return error if too many categories are registered or if the category name is invalid.
|
||||||
|
///
|
||||||
|
/// See [`Lua::heap_dump`] for tracking memory usage by category.
|
||||||
|
#[cfg(any(feature = "luau", doc))]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||||
|
pub fn set_memory_category(&self, category: &str) -> Result<()> {
|
||||||
|
let lua = self.lock();
|
||||||
|
|
||||||
|
if category.contains(|c| !matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_')) {
|
||||||
|
return Err(Error::runtime("invalid memory category name"));
|
||||||
|
}
|
||||||
|
let cat_id = unsafe {
|
||||||
|
let extra = ExtraData::get(lua.state());
|
||||||
|
match ((*extra).mem_categories.iter().enumerate())
|
||||||
|
.find(|&(_, name)| name.as_bytes() == category.as_bytes())
|
||||||
|
{
|
||||||
|
Some((id, _)) => id as u8,
|
||||||
|
None => {
|
||||||
|
let new_id = (*extra).mem_categories.len() as u8;
|
||||||
|
if new_id == 255 {
|
||||||
|
return Err(Error::runtime("too many memory categories registered"));
|
||||||
|
}
|
||||||
|
(*extra).mem_categories.push(CString::new(category).unwrap());
|
||||||
|
new_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
unsafe { ffi::lua_setmemcat(lua.state(), cat_id as i32) };
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dumps the current Lua VM heap state.
|
||||||
|
///
|
||||||
|
/// The returned `HeapDump` can be used to analyze memory usage.
|
||||||
|
/// It's recommended to call [`Lua::gc_collect`] before dumping the heap.
|
||||||
|
#[cfg(any(feature = "luau", doc))]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||||
|
pub fn heap_dump(&self) -> Result<HeapDump> {
|
||||||
|
let lua = self.lock();
|
||||||
|
unsafe { heap_dump::HeapDump::new(lua.state()).ok_or_else(|| Error::runtime("unable to dump heap")) }
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn configure_luau(&self) -> Result<()> {
|
pub(crate) unsafe fn configure_luau(&self) -> Result<()> {
|
||||||
let globals = self.globals();
|
let globals = self.globals();
|
||||||
|
|
||||||
globals.raw_set("collectgarbage", self.create_c_function(lua_collectgarbage)?)?;
|
globals.raw_set("collectgarbage", self.create_c_function(lua_collectgarbage)?)?;
|
||||||
|
globals.raw_set("loadstring", self.create_c_function(lua_loadstring)?)?;
|
||||||
|
|
||||||
// Set `_VERSION` global to include version number
|
// Set `_VERSION` global to include version number
|
||||||
// The environment variable `LUAU_VERSION` set by the build script
|
// The environment variable `LUAU_VERSION` set by the build script
|
||||||
@@ -41,16 +97,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 = ffi::luaL_optstring(state, 1, cstr!("collect"));
|
||||||
let option = CStr::from_ptr(option);
|
let option = CStr::from_ptr(option);
|
||||||
let arg = ffi::luaL_optinteger(state, 2, 0);
|
let arg = ffi::luaL_optinteger(state, 2, 0);
|
||||||
|
let is_sandboxed = (*ExtraData::get(state)).sandboxed;
|
||||||
match option.to_str() {
|
match option.to_str() {
|
||||||
Ok("collect") => {
|
Ok("collect") if !is_sandboxed => {
|
||||||
ffi::lua_gc(state, ffi::LUA_GCCOLLECT, 0);
|
ffi::lua_gc(state, ffi::LUA_GCCOLLECT, 0);
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
Ok("stop") => {
|
Ok("stop") if !is_sandboxed => {
|
||||||
ffi::lua_gc(state, ffi::LUA_GCSTOP, 0);
|
ffi::lua_gc(state, ffi::LUA_GCSTOP, 0);
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
Ok("restart") => {
|
Ok("restart") if !is_sandboxed => {
|
||||||
ffi::lua_gc(state, ffi::LUA_GCRESTART, 0);
|
ffi::lua_gc(state, ffi::LUA_GCRESTART, 0);
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
@@ -60,12 +117,12 @@ unsafe extern "C-unwind" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_
|
|||||||
ffi::lua_pushnumber(state, kbytes + kbytes_rem / 1024.0);
|
ffi::lua_pushnumber(state, kbytes + kbytes_rem / 1024.0);
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
Ok("step") => {
|
Ok("step") if !is_sandboxed => {
|
||||||
let res = ffi::lua_gc(state, ffi::LUA_GCSTEP, arg as _);
|
let res = ffi::lua_gc(state, ffi::LUA_GCSTEP, arg as _);
|
||||||
ffi::lua_pushboolean(state, res);
|
ffi::lua_pushboolean(state, res);
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
Ok("isrunning") => {
|
Ok("isrunning") if !is_sandboxed => {
|
||||||
let res = ffi::lua_gc(state, ffi::LUA_GCISRUNNING, 0);
|
let res = ffi::lua_gc(state, ffi::LUA_GCISRUNNING, 0);
|
||||||
ffi::lua_pushboolean(state, res);
|
ffi::lua_pushboolean(state, res);
|
||||||
1
|
1
|
||||||
@@ -74,4 +131,22 @@ unsafe extern "C-unwind" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
mod heap_dump;
|
||||||
|
mod json;
|
||||||
mod require;
|
mod require;
|
||||||
|
|||||||
+230
-403
@@ -1,98 +1,100 @@
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::VecDeque;
|
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::io::Result as IoResult;
|
use std::io::Result as IoResult;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
use std::path::{Component, Path, PathBuf};
|
|
||||||
use std::result::Result as StdResult;
|
use std::result::Result as StdResult;
|
||||||
use std::{env, fmt, fs, mem, ptr};
|
use std::{fmt, mem, ptr};
|
||||||
|
|
||||||
use crate::error::Result;
|
use crate::error::{Error, Result};
|
||||||
use crate::function::Function;
|
use crate::function::Function;
|
||||||
use crate::state::{callback_error_ext, Lua};
|
use crate::state::{Lua, callback_error_ext};
|
||||||
use crate::table::Table;
|
use crate::table::Table;
|
||||||
use crate::types::MaybeSend;
|
use crate::types::MaybeSend;
|
||||||
|
|
||||||
/// An error that can occur during navigation in the Luau `require` system.
|
// TODO: Rename to FsRequirer
|
||||||
|
pub use fs::TextRequirer;
|
||||||
|
|
||||||
|
/// An error that can occur during navigation in the Luau `require-by-string` system.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub enum NavigateError {
|
pub enum NavigateError {
|
||||||
Ambiguous,
|
Ambiguous,
|
||||||
NotFound,
|
NotFound,
|
||||||
|
Other(Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
trait IntoNavigateResult {
|
trait IntoNavigateResult {
|
||||||
fn into_nav_result(self) -> ffi::luarequire_NavigateResult;
|
fn into_nav_result(self) -> Result<ffi::luarequire_NavigateResult>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
impl IntoNavigateResult for StdResult<(), NavigateError> {
|
impl IntoNavigateResult for StdResult<(), NavigateError> {
|
||||||
fn into_nav_result(self) -> ffi::luarequire_NavigateResult {
|
fn into_nav_result(self) -> Result<ffi::luarequire_NavigateResult> {
|
||||||
match self {
|
match self {
|
||||||
Ok(()) => ffi::luarequire_NavigateResult::Success,
|
Ok(()) => Ok(ffi::luarequire_NavigateResult::Success),
|
||||||
Err(NavigateError::Ambiguous) => ffi::luarequire_NavigateResult::Ambiguous,
|
Err(NavigateError::Ambiguous) => Ok(ffi::luarequire_NavigateResult::Ambiguous),
|
||||||
Err(NavigateError::NotFound) => ffi::luarequire_NavigateResult::NotFound,
|
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")]
|
#[cfg(feature = "luau")]
|
||||||
type WriteResult = ffi::luarequire_WriteResult;
|
type WriteResult = ffi::luarequire_WriteResult;
|
||||||
|
|
||||||
/// A trait for handling modules loading and navigation in the Luau `require` system.
|
#[cfg(feature = "luau")]
|
||||||
pub trait Require: MaybeSend {
|
type ConfigStatus = ffi::luarequire_ConfigStatus;
|
||||||
|
|
||||||
|
/// A trait for handling modules loading and navigation in the Luau `require-by-string` system.
|
||||||
|
pub trait Require {
|
||||||
/// Returns `true` if "require" is permitted for the given chunk name.
|
/// Returns `true` if "require" is permitted for the given chunk name.
|
||||||
fn is_require_allowed(&self, chunk_name: &str) -> bool;
|
fn is_require_allowed(&self, chunk_name: &str) -> bool;
|
||||||
|
|
||||||
/// Resets the internal state to point at the requirer module.
|
/// Resets the internal state to point at the requirer module.
|
||||||
fn reset(&self, chunk_name: &str) -> StdResult<(), NavigateError>;
|
fn reset(&mut self, chunk_name: &str) -> StdResult<(), NavigateError>;
|
||||||
|
|
||||||
/// Resets the internal state to point at an aliased module.
|
/// Resets the internal state to point at an aliased module.
|
||||||
///
|
///
|
||||||
/// This function received an exact path from a configuration file.
|
/// 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
|
/// It's only called when an alias's path cannot be resolved relative to its
|
||||||
/// configuration file.
|
/// configuration file.
|
||||||
fn jump_to_alias(&self, path: &str) -> StdResult<(), NavigateError>;
|
fn jump_to_alias(&mut self, path: &str) -> StdResult<(), NavigateError>;
|
||||||
|
|
||||||
// Navigate to parent directory
|
// Navigate to parent directory
|
||||||
fn to_parent(&self) -> StdResult<(), NavigateError>;
|
fn to_parent(&mut self) -> StdResult<(), NavigateError>;
|
||||||
|
|
||||||
/// Navigate to the given child directory.
|
/// Navigate to the given child directory.
|
||||||
fn to_child(&self, name: &str) -> StdResult<(), NavigateError>;
|
fn to_child(&mut self, name: &str) -> StdResult<(), NavigateError>;
|
||||||
|
|
||||||
/// Returns whether the context is currently pointing at a module
|
/// Returns whether the context is currently pointing at a module.
|
||||||
fn is_module_present(&self) -> bool;
|
fn has_module(&self) -> bool;
|
||||||
|
|
||||||
/// Returns the contents of the current module
|
|
||||||
///
|
|
||||||
/// This function is only called if `is_module_present` returns true.
|
|
||||||
fn contents(&self) -> IoResult<Vec<u8>>;
|
|
||||||
|
|
||||||
/// Returns a chunk name for the current module.
|
|
||||||
///
|
|
||||||
/// This function is only called if `is_module_present` returns true.
|
|
||||||
/// The chunk name is used to identify the module using the debug library.
|
|
||||||
fn chunk_name(&self) -> String;
|
|
||||||
|
|
||||||
/// Provides a cache key representing the current module.
|
/// Provides a cache key representing the current module.
|
||||||
///
|
///
|
||||||
/// This function is only called if `is_module_present` returns true.
|
/// This function is only called if `has_module` returns true.
|
||||||
fn cache_key(&self) -> Vec<u8>;
|
fn cache_key(&self) -> String;
|
||||||
|
|
||||||
/// Returns whether a configuration file is present in the current context.
|
/// Returns whether a configuration is present in the current context.
|
||||||
fn is_config_present(&self) -> bool;
|
fn has_config(&self) -> bool;
|
||||||
|
|
||||||
/// Returns the contents of the configuration file in the current context.
|
/// Returns the contents of the configuration file in the current context.
|
||||||
///
|
///
|
||||||
/// This function is only called if `is_config_present` returns true.
|
/// This function is only called if `has_config` returns true.
|
||||||
fn config(&self) -> IoResult<Vec<u8>>;
|
fn config(&self) -> IoResult<Vec<u8>>;
|
||||||
|
|
||||||
/// Returns a loader that when called, loads the module and returns the result.
|
/// Returns a loader function for the current module, that when called, loads the module
|
||||||
|
/// and returns the result.
|
||||||
///
|
///
|
||||||
/// Loader can be sync or async.
|
/// Loader can be sync or async.
|
||||||
fn loader(&self, lua: &Lua, path: &str, chunk_name: &str, content: &[u8]) -> Result<Function> {
|
/// This function is only called if `has_module` returns true.
|
||||||
let _ = path;
|
fn loader(&self, lua: &Lua) -> Result<Function>;
|
||||||
lua.load(content).set_name(chunk_name).into_function()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for dyn Require {
|
impl fmt::Debug for dyn Require {
|
||||||
@@ -101,213 +103,60 @@ impl fmt::Debug for dyn Require {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The standard implementation of Luau `require` navigation.
|
struct Context {
|
||||||
#[derive(Default)]
|
require: Box<dyn Require>,
|
||||||
pub(super) struct TextRequirer {
|
config_cache: Option<IoResult<Vec<u8>>>,
|
||||||
abs_path: RefCell<PathBuf>,
|
|
||||||
rel_path: RefCell<PathBuf>,
|
|
||||||
module_path: RefCell<PathBuf>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextRequirer {
|
impl Deref for Context {
|
||||||
pub(super) fn new() -> Self {
|
type Target = dyn Require;
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn normalize_chunk_name(chunk_name: &str) -> &str {
|
fn deref(&self) -> &Self::Target {
|
||||||
if let Some((path, line)) = chunk_name.split_once(':') {
|
&*self.require
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_module_path(path: &Path) -> StdResult<PathBuf, NavigateError> {
|
|
||||||
let mut found_path = None;
|
|
||||||
|
|
||||||
let current_ext = (path.extension().and_then(|s| s.to_str()))
|
|
||||||
.map(|s| format!("{s}."))
|
|
||||||
.unwrap_or_default();
|
|
||||||
for ext in ["luau", "lua"] {
|
|
||||||
let candidate = path.with_extension(format!("{current_ext}{ext}"));
|
|
||||||
if candidate.is_file() {
|
|
||||||
if found_path.is_some() {
|
|
||||||
return Err(NavigateError::Ambiguous);
|
|
||||||
}
|
|
||||||
found_path = Some(candidate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if path.is_dir() {
|
|
||||||
if found_path.is_some() {
|
|
||||||
return Err(NavigateError::Ambiguous);
|
|
||||||
}
|
|
||||||
|
|
||||||
for component in ["init.luau", "init.lua"] {
|
|
||||||
let candidate = path.join(component);
|
|
||||||
if candidate.is_file() {
|
|
||||||
if found_path.is_some() {
|
|
||||||
return Err(NavigateError::Ambiguous);
|
|
||||||
}
|
|
||||||
found_path = Some(candidate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if found_path.is_none() {
|
|
||||||
found_path = Some(PathBuf::new());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
found_path.ok_or(NavigateError::NotFound)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Require for TextRequirer {
|
impl DerefMut for Context {
|
||||||
fn is_require_allowed(&self, chunk_name: &str) -> bool {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
chunk_name.starts_with('@')
|
&mut *self.require
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn reset(&self, chunk_name: &str) -> StdResult<(), NavigateError> {
|
impl Context {
|
||||||
if !chunk_name.starts_with('@') {
|
fn new(require: impl Require + MaybeSend + 'static) -> Self {
|
||||||
return Err(NavigateError::NotFound);
|
Context {
|
||||||
|
require: Box::new(require),
|
||||||
|
config_cache: None,
|
||||||
}
|
}
|
||||||
let chunk_name = &Self::normalize_chunk_name(chunk_name)[1..];
|
}
|
||||||
let path = Self::normalize_path(chunk_name.as_ref());
|
}
|
||||||
|
|
||||||
if path.extension() == Some("rs".as_ref()) {
|
macro_rules! try_borrow {
|
||||||
let cwd = match env::current_dir() {
|
($state:expr, $ctx:expr) => {
|
||||||
Ok(cwd) => cwd,
|
match (*($ctx as *const RefCell<Context>)).try_borrow() {
|
||||||
Err(_) => return Err(NavigateError::NotFound),
|
Ok(ctx) => ctx,
|
||||||
};
|
Err(_) => ffi::luaL_error($state, cstr!("require context is already borrowed")),
|
||||||
self.abs_path.replace(Self::normalize_path(&cwd.join(&path)));
|
|
||||||
self.rel_path.replace(path);
|
|
||||||
self.module_path.replace(PathBuf::new());
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if path.is_absolute() {
|
macro_rules! try_borrow_mut {
|
||||||
let module_path = Self::find_module_path(&path)?;
|
($state:expr, $ctx:expr) => {
|
||||||
self.abs_path.replace(path.clone());
|
match (*($ctx as *const RefCell<Context>)).try_borrow_mut() {
|
||||||
self.rel_path.replace(path);
|
Ok(ctx) => ctx,
|
||||||
self.module_path.replace(module_path);
|
Err(_) => ffi::luaL_error($state, cstr!("require context is already borrowed")),
|
||||||
} else {
|
|
||||||
// Relative path
|
|
||||||
let cwd = match env::current_dir() {
|
|
||||||
Ok(cwd) => cwd,
|
|
||||||
Err(_) => return Err(NavigateError::NotFound),
|
|
||||||
};
|
|
||||||
let abs_path = cwd.join(&path);
|
|
||||||
let module_path = Self::find_module_path(&abs_path)?;
|
|
||||||
self.abs_path.replace(Self::normalize_path(&abs_path));
|
|
||||||
self.rel_path.replace(path);
|
|
||||||
self.module_path.replace(module_path);
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn jump_to_alias(&self, path: &str) -> StdResult<(), NavigateError> {
|
|
||||||
let path = Self::normalize_path(path.as_ref());
|
|
||||||
let module_path = Self::find_module_path(&path)?;
|
|
||||||
|
|
||||||
self.abs_path.replace(path.clone());
|
|
||||||
self.rel_path.replace(path);
|
|
||||||
self.module_path.replace(module_path);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_parent(&self) -> StdResult<(), NavigateError> {
|
|
||||||
let mut abs_path = self.abs_path.borrow().clone();
|
|
||||||
if !abs_path.pop() {
|
|
||||||
return Err(NavigateError::NotFound);
|
|
||||||
}
|
|
||||||
let mut rel_parent = self.rel_path.borrow().clone();
|
|
||||||
rel_parent.pop();
|
|
||||||
let module_path = Self::find_module_path(&abs_path)?;
|
|
||||||
|
|
||||||
self.abs_path.replace(abs_path);
|
|
||||||
self.rel_path.replace(Self::normalize_path(&rel_parent));
|
|
||||||
self.module_path.replace(module_path);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_child(&self, name: &str) -> StdResult<(), NavigateError> {
|
|
||||||
let abs_path = self.abs_path.borrow().join(name);
|
|
||||||
let rel_path = self.rel_path.borrow().join(name);
|
|
||||||
let module_path = Self::find_module_path(&abs_path)?;
|
|
||||||
|
|
||||||
self.abs_path.replace(abs_path);
|
|
||||||
self.rel_path.replace(rel_path);
|
|
||||||
self.module_path.replace(module_path);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_module_present(&self) -> bool {
|
|
||||||
self.module_path.borrow().is_file()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn contents(&self) -> IoResult<Vec<u8>> {
|
|
||||||
fs::read(&*self.module_path.borrow())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn chunk_name(&self) -> String {
|
|
||||||
format!("@{}", self.rel_path.borrow().display())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cache_key(&self) -> Vec<u8> {
|
|
||||||
self.module_path.borrow().display().to_string().into_bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_config_present(&self) -> bool {
|
|
||||||
self.abs_path.borrow().join(".luaurc").is_file()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn config(&self) -> IoResult<Vec<u8>> {
|
|
||||||
fs::read(self.abs_path.borrow().join(".luaurc"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
pub(super) unsafe extern "C" fn init_config(config: *mut ffi::luarequire_Configuration) {
|
pub(super) unsafe extern "C-unwind" fn init_config(config: *mut ffi::luarequire_Configuration) {
|
||||||
if config.is_null() {
|
if config.is_null() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn is_require_allowed(
|
unsafe extern "C-unwind" fn is_require_allowed(
|
||||||
_state: *mut ffi::lua_State,
|
state: *mut ffi::lua_State,
|
||||||
ctx: *mut c_void,
|
ctx: *mut c_void,
|
||||||
requirer_chunkname: *const c_char,
|
requirer_chunkname: *const c_char,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
@@ -315,119 +164,134 @@ pub(super) unsafe extern "C" fn init_config(config: *mut ffi::luarequire_Configu
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let this = &*(ctx as *const Box<dyn Require>);
|
let this = try_borrow!(state, ctx);
|
||||||
let chunk_name = CStr::from_ptr(requirer_chunkname).to_string_lossy();
|
let chunk_name = CStr::from_ptr(requirer_chunkname).to_string_lossy();
|
||||||
this.is_require_allowed(&chunk_name)
|
this.is_require_allowed(&chunk_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn reset(
|
unsafe extern "C-unwind" fn reset(
|
||||||
_state: *mut ffi::lua_State,
|
state: *mut ffi::lua_State,
|
||||||
ctx: *mut c_void,
|
ctx: *mut c_void,
|
||||||
requirer_chunkname: *const c_char,
|
requirer_chunkname: *const c_char,
|
||||||
) -> ffi::luarequire_NavigateResult {
|
) -> ffi::luarequire_NavigateResult {
|
||||||
let this = &*(ctx as *const Box<dyn Require>);
|
let mut this = try_borrow_mut!(state, ctx);
|
||||||
let chunk_name = CStr::from_ptr(requirer_chunkname).to_string_lossy();
|
let chunk_name = CStr::from_ptr(requirer_chunkname).to_string_lossy();
|
||||||
this.reset(&chunk_name).into_nav_result()
|
callback_error_ext(state, ptr::null_mut(), true, move |_, _| {
|
||||||
}
|
this.reset(&chunk_name).into_nav_result()
|
||||||
|
|
||||||
unsafe extern "C" fn jump_to_alias(
|
|
||||||
_state: *mut ffi::lua_State,
|
|
||||||
ctx: *mut c_void,
|
|
||||||
path: *const c_char,
|
|
||||||
) -> ffi::luarequire_NavigateResult {
|
|
||||||
let this = &*(ctx as *const Box<dyn Require>);
|
|
||||||
let path = CStr::from_ptr(path).to_string_lossy();
|
|
||||||
this.jump_to_alias(&path).into_nav_result()
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C" fn to_parent(
|
|
||||||
_state: *mut ffi::lua_State,
|
|
||||||
ctx: *mut c_void,
|
|
||||||
) -> ffi::luarequire_NavigateResult {
|
|
||||||
let this = &*(ctx as *const Box<dyn Require>);
|
|
||||||
this.to_parent().into_nav_result()
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C" fn to_child(
|
|
||||||
_state: *mut ffi::lua_State,
|
|
||||||
ctx: *mut c_void,
|
|
||||||
name: *const c_char,
|
|
||||||
) -> ffi::luarequire_NavigateResult {
|
|
||||||
let this = &*(ctx as *const Box<dyn Require>);
|
|
||||||
let name = CStr::from_ptr(name).to_string_lossy();
|
|
||||||
this.to_child(&name).into_nav_result()
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C" fn is_module_present(_state: *mut ffi::lua_State, ctx: *mut c_void) -> bool {
|
|
||||||
let this = &*(ctx as *const Box<dyn Require>);
|
|
||||||
this.is_module_present()
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C" fn get_contents(
|
|
||||||
state: *mut ffi::lua_State,
|
|
||||||
ctx: *mut c_void,
|
|
||||||
buffer: *mut c_char,
|
|
||||||
buffer_size: usize,
|
|
||||||
size_out: *mut usize,
|
|
||||||
) -> WriteResult {
|
|
||||||
let this = &*(ctx as *const Box<dyn Require>);
|
|
||||||
write_to_buffer(state, buffer, buffer_size, size_out, || this.contents())
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C" fn get_chunkname(
|
|
||||||
state: *mut ffi::lua_State,
|
|
||||||
ctx: *mut c_void,
|
|
||||||
buffer: *mut c_char,
|
|
||||||
buffer_size: usize,
|
|
||||||
size_out: *mut usize,
|
|
||||||
) -> WriteResult {
|
|
||||||
let this = &*(ctx as *const Box<dyn Require>);
|
|
||||||
write_to_buffer(state, buffer, buffer_size, size_out, || {
|
|
||||||
Ok(this.chunk_name().into_bytes())
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn get_cache_key(
|
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,
|
state: *mut ffi::lua_State,
|
||||||
ctx: *mut c_void,
|
ctx: *mut c_void,
|
||||||
buffer: *mut c_char,
|
buffer: *mut c_char,
|
||||||
buffer_size: usize,
|
buffer_size: usize,
|
||||||
size_out: *mut usize,
|
size_out: *mut usize,
|
||||||
) -> WriteResult {
|
) -> WriteResult {
|
||||||
let this = &*(ctx as *const Box<dyn Require>);
|
let this = try_borrow!(state, ctx);
|
||||||
write_to_buffer(state, buffer, buffer_size, size_out, || Ok(this.cache_key()))
|
let cache_key = this.cache_key();
|
||||||
|
write_to_buffer(buffer, buffer_size, size_out, cache_key.as_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn is_config_present(_state: *mut ffi::lua_State, ctx: *mut c_void) -> bool {
|
unsafe extern "C-unwind" fn get_config_status(
|
||||||
let this = &*(ctx as *const Box<dyn Require>);
|
state: *mut ffi::lua_State,
|
||||||
this.is_config_present()
|
ctx: *mut c_void,
|
||||||
|
) -> ConfigStatus {
|
||||||
|
let mut this = try_borrow_mut!(state, ctx);
|
||||||
|
if this.has_config() {
|
||||||
|
this.config_cache = Some(this.config());
|
||||||
|
if let Some(Ok(data)) = &this.config_cache {
|
||||||
|
return detect_config_format(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConfigStatus::Absent
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn get_config(
|
unsafe extern "C-unwind" fn get_config(
|
||||||
state: *mut ffi::lua_State,
|
state: *mut ffi::lua_State,
|
||||||
ctx: *mut c_void,
|
ctx: *mut c_void,
|
||||||
buffer: *mut c_char,
|
buffer: *mut c_char,
|
||||||
buffer_size: usize,
|
buffer_size: usize,
|
||||||
size_out: *mut usize,
|
size_out: *mut usize,
|
||||||
) -> WriteResult {
|
) -> WriteResult {
|
||||||
let this = &*(ctx as *const Box<dyn Require>);
|
let mut this = try_borrow_mut!(state, ctx);
|
||||||
write_to_buffer(state, buffer, buffer_size, size_out, || this.config())
|
let config = callback_error_ext(state, ptr::null_mut(), true, move |_, _| {
|
||||||
|
Ok(this.config_cache.take().unwrap_or_else(|| this.config())?)
|
||||||
|
});
|
||||||
|
write_to_buffer(buffer, buffer_size, size_out, &config)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C-unwind" fn load(
|
unsafe extern "C-unwind" fn load(
|
||||||
state: *mut ffi::lua_State,
|
state: *mut ffi::lua_State,
|
||||||
ctx: *mut c_void,
|
ctx: *mut c_void,
|
||||||
path: *const c_char,
|
_path: *const c_char,
|
||||||
chunk_name: *const c_char,
|
_chunkname: *const c_char,
|
||||||
contents: *const c_char,
|
_loadname: *const c_char,
|
||||||
) -> c_int {
|
) -> c_int {
|
||||||
let this = &*(ctx as *const Box<dyn Require>);
|
let this = try_borrow!(state, ctx);
|
||||||
let path = CStr::from_ptr(path).to_string_lossy();
|
callback_error_ext(state, ptr::null_mut(), true, move |extra, _| {
|
||||||
let chunk_name = CStr::from_ptr(chunk_name).to_string_lossy();
|
|
||||||
let contents = CStr::from_ptr(contents).to_bytes();
|
|
||||||
callback_error_ext(state, ptr::null_mut(), false, move |extra, _| {
|
|
||||||
let rawlua = (*extra).raw_lua();
|
let rawlua = (*extra).raw_lua();
|
||||||
rawlua.push(this.loader(rawlua.lua(), &path, &chunk_name, contents)?)?;
|
let loader = this.loader(rawlua.lua())?;
|
||||||
|
rawlua.push(loader)?;
|
||||||
Ok(1)
|
Ok(1)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -435,67 +299,59 @@ pub(super) unsafe extern "C" fn init_config(config: *mut ffi::luarequire_Configu
|
|||||||
(*config).is_require_allowed = is_require_allowed;
|
(*config).is_require_allowed = is_require_allowed;
|
||||||
(*config).reset = reset;
|
(*config).reset = reset;
|
||||||
(*config).jump_to_alias = jump_to_alias;
|
(*config).jump_to_alias = jump_to_alias;
|
||||||
|
(*config).to_alias_override = None;
|
||||||
|
(*config).to_alias_fallback = None;
|
||||||
(*config).to_parent = to_parent;
|
(*config).to_parent = to_parent;
|
||||||
(*config).to_child = to_child;
|
(*config).to_child = to_child;
|
||||||
(*config).is_module_present = is_module_present;
|
(*config).is_module_present = is_module_present;
|
||||||
(*config).get_contents = get_contents;
|
|
||||||
(*config).get_chunkname = get_chunkname;
|
(*config).get_chunkname = get_chunkname;
|
||||||
|
(*config).get_loadname = get_loadname;
|
||||||
(*config).get_cache_key = get_cache_key;
|
(*config).get_cache_key = get_cache_key;
|
||||||
(*config).is_config_present = is_config_present;
|
(*config).get_config_status = get_config_status;
|
||||||
(*config).get_config = get_config;
|
(*config).get_alias = None;
|
||||||
|
(*config).get_config = Some(get_config);
|
||||||
(*config).load = load;
|
(*config).load = load;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Detect configuration file format (JSON or Luau)
|
||||||
|
#[cfg(feature = "luau")]
|
||||||
|
fn detect_config_format(data: &[u8]) -> ConfigStatus {
|
||||||
|
let data = data.trim_ascii();
|
||||||
|
if data.starts_with(b"{") {
|
||||||
|
let data = &data[1..].trim_ascii_start();
|
||||||
|
if data.starts_with(b"\"") || data == b"}" {
|
||||||
|
return ConfigStatus::PresentJson;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConfigStatus::PresentLuau
|
||||||
|
}
|
||||||
|
|
||||||
/// Helper function to write data to a buffer
|
/// Helper function to write data to a buffer
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
unsafe fn write_to_buffer(
|
unsafe fn write_to_buffer(
|
||||||
state: *mut ffi::lua_State,
|
|
||||||
buffer: *mut c_char,
|
buffer: *mut c_char,
|
||||||
buffer_size: usize,
|
buffer_size: usize,
|
||||||
size_out: *mut usize,
|
size_out: *mut usize,
|
||||||
data_fetcher: impl Fn() -> IoResult<Vec<u8>>,
|
data: &[u8],
|
||||||
) -> WriteResult {
|
) -> WriteResult {
|
||||||
struct DataCache(Option<Vec<u8>>);
|
// the buffer must be null terminated as it's a c++ `std::string` data() buffer
|
||||||
|
let is_null_terminated = data.last() == Some(&0);
|
||||||
// The initial buffer size can be too small, to avoid making a second data fetch call,
|
*size_out = data.len() + if is_null_terminated { 0 } else { 1 };
|
||||||
// we cache the content in the first call, and then re-use it.
|
if *size_out > buffer_size {
|
||||||
|
return WriteResult::BufferTooSmall;
|
||||||
let lua = Lua::get_or_init_from_ptr(state);
|
|
||||||
match lua.try_app_data_mut::<DataCache>() {
|
|
||||||
Ok(Some(mut data_cache)) => {
|
|
||||||
if let Some(data) = data_cache.0.take() {
|
|
||||||
mlua_assert!(data.len() <= buffer_size, "buffer is too small");
|
|
||||||
*size_out = data.len();
|
|
||||||
ptr::copy_nonoverlapping(data.as_ptr(), buffer as *mut _, data.len());
|
|
||||||
return WriteResult::Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(None) => {
|
|
||||||
// Init the cache
|
|
||||||
_ = lua.try_set_app_data(DataCache(None));
|
|
||||||
}
|
|
||||||
Err(_) => {}
|
|
||||||
}
|
}
|
||||||
|
ptr::copy_nonoverlapping(data.as_ptr(), buffer as *mut _, data.len());
|
||||||
match data_fetcher() {
|
if !is_null_terminated {
|
||||||
Ok(data) => {
|
*buffer.add(data.len()) = 0;
|
||||||
*size_out = data.len();
|
|
||||||
if *size_out > buffer_size {
|
|
||||||
// Cache the data for the next call to avoid getting the contents again
|
|
||||||
if let Ok(Some(mut data_cache)) = lua.try_app_data_mut::<DataCache>() {
|
|
||||||
data_cache.0 = Some(data);
|
|
||||||
}
|
|
||||||
return WriteResult::BufferTooSmall;
|
|
||||||
}
|
|
||||||
ptr::copy_nonoverlapping(data.as_ptr(), buffer as *mut _, data.len());
|
|
||||||
WriteResult::Success
|
|
||||||
}
|
|
||||||
Err(_) => WriteResult::Failure,
|
|
||||||
}
|
}
|
||||||
|
WriteResult::Success
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
pub fn create_require_function<R: Require + 'static>(lua: &Lua, require: R) -> Result<Function> {
|
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 {
|
unsafe extern "C-unwind" fn find_current_file(state: *mut ffi::lua_State) -> c_int {
|
||||||
let mut ar: ffi::lua_Debug = mem::zeroed();
|
let mut ar: ffi::lua_Debug = mem::zeroed();
|
||||||
for level in 2.. {
|
for level in 2.. {
|
||||||
@@ -511,18 +367,20 @@ pub fn create_require_function<R: Require + 'static>(lua: &Lua, require: R) -> R
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C-unwind" fn get_cache_key(state: *mut ffi::lua_State) -> c_int {
|
unsafe extern "C-unwind" fn get_cache_key(state: *mut ffi::lua_State) -> c_int {
|
||||||
let requirer = ffi::lua_touserdata(state, ffi::lua_upvalueindex(1)) as *const Box<dyn Require>;
|
let ctx = ffi::lua_touserdata(state, ffi::lua_upvalueindex(1));
|
||||||
let cache_key = (*requirer).cache_key();
|
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());
|
ffi::lua_pushlstring(state, cache_key.as_ptr() as *const _, cache_key.len());
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
let (get_cache_key, find_current_file, proxyrequire, registered_modules, loader_cache) = unsafe {
|
let (get_cache_key, find_current_file, proxyrequire, registered_modules, loader_cache) = unsafe {
|
||||||
lua.exec_raw::<(Function, Function, Function, Table, Table)>((), move |state| {
|
lua.exec_raw::<(Function, Function, Function, Table, Table)>((), move |state| {
|
||||||
let requirer_ptr = ffi::lua_newuserdata_t::<Box<dyn Require>>(state, Box::new(require));
|
let context = Context::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_pushcclosured(state, get_cache_key, cstr!("get_cache_key"), 1);
|
||||||
ffi::lua_pushcfunctiond(state, find_current_file, cstr!("find_current_file"));
|
ffi::lua_pushcfunctiond(state, find_current_file, cstr!("find_current_file"));
|
||||||
ffi::luarequire_pushproxyrequire(state, init_config, requirer_ptr as *mut _);
|
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, ffi::LUA_REGISTERED_MODULES_TABLE);
|
||||||
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("__MLUA_LOADER_CACHE"));
|
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("__MLUA_LOADER_CACHE"));
|
||||||
})
|
})
|
||||||
@@ -540,10 +398,26 @@ pub fn create_require_function<R: Require + 'static>(lua: &Lua, require: R) -> R
|
|||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
let (error, r#type) = unsafe {
|
unsafe extern "C-unwind" fn to_lowercase(state: *mut ffi::lua_State) -> c_int {
|
||||||
lua.exec_raw::<(Function, Function)>((), move |state| {
|
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, error, cstr!("error"));
|
||||||
ffi::lua_pushcfunctiond(state, r#type, cstr!("type"));
|
ffi::lua_pushcfunctiond(state, r#type, cstr!("type"));
|
||||||
|
ffi::lua_pushcfunctiond(state, to_lowercase, cstr!("to_lowercase"));
|
||||||
})
|
})
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
@@ -556,6 +430,7 @@ pub fn create_require_function<R: Require + 'static>(lua: &Lua, require: R) -> R
|
|||||||
env.raw_set("LOADER_CACHE", loader_cache)?;
|
env.raw_set("LOADER_CACHE", loader_cache)?;
|
||||||
env.raw_set("error", error)?;
|
env.raw_set("error", error)?;
|
||||||
env.raw_set("type", r#type)?;
|
env.raw_set("type", r#type)?;
|
||||||
|
env.raw_set("to_lowercase", to_lowercase)?;
|
||||||
|
|
||||||
lua.load(
|
lua.load(
|
||||||
r#"
|
r#"
|
||||||
@@ -565,7 +440,7 @@ pub fn create_require_function<R: Require + 'static>(lua: &Lua, require: R) -> R
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Check if the module (path) is explicitly registered
|
-- Check if the module (path) is explicitly registered
|
||||||
local maybe_result = REGISTERED_MODULES[path]
|
local maybe_result = REGISTERED_MODULES[to_lowercase(path)]
|
||||||
if maybe_result ~= nil then
|
if maybe_result ~= nil then
|
||||||
return maybe_result
|
return maybe_result
|
||||||
end
|
end
|
||||||
@@ -593,52 +468,4 @@ pub fn create_require_function<R: Require + 'static>(lua: &Lua, require: R) -> R
|
|||||||
.into_function()
|
.into_function()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
mod fs;
|
||||||
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}"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,278 @@
|
|||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::io::Result as IoResult;
|
||||||
|
use std::path::{Component, Path, PathBuf};
|
||||||
|
use std::result::Result as StdResult;
|
||||||
|
use std::{env, fs};
|
||||||
|
|
||||||
|
use crate::error::Result;
|
||||||
|
use crate::function::Function;
|
||||||
|
use crate::state::Lua;
|
||||||
|
|
||||||
|
use super::{NavigateError, 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"];
|
||||||
|
|
||||||
|
/// The filename for the JSON configuration file.
|
||||||
|
const LUAURC_CONFIG_FILENAME: &str = ".luaurc";
|
||||||
|
|
||||||
|
/// The filename for the Luau configuration file.
|
||||||
|
const LUAU_CONFIG_FILENAME: &str = ".config.luau";
|
||||||
|
|
||||||
|
/// 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(':')
|
||||||
|
&& 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(Self::LUAURC_CONFIG_FILENAME).is_file()
|
||||||
|
|| self.abs_path.is_dir() && self.abs_path.join(Self::LUAU_CONFIG_FILENAME).is_file()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn config(&self) -> IoResult<Vec<u8>> {
|
||||||
|
if self.abs_path.join(Self::LUAURC_CONFIG_FILENAME).is_file() {
|
||||||
|
return fs::read(self.abs_path.join(Self::LUAURC_CONFIG_FILENAME));
|
||||||
|
}
|
||||||
|
fs::read(self.abs_path.join(Self::LUAU_CONFIG_FILENAME))
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-14
@@ -28,9 +28,7 @@ impl MemoryState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
#[rustversion::since(1.85)]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(clippy::incompatible_msrv)]
|
|
||||||
pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
|
pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
|
||||||
let mut mem_state = ptr::null_mut();
|
let mut mem_state = ptr::null_mut();
|
||||||
if !ptr::fn_addr_eq(ffi::lua_getallocf(state, &mut mem_state), ALLOCATOR) {
|
if !ptr::fn_addr_eq(ffi::lua_getallocf(state, &mut mem_state), ALLOCATOR) {
|
||||||
@@ -39,17 +37,6 @@ impl MemoryState {
|
|||||||
mem_state as *mut MemoryState
|
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();
|
|
||||||
}
|
|
||||||
mem_state as *mut MemoryState
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn used_memory(&self) -> usize {
|
pub(crate) fn used_memory(&self) -> usize {
|
||||||
self.used_memory as usize
|
self.used_memory as usize
|
||||||
@@ -83,7 +70,7 @@ impl MemoryState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Does nothing apart from calling `f()`, we don't need to bypass any limits
|
// Does nothing apart from calling `f()`, we don't need to bypass any limits
|
||||||
#[cfg(any(feature = "lua52", feature = "lua53", feature = "lua54"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) unsafe fn relax_limit_with(_state: *mut ffi::lua_State, f: impl FnOnce()) {
|
pub(crate) unsafe fn relax_limit_with(_state: *mut ffi::lua_State, f: impl FnOnce()) {
|
||||||
f();
|
f();
|
||||||
|
|||||||
+27
-1
@@ -1,4 +1,4 @@
|
|||||||
use std::collections::{vec_deque, VecDeque};
|
use std::collections::{VecDeque, vec_deque};
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
@@ -204,6 +204,23 @@ impl IntoLuaMulti for MultiValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IntoLuaMulti for &MultiValue {
|
||||||
|
#[inline]
|
||||||
|
fn into_lua_multi(self, _: &Lua) -> Result<MultiValue> {
|
||||||
|
Ok(self.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
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 {
|
||||||
|
lua.push_value(value)?;
|
||||||
|
}
|
||||||
|
Ok(nresults)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FromLuaMulti for MultiValue {
|
impl FromLuaMulti for MultiValue {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_lua_multi(values: MultiValue, _: &Lua) -> Result<Self> {
|
fn from_lua_multi(values: MultiValue, _: &Lua) -> Result<Self> {
|
||||||
@@ -297,6 +314,15 @@ impl<T: IntoLua> IntoLuaMulti for Variadic<T> {
|
|||||||
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
|
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
|
||||||
MultiValue::from_lua_iter(lua, self)
|
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> {
|
impl<T: FromLua> FromLuaMulti for Variadic<T> {
|
||||||
|
|||||||
+16
-14
@@ -2,18 +2,19 @@
|
|||||||
|
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
AnyUserData as LuaAnyUserData, Chunk as LuaChunk, Either as LuaEither, Error as LuaError,
|
AnyUserData as LuaAnyUserData, BorrowedBytes as LuaBorrowedBytes, BorrowedStr as LuaBorrowedStr,
|
||||||
ErrorContext as LuaErrorContext, ExternalError as LuaExternalError, ExternalResult as LuaExternalResult,
|
Chunk as LuaChunk, Either as LuaEither, Error as LuaError, ErrorContext as LuaErrorContext,
|
||||||
FromLua, FromLuaMulti, Function as LuaFunction, FunctionInfo as LuaFunctionInfo, GCMode as LuaGCMode,
|
ExternalError as LuaExternalError, ExternalResult as LuaExternalResult, FromLua, FromLuaMulti,
|
||||||
Integer as LuaInteger, IntoLua, IntoLuaMulti, LightUserData as LuaLightUserData, Lua, LuaNativeFn,
|
Function as LuaFunction, GCMode as LuaGCMode, Integer as LuaInteger, IntoLua, IntoLuaMulti,
|
||||||
LuaNativeFnMut, LuaOptions, MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil,
|
LightUserData as LuaLightUserData, Lua, LuaNativeFn, LuaNativeFnMut, LuaOptions, LuaString,
|
||||||
Number as LuaNumber, ObjectLike as LuaObjectLike, RegistryKey as LuaRegistryKey, Result as LuaResult,
|
MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber,
|
||||||
StdLib as LuaStdLib, String as LuaString, Table as LuaTable, TablePairs as LuaTablePairs,
|
ObjectLike as LuaObjectLike, RegistryKey as LuaRegistryKey, Result as LuaResult, StdLib as LuaStdLib,
|
||||||
TableSequence as LuaTableSequence, Thread as LuaThread, ThreadStatus as LuaThreadStatus,
|
Table as LuaTable, Thread as LuaThread, ThreadStatus as LuaThreadStatus, UserData as LuaUserData,
|
||||||
UserData as LuaUserData, UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable,
|
UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable,
|
||||||
UserDataMethods as LuaUserDataMethods, UserDataRef as LuaUserDataRef,
|
UserDataMethods as LuaUserDataMethods, UserDataRef as LuaUserDataRef,
|
||||||
UserDataRefMut as LuaUserDataRefMut, UserDataRegistry as LuaUserDataRegistry, Value as LuaValue,
|
UserDataRefMut as LuaUserDataRefMut, UserDataRegistry as LuaUserDataRegistry, Value as LuaValue,
|
||||||
Variadic as LuaVariadic, VmState as LuaVmState, WeakLua,
|
Variadic as LuaVariadic, VmState as LuaVmState, WeakLua, function::FunctionInfo as LuaFunctionInfo,
|
||||||
|
table::TablePairs as LuaTablePairs, table::TableSequence as LuaTableSequence,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
@@ -23,16 +24,17 @@ pub use crate::HookTriggers as LuaHookTriggers;
|
|||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
CompileConstant as LuaCompileConstant, CoverageInfo as LuaCoverageInfo,
|
CompileConstant as LuaCompileConstant, NavigateError as LuaNavigateError, Require as LuaRequire,
|
||||||
NavigateError as LuaNavigateError, Require as LuaRequire, Vector as LuaVector,
|
TextRequirer as LuaTextRequirer, Vector as LuaVector,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use crate::{AsyncThread as LuaAsyncThread, LuaNativeAsyncFn};
|
pub use crate::{AsyncThread as LuaAsyncThread, LuaNativeAsyncFn};
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
DeserializeOptions as LuaDeserializeOptions, LuaSerdeExt, SerializeOptions as LuaSerializeOptions,
|
DeserializeOptions as LuaDeserializeOptions, LuaSerdeExt, SerializableValue as LuaSerializableValue,
|
||||||
|
SerializeOptions as LuaSerializeOptions,
|
||||||
};
|
};
|
||||||
|
|||||||
+4
-10
@@ -8,9 +8,7 @@ use crate::state::{Lua, LuaGuard, RawLua};
|
|||||||
use crate::traits::{FromLuaMulti, IntoLuaMulti};
|
use crate::traits::{FromLuaMulti, IntoLuaMulti};
|
||||||
use crate::types::{Callback, CallbackUpvalue, ScopedCallback, ValueRef};
|
use crate::types::{Callback, CallbackUpvalue, ScopedCallback, ValueRef};
|
||||||
use crate::userdata::{AnyUserData, UserData, UserDataRegistry, UserDataStorage};
|
use crate::userdata::{AnyUserData, UserData, UserDataRegistry, UserDataStorage};
|
||||||
use crate::util::{
|
use crate::util::{self, StackGuard, check_stack, get_metatable_ptr, get_userdata, take_userdata};
|
||||||
self, assert_stack, check_stack, get_metatable_ptr, get_userdata, take_userdata, StackGuard,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and
|
/// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and
|
||||||
/// callbacks that are not required to be `Send` or `'static`.
|
/// callbacks that are not required to be `Send` or `'static`.
|
||||||
@@ -284,22 +282,18 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
|||||||
/// Shortens the lifetime of the userdata to the lifetime of the scope.
|
/// Shortens the lifetime of the userdata to the lifetime of the scope.
|
||||||
fn seal_userdata<T: 'env>(&self, ud: &AnyUserData) {
|
fn seal_userdata<T: 'env>(&self, ud: &AnyUserData) {
|
||||||
let destructor: DestructorCallback = Box::new(|rawlua, vref| unsafe {
|
let destructor: DestructorCallback = Box::new(|rawlua, vref| unsafe {
|
||||||
let state = rawlua.state();
|
|
||||||
let _sg = StackGuard::new(state);
|
|
||||||
assert_stack(state, 2);
|
|
||||||
|
|
||||||
// Ensure that userdata is not destructed
|
// Ensure that userdata is not destructed
|
||||||
match rawlua.push_userdata_ref(&vref) {
|
match rawlua.get_userdata_ref_type_id(&vref) {
|
||||||
Ok(Some(_)) => {}
|
Ok(Some(_)) => {}
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
// Deregister metatable
|
// Deregister metatable
|
||||||
let mt_ptr = get_metatable_ptr(state, -1);
|
let mt_ptr = get_metatable_ptr(rawlua.ref_thread(), vref.index);
|
||||||
rawlua.deregister_userdata_metatable(mt_ptr);
|
rawlua.deregister_userdata_metatable(mt_ptr);
|
||||||
}
|
}
|
||||||
Err(_) => return vec![],
|
Err(_) => return vec![],
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = take_userdata::<UserDataStorage<T>>(state);
|
let data = take_userdata::<UserDataStorage<T>>(rawlua.ref_thread(), vref.index);
|
||||||
vec![Box::new(move || drop(data))]
|
vec![Box::new(move || drop(data))]
|
||||||
});
|
});
|
||||||
self.destructors.0.borrow_mut().push((ud.0.clone(), destructor));
|
self.destructors.0.borrow_mut().push((ud.0.clone(), destructor));
|
||||||
|
|||||||
+48
-14
@@ -4,7 +4,6 @@ use std::cell::RefCell;
|
|||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::result::Result as StdResult;
|
use std::result::Result as StdResult;
|
||||||
use std::string::String as StdString;
|
|
||||||
|
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
use serde::de::{self, IntoDeserializer};
|
use serde::de::{self, IntoDeserializer};
|
||||||
@@ -15,11 +14,12 @@ use crate::userdata::AnyUserData;
|
|||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
|
|
||||||
/// A struct for deserializing Lua values into Rust values.
|
/// A struct for deserializing Lua values into Rust values.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Deserializer {
|
pub struct Deserializer {
|
||||||
value: Value,
|
value: Value,
|
||||||
options: Options,
|
options: Options,
|
||||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||||
|
len: Option<usize>, // A length hint for sequences
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A struct with options to change default deserializer behavior.
|
/// A struct with options to change default deserializer behavior.
|
||||||
@@ -54,6 +54,19 @@ pub struct Options {
|
|||||||
///
|
///
|
||||||
/// Default: **false**
|
/// Default: **false**
|
||||||
pub encode_empty_tables_as_array: bool,
|
pub encode_empty_tables_as_array: bool,
|
||||||
|
|
||||||
|
/// If true, enable detection of mixed tables.
|
||||||
|
///
|
||||||
|
/// A mixed table is a table that has both array-like and map-like entries or several borders.
|
||||||
|
/// See [`The Length Operator`] documentation for details about borders.
|
||||||
|
///
|
||||||
|
/// When this option is disabled, a table with a non-zero length (with one or more borders) will
|
||||||
|
/// be always encoded as an array.
|
||||||
|
///
|
||||||
|
/// Default: **false**
|
||||||
|
///
|
||||||
|
/// [`The Length Operator`]: https://www.lua.org/manual/5.4/manual.html#3.4.7
|
||||||
|
pub detect_mixed_tables: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Options {
|
impl Default for Options {
|
||||||
@@ -70,6 +83,7 @@ impl Options {
|
|||||||
deny_recursive_tables: true,
|
deny_recursive_tables: true,
|
||||||
sort_keys: false,
|
sort_keys: false,
|
||||||
encode_empty_tables_as_array: false,
|
encode_empty_tables_as_array: false,
|
||||||
|
detect_mixed_tables: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,6 +122,15 @@ impl Options {
|
|||||||
self.encode_empty_tables_as_array = enabled;
|
self.encode_empty_tables_as_array = enabled;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets [`detect_mixed_tables`] option.
|
||||||
|
///
|
||||||
|
/// [`detect_mixed_tables`]: #structfield.detect_mixed_tables
|
||||||
|
#[must_use]
|
||||||
|
pub const fn detect_mixed_tables(mut self, enable: bool) -> Self {
|
||||||
|
self.detect_mixed_tables = enable;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deserializer {
|
impl Deserializer {
|
||||||
@@ -121,7 +144,7 @@ impl Deserializer {
|
|||||||
Deserializer {
|
Deserializer {
|
||||||
value,
|
value,
|
||||||
options,
|
options,
|
||||||
visited: Rc::new(RefCell::new(FxHashSet::default())),
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,8 +153,14 @@ impl Deserializer {
|
|||||||
value,
|
value,
|
||||||
options,
|
options,
|
||||||
visited,
|
visited,
|
||||||
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn with_len(mut self, len: usize) -> Self {
|
||||||
|
self.len = Some(len);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> serde::Deserializer<'de> for Deserializer {
|
impl<'de> serde::Deserializer<'de> for Deserializer {
|
||||||
@@ -155,17 +184,22 @@ impl<'de> serde::Deserializer<'de> for Deserializer {
|
|||||||
Ok(s) => visitor.visit_str(&s),
|
Ok(s) => visitor.visit_str(&s),
|
||||||
Err(_) => visitor.visit_bytes(&s.as_bytes()),
|
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) => {
|
||||||
Value::Table(ref t) if self.options.encode_empty_tables_as_array && t.is_empty() => {
|
if let Some(len) = t.encode_as_array(self.options) {
|
||||||
self.deserialize_seq(visitor)
|
self.with_len(len).deserialize_seq(visitor)
|
||||||
|
} else {
|
||||||
|
self.deserialize_map(visitor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Value::Table(_) => self.deserialize_map(visitor),
|
|
||||||
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_none(),
|
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_none(),
|
||||||
Value::UserData(ud) if ud.is_serializable() => {
|
Value::UserData(ud) if ud.is_serializable() => {
|
||||||
serde_userdata(ud, |value| value.deserialize_any(visitor))
|
serde_userdata(ud, |value| value.deserialize_any(visitor))
|
||||||
}
|
}
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
Value::Buffer(buf) => visitor.visit_bytes(unsafe { buf.as_slice() }),
|
Value::Buffer(buf) => {
|
||||||
|
let lua = buf.0.lua.lock();
|
||||||
|
visitor.visit_bytes(buf.as_slice(&lua))
|
||||||
|
}
|
||||||
Value::Function(_)
|
Value::Function(_)
|
||||||
| Value::Thread(_)
|
| Value::Thread(_)
|
||||||
| Value::UserData(_)
|
| Value::UserData(_)
|
||||||
@@ -208,14 +242,14 @@ impl<'de> serde::Deserializer<'de> for Deserializer {
|
|||||||
Value::Table(table) => {
|
Value::Table(table) => {
|
||||||
let _guard = RecursionGuard::new(&table, &self.visited);
|
let _guard = RecursionGuard::new(&table, &self.visited);
|
||||||
|
|
||||||
let mut iter = table.pairs::<StdString, Value>();
|
let mut iter = table.pairs::<String, Value>();
|
||||||
let (variant, value) = match iter.next() {
|
let (variant, value) = match iter.next() {
|
||||||
Some(v) => v?,
|
Some(v) => v?,
|
||||||
None => {
|
None => {
|
||||||
return Err(de::Error::invalid_value(
|
return Err(de::Error::invalid_value(
|
||||||
de::Unexpected::Map,
|
de::Unexpected::Map,
|
||||||
&"map with a single key",
|
&"map with a single key",
|
||||||
))
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -267,14 +301,14 @@ impl<'de> serde::Deserializer<'de> for Deserializer {
|
|||||||
Value::Table(t) => {
|
Value::Table(t) => {
|
||||||
let _guard = RecursionGuard::new(&t, &self.visited);
|
let _guard = RecursionGuard::new(&t, &self.visited);
|
||||||
|
|
||||||
let len = t.raw_len();
|
let len = self.len.unwrap_or_else(|| t.raw_len());
|
||||||
let mut deserializer = SeqDeserializer {
|
let mut deserializer = SeqDeserializer {
|
||||||
seq: t.sequence_values(),
|
seq: t.sequence_values().with_len(len),
|
||||||
options: self.options,
|
options: self.options,
|
||||||
visited: self.visited,
|
visited: self.visited,
|
||||||
};
|
};
|
||||||
let seq = visitor.visit_seq(&mut deserializer)?;
|
let seq = visitor.visit_seq(&mut deserializer)?;
|
||||||
if deserializer.seq.count() == 0 {
|
if deserializer.seq.next().is_none() {
|
||||||
Ok(seq)
|
Ok(seq)
|
||||||
} else {
|
} else {
|
||||||
Err(de::Error::invalid_length(len, &"fewer elements in the table"))
|
Err(de::Error::invalid_length(len, &"fewer elements in the table"))
|
||||||
@@ -586,7 +620,7 @@ impl<'de> de::MapAccess<'de> for MapDeserializer<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct EnumDeserializer {
|
struct EnumDeserializer {
|
||||||
variant: StdString,
|
variant: String,
|
||||||
value: Option<Value>,
|
value: Option<Value>,
|
||||||
options: Options,
|
options: Options,
|
||||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ use crate::util::check_stack;
|
|||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
|
|
||||||
/// Trait for serializing/deserializing Lua values using Serde.
|
/// 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 {
|
pub trait LuaSerdeExt: Sealed {
|
||||||
/// A special value (lightuserdata) to encode/decode optional (none) values.
|
/// A special value (lightuserdata) to encode/decode optional (none) values.
|
||||||
///
|
///
|
||||||
|
|||||||
+8
-8
@@ -1,6 +1,6 @@
|
|||||||
//! Serialize a Rust data structure into Lua value.
|
//! Serialize a Rust data structure into Lua value.
|
||||||
|
|
||||||
use serde::{ser, Serialize};
|
use serde::{Serialize, ser};
|
||||||
|
|
||||||
use super::LuaSerdeExt;
|
use super::LuaSerdeExt;
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
@@ -256,7 +256,7 @@ impl<'a> ser::Serializer for Serializer<'a> {
|
|||||||
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
|
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
|
||||||
let table = self.lua.create_table_with_capacity(len.unwrap_or(0), 0)?;
|
let table = self.lua.create_table_with_capacity(len.unwrap_or(0), 0)?;
|
||||||
if self.options.set_array_metatable {
|
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))
|
Ok(SerializeSeq::new(self.lua, table, self.options))
|
||||||
}
|
}
|
||||||
@@ -529,12 +529,12 @@ impl ser::SerializeStruct for SerializeStruct<'_> {
|
|||||||
fn end(self) -> Result<Value> {
|
fn end(self) -> Result<Value> {
|
||||||
match self.inner {
|
match self.inner {
|
||||||
Some(table @ Value::Table(_)) => Ok(table),
|
Some(table @ Value::Table(_)) => Ok(table),
|
||||||
Some(value) if self.options.detect_serde_json_arbitrary_precision => {
|
Some(value @ Value::String(_)) if self.options.detect_serde_json_arbitrary_precision => {
|
||||||
let number_s = value.as_str().expect("not an arbitrary precision number");
|
let number_s = value.to_string()?;
|
||||||
if number_s.contains(['.', 'e', 'E']) {
|
if number_s.contains(['.', 'e', 'E'])
|
||||||
if let Ok(number) = number_s.parse().map(Value::Number) {
|
&& let Ok(number) = number_s.parse().map(Value::Number)
|
||||||
return Ok(number);
|
{
|
||||||
}
|
return Ok(number);
|
||||||
}
|
}
|
||||||
Ok(number_s
|
Ok(number_s
|
||||||
.parse()
|
.parse()
|
||||||
|
|||||||
+378
-149
@@ -8,14 +8,14 @@ use std::result::Result as StdResult;
|
|||||||
use std::{fmt, mem, ptr};
|
use std::{fmt, mem, ptr};
|
||||||
|
|
||||||
use crate::chunk::{AsChunk, Chunk};
|
use crate::chunk::{AsChunk, Chunk};
|
||||||
|
use crate::debug::Debug;
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::function::Function;
|
use crate::function::Function;
|
||||||
use crate::hook::Debug;
|
|
||||||
use crate::memory::MemoryState;
|
use crate::memory::MemoryState;
|
||||||
use crate::multi::MultiValue;
|
use crate::multi::MultiValue;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
use crate::stdlib::StdLib;
|
use crate::stdlib::StdLib;
|
||||||
use crate::string::String;
|
use crate::string::LuaString;
|
||||||
use crate::table::Table;
|
use crate::table::Table;
|
||||||
use crate::thread::Thread;
|
use crate::thread::Thread;
|
||||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||||
@@ -24,13 +24,11 @@ use crate::types::{
|
|||||||
ReentrantMutexGuard, RegistryKey, VmState, XRc, XWeak,
|
ReentrantMutexGuard, RegistryKey, VmState, XRc, XWeak,
|
||||||
};
|
};
|
||||||
use crate::userdata::{AnyUserData, UserData, UserDataProxy, UserDataRegistry, UserDataStorage};
|
use crate::userdata::{AnyUserData, UserData, UserDataProxy, UserDataRegistry, UserDataStorage};
|
||||||
use crate::util::{
|
use crate::util::{StackGuard, assert_stack, check_stack, protect_lua_closure, push_string, rawset_field};
|
||||||
assert_stack, check_stack, protect_lua_closure, push_string, push_table, rawset_field, StackGuard,
|
|
||||||
};
|
|
||||||
use crate::value::{Nil, Value};
|
use crate::value::{Nil, Value};
|
||||||
|
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
use crate::{hook::HookTriggers, types::HookKind};
|
use crate::{debug::HookTriggers, types::HookKind};
|
||||||
|
|
||||||
#[cfg(any(feature = "luau", doc))]
|
#[cfg(any(feature = "luau", doc))]
|
||||||
use crate::{buffer::Buffer, chunk::Compiler};
|
use crate::{buffer::Buffer, chunk::Compiler};
|
||||||
@@ -39,9 +37,10 @@ use crate::{buffer::Buffer, chunk::Compiler};
|
|||||||
use {
|
use {
|
||||||
crate::types::LightUserData,
|
crate::types::LightUserData,
|
||||||
std::future::{self, Future},
|
std::future::{self, Future},
|
||||||
|
std::task::Poll,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
pub(crate) use extra::ExtraData;
|
pub(crate) use extra::ExtraData;
|
||||||
@@ -74,8 +73,8 @@ pub(crate) struct LuaGuard(ArcReentrantMutexGuard<RawLua>);
|
|||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum GCMode {
|
pub enum GCMode {
|
||||||
Incremental,
|
Incremental,
|
||||||
#[cfg(feature = "lua54")]
|
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua55", feature = "lua54"))))]
|
||||||
Generational,
|
Generational,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,7 +249,7 @@ impl Lua {
|
|||||||
ffi::luaL_loadstring as _,
|
ffi::luaL_loadstring as _,
|
||||||
ffi::luaL_openlibs as _,
|
ffi::luaL_openlibs as _,
|
||||||
]);
|
]);
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||||
{
|
{
|
||||||
_symbols.push(ffi::lua_getglobal as _);
|
_symbols.push(ffi::lua_getglobal as _);
|
||||||
_symbols.push(ffi::lua_setglobal as _);
|
_symbols.push(ffi::lua_setglobal as _);
|
||||||
@@ -338,6 +337,39 @@ impl Lua {
|
|||||||
R::from_stack_multi(nresults, &lua)
|
R::from_stack_multi(nresults, &lua)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Runs callback with the inner RawLua value. It can be used to manually push and get values on
|
||||||
|
/// the stack.
|
||||||
|
///
|
||||||
|
/// This function is safe because all unsafe actions with RawLua can only be done with unsafe
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// # use mlua::{Lua, Result, FromLua, IntoLua};
|
||||||
|
/// # fn main() -> Result<()> {
|
||||||
|
/// let lua = Lua::new();
|
||||||
|
/// let n: i32 = {
|
||||||
|
/// let num = 11i32;
|
||||||
|
/// lua.exec_raw_lua(|lua| {
|
||||||
|
/// unsafe {
|
||||||
|
/// <i32 as IntoLua>::push_into_stack(num, lua)?;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let n = unsafe {
|
||||||
|
/// <i32 as FromLua>::from_stack(-1, lua)?
|
||||||
|
/// };
|
||||||
|
/// Result::Ok(n)
|
||||||
|
/// })
|
||||||
|
/// }?;
|
||||||
|
/// assert_eq!(n, 11);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn exec_raw_lua<R>(&self, f: impl FnOnce(&RawLua) -> R) -> R {
|
||||||
|
let lua = self.lock();
|
||||||
|
f(&lua)
|
||||||
|
}
|
||||||
|
|
||||||
/// Loads the specified subset of the standard libraries into an existing Lua state.
|
/// Loads the specified subset of the standard libraries into an existing Lua state.
|
||||||
///
|
///
|
||||||
/// Use the [`StdLib`] flags to specify the libraries you want to load.
|
/// Use the [`StdLib`] flags to specify the libraries you want to load.
|
||||||
@@ -360,6 +392,8 @@ impl Lua {
|
|||||||
if cfg!(feature = "luau") && !modname.starts_with('@') {
|
if cfg!(feature = "luau") && !modname.starts_with('@') {
|
||||||
return Err(Error::runtime("module name must begin with '@'"));
|
return Err(Error::runtime("module name must begin with '@'"));
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "luau")]
|
||||||
|
let modname = modname.to_ascii_lowercase();
|
||||||
unsafe {
|
unsafe {
|
||||||
self.exec_raw::<()>(value, |state| {
|
self.exec_raw::<()>(value, |state| {
|
||||||
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, LOADED_MODULES_KEY);
|
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, LOADED_MODULES_KEY);
|
||||||
@@ -377,11 +411,11 @@ impl Lua {
|
|||||||
///
|
///
|
||||||
/// This is similar to setting the [`package.preload[modname]`] field.
|
/// This is similar to setting the [`package.preload[modname]`] field.
|
||||||
///
|
///
|
||||||
/// [`package.preload[modname]`]: https://www.lua.org/manual/5.4/manual.html#pdf-package.preload
|
/// [`package.preload[modname]`]: <https://www.lua.org/manual/5.4/manual.html#pdf-package.preload>
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||||
pub fn preload_module(&self, modname: &str, func: Function) -> Result<()> {
|
pub fn preload_module(&self, modname: &str, func: Function) -> Result<()> {
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||||
let preload = unsafe {
|
let preload = unsafe {
|
||||||
self.exec_raw::<Option<Table>>((), |state| {
|
self.exec_raw::<Option<Table>>((), |state| {
|
||||||
ffi::lua_getfield(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_PRELOAD_TABLE);
|
ffi::lua_getfield(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_PRELOAD_TABLE);
|
||||||
@@ -488,6 +522,7 @@ impl Lua {
|
|||||||
/// - Set globals to read-only (and activates safeenv)
|
/// - Set globals to read-only (and activates safeenv)
|
||||||
/// - Setup local environment table that performs writes locally and proxies reads to the global
|
/// - Setup local environment table that performs writes locally and proxies reads to the global
|
||||||
/// environment.
|
/// environment.
|
||||||
|
/// - Allow only `count` mode in `collectgarbage` function.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
@@ -545,7 +580,7 @@ impl Lua {
|
|||||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||||
pub fn set_global_hook<F>(&self, triggers: HookTriggers, callback: F) -> Result<()>
|
pub fn set_global_hook<F>(&self, triggers: HookTriggers, callback: F) -> Result<()>
|
||||||
where
|
where
|
||||||
F: Fn(&Lua, Debug) -> Result<VmState> + MaybeSend + 'static,
|
F: Fn(&Lua, &Debug) -> Result<VmState> + MaybeSend + 'static,
|
||||||
{
|
{
|
||||||
let lua = self.lock();
|
let lua = self.lock();
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -578,7 +613,7 @@ impl Lua {
|
|||||||
/// # fn main() -> Result<()> {
|
/// # fn main() -> Result<()> {
|
||||||
/// let lua = Lua::new();
|
/// let lua = Lua::new();
|
||||||
/// lua.set_hook(HookTriggers::EVERY_LINE, |_lua, debug| {
|
/// lua.set_hook(HookTriggers::EVERY_LINE, |_lua, debug| {
|
||||||
/// println!("line {}", debug.curr_line());
|
/// println!("line {:?}", debug.current_line());
|
||||||
/// Ok(VmState::Continue)
|
/// Ok(VmState::Continue)
|
||||||
/// });
|
/// });
|
||||||
///
|
///
|
||||||
@@ -595,7 +630,7 @@ impl Lua {
|
|||||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||||
pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F) -> Result<()>
|
pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F) -> Result<()>
|
||||||
where
|
where
|
||||||
F: Fn(&Lua, Debug) -> Result<VmState> + MaybeSend + 'static,
|
F: Fn(&Lua, &Debug) -> Result<VmState> + MaybeSend + 'static,
|
||||||
{
|
{
|
||||||
let lua = self.lock();
|
let lua = self.lock();
|
||||||
unsafe { lua.set_thread_hook(lua.state(), HookKind::Thread(triggers, XRc::new(callback))) }
|
unsafe { lua.set_thread_hook(lua.state(), HookKind::Thread(triggers, XRc::new(callback))) }
|
||||||
@@ -630,13 +665,13 @@ impl Lua {
|
|||||||
///
|
///
|
||||||
/// Any Luau code is guaranteed to call this handler "eventually"
|
/// Any Luau code is guaranteed to call this handler "eventually"
|
||||||
/// (in practice this can happen at any function call or at any loop iteration).
|
/// (in practice this can happen at any function call or at any loop iteration).
|
||||||
|
/// This is similar to `Lua::set_hook` but in more simplified form.
|
||||||
///
|
///
|
||||||
/// The provided interrupt function can error, and this error will be propagated through
|
/// The provided interrupt function can error, and this error will be propagated through
|
||||||
/// the Luau code that was executing at the time the interrupt was triggered.
|
/// the Luau code that was executing at the time the interrupt was triggered.
|
||||||
/// Also this can be used to implement continuous execution limits by instructing Luau VM to
|
/// Also this can be used to implement continuous execution limits by instructing Luau VM to
|
||||||
/// yield by returning [`VmState::Yield`].
|
/// yield by returning [`VmState::Yield`]. The yield will happen only at yieldable points
|
||||||
///
|
/// of execution (not across metamethod/C-call boundaries).
|
||||||
/// This is similar to [`Lua::set_hook`] but in more simplified form.
|
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@@ -694,7 +729,10 @@ impl Lua {
|
|||||||
match result {
|
match result {
|
||||||
VmState::Continue => {}
|
VmState::Continue => {}
|
||||||
VmState::Yield => {
|
VmState::Yield => {
|
||||||
ffi::lua_yield(state, 0);
|
// We can yield only at yieldable points, otherwise ignore and continue
|
||||||
|
if ffi::lua_isyieldable(state) != 0 {
|
||||||
|
ffi::lua_yield(state, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -809,15 +847,14 @@ impl Lua {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the warning function to be used by Lua to emit warnings.
|
/// Sets the warning function to be used by Lua to emit warnings.
|
||||||
#[cfg(feature = "lua54")]
|
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua55", feature = "lua54"))))]
|
||||||
pub fn set_warning_function<F>(&self, callback: F)
|
pub fn set_warning_function<F>(&self, callback: F)
|
||||||
where
|
where
|
||||||
F: Fn(&Lua, &str, bool) -> Result<()> + MaybeSend + 'static,
|
F: Fn(&Lua, &str, bool) -> Result<()> + MaybeSend + 'static,
|
||||||
{
|
{
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::os::raw::{c_char, c_void};
|
use std::os::raw::{c_char, c_void};
|
||||||
use std::string::String as StdString;
|
|
||||||
|
|
||||||
unsafe extern "C-unwind" fn warn_proc(ud: *mut c_void, msg: *const c_char, tocont: c_int) {
|
unsafe extern "C-unwind" fn warn_proc(ud: *mut c_void, msg: *const c_char, tocont: c_int) {
|
||||||
let extra = ud as *mut ExtraData;
|
let extra = ud as *mut ExtraData;
|
||||||
@@ -827,7 +864,7 @@ impl Lua {
|
|||||||
if XRc::strong_count(&warn_callback) > 2 {
|
if XRc::strong_count(&warn_callback) > 2 {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let msg = StdString::from_utf8_lossy(CStr::from_ptr(msg).to_bytes());
|
let msg = String::from_utf8_lossy(CStr::from_ptr(msg).to_bytes());
|
||||||
warn_callback((*extra).lua(), &msg, tocont != 0)
|
warn_callback((*extra).lua(), &msg, tocont != 0)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -842,8 +879,8 @@ impl Lua {
|
|||||||
/// Removes warning function previously set by `set_warning_function`.
|
/// Removes warning function previously set by `set_warning_function`.
|
||||||
///
|
///
|
||||||
/// This function has no effect if a warning function was not previously set.
|
/// This function has no effect if a warning function was not previously set.
|
||||||
#[cfg(feature = "lua54")]
|
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua55", feature = "lua54"))))]
|
||||||
pub fn remove_warning_function(&self) {
|
pub fn remove_warning_function(&self) {
|
||||||
let lua = self.lock();
|
let lua = self.lock();
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -856,8 +893,8 @@ impl Lua {
|
|||||||
///
|
///
|
||||||
/// A message in a call with `incomplete` set to `true` should be continued in
|
/// A message in a call with `incomplete` set to `true` should be continued in
|
||||||
/// another call to this function.
|
/// another call to this function.
|
||||||
#[cfg(feature = "lua54")]
|
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua55", feature = "lua54"))))]
|
||||||
pub fn warning(&self, msg: impl AsRef<str>, incomplete: bool) {
|
pub fn warning(&self, msg: impl AsRef<str>, incomplete: bool) {
|
||||||
let msg = msg.as_ref();
|
let msg = msg.as_ref();
|
||||||
let mut bytes = vec![0; msg.len() + 1];
|
let mut bytes = vec![0; msg.len() + 1];
|
||||||
@@ -870,18 +907,16 @@ impl Lua {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets information about the interpreter runtime stack.
|
/// Gets information about the interpreter runtime stack at the given level.
|
||||||
///
|
///
|
||||||
/// This function returns [`Debug`] structure that can be used to get information about the
|
/// This function calls callback `f`, passing the [`Debug`] structure that can be used to get
|
||||||
/// function executing at a given level. Level `0` is the current running function, whereas
|
/// information about the function executing at a given level.
|
||||||
/// level `n+1` is the function that has called level `n` (except for tail calls, which do
|
/// Level `0` is the current running function, whereas level `n+1` is the function that has
|
||||||
/// not count in the stack).
|
/// called level `n` (except for tail calls, which do not count in the stack).
|
||||||
///
|
pub fn inspect_stack<R>(&self, level: usize, f: impl FnOnce(&Debug) -> R) -> Option<R> {
|
||||||
/// [`Debug`]: crate::hook::Debug
|
|
||||||
pub fn inspect_stack(&self, level: usize) -> Option<Debug> {
|
|
||||||
let lua = self.lock();
|
let lua = self.lock();
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut ar: ffi::lua_Debug = mem::zeroed();
|
let mut ar = mem::zeroed::<ffi::lua_Debug>();
|
||||||
let level = level as c_int;
|
let level = level as c_int;
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
if ffi::lua_getstack(lua.state(), level, &mut ar) == 0 {
|
if ffi::lua_getstack(lua.state(), level, &mut ar) == 0 {
|
||||||
@@ -891,7 +926,28 @@ impl Lua {
|
|||||||
if ffi::lua_getinfo(lua.state(), level, cstr!(""), &mut ar) == 0 {
|
if ffi::lua_getinfo(lua.state(), level, cstr!(""), &mut ar) == 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Some(Debug::new_owned(lua, level, ar))
|
|
||||||
|
Some(f(&Debug::new(&lua, level, &mut ar)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a traceback of the call stack at the given level.
|
||||||
|
///
|
||||||
|
/// The `msg` parameter, if provided, is added at the beginning of the traceback.
|
||||||
|
/// The `level` parameter works the same way as in [`Lua::inspect_stack`].
|
||||||
|
pub fn traceback(&self, msg: Option<&str>, level: usize) -> Result<LuaString> {
|
||||||
|
let lua = self.lock();
|
||||||
|
unsafe {
|
||||||
|
check_stack(lua.state(), 3)?;
|
||||||
|
protect_lua!(lua.state(), 0, 1, |state| {
|
||||||
|
let msg = match msg {
|
||||||
|
Some(s) => ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len()),
|
||||||
|
None => ptr::null(),
|
||||||
|
};
|
||||||
|
// `protect_lua` adds it's own call frame, so we need to increase level by 1
|
||||||
|
ffi::luaL_traceback(state, state, msg, (level + 1) as c_int);
|
||||||
|
})?;
|
||||||
|
Ok(LuaString(lua.pop_ref()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -930,7 +986,13 @@ impl Lua {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the garbage collector is currently running automatically.
|
/// Returns `true` if the garbage collector is currently running automatically.
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
#[cfg(any(
|
||||||
|
feature = "lua55",
|
||||||
|
feature = "lua54",
|
||||||
|
feature = "lua53",
|
||||||
|
feature = "lua52",
|
||||||
|
feature = "luau"
|
||||||
|
))]
|
||||||
pub fn gc_is_running(&self) -> bool {
|
pub fn gc_is_running(&self) -> bool {
|
||||||
let lua = self.lock();
|
let lua = self.lock();
|
||||||
unsafe { ffi::lua_gc(lua.main_state(), ffi::LUA_GCISRUNNING, 0) != 0 }
|
unsafe { ffi::lua_gc(lua.main_state(), ffi::LUA_GCISRUNNING, 0) != 0 }
|
||||||
@@ -995,8 +1057,12 @@ impl Lua {
|
|||||||
let lua = self.lock();
|
let lua = self.lock();
|
||||||
let state = lua.main_state();
|
let state = lua.main_state();
|
||||||
unsafe {
|
unsafe {
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(feature = "lua55")]
|
||||||
|
return ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPPAUSE, pause);
|
||||||
|
|
||||||
|
#[cfg(not(any(feature = "lua55", feature = "luau")))]
|
||||||
return ffi::lua_gc(state, ffi::LUA_GCSETPAUSE, pause);
|
return ffi::lua_gc(state, ffi::LUA_GCSETPAUSE, pause);
|
||||||
|
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
return ffi::lua_gc(state, ffi::LUA_GCSETGOAL, pause);
|
return ffi::lua_gc(state, ffi::LUA_GCSETGOAL, pause);
|
||||||
}
|
}
|
||||||
@@ -1010,7 +1076,18 @@ impl Lua {
|
|||||||
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#2.5
|
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#2.5
|
||||||
pub fn gc_set_step_multiplier(&self, step_multiplier: c_int) -> c_int {
|
pub fn gc_set_step_multiplier(&self, step_multiplier: c_int) -> c_int {
|
||||||
let lua = self.lock();
|
let lua = self.lock();
|
||||||
unsafe { ffi::lua_gc(lua.main_state(), ffi::LUA_GCSETSTEPMUL, step_multiplier) }
|
unsafe {
|
||||||
|
#[cfg(feature = "lua55")]
|
||||||
|
return ffi::lua_gc(
|
||||||
|
lua.main_state(),
|
||||||
|
ffi::LUA_GCPARAM,
|
||||||
|
ffi::LUA_GCPSTEPMUL,
|
||||||
|
step_multiplier,
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "lua55"))]
|
||||||
|
return ffi::lua_gc(lua.main_state(), ffi::LUA_GCSETSTEPMUL, step_multiplier);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Changes the collector to incremental mode with the given parameters.
|
/// Changes the collector to incremental mode with the given parameters.
|
||||||
@@ -1052,9 +1129,16 @@ impl Lua {
|
|||||||
GCMode::Incremental
|
GCMode::Incremental
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "lua55")]
|
||||||
|
let prev_mode = unsafe {
|
||||||
|
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPPAUSE, pause);
|
||||||
|
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPSTEPMUL, step_multiplier);
|
||||||
|
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPSTEPSIZE, step_size);
|
||||||
|
ffi::lua_gc(state, ffi::LUA_GCINC)
|
||||||
|
};
|
||||||
#[cfg(feature = "lua54")]
|
#[cfg(feature = "lua54")]
|
||||||
let prev_mode = unsafe { ffi::lua_gc(state, ffi::LUA_GCINC, pause, step_multiplier, step_size) };
|
let prev_mode = unsafe { ffi::lua_gc(state, ffi::LUA_GCINC, pause, step_multiplier, step_size) };
|
||||||
#[cfg(feature = "lua54")]
|
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||||
match prev_mode {
|
match prev_mode {
|
||||||
ffi::LUA_GCINC => GCMode::Incremental,
|
ffi::LUA_GCINC => GCMode::Incremental,
|
||||||
ffi::LUA_GCGEN => GCMode::Generational,
|
ffi::LUA_GCGEN => GCMode::Generational,
|
||||||
@@ -1068,11 +1152,19 @@ impl Lua {
|
|||||||
/// can be found in the Lua 5.4 [documentation][lua_doc].
|
/// can be found in the Lua 5.4 [documentation][lua_doc].
|
||||||
///
|
///
|
||||||
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#2.5.2
|
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#2.5.2
|
||||||
#[cfg(feature = "lua54")]
|
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua55", feature = "lua54"))))]
|
||||||
pub fn gc_gen(&self, minor_multiplier: c_int, major_multiplier: c_int) -> GCMode {
|
pub fn gc_gen(&self, minor_multiplier: c_int, major_multiplier: c_int) -> GCMode {
|
||||||
let lua = self.lock();
|
let lua = self.lock();
|
||||||
let state = lua.main_state();
|
let state = lua.main_state();
|
||||||
|
#[cfg(feature = "lua55")]
|
||||||
|
let prev_mode = unsafe {
|
||||||
|
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPMINORMUL, minor_multiplier);
|
||||||
|
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPMINORMAJOR, major_multiplier);
|
||||||
|
// TODO: LUA_GCPMAJORMINOR
|
||||||
|
ffi::lua_gc(state, ffi::LUA_GCGEN)
|
||||||
|
};
|
||||||
|
#[cfg(not(feature = "lua55"))]
|
||||||
let prev_mode = unsafe { ffi::lua_gc(state, ffi::LUA_GCGEN, minor_multiplier, major_multiplier) };
|
let prev_mode = unsafe { ffi::lua_gc(state, ffi::LUA_GCGEN, minor_multiplier, major_multiplier) };
|
||||||
match prev_mode {
|
match prev_mode {
|
||||||
ffi::LUA_GCGEN => GCMode::Generational,
|
ffi::LUA_GCGEN => GCMode::Generational,
|
||||||
@@ -1112,10 +1204,10 @@ impl Lua {
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[allow(clippy::result_unit_err)]
|
#[allow(clippy::result_unit_err)]
|
||||||
pub fn set_fflag(name: &str, enabled: bool) -> StdResult<(), ()> {
|
pub fn set_fflag(name: &str, enabled: bool) -> StdResult<(), ()> {
|
||||||
if let Ok(name) = std::ffi::CString::new(name) {
|
if let Ok(name) = std::ffi::CString::new(name)
|
||||||
if unsafe { ffi::luau_setfflag(name.as_ptr(), enabled as c_int) != 0 } {
|
&& unsafe { ffi::luau_setfflag(name.as_ptr(), enabled as c_int) != 0 }
|
||||||
return Ok(());
|
{
|
||||||
}
|
return Ok(());
|
||||||
}
|
}
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
@@ -1150,36 +1242,52 @@ impl Lua {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create and return an interned Lua string.
|
/// Creates and returns an interned Lua string.
|
||||||
///
|
///
|
||||||
/// Lua strings can be arbitrary `[u8]` data including embedded nulls, so in addition to `&str`
|
/// Lua strings can be arbitrary `[u8]` data including embedded nulls, so in addition to `&str`
|
||||||
/// and `&String`, you can also pass plain `&[u8]` here.
|
/// and `&String`, you can also pass plain `&[u8]` here.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn create_string(&self, s: impl AsRef<[u8]>) -> Result<String> {
|
pub fn create_string(&self, s: impl AsRef<[u8]>) -> Result<LuaString> {
|
||||||
unsafe { self.lock().create_string(s) }
|
unsafe { self.lock().create_string(s.as_ref()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create and return a Luau [buffer] object from a byte slice of data.
|
/// Creates and returns an external Lua string.
|
||||||
|
///
|
||||||
|
/// External string is a string where the memory is managed by Rust code, and Lua only holds a
|
||||||
|
/// reference to it. This can be used to avoid copying large strings into Lua memory.
|
||||||
|
#[cfg(feature = "lua55")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "lua55")))]
|
||||||
|
#[inline]
|
||||||
|
pub fn create_external_string(&self, s: impl Into<Vec<u8>>) -> Result<LuaString> {
|
||||||
|
unsafe { self.lock().create_external_string(s.into()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates and returns a Luau [buffer] object from a byte slice of data.
|
||||||
///
|
///
|
||||||
/// [buffer]: https://luau.org/library#buffer-library
|
/// [buffer]: https://luau.org/library#buffer-library
|
||||||
#[cfg(any(feature = "luau", doc))]
|
#[cfg(any(feature = "luau", doc))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||||
pub fn create_buffer(&self, buf: impl AsRef<[u8]>) -> Result<Buffer> {
|
pub fn create_buffer(&self, data: impl AsRef<[u8]>) -> Result<Buffer> {
|
||||||
let lua = self.lock();
|
let lua = self.lock();
|
||||||
let state = lua.state();
|
let data = data.as_ref();
|
||||||
unsafe {
|
unsafe {
|
||||||
if lua.unlikely_memory_error() {
|
let (ptr, buffer) = lua.create_buffer_with_capacity(data.len())?;
|
||||||
crate::util::push_buffer(state, buf.as_ref(), false)?;
|
ptr.copy_from_nonoverlapping(data.as_ptr(), data.len());
|
||||||
return Ok(Buffer(lua.pop_ref()));
|
Ok(buffer)
|
||||||
}
|
|
||||||
|
|
||||||
let _sg = StackGuard::new(state);
|
|
||||||
check_stack(state, 3)?;
|
|
||||||
crate::util::push_buffer(state, buf.as_ref(), true)?;
|
|
||||||
Ok(Buffer(lua.pop_ref()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates and returns a Luau [buffer] object with the specified size.
|
||||||
|
///
|
||||||
|
/// Size limit is 1GB. All bytes will be initialized to zero.
|
||||||
|
///
|
||||||
|
/// [buffer]: https://luau.org/library#buffer-library
|
||||||
|
#[cfg(any(feature = "luau", doc))]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||||
|
pub fn create_buffer_with_capacity(&self, size: usize) -> Result<Buffer> {
|
||||||
|
unsafe { Ok(self.lock().create_buffer_with_capacity(size)?.1) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates and returns a new empty table.
|
/// Creates and returns a new empty table.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn create_table(&self) -> Result<Table> {
|
pub fn create_table(&self) -> Result<Table> {
|
||||||
@@ -1202,28 +1310,7 @@ impl Lua {
|
|||||||
K: IntoLua,
|
K: IntoLua,
|
||||||
V: IntoLua,
|
V: IntoLua,
|
||||||
{
|
{
|
||||||
let lua = self.lock();
|
unsafe { self.lock().create_table_from(iter) }
|
||||||
let state = lua.state();
|
|
||||||
unsafe {
|
|
||||||
let _sg = StackGuard::new(state);
|
|
||||||
check_stack(state, 6)?;
|
|
||||||
|
|
||||||
let iter = iter.into_iter();
|
|
||||||
let lower_bound = iter.size_hint().0;
|
|
||||||
let protect = !lua.unlikely_memory_error();
|
|
||||||
push_table(state, 0, lower_bound, protect)?;
|
|
||||||
for (k, v) in iter {
|
|
||||||
lua.push(k)?;
|
|
||||||
lua.push(v)?;
|
|
||||||
if protect {
|
|
||||||
protect_lua!(state, 3, 1, fn(state) ffi::lua_rawset(state, -3))?;
|
|
||||||
} else {
|
|
||||||
ffi::lua_rawset(state, -3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Table(lua.pop_ref()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a table from an iterator of values, using `1..` as the keys.
|
/// Creates a table from an iterator of values, using `1..` as the keys.
|
||||||
@@ -1309,8 +1396,29 @@ impl Lua {
|
|||||||
/// This function is unsafe because provides a way to execute unsafe C function.
|
/// This function is unsafe because provides a way to execute unsafe C function.
|
||||||
pub unsafe fn create_c_function(&self, func: ffi::lua_CFunction) -> Result<Function> {
|
pub unsafe fn create_c_function(&self, func: ffi::lua_CFunction) -> Result<Function> {
|
||||||
let lua = self.lock();
|
let lua = self.lock();
|
||||||
ffi::lua_pushcfunction(lua.ref_thread(), func);
|
if cfg!(any(
|
||||||
Ok(Function(lua.pop_ref_thread()))
|
feature = "lua55",
|
||||||
|
feature = "lua54",
|
||||||
|
feature = "lua53",
|
||||||
|
feature = "lua52"
|
||||||
|
)) {
|
||||||
|
ffi::lua_pushcfunction(lua.ref_thread(), func);
|
||||||
|
return Ok(Function(lua.pop_ref_thread()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lua <5.2 requires memory allocation to push a C function
|
||||||
|
let state = lua.state();
|
||||||
|
{
|
||||||
|
let _sg = StackGuard::new(state);
|
||||||
|
check_stack(state, 3)?;
|
||||||
|
|
||||||
|
if lua.unlikely_memory_error() {
|
||||||
|
ffi::lua_pushcfunction(state, func);
|
||||||
|
} else {
|
||||||
|
protect_lua!(state, 0, 1, |state| ffi::lua_pushcfunction(state, func))?;
|
||||||
|
}
|
||||||
|
Ok(Function(lua.pop_ref()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wraps a Rust async function or closure, creating a callable Lua function handle to it.
|
/// Wraps a Rust async function or closure, creating a callable Lua function handle to it.
|
||||||
@@ -1390,8 +1498,8 @@ impl Lua {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a Lua userdata object from a custom serializable userdata type.
|
/// Creates a Lua userdata object from a custom serializable userdata type.
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn create_ser_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
pub fn create_ser_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
||||||
where
|
where
|
||||||
@@ -1418,8 +1526,8 @@ impl Lua {
|
|||||||
/// Creates a Lua userdata object from a custom serializable Rust type.
|
/// Creates a Lua userdata object from a custom serializable Rust type.
|
||||||
///
|
///
|
||||||
/// See [`Lua::create_any_userdata`] for more details.
|
/// See [`Lua::create_any_userdata`] for more details.
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn create_ser_any_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
pub fn create_ser_any_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
||||||
where
|
where
|
||||||
@@ -1439,7 +1547,7 @@ impl Lua {
|
|||||||
let lua = self.lock();
|
let lua = self.lock();
|
||||||
unsafe {
|
unsafe {
|
||||||
// Deregister the type if it already registered
|
// Deregister the type if it already registered
|
||||||
if let Some(&table_id) = (*lua.extra.get()).registered_userdata_t.get(&type_id) {
|
if let Some(table_id) = (*lua.extra.get()).registered_userdata_t.remove(&type_id) {
|
||||||
ffi::luaL_unref(lua.state(), ffi::LUA_REGISTRYINDEX, table_id);
|
ffi::luaL_unref(lua.state(), ffi::LUA_REGISTRYINDEX, table_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1490,7 +1598,27 @@ impl Lua {
|
|||||||
unsafe { self.lock().make_userdata(UserDataStorage::new(ud)) }
|
unsafe { self.lock().make_userdata(UserDataStorage::new(ud)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the metatable for a Lua builtin type.
|
/// Gets the metatable of a Lua built-in (primitive) type.
|
||||||
|
///
|
||||||
|
/// The metatable is shared by all values of the given type.
|
||||||
|
///
|
||||||
|
/// See [`Lua::set_type_metatable`] for examples.
|
||||||
|
#[allow(private_bounds)]
|
||||||
|
pub fn type_metatable<T: LuaType>(&self) -> Option<Table> {
|
||||||
|
let lua = self.lock();
|
||||||
|
let state = lua.state();
|
||||||
|
unsafe {
|
||||||
|
let _sg = StackGuard::new(state);
|
||||||
|
assert_stack(state, 2);
|
||||||
|
|
||||||
|
if lua.push_primitive_type::<T>() && ffi::lua_getmetatable(state, -1) != 0 {
|
||||||
|
return Some(Table(lua.pop_ref()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the metatable for a Lua built-in (primitive) type.
|
||||||
///
|
///
|
||||||
/// The metatable will be shared by all values of the given type.
|
/// The metatable will be shared by all values of the given type.
|
||||||
///
|
///
|
||||||
@@ -1517,44 +1645,13 @@ impl Lua {
|
|||||||
let _sg = StackGuard::new(state);
|
let _sg = StackGuard::new(state);
|
||||||
assert_stack(state, 2);
|
assert_stack(state, 2);
|
||||||
|
|
||||||
match T::TYPE_ID {
|
if lua.push_primitive_type::<T>() {
|
||||||
ffi::LUA_TBOOLEAN => {
|
match metatable {
|
||||||
ffi::lua_pushboolean(state, 0);
|
Some(metatable) => lua.push_ref(&metatable.0),
|
||||||
|
None => ffi::lua_pushnil(state),
|
||||||
}
|
}
|
||||||
ffi::LUA_TLIGHTUSERDATA => {
|
ffi::lua_setmetatable(state, -2);
|
||||||
ffi::lua_pushlightuserdata(state, ptr::null_mut());
|
|
||||||
}
|
|
||||||
ffi::LUA_TNUMBER => {
|
|
||||||
ffi::lua_pushnumber(state, 0.);
|
|
||||||
}
|
|
||||||
#[cfg(feature = "luau")]
|
|
||||||
ffi::LUA_TVECTOR => {
|
|
||||||
#[cfg(not(feature = "luau-vector4"))]
|
|
||||||
ffi::lua_pushvector(state, 0., 0., 0.);
|
|
||||||
#[cfg(feature = "luau-vector4")]
|
|
||||||
ffi::lua_pushvector(state, 0., 0., 0., 0.);
|
|
||||||
}
|
|
||||||
ffi::LUA_TSTRING => {
|
|
||||||
ffi::lua_pushstring(state, b"\0" as *const u8 as *const _);
|
|
||||||
}
|
|
||||||
ffi::LUA_TFUNCTION => match self.load("function() end").eval::<Function>() {
|
|
||||||
Ok(func) => lua.push_ref(&func.0),
|
|
||||||
Err(_) => return,
|
|
||||||
},
|
|
||||||
ffi::LUA_TTHREAD => {
|
|
||||||
ffi::lua_pushthread(state);
|
|
||||||
}
|
|
||||||
#[cfg(feature = "luau")]
|
|
||||||
ffi::LUA_TBUFFER => {
|
|
||||||
ffi::lua_newbuffer(state, 0);
|
|
||||||
}
|
|
||||||
_ => return,
|
|
||||||
}
|
}
|
||||||
match metatable {
|
|
||||||
Some(metatable) => lua.push_ref(&metatable.0),
|
|
||||||
None => ffi::lua_pushnil(state),
|
|
||||||
}
|
|
||||||
ffi::lua_setmetatable(state, -2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1565,7 +1662,7 @@ impl Lua {
|
|||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(state);
|
let _sg = StackGuard::new(state);
|
||||||
assert_stack(state, 1);
|
assert_stack(state, 1);
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||||
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS);
|
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS);
|
||||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||||
ffi::lua_pushvalue(state, ffi::LUA_GLOBALSINDEX);
|
ffi::lua_pushvalue(state, ffi::LUA_GLOBALSINDEX);
|
||||||
@@ -1573,6 +1670,39 @@ impl Lua {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the global environment.
|
||||||
|
///
|
||||||
|
/// This will replace the current global environment with the provided `globals` table.
|
||||||
|
///
|
||||||
|
/// For Lua 5.2+ the globals table is stored in the registry and shared between all threads.
|
||||||
|
/// For Lua 5.1 and Luau the globals table is stored in each thread.
|
||||||
|
///
|
||||||
|
/// Please note that any existing Lua functions have cached global environment and will not
|
||||||
|
/// see the changes made by this method.
|
||||||
|
/// To update the environment for existing Lua functions, use [`Function::set_environment`].
|
||||||
|
pub fn set_globals(&self, globals: Table) -> Result<()> {
|
||||||
|
let lua = self.lock();
|
||||||
|
let state = lua.state();
|
||||||
|
unsafe {
|
||||||
|
#[cfg(feature = "luau")]
|
||||||
|
if (*lua.extra.get()).sandboxed {
|
||||||
|
return Err(Error::runtime("cannot change globals in a sandboxed Lua state"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let _sg = StackGuard::new(state);
|
||||||
|
check_stack(state, 1)?;
|
||||||
|
|
||||||
|
lua.push_ref(&globals.0);
|
||||||
|
|
||||||
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||||
|
ffi::lua_rawseti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS);
|
||||||
|
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||||
|
ffi::lua_replace(state, ffi::LUA_GLOBALSINDEX);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a handle to the active `Thread`.
|
/// Returns a handle to the active `Thread`.
|
||||||
///
|
///
|
||||||
/// For calls to `Lua` this will be the main Lua thread, for parameters given to a callback,
|
/// For calls to `Lua` this will be the main Lua thread, for parameters given to a callback,
|
||||||
@@ -1610,7 +1740,7 @@ impl Lua {
|
|||||||
///
|
///
|
||||||
/// To succeed, the value must be a string (in which case this is a no-op), an integer, or a
|
/// To succeed, the value must be a string (in which case this is a no-op), an integer, or a
|
||||||
/// number.
|
/// number.
|
||||||
pub fn coerce_string(&self, v: Value) -> Result<Option<String>> {
|
pub fn coerce_string(&self, v: Value) -> Result<Option<LuaString>> {
|
||||||
Ok(match v {
|
Ok(match v {
|
||||||
Value::String(s) => Some(s),
|
Value::String(s) => Some(s),
|
||||||
v => unsafe {
|
v => unsafe {
|
||||||
@@ -1628,7 +1758,7 @@ impl Lua {
|
|||||||
})?
|
})?
|
||||||
};
|
};
|
||||||
if !res.is_null() {
|
if !res.is_null() {
|
||||||
Some(String(lua.pop_ref()))
|
Some(LuaString(lua.pop_ref()))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -1654,11 +1784,7 @@ impl Lua {
|
|||||||
lua.push_value(&v)?;
|
lua.push_value(&v)?;
|
||||||
let mut isint = 0;
|
let mut isint = 0;
|
||||||
let i = ffi::lua_tointegerx(state, -1, &mut isint);
|
let i = ffi::lua_tointegerx(state, -1, &mut isint);
|
||||||
if isint == 0 {
|
if isint == 0 { None } else { Some(i) }
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(i)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1680,11 +1806,7 @@ impl Lua {
|
|||||||
lua.push_value(&v)?;
|
lua.push_value(&v)?;
|
||||||
let mut isnum = 0;
|
let mut isnum = 0;
|
||||||
let n = ffi::lua_tonumberx(state, -1, &mut isnum);
|
let n = ffi::lua_tonumberx(state, -1, &mut isnum);
|
||||||
if isnum == 0 {
|
if isnum == 0 { None } else { Some(n) }
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(n)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1978,7 +2100,7 @@ impl Lua {
|
|||||||
/// Panics if the data object of type `T` is currently mutably borrowed. Multiple immutable
|
/// Panics if the data object of type `T` is currently mutably borrowed. Multiple immutable
|
||||||
/// reads can be taken out at the same time.
|
/// reads can be taken out at the same time.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn app_data_ref<T: 'static>(&self) -> Option<AppDataRef<T>> {
|
pub fn app_data_ref<T: 'static>(&self) -> Option<AppDataRef<'_, T>> {
|
||||||
let guard = self.lock_arc();
|
let guard = self.lock_arc();
|
||||||
let extra = unsafe { &*guard.extra.get() };
|
let extra = unsafe { &*guard.extra.get() };
|
||||||
extra.app_data.borrow(Some(guard))
|
extra.app_data.borrow(Some(guard))
|
||||||
@@ -1986,7 +2108,7 @@ impl Lua {
|
|||||||
|
|
||||||
/// Tries to get a reference to an application data object stored by [`Lua::set_app_data`] of
|
/// Tries to get a reference to an application data object stored by [`Lua::set_app_data`] of
|
||||||
/// type `T`.
|
/// type `T`.
|
||||||
pub fn try_app_data_ref<T: 'static>(&self) -> StdResult<Option<AppDataRef<T>>, BorrowError> {
|
pub fn try_app_data_ref<T: 'static>(&self) -> StdResult<Option<AppDataRef<'_, T>>, BorrowError> {
|
||||||
let guard = self.lock_arc();
|
let guard = self.lock_arc();
|
||||||
let extra = unsafe { &*guard.extra.get() };
|
let extra = unsafe { &*guard.extra.get() };
|
||||||
extra.app_data.try_borrow(Some(guard))
|
extra.app_data.try_borrow(Some(guard))
|
||||||
@@ -1999,7 +2121,7 @@ impl Lua {
|
|||||||
///
|
///
|
||||||
/// Panics if the data object of type `T` is currently borrowed.
|
/// Panics if the data object of type `T` is currently borrowed.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn app_data_mut<T: 'static>(&self) -> Option<AppDataRefMut<T>> {
|
pub fn app_data_mut<T: 'static>(&self) -> Option<AppDataRefMut<'_, T>> {
|
||||||
let guard = self.lock_arc();
|
let guard = self.lock_arc();
|
||||||
let extra = unsafe { &*guard.extra.get() };
|
let extra = unsafe { &*guard.extra.get() };
|
||||||
extra.app_data.borrow_mut(Some(guard))
|
extra.app_data.borrow_mut(Some(guard))
|
||||||
@@ -2007,7 +2129,7 @@ impl Lua {
|
|||||||
|
|
||||||
/// Tries to get a mutable reference to an application data object stored by
|
/// Tries to get a mutable reference to an application data object stored by
|
||||||
/// [`Lua::set_app_data`] of type `T`.
|
/// [`Lua::set_app_data`] of type `T`.
|
||||||
pub fn try_app_data_mut<T: 'static>(&self) -> StdResult<Option<AppDataRefMut<T>>, BorrowMutError> {
|
pub fn try_app_data_mut<T: 'static>(&self) -> StdResult<Option<AppDataRefMut<'_, T>>, BorrowMutError> {
|
||||||
let guard = self.lock_arc();
|
let guard = self.lock_arc();
|
||||||
let extra = unsafe { &*guard.extra.get() };
|
let extra = unsafe { &*guard.extra.get() };
|
||||||
extra.app_data.try_borrow_mut(Some(guard))
|
extra.app_data.try_borrow_mut(Some(guard))
|
||||||
@@ -2036,6 +2158,112 @@ impl Lua {
|
|||||||
LightUserData(&ASYNC_POLL_PENDING as *const u8 as *mut std::os::raw::c_void)
|
LightUserData(&ASYNC_POLL_PENDING as *const u8 as *mut std::os::raw::c_void)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn poll_terminate() -> LightUserData {
|
||||||
|
static ASYNC_POLL_TERMINATE: u8 = 0;
|
||||||
|
LightUserData(&ASYNC_POLL_TERMINATE as *const u8 as *mut std::os::raw::c_void)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn poll_yield() -> LightUserData {
|
||||||
|
static ASYNC_POLL_YIELD: u8 = 0;
|
||||||
|
LightUserData(&ASYNC_POLL_YIELD as *const u8 as *mut std::os::raw::c_void)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Suspends the current async function, returning the provided arguments to caller.
|
||||||
|
///
|
||||||
|
/// This function is similar to [`coroutine.yield`] but allow yielding Rust functions
|
||||||
|
/// and passing values to the caller.
|
||||||
|
/// Please note that you cannot cross [`Thread`] boundaries (e.g. calling `yield_with` on one
|
||||||
|
/// thread and resuming on another).
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Async iterator:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use mlua::{Lua, Result};
|
||||||
|
/// #
|
||||||
|
/// async fn generator(lua: Lua, _: ()) -> Result<()> {
|
||||||
|
/// for i in 0..10 {
|
||||||
|
/// lua.yield_with::<()>(i).await?;
|
||||||
|
/// }
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() -> Result<()> {
|
||||||
|
/// let lua = Lua::new();
|
||||||
|
/// lua.globals().set("generator", lua.create_async_function(generator)?)?;
|
||||||
|
///
|
||||||
|
/// lua.load(r#"
|
||||||
|
/// local n = 0
|
||||||
|
/// for i in coroutine.wrap(generator) do
|
||||||
|
/// n = n + i
|
||||||
|
/// end
|
||||||
|
/// assert(n == 45)
|
||||||
|
/// "#)
|
||||||
|
/// .exec()
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Exchange values on yield:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use mlua::{Lua, Result, Value};
|
||||||
|
/// #
|
||||||
|
/// async fn pingpong(lua: Lua, mut val: i32) -> Result<()> {
|
||||||
|
/// loop {
|
||||||
|
/// val = lua.yield_with::<i32>(val).await? + 1;
|
||||||
|
/// }
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// # fn main() -> Result<()> {
|
||||||
|
/// let lua = Lua::new();
|
||||||
|
///
|
||||||
|
/// let co = lua.create_thread(lua.create_async_function(pingpong)?)?;
|
||||||
|
/// assert_eq!(co.resume::<i32>(1)?, 1);
|
||||||
|
/// assert_eq!(co.resume::<i32>(2)?, 3);
|
||||||
|
/// assert_eq!(co.resume::<i32>(3)?, 4);
|
||||||
|
///
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`coroutine.yield`]: https://www.lua.org/manual/5.4/manual.html#pdf-coroutine.yield
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||||
|
pub async fn yield_with<R: FromLuaMulti>(&self, args: impl IntoLuaMulti) -> Result<R> {
|
||||||
|
let mut args = Some(args.into_lua_multi(self)?);
|
||||||
|
future::poll_fn(move |_cx| match args.take() {
|
||||||
|
Some(args) => unsafe {
|
||||||
|
let lua = self.lock();
|
||||||
|
lua.push(Self::poll_yield())?; // yield marker
|
||||||
|
if args.len() <= 1 {
|
||||||
|
lua.push(args.front())?;
|
||||||
|
} else {
|
||||||
|
lua.push(lua.create_sequence_from(&args)?)?;
|
||||||
|
}
|
||||||
|
lua.push(args.len())?;
|
||||||
|
Poll::Pending
|
||||||
|
},
|
||||||
|
None => unsafe {
|
||||||
|
let lua = self.lock();
|
||||||
|
let state = lua.state();
|
||||||
|
let top = ffi::lua_gettop(state);
|
||||||
|
if top == 0 || ffi::lua_type(state, 1) != ffi::LUA_TUSERDATA {
|
||||||
|
// This must be impossible scenario if used correctly
|
||||||
|
return Poll::Ready(R::from_stack_multi(0, &lua));
|
||||||
|
}
|
||||||
|
let _sg = StackGuard::with_top(state, 1);
|
||||||
|
Poll::Ready(R::from_stack_multi(top - 1, &lua))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a weak reference to the Lua instance.
|
/// Returns a weak reference to the Lua instance.
|
||||||
///
|
///
|
||||||
/// This is useful for creating a reference to the Lua instance that does not prevent it from
|
/// This is useful for creating a reference to the Lua instance that does not prevent it from
|
||||||
@@ -2045,7 +2273,6 @@ impl Lua {
|
|||||||
WeakLua(XRc::downgrade(&self.raw))
|
WeakLua(XRc::downgrade(&self.raw))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Luau version located in `luau/mod.rs`
|
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
fn disable_c_modules(&self) -> Result<()> {
|
fn disable_c_modules(&self) -> Result<()> {
|
||||||
let package: Table = self.globals().get("package")?;
|
let package: Table = self.globals().get("package")?;
|
||||||
@@ -2059,7 +2286,7 @@ impl Lua {
|
|||||||
})?,
|
})?,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||||
let searchers: Table = package.get("searchers")?;
|
let searchers: Table = package.get("searchers")?;
|
||||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||||
let searchers: Table = package.get("loaders")?;
|
let searchers: Table = package.get("loaders")?;
|
||||||
@@ -2068,13 +2295,15 @@ impl Lua {
|
|||||||
|
|
||||||
// The third and fourth searchers looks for a loader as a C library
|
// The third and fourth searchers looks for a loader as a C library
|
||||||
searchers.raw_set(3, loader)?;
|
searchers.raw_set(3, loader)?;
|
||||||
searchers.raw_remove(4)?;
|
if searchers.raw_len() >= 4 {
|
||||||
|
searchers.raw_remove(4)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn lock(&self) -> ReentrantMutexGuard<RawLua> {
|
pub(crate) fn lock(&self) -> ReentrantMutexGuard<'_, RawLua> {
|
||||||
let rawlua = self.raw.lock();
|
let rawlua = self.raw.lock();
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
if unsafe { (*rawlua.extra.get()).running_gc } {
|
if unsafe { (*rawlua.extra.get()).running_gc } {
|
||||||
|
|||||||
+43
-9
@@ -14,7 +14,7 @@ use crate::state::RawLua;
|
|||||||
use crate::stdlib::StdLib;
|
use crate::stdlib::StdLib;
|
||||||
use crate::types::{AppData, ReentrantMutex, XRc};
|
use crate::types::{AppData, ReentrantMutex, XRc};
|
||||||
use crate::userdata::RawUserDataRegistry;
|
use crate::userdata::RawUserDataRegistry;
|
||||||
use crate::util::{get_internal_metatable, push_internal_userdata, TypeKey, WrappedFailure};
|
use crate::util::{TypeKey, WrappedFailure, get_internal_metatable, push_internal_userdata};
|
||||||
|
|
||||||
#[cfg(any(feature = "luau", doc))]
|
#[cfg(any(feature = "luau", doc))]
|
||||||
use crate::chunk::Compiler;
|
use crate::chunk::Compiler;
|
||||||
@@ -28,7 +28,7 @@ use super::{Lua, WeakLua};
|
|||||||
static EXTRA_REGISTRY_KEY: u8 = 0;
|
static EXTRA_REGISTRY_KEY: u8 = 0;
|
||||||
|
|
||||||
const WRAPPED_FAILURE_POOL_DEFAULT_CAPACITY: usize = 64;
|
const WRAPPED_FAILURE_POOL_DEFAULT_CAPACITY: usize = 64;
|
||||||
const REF_STACK_RESERVE: c_int = 2;
|
const REF_STACK_RESERVE: c_int = 3;
|
||||||
|
|
||||||
/// Data associated with the Lua state.
|
/// Data associated with the Lua state.
|
||||||
pub(crate) struct ExtraData {
|
pub(crate) struct ExtraData {
|
||||||
@@ -44,8 +44,9 @@ pub(crate) struct ExtraData {
|
|||||||
// When Lua instance dropped, setting `None` would prevent collecting `RegistryKey`s
|
// When Lua instance dropped, setting `None` would prevent collecting `RegistryKey`s
|
||||||
pub(super) registry_unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
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: AppData,
|
||||||
|
pub(super) app_data_priv: AppData,
|
||||||
|
|
||||||
pub(super) safe: bool,
|
pub(super) safe: bool,
|
||||||
pub(super) libs: StdLib,
|
pub(super) libs: StdLib,
|
||||||
@@ -63,7 +64,7 @@ pub(crate) struct ExtraData {
|
|||||||
pub(super) wrapped_failure_top: usize,
|
pub(super) wrapped_failure_top: usize,
|
||||||
// Pool of `Thread`s (coroutines) for async execution
|
// Pool of `Thread`s (coroutines) for async execution
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
pub(super) thread_pool: Vec<c_int>,
|
pub(super) thread_pool: Vec<crate::types::ValueRefIndex>,
|
||||||
|
|
||||||
// Address of `WrappedFailure` metatable
|
// Address of `WrappedFailure` metatable
|
||||||
pub(super) wrapped_failure_mt_ptr: *const c_void,
|
pub(super) wrapped_failure_mt_ptr: *const c_void,
|
||||||
@@ -75,8 +76,8 @@ pub(crate) struct ExtraData {
|
|||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
pub(super) hook_callback: Option<crate::types::HookCallback>,
|
pub(super) hook_callback: Option<crate::types::HookCallback>,
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
pub(super) hook_triggers: crate::hook::HookTriggers,
|
pub(super) hook_triggers: crate::debug::HookTriggers,
|
||||||
#[cfg(feature = "lua54")]
|
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||||
pub(super) warn_callback: Option<crate::types::WarnCallback>,
|
pub(super) warn_callback: Option<crate::types::WarnCallback>,
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
pub(super) interrupt_callback: Option<crate::types::InterruptCallback>,
|
pub(super) interrupt_callback: Option<crate::types::InterruptCallback>,
|
||||||
@@ -88,11 +89,13 @@ pub(crate) struct ExtraData {
|
|||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
pub(crate) running_gc: bool,
|
pub(crate) running_gc: bool,
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
pub(super) sandboxed: bool,
|
pub(crate) sandboxed: bool,
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
pub(super) compiler: Option<Compiler>,
|
pub(super) compiler: Option<Compiler>,
|
||||||
#[cfg(feature = "luau-jit")]
|
#[cfg(feature = "luau-jit")]
|
||||||
pub(super) enable_jit: bool,
|
pub(super) enable_jit: bool,
|
||||||
|
#[cfg(feature = "luau")]
|
||||||
|
pub(crate) mem_categories: Vec<std::ffi::CString>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for ExtraData {
|
impl Drop for ExtraData {
|
||||||
@@ -159,6 +162,7 @@ impl ExtraData {
|
|||||||
last_checked_userdata_mt: (ptr::null(), None),
|
last_checked_userdata_mt: (ptr::null(), None),
|
||||||
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
|
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
|
||||||
app_data: AppData::default(),
|
app_data: AppData::default(),
|
||||||
|
app_data_priv: AppData::default(),
|
||||||
safe: false,
|
safe: false,
|
||||||
libs: StdLib::NONE,
|
libs: StdLib::NONE,
|
||||||
skip_memory_check: false,
|
skip_memory_check: false,
|
||||||
@@ -178,7 +182,7 @@ impl ExtraData {
|
|||||||
hook_callback: None,
|
hook_callback: None,
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
hook_triggers: Default::default(),
|
hook_triggers: Default::default(),
|
||||||
#[cfg(feature = "lua54")]
|
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||||
warn_callback: None,
|
warn_callback: None,
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
interrupt_callback: None,
|
interrupt_callback: None,
|
||||||
@@ -194,6 +198,8 @@ impl ExtraData {
|
|||||||
enable_jit: true,
|
enable_jit: true,
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
running_gc: false,
|
running_gc: false,
|
||||||
|
#[cfg(feature = "luau")]
|
||||||
|
mem_categories: vec![std::ffi::CString::new("main").unwrap()],
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Store it in the registry
|
// Store it in the registry
|
||||||
@@ -210,7 +216,7 @@ impl ExtraData {
|
|||||||
self.weak.write(WeakLua(XRc::downgrade(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")]
|
#[cfg(feature = "luau")]
|
||||||
if cfg!(not(feature = "module")) {
|
if cfg!(not(feature = "module")) {
|
||||||
// In the main app we can use `lua_callbacks` to access ExtraData
|
// In the main app we can use `lua_callbacks` to access ExtraData
|
||||||
@@ -257,4 +263,32 @@ impl ExtraData {
|
|||||||
pub(super) unsafe fn weak(&self) -> &WeakLua {
|
pub(super) unsafe fn weak(&self) -> &WeakLua {
|
||||||
self.weak.assume_init_ref()
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+243
-124
@@ -5,32 +5,31 @@ use std::mem;
|
|||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
use std::panic::resume_unwind;
|
use std::panic::resume_unwind;
|
||||||
use std::ptr::{self, NonNull};
|
use std::ptr::{self, NonNull};
|
||||||
use std::result::Result as StdResult;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::chunk::ChunkMode;
|
use crate::chunk::ChunkMode;
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::function::Function;
|
use crate::function::Function;
|
||||||
use crate::memory::{MemoryState, ALLOCATOR};
|
use crate::memory::{ALLOCATOR, MemoryState};
|
||||||
use crate::state::util::{callback_error_ext, ref_stack_pop};
|
use crate::state::util::callback_error_ext;
|
||||||
use crate::stdlib::StdLib;
|
use crate::stdlib::StdLib;
|
||||||
use crate::string::String;
|
use crate::string::LuaString;
|
||||||
use crate::table::Table;
|
use crate::table::Table;
|
||||||
use crate::thread::Thread;
|
use crate::thread::Thread;
|
||||||
use crate::traits::IntoLua;
|
use crate::traits::IntoLua;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
AppDataRef, AppDataRefMut, Callback, CallbackUpvalue, DestructedUserdata, Integer, LightUserData,
|
AppDataRef, AppDataRefMut, Callback, CallbackUpvalue, DestructedUserdata, Integer, LightUserData,
|
||||||
MaybeSend, ReentrantMutex, RegistryKey, ValueRef, XRc,
|
LuaType, MaybeSend, ReentrantMutex, RegistryKey, ValueRef, XRc,
|
||||||
};
|
};
|
||||||
use crate::userdata::{
|
use crate::userdata::{
|
||||||
init_userdata_metatable, AnyUserData, MetaMethod, RawUserDataRegistry, UserData, UserDataRegistry,
|
AnyUserData, MetaMethod, RawUserDataRegistry, UserData, UserDataRegistry, UserDataStorage,
|
||||||
UserDataStorage,
|
init_userdata_metatable,
|
||||||
};
|
};
|
||||||
use crate::util::{
|
use crate::util::{
|
||||||
assert_stack, check_stack, get_destructed_userdata_metatable, get_internal_userdata, get_main_state,
|
StackGuard, WrappedFailure, assert_stack, check_stack, get_destructed_userdata_metatable,
|
||||||
get_metatable_ptr, get_userdata, init_error_registry, init_internal_metatable, pop_error,
|
get_internal_userdata, get_main_state, get_metatable_ptr, get_userdata, init_error_registry,
|
||||||
push_internal_userdata, push_string, push_table, rawset_field, safe_pcall, safe_xpcall, short_type_name,
|
init_internal_metatable, pop_error, push_internal_userdata, push_string, push_table, push_userdata,
|
||||||
StackGuard, WrappedFailure,
|
rawset_field, safe_pcall, safe_xpcall, short_type_name,
|
||||||
};
|
};
|
||||||
use crate::value::{Nil, Value};
|
use crate::value::{Nil, Value};
|
||||||
|
|
||||||
@@ -39,7 +38,7 @@ use super::{Lua, LuaOptions, WeakLua};
|
|||||||
|
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
use crate::{
|
use crate::{
|
||||||
hook::Debug,
|
debug::Debug,
|
||||||
types::{HookCallback, HookKind, VmState},
|
types::{HookCallback, HookKind, VmState},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -123,6 +122,12 @@ impl RawLua {
|
|||||||
|
|
||||||
pub(super) unsafe fn new(libs: StdLib, options: &LuaOptions) -> XRc<ReentrantMutex<Self>> {
|
pub(super) unsafe fn new(libs: StdLib, options: &LuaOptions) -> XRc<ReentrantMutex<Self>> {
|
||||||
let mem_state: *mut MemoryState = Box::into_raw(Box::default());
|
let mem_state: *mut MemoryState = Box::into_raw(Box::default());
|
||||||
|
#[cfg(feature = "lua55")]
|
||||||
|
let mut state = {
|
||||||
|
let seed = ffi::luaL_makeseed(ptr::null_mut());
|
||||||
|
ffi::lua_newstate(ALLOCATOR, mem_state as *mut c_void, seed)
|
||||||
|
};
|
||||||
|
#[cfg(not(feature = "lua55"))]
|
||||||
let mut state = ffi::lua_newstate(ALLOCATOR, mem_state as *mut c_void);
|
let mut state = ffi::lua_newstate(ALLOCATOR, mem_state as *mut c_void);
|
||||||
// If state is null then switch to Lua internal allocator
|
// If state is null then switch to Lua internal allocator
|
||||||
if state.is_null() {
|
if state.is_null() {
|
||||||
@@ -154,7 +159,7 @@ impl RawLua {
|
|||||||
(|| -> Result<()> {
|
(|| -> Result<()> {
|
||||||
let _sg = StackGuard::new(state);
|
let _sg = StackGuard::new(state);
|
||||||
|
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||||
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS);
|
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS);
|
||||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||||
ffi::lua_pushvalue(state, ffi::LUA_GLOBALSINDEX);
|
ffi::lua_pushvalue(state, ffi::LUA_GLOBALSINDEX);
|
||||||
@@ -209,7 +214,7 @@ impl RawLua {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Init serde metatables
|
// Init serde metatables
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
crate::serde::init_metatables(state)?;
|
crate::serde::init_metatables(state)?;
|
||||||
|
|
||||||
Ok::<_, Error>(())
|
Ok::<_, Error>(())
|
||||||
@@ -297,7 +302,7 @@ impl RawLua {
|
|||||||
if is_safe {
|
if is_safe {
|
||||||
let curr_libs = (*self.extra.get()).libs;
|
let curr_libs = (*self.extra.get()).libs;
|
||||||
if (curr_libs ^ (curr_libs | libs)).contains(StdLib::PACKAGE) {
|
if (curr_libs ^ (curr_libs | libs)).contains(StdLib::PACKAGE) {
|
||||||
mlua_expect!(self.lua().disable_c_modules(), "Error during disabling C modules");
|
mlua_expect!(self.lua().disable_c_modules(), "Error disabling C modules");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
@@ -307,27 +312,27 @@ impl RawLua {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See [`Lua::try_set_app_data`]
|
/// Private version of [`Lua::try_set_app_data`]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn try_set_app_data<T: MaybeSend + 'static>(&self, data: T) -> StdResult<Option<T>, T> {
|
pub(crate) fn set_priv_app_data<T: MaybeSend + 'static>(&self, data: T) -> Option<T> {
|
||||||
let extra = unsafe { &*self.extra.get() };
|
let extra = unsafe { &*self.extra.get() };
|
||||||
extra.app_data.try_insert(data)
|
extra.app_data_priv.insert(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See [`Lua::app_data_ref`]
|
/// Private version of [`Lua::app_data_ref`]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn app_data_ref_unguarded<T: 'static>(&self) -> Option<AppDataRef<T>> {
|
pub(crate) fn priv_app_data_ref<T: 'static>(&self) -> Option<AppDataRef<'_, T>> {
|
||||||
let extra = unsafe { &*self.extra.get() };
|
let extra = unsafe { &*self.extra.get() };
|
||||||
extra.app_data.borrow(None)
|
extra.app_data_priv.borrow(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See [`Lua::app_data_mut`]
|
/// Private version of [`Lua::app_data_mut`]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn app_data_mut_unguarded<T: 'static>(&self) -> Option<AppDataRefMut<T>> {
|
pub(crate) fn priv_app_data_mut<T: 'static>(&self) -> Option<AppDataRefMut<'_, T>> {
|
||||||
let extra = unsafe { &*self.extra.get() };
|
let extra = unsafe { &*self.extra.get() };
|
||||||
extra.app_data.borrow_mut(None)
|
extra.app_data_priv.borrow_mut(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See [`Lua::create_registry_value`]
|
/// See [`Lua::create_registry_value`]
|
||||||
@@ -417,7 +422,7 @@ impl RawLua {
|
|||||||
VmState::Yield => {
|
VmState::Yield => {
|
||||||
// Only count and line events can yield
|
// Only count and line events can yield
|
||||||
if event == ffi::LUA_HOOKCOUNT || event == ffi::LUA_HOOKLINE {
|
if event == ffi::LUA_HOOKCOUNT || event == ffi::LUA_HOOKLINE {
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||||
if ffi::lua_isyieldable(state) != 0 {
|
if ffi::lua_isyieldable(state) != 0 {
|
||||||
ffi::lua_yield(state, 0);
|
ffi::lua_yield(state, 0);
|
||||||
}
|
}
|
||||||
@@ -436,8 +441,8 @@ impl RawLua {
|
|||||||
match (*extra).hook_callback.clone() {
|
match (*extra).hook_callback.clone() {
|
||||||
Some(hook_callback) => {
|
Some(hook_callback) => {
|
||||||
let rawlua = (*extra).raw_lua();
|
let rawlua = (*extra).raw_lua();
|
||||||
let debug = Debug::new(rawlua, ar);
|
let debug = Debug::new(rawlua, 0, ar);
|
||||||
hook_callback((*extra).lua(), debug)
|
hook_callback((*extra).lua(), &debug)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
ffi::lua_sethook(state, None, 0, 0);
|
ffi::lua_sethook(state, None, 0, 0);
|
||||||
@@ -466,9 +471,9 @@ impl RawLua {
|
|||||||
|
|
||||||
let status = callback_error_ext(state, ptr::null_mut(), false, |extra, _| {
|
let status = callback_error_ext(state, ptr::null_mut(), false, |extra, _| {
|
||||||
let rawlua = (*extra).raw_lua();
|
let rawlua = (*extra).raw_lua();
|
||||||
let debug = Debug::new(rawlua, ar);
|
let debug = Debug::new(rawlua, 0, ar);
|
||||||
let hook_callback = (*hook_callback_ptr).clone();
|
let hook_callback = (*hook_callback_ptr).clone();
|
||||||
hook_callback((*extra).lua(), debug)
|
hook_callback((*extra).lua(), &debug)
|
||||||
});
|
});
|
||||||
process_status(state, (*ar).event, status)
|
process_status(state, (*ar).event, status)
|
||||||
}
|
}
|
||||||
@@ -511,17 +516,48 @@ impl RawLua {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// See [`Lua::create_string`]
|
/// See [`Lua::create_string`]
|
||||||
pub(crate) unsafe fn create_string(&self, s: impl AsRef<[u8]>) -> Result<String> {
|
pub(crate) unsafe fn create_string(&self, s: &[u8]) -> Result<LuaString> {
|
||||||
let state = self.state();
|
let state = self.state();
|
||||||
if self.unlikely_memory_error() {
|
if self.unlikely_memory_error() {
|
||||||
push_string(state, s.as_ref(), false)?;
|
push_string(state, s, false)?;
|
||||||
return Ok(String(self.pop_ref()));
|
return Ok(LuaString(self.pop_ref()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let _sg = StackGuard::new(state);
|
let _sg = StackGuard::new(state);
|
||||||
check_stack(state, 3)?;
|
check_stack(state, 3)?;
|
||||||
push_string(state, s.as_ref(), true)?;
|
push_string(state, s, true)?;
|
||||||
Ok(String(self.pop_ref()))
|
Ok(LuaString(self.pop_ref()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an external string, that is, a string that uses memory not managed by Lua.
|
||||||
|
///
|
||||||
|
/// Modifies the input data to add `\0` terminator.
|
||||||
|
#[cfg(feature = "lua55")]
|
||||||
|
pub(crate) unsafe fn create_external_string(&self, bytes: Vec<u8>) -> Result<LuaString> {
|
||||||
|
let state = self.state();
|
||||||
|
if self.unlikely_memory_error() {
|
||||||
|
crate::util::push_external_string(state, bytes, false)?;
|
||||||
|
return Ok(LuaString(self.pop_ref()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let _sg = StackGuard::new(state);
|
||||||
|
check_stack(state, 3)?;
|
||||||
|
crate::util::push_external_string(state, bytes, true)?;
|
||||||
|
Ok(LuaString(self.pop_ref()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "luau")]
|
||||||
|
pub(crate) unsafe fn create_buffer_with_capacity(&self, size: usize) -> Result<(*mut u8, crate::Buffer)> {
|
||||||
|
let state = self.state();
|
||||||
|
if self.unlikely_memory_error() {
|
||||||
|
let ptr = crate::util::push_buffer(state, size, false)?;
|
||||||
|
return Ok((ptr, crate::Buffer(self.pop_ref())));
|
||||||
|
}
|
||||||
|
|
||||||
|
let _sg = StackGuard::new(state);
|
||||||
|
check_stack(state, 3)?;
|
||||||
|
let ptr = crate::util::push_buffer(state, size, true)?;
|
||||||
|
Ok((ptr, crate::Buffer(self.pop_ref())))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See [`Lua::create_table_with_capacity`]
|
/// See [`Lua::create_table_with_capacity`]
|
||||||
@@ -538,6 +574,34 @@ impl RawLua {
|
|||||||
Ok(Table(self.pop_ref()))
|
Ok(Table(self.pop_ref()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// See [`Lua::create_table_from`]
|
||||||
|
pub(crate) unsafe fn create_table_from<I, K, V>(&self, iter: I) -> Result<Table>
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = (K, V)>,
|
||||||
|
K: IntoLua,
|
||||||
|
V: IntoLua,
|
||||||
|
{
|
||||||
|
let state = self.state();
|
||||||
|
let _sg = StackGuard::new(state);
|
||||||
|
check_stack(state, 6)?;
|
||||||
|
|
||||||
|
let iter = iter.into_iter();
|
||||||
|
let lower_bound = iter.size_hint().0;
|
||||||
|
let protect = !self.unlikely_memory_error();
|
||||||
|
push_table(state, 0, lower_bound, protect)?;
|
||||||
|
for (k, v) in iter {
|
||||||
|
self.push(k)?;
|
||||||
|
self.push(v)?;
|
||||||
|
if protect {
|
||||||
|
protect_lua!(state, 3, 1, fn(state) ffi::lua_rawset(state, -3))?;
|
||||||
|
} else {
|
||||||
|
ffi::lua_rawset(state, -3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Table(self.pop_ref()))
|
||||||
|
}
|
||||||
|
|
||||||
/// See [`Lua::create_sequence_from`]
|
/// See [`Lua::create_sequence_from`]
|
||||||
pub(crate) unsafe fn create_sequence_from<T, I>(&self, iter: I) -> Result<Table>
|
pub(crate) unsafe fn create_sequence_from<T, I>(&self, iter: I) -> Result<Table>
|
||||||
where
|
where
|
||||||
@@ -597,7 +661,7 @@ impl RawLua {
|
|||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
pub(crate) unsafe fn create_recycled_thread(&self, func: &Function) -> Result<Thread> {
|
pub(crate) unsafe fn create_recycled_thread(&self, func: &Function) -> Result<Thread> {
|
||||||
if let Some(index) = (*self.extra.get()).thread_pool.pop() {
|
if let Some(index) = (*self.extra.get()).thread_pool.pop() {
|
||||||
let thread_state = ffi::lua_tothread(self.ref_thread(), index);
|
let thread_state = ffi::lua_tothread(self.ref_thread(), *index.0);
|
||||||
ffi::lua_xpush(self.ref_thread(), thread_state, func.0.index);
|
ffi::lua_xpush(self.ref_thread(), thread_state, func.0.index);
|
||||||
|
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
@@ -613,63 +677,69 @@ impl RawLua {
|
|||||||
self.create_thread(func)
|
self.create_thread(func)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets thread (coroutine) and returns it to the pool for later use.
|
/// Returns the thread to the pool for later use.
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
pub(crate) unsafe fn recycle_thread(&self, thread: &mut Thread) {
|
pub(crate) unsafe fn recycle_thread(&self, thread: &mut Thread) {
|
||||||
let thread_state = thread.1;
|
|
||||||
let extra = &mut *self.extra.get();
|
let extra = &mut *self.extra.get();
|
||||||
if extra.thread_pool.len() == extra.thread_pool.capacity() {
|
if extra.thread_pool.len() < extra.thread_pool.capacity()
|
||||||
#[cfg(feature = "lua54")]
|
&& let Some(index) = thread.0.index_count.take()
|
||||||
if ffi::lua_status(thread_state) != ffi::LUA_OK {
|
{
|
||||||
// Close all to-be-closed variables without returning thread to the pool
|
extra.thread_pool.push(index);
|
||||||
#[cfg(not(feature = "vendored"))]
|
}
|
||||||
ffi::lua_resetthread(thread_state);
|
}
|
||||||
#[cfg(feature = "vendored")]
|
|
||||||
ffi::lua_closethread(thread_state, self.state());
|
/// Pushes a primitive type value onto the Lua stack.
|
||||||
|
pub(crate) unsafe fn push_primitive_type<T: LuaType>(&self) -> bool {
|
||||||
|
match T::TYPE_ID {
|
||||||
|
ffi::LUA_TBOOLEAN => {
|
||||||
|
ffi::lua_pushboolean(self.state(), 0);
|
||||||
}
|
}
|
||||||
return;
|
ffi::LUA_TLIGHTUSERDATA => {
|
||||||
}
|
ffi::lua_pushlightuserdata(self.state(), ptr::null_mut());
|
||||||
|
|
||||||
let mut reset_ok = false;
|
|
||||||
if ffi::lua_status(thread_state) == ffi::LUA_OK {
|
|
||||||
if ffi::lua_gettop(thread_state) > 0 {
|
|
||||||
ffi::lua_settop(thread_state, 0);
|
|
||||||
}
|
}
|
||||||
reset_ok = true;
|
ffi::LUA_TNUMBER => {
|
||||||
}
|
ffi::lua_pushnumber(self.state(), 0.);
|
||||||
|
}
|
||||||
#[cfg(feature = "lua54")]
|
#[cfg(feature = "luau")]
|
||||||
if !reset_ok {
|
ffi::LUA_TVECTOR => {
|
||||||
#[cfg(not(feature = "vendored"))]
|
#[cfg(not(feature = "luau-vector4"))]
|
||||||
let status = ffi::lua_resetthread(thread_state);
|
ffi::lua_pushvector(self.state(), 0., 0., 0.);
|
||||||
#[cfg(feature = "vendored")]
|
#[cfg(feature = "luau-vector4")]
|
||||||
let status = ffi::lua_closethread(thread_state, self.state());
|
ffi::lua_pushvector(self.state(), 0., 0., 0., 0.);
|
||||||
reset_ok = status == ffi::LUA_OK;
|
}
|
||||||
}
|
ffi::LUA_TSTRING => {
|
||||||
#[cfg(feature = "luau")]
|
ffi::lua_pushstring(self.state(), b"\0" as *const u8 as *const _);
|
||||||
if !reset_ok {
|
}
|
||||||
ffi::lua_resetthread(thread_state);
|
ffi::LUA_TFUNCTION => {
|
||||||
reset_ok = true;
|
unsafe extern "C-unwind" fn func(_state: *mut ffi::lua_State) -> c_int {
|
||||||
}
|
0
|
||||||
|
}
|
||||||
if reset_ok {
|
ffi::lua_pushcfunction(self.state(), func);
|
||||||
extra.thread_pool.push(thread.0.index);
|
}
|
||||||
thread.0.drop = false; // Prevent thread from being garbage collected
|
ffi::LUA_TTHREAD => {
|
||||||
|
ffi::lua_pushthread(self.state());
|
||||||
|
}
|
||||||
|
#[cfg(feature = "luau")]
|
||||||
|
ffi::LUA_TBUFFER => {
|
||||||
|
ffi::lua_newbuffer(self.state(), 0);
|
||||||
|
}
|
||||||
|
_ => return false,
|
||||||
}
|
}
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pushes a value that implements `IntoLua` onto the Lua stack.
|
/// Pushes a value that implements `IntoLua` onto the Lua stack.
|
||||||
///
|
///
|
||||||
/// Uses up to 2 stack spaces to push a single value, does not call `checkstack`.
|
/// Uses up to 2 stack spaces to push a single value, does not call `checkstack`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) unsafe fn push(&self, value: impl IntoLua) -> Result<()> {
|
pub unsafe fn push(&self, value: impl IntoLua) -> Result<()> {
|
||||||
value.push_into_stack(self)
|
value.push_into_stack(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pushes a `Value` (by reference) onto the Lua stack.
|
/// Pushes a `Value` (by reference) onto the Lua stack.
|
||||||
///
|
///
|
||||||
/// Uses 2 stack spaces, does not call `checkstack`.
|
/// Uses 2 stack spaces, does not call `checkstack`.
|
||||||
pub(crate) unsafe fn push_value(&self, value: &Value) -> Result<()> {
|
pub unsafe fn push_value(&self, value: &Value) -> Result<()> {
|
||||||
let state = self.state();
|
let state = self.state();
|
||||||
match value {
|
match value {
|
||||||
Value::Nil => ffi::lua_pushnil(state),
|
Value::Nil => ffi::lua_pushnil(state),
|
||||||
@@ -702,8 +772,9 @@ impl RawLua {
|
|||||||
|
|
||||||
/// Pops a value from the Lua stack.
|
/// Pops a value from the Lua stack.
|
||||||
///
|
///
|
||||||
/// Uses 2 stack spaces, does not call `checkstack`.
|
/// Uses up to 1 stack spaces, does not call `checkstack`.
|
||||||
pub(crate) unsafe fn pop_value(&self) -> Value {
|
#[inline]
|
||||||
|
pub unsafe fn pop_value(&self) -> Value {
|
||||||
let value = self.stack_value(-1, None);
|
let value = self.stack_value(-1, None);
|
||||||
ffi::lua_pop(self.state(), 1);
|
ffi::lua_pop(self.state(), 1);
|
||||||
value
|
value
|
||||||
@@ -711,7 +782,7 @@ impl RawLua {
|
|||||||
|
|
||||||
/// Returns value at given stack index without popping it.
|
/// Returns value at given stack index without popping it.
|
||||||
///
|
///
|
||||||
/// Uses 2 stack spaces, does not call checkstack.
|
/// Uses up to 1 stack spaces, does not call `checkstack`.
|
||||||
pub(crate) unsafe fn stack_value(&self, idx: c_int, type_hint: Option<c_int>) -> Value {
|
pub(crate) unsafe fn stack_value(&self, idx: c_int, type_hint: Option<c_int>) -> Value {
|
||||||
let state = self.state();
|
let state = self.state();
|
||||||
match type_hint.unwrap_or_else(|| ffi::lua_type(state, idx)) {
|
match type_hint.unwrap_or_else(|| ffi::lua_type(state, idx)) {
|
||||||
@@ -721,7 +792,7 @@ impl RawLua {
|
|||||||
|
|
||||||
ffi::LUA_TLIGHTUSERDATA => Value::LightUserData(LightUserData(ffi::lua_touserdata(state, idx))),
|
ffi::LUA_TLIGHTUSERDATA => Value::LightUserData(LightUserData(ffi::lua_touserdata(state, idx))),
|
||||||
|
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||||
ffi::LUA_TNUMBER => {
|
ffi::LUA_TNUMBER => {
|
||||||
if ffi::lua_isinteger(state, idx) != 0 {
|
if ffi::lua_isinteger(state, idx) != 0 {
|
||||||
Value::Integer(ffi::lua_tointeger(state, idx))
|
Value::Integer(ffi::lua_tointeger(state, idx))
|
||||||
@@ -736,7 +807,7 @@ impl RawLua {
|
|||||||
|
|
||||||
let n = ffi::lua_tonumber(state, idx);
|
let n = ffi::lua_tonumber(state, idx);
|
||||||
match num_traits::cast(n) {
|
match num_traits::cast(n) {
|
||||||
Some(i) if (n - (i as Number)).abs() < Number::EPSILON => Value::Integer(i),
|
Some(i) if n.to_bits() == (i as Number).to_bits() => Value::Integer(i),
|
||||||
_ => Value::Number(n),
|
_ => Value::Number(n),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -753,7 +824,7 @@ impl RawLua {
|
|||||||
|
|
||||||
ffi::LUA_TSTRING => {
|
ffi::LUA_TSTRING => {
|
||||||
ffi::lua_xpush(state, self.ref_thread(), idx);
|
ffi::lua_xpush(state, self.ref_thread(), idx);
|
||||||
Value::String(String(self.pop_ref_thread()))
|
Value::String(LuaString(self.pop_ref_thread()))
|
||||||
}
|
}
|
||||||
|
|
||||||
ffi::LUA_TTABLE => {
|
ffi::LUA_TTABLE => {
|
||||||
@@ -824,21 +895,14 @@ impl RawLua {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) unsafe fn pop_ref(&self) -> ValueRef {
|
pub(crate) unsafe fn pop_ref(&self) -> ValueRef {
|
||||||
ffi::lua_xmove(self.state(), self.ref_thread(), 1);
|
ffi::lua_xmove(self.state(), self.ref_thread(), 1);
|
||||||
let index = ref_stack_pop(self.extra.get());
|
let index = (*self.extra.get()).ref_stack_pop();
|
||||||
ValueRef::new(self, index)
|
ValueRef::new(self, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Same as `pop_ref` but assumes the value is already on the reference thread
|
// Same as `pop_ref` but assumes the value is already on the reference thread
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) unsafe fn pop_ref_thread(&self) -> ValueRef {
|
pub(crate) unsafe fn pop_ref_thread(&self) -> ValueRef {
|
||||||
let index = ref_stack_pop(self.extra.get());
|
let index = (*self.extra.get()).ref_stack_pop();
|
||||||
ValueRef::new(self, index)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) unsafe fn clone_ref(&self, vref: &ValueRef) -> ValueRef {
|
|
||||||
ffi::lua_pushvalue(self.ref_thread(), vref.index);
|
|
||||||
let index = ref_stack_pop(self.extra.get());
|
|
||||||
ValueRef::new(self, index)
|
ValueRef::new(self, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -859,7 +923,7 @@ impl RawLua {
|
|||||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||||
ffi::lua_xpush(self.ref_thread(), state, ExtraData::ERROR_TRACEBACK_IDX);
|
ffi::lua_xpush(self.ref_thread(), state, ExtraData::ERROR_TRACEBACK_IDX);
|
||||||
// Lua 5.2+ support light C functions that does not require extra allocations
|
// Lua 5.2+ support light C functions that does not require extra allocations
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||||
ffi::lua_pushcfunction(state, crate::util::error_traceback);
|
ffi::lua_pushcfunction(state, crate::util::error_traceback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -928,7 +992,7 @@ impl RawLua {
|
|||||||
// We generate metatable first to make sure it *always* available when userdata pushed
|
// We generate metatable first to make sure it *always* available when userdata pushed
|
||||||
let mt_id = get_metatable_id()?;
|
let mt_id = get_metatable_id()?;
|
||||||
let protect = !self.unlikely_memory_error();
|
let protect = !self.unlikely_memory_error();
|
||||||
crate::util::push_userdata(state, data, protect)?;
|
push_userdata(state, data, protect)?;
|
||||||
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, mt_id);
|
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, mt_id);
|
||||||
ffi::lua_setmetatable(state, -2);
|
ffi::lua_setmetatable(state, -2);
|
||||||
|
|
||||||
@@ -1056,6 +1120,18 @@ impl RawLua {
|
|||||||
field_setters_index = Some(ffi::lua_absindex(state, -1));
|
field_setters_index = Some(ffi::lua_absindex(state, -1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create methods namecall table
|
||||||
|
#[cfg_attr(not(feature = "luau"), allow(unused_mut))]
|
||||||
|
let mut methods_map = None;
|
||||||
|
#[cfg(feature = "luau")]
|
||||||
|
if registry.enable_namecall {
|
||||||
|
let map: &mut rustc_hash::FxHashMap<_, crate::types::CallbackPtr> =
|
||||||
|
methods_map.get_or_insert_default();
|
||||||
|
for (k, m) in ®istry.methods {
|
||||||
|
map.insert(k.as_bytes().to_vec(), &**m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut methods_index = None;
|
let mut methods_index = None;
|
||||||
let methods_nrec = registry.methods.len();
|
let methods_nrec = registry.methods.len();
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
@@ -1103,6 +1179,7 @@ impl RawLua {
|
|||||||
field_getters_index,
|
field_getters_index,
|
||||||
field_setters_index,
|
field_setters_index,
|
||||||
methods_index,
|
methods_index,
|
||||||
|
methods_map,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Update stack guard to keep metatable after return
|
// Update stack guard to keep metatable after return
|
||||||
@@ -1142,10 +1219,11 @@ impl RawLua {
|
|||||||
Ok(type_id) => Ok(type_id),
|
Ok(type_id) => Ok(type_id),
|
||||||
Err(Error::UserDataTypeMismatch) if ffi::lua_type(state, idx) != ffi::LUA_TUSERDATA => {
|
Err(Error::UserDataTypeMismatch) if ffi::lua_type(state, idx) != ffi::LUA_TUSERDATA => {
|
||||||
// Report `FromLuaConversionError` instead
|
// Report `FromLuaConversionError` instead
|
||||||
let idx_type_name = CStr::from_ptr(ffi::luaL_typename(state, idx));
|
let type_name = CStr::from_ptr(ffi::lua_typename(state, ffi::lua_type(state, idx)))
|
||||||
let idx_type_name = idx_type_name.to_str().unwrap();
|
.to_str()
|
||||||
|
.unwrap_or("unknown");
|
||||||
let message = format!("expected userdata of type '{}'", short_type_name::<T>());
|
let message = format!("expected userdata of type '{}'", short_type_name::<T>());
|
||||||
Err(Error::from_lua_conversion(idx_type_name, "userdata", message))
|
Err(Error::from_lua_conversion(type_name, "userdata", message))
|
||||||
}
|
}
|
||||||
Err(err) => Err(err),
|
Err(err) => Err(err),
|
||||||
}
|
}
|
||||||
@@ -1226,7 +1304,13 @@ impl RawLua {
|
|||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
pub(crate) fn create_async_callback(&self, func: AsyncCallback) -> Result<Function> {
|
pub(crate) fn create_async_callback(&self, func: AsyncCallback) -> Result<Function> {
|
||||||
// Ensure that the coroutine library is loaded
|
// Ensure that the coroutine library is loaded
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
#[cfg(any(
|
||||||
|
feature = "lua55",
|
||||||
|
feature = "lua54",
|
||||||
|
feature = "lua53",
|
||||||
|
feature = "lua52",
|
||||||
|
feature = "luau"
|
||||||
|
))]
|
||||||
unsafe {
|
unsafe {
|
||||||
if !(*self.extra.get()).libs.contains(StdLib::COROUTINE) {
|
if !(*self.extra.get()).libs.contains(StdLib::COROUTINE) {
|
||||||
load_std_libs(self.main_state(), StdLib::COROUTINE)?;
|
load_std_libs(self.main_state(), StdLib::COROUTINE)?;
|
||||||
@@ -1234,7 +1318,7 @@ impl RawLua {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C-unwind" fn call_callback(state: *mut ffi::lua_State) -> c_int {
|
unsafe extern "C-unwind" fn get_future_callback(state: *mut ffi::lua_State) -> c_int {
|
||||||
// Async functions cannot be scoped and therefore destroyed,
|
// Async functions cannot be scoped and therefore destroyed,
|
||||||
// so the first upvalue is always valid
|
// so the first upvalue is always valid
|
||||||
let upvalue = get_userdata::<AsyncCallbackUpvalue>(state, ffi::lua_upvalueindex(1));
|
let upvalue = get_userdata::<AsyncCallbackUpvalue>(state, ffi::lua_upvalueindex(1));
|
||||||
@@ -1244,38 +1328,46 @@ impl RawLua {
|
|||||||
let rawlua = (*extra).raw_lua();
|
let rawlua = (*extra).raw_lua();
|
||||||
|
|
||||||
let func = &*(*upvalue).data;
|
let func = &*(*upvalue).data;
|
||||||
let fut = func(rawlua, nargs);
|
let fut = Some(func(rawlua, nargs));
|
||||||
let extra = XRc::clone(&(*upvalue).extra);
|
let extra = XRc::clone(&(*upvalue).extra);
|
||||||
let protect = !rawlua.unlikely_memory_error();
|
let protect = !rawlua.unlikely_memory_error();
|
||||||
push_internal_userdata(state, AsyncPollUpvalue { data: fut, extra }, protect)?;
|
push_internal_userdata(state, AsyncPollUpvalue { data: fut, extra }, protect)?;
|
||||||
if protect {
|
|
||||||
protect_lua!(state, 1, 1, fn(state) {
|
|
||||||
ffi::lua_pushcclosure(state, poll_future, 1);
|
|
||||||
})?;
|
|
||||||
} else {
|
|
||||||
ffi::lua_pushcclosure(state, poll_future, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(1)
|
Ok(1)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C-unwind" fn poll_future(state: *mut ffi::lua_State) -> c_int {
|
unsafe extern "C-unwind" fn poll_future(state: *mut ffi::lua_State) -> c_int {
|
||||||
let upvalue = get_userdata::<AsyncPollUpvalue>(state, ffi::lua_upvalueindex(1));
|
// Future is always passed in the first argument
|
||||||
callback_error_ext(state, (*upvalue).extra.get(), true, |extra, _| {
|
let future = get_userdata::<AsyncPollUpvalue>(state, 1);
|
||||||
|
callback_error_ext(state, (*future).extra.get(), true, |extra, nargs| {
|
||||||
// Lua ensures that `LUA_MINSTACK` stack spaces are available (after pushing arguments)
|
// Lua ensures that `LUA_MINSTACK` stack spaces are available (after pushing arguments)
|
||||||
// The lock must be already held as the future is polled
|
// The lock must be already held as the future is polled
|
||||||
let rawlua = (*extra).raw_lua();
|
let rawlua = (*extra).raw_lua();
|
||||||
|
|
||||||
let fut = &mut (*upvalue).data;
|
if nargs == 2 && ffi::lua_tolightuserdata(state, -1) == Lua::poll_terminate().0 {
|
||||||
|
// Destroy the future and terminate the Lua thread
|
||||||
|
(*future).data.take();
|
||||||
|
ffi::lua_pushinteger(state, -1);
|
||||||
|
return Ok(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let fut = &mut (*future).data;
|
||||||
let mut ctx = Context::from_waker(rawlua.waker());
|
let mut ctx = Context::from_waker(rawlua.waker());
|
||||||
match fut.as_mut().poll(&mut ctx) {
|
match fut.as_mut().map(|fut| fut.as_mut().poll(&mut ctx)) {
|
||||||
Poll::Pending => {
|
Some(Poll::Pending) => {
|
||||||
|
let fut_nvals = ffi::lua_gettop(state) - 1; // Exclude the future itself
|
||||||
|
if fut_nvals >= 3 && ffi::lua_tolightuserdata(state, -3) == Lua::poll_yield().0 {
|
||||||
|
// We have some values to yield
|
||||||
|
ffi::lua_pushnil(state);
|
||||||
|
ffi::lua_replace(state, -4);
|
||||||
|
return Ok(3);
|
||||||
|
}
|
||||||
ffi::lua_pushnil(state);
|
ffi::lua_pushnil(state);
|
||||||
ffi::lua_pushlightuserdata(state, Lua::poll_pending().0);
|
ffi::lua_pushlightuserdata(state, Lua::poll_pending().0);
|
||||||
Ok(2)
|
Ok(2)
|
||||||
}
|
}
|
||||||
Poll::Ready(nresults) => {
|
Some(Poll::Ready(nresults)) => {
|
||||||
match nresults? {
|
match nresults? {
|
||||||
nresults if nresults < 3 => {
|
nresults if nresults < 3 => {
|
||||||
// Fast path for up to 2 results without creating a table
|
// Fast path for up to 2 results without creating a table
|
||||||
@@ -1293,12 +1385,13 @@ impl RawLua {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
None => Err(Error::CallbackDestructed),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let state = self.state();
|
let state = self.state();
|
||||||
let get_poll = unsafe {
|
let get_future = unsafe {
|
||||||
let _sg = StackGuard::new(state);
|
let _sg = StackGuard::new(state);
|
||||||
check_stack(state, 4)?;
|
check_stack(state, 4)?;
|
||||||
|
|
||||||
@@ -1308,10 +1401,10 @@ impl RawLua {
|
|||||||
push_internal_userdata(state, upvalue, protect)?;
|
push_internal_userdata(state, upvalue, protect)?;
|
||||||
if protect {
|
if protect {
|
||||||
protect_lua!(state, 1, 1, fn(state) {
|
protect_lua!(state, 1, 1, fn(state) {
|
||||||
ffi::lua_pushcclosure(state, call_callback, 1);
|
ffi::lua_pushcclosure(state, get_future_callback, 1);
|
||||||
})?;
|
})?;
|
||||||
} else {
|
} else {
|
||||||
ffi::lua_pushcclosure(state, call_callback, 1);
|
ffi::lua_pushcclosure(state, get_future_callback, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Function(self.pop_ref())
|
Function(self.pop_ref())
|
||||||
@@ -1330,16 +1423,19 @@ impl RawLua {
|
|||||||
let coroutine = lua.globals().get::<Table>("coroutine")?;
|
let coroutine = lua.globals().get::<Table>("coroutine")?;
|
||||||
|
|
||||||
// Prepare environment for the async poller
|
// Prepare environment for the async poller
|
||||||
let env = lua.create_table_with_capacity(0, 3)?;
|
let env = lua.create_table_with_capacity(0, 4)?;
|
||||||
env.set("get_poll", get_poll)?;
|
env.set("get_future", get_future)?;
|
||||||
|
env.set("poll", unsafe { lua.create_c_function(poll_future)? })?;
|
||||||
env.set("yield", coroutine.get::<Function>("yield")?)?;
|
env.set("yield", coroutine.get::<Function>("yield")?)?;
|
||||||
env.set("unpack", unsafe { lua.create_c_function(unpack)? })?;
|
env.set("unpack", unsafe { lua.create_c_function(unpack)? })?;
|
||||||
|
|
||||||
lua.load(
|
lua.load(
|
||||||
r#"
|
r#"
|
||||||
local poll = get_poll(...)
|
local poll, yield = poll, yield
|
||||||
|
local future = get_future(...)
|
||||||
|
local nres, res, res2 = poll(future)
|
||||||
while true do
|
while true do
|
||||||
local nres, res, res2 = poll()
|
-- Poll::Ready branch, `nres` is the number of results
|
||||||
if nres ~= nil then
|
if nres ~= nil then
|
||||||
if nres == 0 then
|
if nres == 0 then
|
||||||
return
|
return
|
||||||
@@ -1347,11 +1443,28 @@ impl RawLua {
|
|||||||
return res
|
return res
|
||||||
elseif nres == 2 then
|
elseif nres == 2 then
|
||||||
return res, res2
|
return res, res2
|
||||||
|
elseif nres < 0 then
|
||||||
|
-- Negative `nres` means that the future is terminated
|
||||||
|
-- It must stay yielded and never be resumed again
|
||||||
|
yield()
|
||||||
else
|
else
|
||||||
return unpack(res, nres)
|
return unpack(res, nres)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
yield(res) -- `res` is a "pending" value
|
|
||||||
|
-- Poll::Pending branch
|
||||||
|
if res2 == nil then
|
||||||
|
-- `res` is a "pending" value
|
||||||
|
-- `yield` can return a signal to drop the future that we should propagate
|
||||||
|
-- to the poller
|
||||||
|
nres, res, res2 = poll(future, yield(res))
|
||||||
|
elseif res2 == 0 then
|
||||||
|
nres, res, res2 = poll(future, yield())
|
||||||
|
elseif res2 == 1 then
|
||||||
|
nres, res, res2 = poll(future, yield(res))
|
||||||
|
else
|
||||||
|
nres, res, res2 = poll(future, yield(unpack(res, res2)))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
@@ -1363,14 +1476,14 @@ impl RawLua {
|
|||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) unsafe fn waker(&self) -> &Waker {
|
pub(crate) fn waker(&self) -> &Waker {
|
||||||
(*self.extra.get()).waker.as_ref()
|
unsafe { (*self.extra.get()).waker.as_ref() }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) unsafe fn set_waker(&self, waker: NonNull<Waker>) -> NonNull<Waker> {
|
pub(crate) fn set_waker(&self, waker: NonNull<Waker>) -> NonNull<Waker> {
|
||||||
mem::replace(&mut (*self.extra.get()).waker, waker)
|
unsafe { mem::replace(&mut (*self.extra.get()).waker, waker) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1410,7 +1523,13 @@ unsafe fn load_std_libs(state: *mut ffi::lua_State, libs: StdLib) -> Result<()>
|
|||||||
#[cfg(feature = "luajit")]
|
#[cfg(feature = "luajit")]
|
||||||
let _gc_guard = GcGuard::new(state);
|
let _gc_guard = GcGuard::new(state);
|
||||||
|
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
#[cfg(any(
|
||||||
|
feature = "lua55",
|
||||||
|
feature = "lua54",
|
||||||
|
feature = "lua53",
|
||||||
|
feature = "lua52",
|
||||||
|
feature = "luau"
|
||||||
|
))]
|
||||||
{
|
{
|
||||||
if libs.contains(StdLib::COROUTINE) {
|
if libs.contains(StdLib::COROUTINE) {
|
||||||
requiref(state, ffi::LUA_COLIBNAME, ffi::luaopen_coroutine, 1)?;
|
requiref(state, ffi::LUA_COLIBNAME, ffi::luaopen_coroutine, 1)?;
|
||||||
@@ -1434,7 +1553,7 @@ unsafe fn load_std_libs(state: *mut ffi::lua_State, libs: StdLib) -> Result<()>
|
|||||||
requiref(state, ffi::LUA_STRLIBNAME, ffi::luaopen_string, 1)?;
|
requiref(state, ffi::LUA_STRLIBNAME, ffi::luaopen_string, 1)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||||
{
|
{
|
||||||
if libs.contains(StdLib::UTF8) {
|
if libs.contains(StdLib::UTF8) {
|
||||||
requiref(state, ffi::LUA_UTF8LIBNAME, ffi::luaopen_utf8, 1)?;
|
requiref(state, ffi::LUA_UTF8LIBNAME, ffi::luaopen_utf8, 1)?;
|
||||||
|
|||||||
+3
-31
@@ -1,11 +1,11 @@
|
|||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
use std::panic::{AssertUnwindSafe, catch_unwind};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::state::{ExtraData, RawLua};
|
use crate::state::{ExtraData, RawLua};
|
||||||
use crate::util::{self, get_internal_metatable, WrappedFailure};
|
use crate::util::{self, WrappedFailure, get_internal_metatable};
|
||||||
|
|
||||||
struct StateGuard<'a>(&'a RawLua, *mut ffi::lua_State);
|
struct StateGuard<'a>(&'a RawLua, *mut ffi::lua_State);
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ where
|
|||||||
PreallocatedFailure::New(_) => {
|
PreallocatedFailure::New(_) => {
|
||||||
ffi::lua_rotate(state, 1, -1);
|
ffi::lua_rotate(state, 1, -1);
|
||||||
ffi::lua_xmove(state, ref_thread, 1);
|
ffi::lua_xmove(state, ref_thread, 1);
|
||||||
let index = ref_stack_pop(extra);
|
let index = (*extra).ref_stack_pop();
|
||||||
(*extra).wrapped_failure_pool.push(index);
|
(*extra).wrapped_failure_pool.push(index);
|
||||||
(*extra).wrapped_failure_top += 1;
|
(*extra).wrapped_failure_top += 1;
|
||||||
}
|
}
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
|
|||||||
+19
-4
@@ -6,10 +6,22 @@ pub struct StdLib(u32);
|
|||||||
|
|
||||||
impl StdLib {
|
impl StdLib {
|
||||||
/// [`coroutine`](https://www.lua.org/manual/5.4/manual.html#6.2) library
|
/// [`coroutine`](https://www.lua.org/manual/5.4/manual.html#6.2) library
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
#[cfg(any(
|
||||||
|
feature = "lua55",
|
||||||
|
feature = "lua54",
|
||||||
|
feature = "lua53",
|
||||||
|
feature = "lua52",
|
||||||
|
feature = "luau"
|
||||||
|
))]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
docsrs,
|
docsrs,
|
||||||
doc(cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau")))
|
doc(cfg(any(
|
||||||
|
feature = "lua55",
|
||||||
|
feature = "lua54",
|
||||||
|
feature = "lua53",
|
||||||
|
feature = "lua52",
|
||||||
|
feature = "luau"
|
||||||
|
)))
|
||||||
)]
|
)]
|
||||||
pub const COROUTINE: StdLib = StdLib(1);
|
pub const COROUTINE: StdLib = StdLib(1);
|
||||||
|
|
||||||
@@ -28,8 +40,11 @@ impl StdLib {
|
|||||||
pub const STRING: StdLib = StdLib(1 << 4);
|
pub const STRING: StdLib = StdLib(1 << 4);
|
||||||
|
|
||||||
/// [`utf8`](https://www.lua.org/manual/5.4/manual.html#6.5) library
|
/// [`utf8`](https://www.lua.org/manual/5.4/manual.html#6.5) library
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))))]
|
#[cfg_attr(
|
||||||
|
docsrs,
|
||||||
|
doc(cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "luau")))
|
||||||
|
)]
|
||||||
pub const UTF8: StdLib = StdLib(1 << 5);
|
pub const UTF8: StdLib = StdLib(1 << 5);
|
||||||
|
|
||||||
/// [`bit`](https://www.lua.org/manual/5.2/manual.html#6.7) library
|
/// [`bit`](https://www.lua.org/manual/5.2/manual.html#6.7) library
|
||||||
|
|||||||
+49
-53
@@ -2,7 +2,6 @@ use std::borrow::{Borrow, Cow};
|
|||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::os::raw::{c_int, c_void};
|
use std::os::raw::{c_int, c_void};
|
||||||
use std::string::String as StdString;
|
|
||||||
use std::{cmp, fmt, slice, str};
|
use std::{cmp, fmt, slice, str};
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
@@ -11,7 +10,7 @@ use crate::traits::IntoLua;
|
|||||||
use crate::types::{LuaType, ValueRef};
|
use crate::types::{LuaType, ValueRef};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
use {
|
use {
|
||||||
serde::ser::{Serialize, Serializer},
|
serde::ser::{Serialize, Serializer},
|
||||||
std::result::Result as StdResult,
|
std::result::Result as StdResult,
|
||||||
@@ -21,37 +20,37 @@ use {
|
|||||||
///
|
///
|
||||||
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
|
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct String(pub(crate) ValueRef);
|
pub struct LuaString(pub(crate) ValueRef);
|
||||||
|
|
||||||
impl String {
|
impl LuaString {
|
||||||
/// Get a [`BorrowedStr`] if the Lua string is valid UTF-8.
|
/// Get a [`BorrowedStr`] if the Lua string is valid UTF-8.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use mlua::{Lua, Result, String};
|
/// # use mlua::{Lua, LuaString, Result};
|
||||||
/// # fn main() -> Result<()> {
|
/// # fn main() -> Result<()> {
|
||||||
/// # let lua = Lua::new();
|
/// # let lua = Lua::new();
|
||||||
/// let globals = lua.globals();
|
/// let globals = lua.globals();
|
||||||
///
|
///
|
||||||
/// let version: String = globals.get("_VERSION")?;
|
/// let version: LuaString = globals.get("_VERSION")?;
|
||||||
/// assert!(version.to_str()?.contains("Lua"));
|
/// assert!(version.to_str()?.contains("Lua"));
|
||||||
///
|
///
|
||||||
/// let non_utf8: String = lua.load(r#" "test\255" "#).eval()?;
|
/// let non_utf8: LuaString = lua.load(r#" "test\255" "#).eval()?;
|
||||||
/// assert!(non_utf8.to_str().is_err());
|
/// assert!(non_utf8.to_str().is_err());
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_str(&self) -> Result<BorrowedStr> {
|
pub fn to_str(&self) -> Result<BorrowedStr<'_>> {
|
||||||
BorrowedStr::try_from(self)
|
BorrowedStr::try_from(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts this string to a [`StdString`].
|
/// Converts this Lua string to a [`String`].
|
||||||
///
|
///
|
||||||
/// Any non-Unicode sequences are replaced with [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD].
|
/// 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
|
/// This method returns [`String`] instead of [`Cow<'_, str>`] because lifetime cannot be
|
||||||
/// bound to a weak Lua object.
|
/// bound to a weak Lua object.
|
||||||
///
|
///
|
||||||
/// [U+FFFD]: std::char::REPLACEMENT_CHARACTER
|
/// [U+FFFD]: std::char::REPLACEMENT_CHARACTER
|
||||||
@@ -70,11 +69,11 @@ impl String {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_string_lossy(&self) -> StdString {
|
pub fn to_string_lossy(&self) -> String {
|
||||||
StdString::from_utf8_lossy(&self.as_bytes()).into_owned()
|
String::from_utf8_lossy(&self.as_bytes()).into_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an object that implements [`Display`] for safely printing a Lua [`String`] that may
|
/// Returns an object that implements [`Display`] for safely printing a [`LuaString`] that may
|
||||||
/// contain non-Unicode data.
|
/// contain non-Unicode data.
|
||||||
///
|
///
|
||||||
/// This may perform lossy conversion.
|
/// This may perform lossy conversion.
|
||||||
@@ -86,35 +85,35 @@ impl String {
|
|||||||
|
|
||||||
/// Get the bytes that make up this string.
|
/// Get the bytes that make up this string.
|
||||||
///
|
///
|
||||||
/// The returned slice will not contain the terminating nul byte, but will contain any nul
|
/// The returned slice will not contain the terminating null byte, but will contain any null
|
||||||
/// bytes embedded into the Lua string.
|
/// bytes embedded into the Lua string.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use mlua::{Lua, Result, String};
|
/// # use mlua::{Lua, LuaString, Result};
|
||||||
/// # fn main() -> Result<()> {
|
/// # fn main() -> Result<()> {
|
||||||
/// # let lua = Lua::new();
|
/// # let lua = Lua::new();
|
||||||
/// let non_utf8: String = lua.load(r#" "test\255" "#).eval()?;
|
/// let non_utf8: LuaString = lua.load(r#" "test\255" "#).eval()?;
|
||||||
/// assert!(non_utf8.to_str().is_err()); // oh no :(
|
/// assert!(non_utf8.to_str().is_err()); // oh no :(
|
||||||
/// assert_eq!(non_utf8.as_bytes(), &b"test\xff"[..]);
|
/// assert_eq!(non_utf8.as_bytes(), &b"test\xff"[..]);
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_bytes(&self) -> BorrowedBytes {
|
pub fn as_bytes(&self) -> BorrowedBytes<'_> {
|
||||||
BorrowedBytes::from(self)
|
BorrowedBytes::from(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the bytes that make up this string, including the trailing nul byte.
|
/// Get the bytes that make up this string, including the trailing null byte.
|
||||||
pub fn as_bytes_with_nul(&self) -> BorrowedBytes {
|
pub fn as_bytes_with_nul(&self) -> BorrowedBytes<'_> {
|
||||||
let BorrowedBytes { buf, borrow, _lua } = BorrowedBytes::from(self);
|
let BorrowedBytes { buf, borrow, _lua } = BorrowedBytes::from(self);
|
||||||
// Include the trailing nul byte (it's always present but excluded by default)
|
// 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) };
|
let buf = unsafe { slice::from_raw_parts((*buf).as_ptr(), (*buf).len() + 1) };
|
||||||
BorrowedBytes { buf, borrow, _lua }
|
BorrowedBytes { buf, borrow, _lua }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Does not return the terminating nul byte
|
// Does not return the terminating null byte
|
||||||
unsafe fn to_slice(&self) -> (&[u8], Lua) {
|
unsafe fn to_slice(&self) -> (&[u8], Lua) {
|
||||||
let lua = self.0.lua.upgrade();
|
let lua = self.0.lua.upgrade();
|
||||||
let slice = {
|
let slice = {
|
||||||
@@ -135,7 +134,7 @@ impl String {
|
|||||||
(slice, lua)
|
(slice, lua)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts this string to a generic C pointer.
|
/// Converts this Lua string to a generic C pointer.
|
||||||
///
|
///
|
||||||
/// There is no way to convert the pointer back to its original value.
|
/// There is no way to convert the pointer back to its original value.
|
||||||
///
|
///
|
||||||
@@ -146,7 +145,7 @@ impl String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for String {
|
impl fmt::Debug for LuaString {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let bytes = self.as_bytes();
|
let bytes = self.as_bytes();
|
||||||
// Check if the string is valid utf8
|
// Check if the string is valid utf8
|
||||||
@@ -162,12 +161,12 @@ impl fmt::Debug for String {
|
|||||||
|
|
||||||
// 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`.
|
// This makes our `LuaString` comparable with `Vec<u8>`, `[u8]`, `&str` and `String`.
|
||||||
//
|
//
|
||||||
// The only downside is that this disallows a comparison with `Cow<str>`, as that only implements
|
// The only downside is that this disallows a comparison with `Cow<str>`, as that only implements
|
||||||
// `AsRef<str>`, which collides with this impl. Requiring `AsRef<str>` would fix that, but limit us
|
// `AsRef<str>`, which collides with this impl. Requiring `AsRef<str>` would fix that, but limit us
|
||||||
// in other ways.
|
// in other ways.
|
||||||
impl<T> PartialEq<T> for String
|
impl<T> PartialEq<T> for LuaString
|
||||||
where
|
where
|
||||||
T: AsRef<[u8]> + ?Sized,
|
T: AsRef<[u8]> + ?Sized,
|
||||||
{
|
{
|
||||||
@@ -176,43 +175,43 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for String {
|
impl PartialEq for LuaString {
|
||||||
fn eq(&self, other: &String) -> bool {
|
fn eq(&self, other: &LuaString) -> bool {
|
||||||
self.as_bytes() == other.as_bytes()
|
self.as_bytes() == other.as_bytes()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for String {}
|
impl Eq for LuaString {}
|
||||||
|
|
||||||
impl<T> PartialOrd<T> for String
|
impl<T> PartialOrd<T> for LuaString
|
||||||
where
|
where
|
||||||
T: AsRef<[u8]> + ?Sized,
|
T: AsRef<[u8]> + ?Sized,
|
||||||
{
|
{
|
||||||
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
|
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
|
||||||
self.as_bytes().partial_cmp(&other.as_ref())
|
<[u8]>::partial_cmp(&self.as_bytes(), other.as_ref())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for String {
|
impl PartialOrd for LuaString {
|
||||||
fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
|
fn partial_cmp(&self, other: &LuaString) -> Option<cmp::Ordering> {
|
||||||
Some(self.cmp(other))
|
Some(self.cmp(other))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ord for String {
|
impl Ord for LuaString {
|
||||||
fn cmp(&self, other: &String) -> cmp::Ordering {
|
fn cmp(&self, other: &LuaString) -> cmp::Ordering {
|
||||||
self.as_bytes().cmp(&other.as_bytes())
|
self.as_bytes().cmp(&other.as_bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for String {
|
impl Hash for LuaString {
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
self.as_bytes().hash(state);
|
self.as_bytes().hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
impl Serialize for String {
|
impl Serialize for LuaString {
|
||||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
@@ -224,7 +223,7 @@ impl Serialize for String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Display<'a>(&'a String);
|
struct Display<'a>(&'a LuaString);
|
||||||
|
|
||||||
impl fmt::Display for Display<'_> {
|
impl fmt::Display for Display<'_> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
@@ -237,7 +236,7 @@ impl fmt::Display for Display<'_> {
|
|||||||
pub struct BorrowedStr<'a> {
|
pub struct BorrowedStr<'a> {
|
||||||
// `buf` points to a readonly memory managed by Lua
|
// `buf` points to a readonly memory managed by Lua
|
||||||
pub(crate) buf: &'a str,
|
pub(crate) buf: &'a str,
|
||||||
pub(crate) borrow: Cow<'a, String>,
|
pub(crate) borrow: Cow<'a, LuaString>,
|
||||||
pub(crate) _lua: Lua,
|
pub(crate) _lua: Lua,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,17 +301,14 @@ impl Ord for BorrowedStr<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TryFrom<&'a String> for BorrowedStr<'a> {
|
impl<'a> TryFrom<&'a LuaString> for BorrowedStr<'a> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_from(value: &'a String) -> Result<Self> {
|
fn try_from(value: &'a LuaString) -> Result<Self> {
|
||||||
let BorrowedBytes { buf, borrow, _lua } = BorrowedBytes::from(value);
|
let BorrowedBytes { buf, borrow, _lua } = BorrowedBytes::from(value);
|
||||||
let buf = str::from_utf8(buf).map_err(|e| Error::FromLuaConversionError {
|
let buf =
|
||||||
from: "string",
|
str::from_utf8(buf).map_err(|e| Error::from_lua_conversion("string", "&str", e.to_string()))?;
|
||||||
to: "&str".to_string(),
|
|
||||||
message: Some(e.to_string()),
|
|
||||||
})?;
|
|
||||||
Ok(Self { buf, borrow, _lua })
|
Ok(Self { buf, borrow, _lua })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -321,7 +317,7 @@ impl<'a> TryFrom<&'a String> for BorrowedStr<'a> {
|
|||||||
pub struct BorrowedBytes<'a> {
|
pub struct BorrowedBytes<'a> {
|
||||||
// `buf` points to a readonly memory managed by Lua
|
// `buf` points to a readonly memory managed by Lua
|
||||||
pub(crate) buf: &'a [u8],
|
pub(crate) buf: &'a [u8],
|
||||||
pub(crate) borrow: Cow<'a, String>,
|
pub(crate) borrow: Cow<'a, LuaString>,
|
||||||
pub(crate) _lua: Lua,
|
pub(crate) _lua: Lua,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,9 +385,9 @@ impl<'a> IntoIterator for &'a BorrowedBytes<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a String> for BorrowedBytes<'a> {
|
impl<'a> From<&'a LuaString> for BorrowedBytes<'a> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: &'a String) -> Self {
|
fn from(value: &'a LuaString) -> Self {
|
||||||
let (buf, _lua) = unsafe { value.to_slice() };
|
let (buf, _lua) = unsafe { value.to_slice() };
|
||||||
let borrow = Cow::Borrowed(value);
|
let borrow = Cow::Borrowed(value);
|
||||||
Self { buf, borrow, _lua }
|
Self { buf, borrow, _lua }
|
||||||
@@ -400,7 +396,7 @@ impl<'a> From<&'a String> for BorrowedBytes<'a> {
|
|||||||
|
|
||||||
struct WrappedString<T: AsRef<[u8]>>(T);
|
struct WrappedString<T: AsRef<[u8]>>(T);
|
||||||
|
|
||||||
impl String {
|
impl LuaString {
|
||||||
/// Wraps bytes, returning an opaque type that implements [`IntoLua`] trait.
|
/// Wraps bytes, returning an opaque type that implements [`IntoLua`] trait.
|
||||||
///
|
///
|
||||||
/// This function uses [`Lua::create_string`] under the hood.
|
/// This function uses [`Lua::create_string`] under the hood.
|
||||||
@@ -415,7 +411,7 @@ impl<T: AsRef<[u8]>> IntoLua for WrappedString<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LuaType for String {
|
impl LuaType for LuaString {
|
||||||
const TYPE_ID: c_int = ffi::LUA_TSTRING;
|
const TYPE_ID: c_int = ffi::LUA_TSTRING;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,9 +420,9 @@ mod assertions {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[cfg(not(feature = "send"))]
|
#[cfg(not(feature = "send"))]
|
||||||
static_assertions::assert_not_impl_any!(String: Send);
|
static_assertions::assert_not_impl_any!(LuaString: Send);
|
||||||
#[cfg(feature = "send")]
|
#[cfg(feature = "send")]
|
||||||
static_assertions::assert_impl_all!(String: Send, Sync);
|
static_assertions::assert_impl_all!(LuaString: Send, Sync);
|
||||||
#[cfg(feature = "send")]
|
#[cfg(feature = "send")]
|
||||||
static_assertions::assert_impl_all!(BorrowedBytes: Send, Sync);
|
static_assertions::assert_impl_all!(BorrowedBytes: Send, Sync);
|
||||||
#[cfg(feature = "send")]
|
#[cfg(feature = "send")]
|
||||||
|
|||||||
+317
-86
@@ -1,21 +1,179 @@
|
|||||||
|
//! Lua table handling.
|
||||||
|
//!
|
||||||
|
//! Tables are Lua's primary data structure, used for arrays, dictionaries, objects, modules,
|
||||||
|
//! and more. This module provides types for creating and manipulating Lua tables from Rust.
|
||||||
|
//!
|
||||||
|
//! # Main Types
|
||||||
|
//!
|
||||||
|
//! - [`Table`] - A handle to a Lua table.
|
||||||
|
//! - [`TablePairs`] - An iterator over key-value pairs in a table.
|
||||||
|
//! - [`TableSequence`] - An iterator over the array (sequence) portion of a table.
|
||||||
|
//!
|
||||||
|
//! # Basic Operations
|
||||||
|
//!
|
||||||
|
//! Tables support key-value access similar to Rust's `HashMap`:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use mlua::{Lua, Result};
|
||||||
|
//! # fn main() -> Result<()> {
|
||||||
|
//! let lua = Lua::new();
|
||||||
|
//! let table = lua.create_table()?;
|
||||||
|
//!
|
||||||
|
//! // Set and get values
|
||||||
|
//! table.set("key", "value")?;
|
||||||
|
//! let value: String = table.get("key")?;
|
||||||
|
//! assert_eq!(value, "value");
|
||||||
|
//!
|
||||||
|
//! // Keys and values can be any Lua-compatible type
|
||||||
|
//! table.set(1, "first")?;
|
||||||
|
//! table.set("nested", lua.create_table()?)?;
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Array Operations
|
||||||
|
//!
|
||||||
|
//! Tables can be used as arrays with 1-based indexing:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use mlua::{Lua, Result};
|
||||||
|
//! # fn main() -> Result<()> {
|
||||||
|
//! let lua = Lua::new();
|
||||||
|
//! let array = lua.create_table()?;
|
||||||
|
//!
|
||||||
|
//! // Push values to the end (like Vec::push)
|
||||||
|
//! array.push("first")?;
|
||||||
|
//! array.push("second")?;
|
||||||
|
//! array.push("third")?;
|
||||||
|
//!
|
||||||
|
//! // Pop from the end
|
||||||
|
//! let last: String = array.pop()?;
|
||||||
|
//! assert_eq!(last, "third");
|
||||||
|
//!
|
||||||
|
//! // Get length
|
||||||
|
//! assert_eq!(array.raw_len(), 2);
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Iteration
|
||||||
|
//!
|
||||||
|
//! Iterate over all key-value pairs with [`Table::pairs`]:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use mlua::{Lua, Result, Value};
|
||||||
|
//! # fn main() -> Result<()> {
|
||||||
|
//! let lua = Lua::new();
|
||||||
|
//! let table = lua.create_table()?;
|
||||||
|
//! table.set("a", 1)?;
|
||||||
|
//! table.set("b", 2)?;
|
||||||
|
//!
|
||||||
|
//! for pair in table.pairs::<String, i32>() {
|
||||||
|
//! let (key, value) = pair?;
|
||||||
|
//! println!("{key} = {value}");
|
||||||
|
//! }
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! For array portions, use [`Table::sequence_values`]:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use mlua::{Lua, Result};
|
||||||
|
//! # fn main() -> Result<()> {
|
||||||
|
//! let lua = Lua::new();
|
||||||
|
//! let array = lua.create_sequence_from(["a", "b", "c"])?;
|
||||||
|
//!
|
||||||
|
//! for value in array.sequence_values::<String>() {
|
||||||
|
//! println!("{}", value?);
|
||||||
|
//! }
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Raw vs Normal Access
|
||||||
|
//!
|
||||||
|
//! Methods prefixed with `raw_` (like [`Table::raw_get`], [`Table::raw_set`]) bypass
|
||||||
|
//! metamethods, directly accessing the table's contents. Normal methods may trigger
|
||||||
|
//! `__index`, `__newindex`, and other metamethods:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use mlua::{Lua, Result};
|
||||||
|
//! # fn main() -> Result<()> {
|
||||||
|
//! let lua = Lua::new();
|
||||||
|
//!
|
||||||
|
//! // raw_set bypasses __newindex metamethod
|
||||||
|
//! let t = lua.create_table()?;
|
||||||
|
//! t.raw_set("key", "value")?;
|
||||||
|
//!
|
||||||
|
//! // raw_get bypasses __index metamethod
|
||||||
|
//! let v: String = t.raw_get("key")?;
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Metatables
|
||||||
|
//!
|
||||||
|
//! Tables can have metatables that customize their behavior:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use mlua::{Lua, Result};
|
||||||
|
//! # fn main() -> Result<()> {
|
||||||
|
//! let lua = Lua::new();
|
||||||
|
//!
|
||||||
|
//! let table = lua.create_table()?;
|
||||||
|
//! let metatable = lua.create_table()?;
|
||||||
|
//!
|
||||||
|
//! // Set a default value via __index
|
||||||
|
//! metatable.set("__index", lua.create_function(|_, _: ()| Ok("default"))?)?;
|
||||||
|
//! table.set_metatable(Some(metatable))?;
|
||||||
|
//!
|
||||||
|
//! // Accessing missing keys returns "default"
|
||||||
|
//! let value: String = table.get("missing")?;
|
||||||
|
//! assert_eq!(value, "default");
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Global Table
|
||||||
|
//!
|
||||||
|
//! The Lua global environment is itself a table, accessible via [`Lua::globals`]:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use mlua::{Lua, Result};
|
||||||
|
//! # fn main() -> Result<()> {
|
||||||
|
//! let lua = Lua::new();
|
||||||
|
//! let globals = lua.globals();
|
||||||
|
//!
|
||||||
|
//! // Set a global variable
|
||||||
|
//! globals.set("my_var", 42)?;
|
||||||
|
//!
|
||||||
|
//! // Now accessible from Lua code
|
||||||
|
//! let result: i32 = lua.load("my_var + 8").eval()?;
|
||||||
|
//! assert_eq!(result, 50);
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! [`Lua::globals`]: crate::Lua::globals
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::os::raw::{c_int, c_void};
|
use std::os::raw::c_void;
|
||||||
use std::string::String as StdString;
|
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::function::Function;
|
use crate::function::Function;
|
||||||
use crate::state::{LuaGuard, RawLua};
|
use crate::state::{LuaGuard, RawLua, WeakLua};
|
||||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike};
|
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike};
|
||||||
use crate::types::{Integer, LuaType, ValueRef};
|
use crate::types::{Integer, ValueRef};
|
||||||
use crate::util::{assert_stack, check_stack, get_metatable_ptr, StackGuard};
|
use crate::util::{StackGuard, assert_stack, check_stack, get_metatable_ptr};
|
||||||
use crate::value::{Nil, Value};
|
use crate::value::{Nil, Value};
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
use futures_util::future::{self, Either, Future};
|
use crate::function::AsyncCallFuture;
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
use {
|
use {
|
||||||
rustc_hash::FxHashSet,
|
rustc_hash::FxHashSet,
|
||||||
serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer},
|
serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer},
|
||||||
@@ -211,7 +369,7 @@ impl Table {
|
|||||||
///
|
///
|
||||||
/// let always_equals_mt = lua.create_table()?;
|
/// let always_equals_mt = lua.create_table()?;
|
||||||
/// always_equals_mt.set("__eq", lua.create_function(|_, (_t1, _t2): (Table, Table)| Ok(true))?)?;
|
/// 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(&table1.clone())?);
|
||||||
/// assert!(table1.equals(&table2)?);
|
/// assert!(table1.equals(&table2)?);
|
||||||
@@ -226,15 +384,15 @@ impl Table {
|
|||||||
// Compare using `__eq` metamethod if exists
|
// Compare using `__eq` metamethod if exists
|
||||||
// First, check the self for the metamethod.
|
// First, check the self for the metamethod.
|
||||||
// If self does not define it, then check the other table.
|
// If self does not define it, then check the other table.
|
||||||
if let Some(mt) = self.metatable() {
|
if let Some(mt) = self.metatable()
|
||||||
if mt.contains_key("__eq")? {
|
&& let Some(eq_func) = mt.get::<Option<Function>>("__eq")?
|
||||||
return mt.get::<Function>("__eq")?.call((self, other));
|
{
|
||||||
}
|
return eq_func.call((self, other));
|
||||||
}
|
}
|
||||||
if let Some(mt) = other.metatable() {
|
if let Some(mt) = other.metatable()
|
||||||
if mt.contains_key("__eq")? {
|
&& let Some(eq_func) = mt.get::<Option<Function>>("__eq")?
|
||||||
return mt.get::<Function>("__eq")?.call((self, other));
|
{
|
||||||
}
|
return eq_func.call((self, other));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(false)
|
Ok(false)
|
||||||
@@ -416,14 +574,7 @@ impl Table {
|
|||||||
|
|
||||||
lua.push_ref(&self.0);
|
lua.push_ref(&self.0);
|
||||||
|
|
||||||
// Clear array part
|
// This is safe as long as we don't assign new keys
|
||||||
for i in 1..=ffi::lua_rawlen(state, -1) {
|
|
||||||
ffi::lua_pushnil(state);
|
|
||||||
ffi::lua_rawseti(state, -2, i as Integer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear hash part
|
|
||||||
// It must be safe as long as we don't use invalid keys
|
|
||||||
ffi::lua_pushnil(state);
|
ffi::lua_pushnil(state);
|
||||||
while ffi::lua_next(state, -2) != 0 {
|
while ffi::lua_next(state, -2) != 0 {
|
||||||
ffi::lua_pop(state, 1); // pop value
|
ffi::lua_pop(state, 1); // pop value
|
||||||
@@ -487,16 +638,12 @@ impl Table {
|
|||||||
/// [`getmetatable`]: https://www.lua.org/manual/5.4/manual.html#pdf-getmetatable
|
/// [`getmetatable`]: https://www.lua.org/manual/5.4/manual.html#pdf-getmetatable
|
||||||
pub fn metatable(&self) -> Option<Table> {
|
pub fn metatable(&self) -> Option<Table> {
|
||||||
let lua = self.0.lua.lock();
|
let lua = self.0.lua.lock();
|
||||||
let state = lua.state();
|
let ref_thread = lua.ref_thread();
|
||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(state);
|
if ffi::lua_getmetatable(ref_thread, self.0.index) == 0 {
|
||||||
assert_stack(state, 2);
|
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
|
||||||
if ffi::lua_getmetatable(state, -1) == 0 {
|
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(Table(lua.pop_ref()))
|
Some(Table(lua.pop_ref_thread()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -505,27 +652,23 @@ impl Table {
|
|||||||
///
|
///
|
||||||
/// If `metatable` is `None`, the metatable is removed (if no metatable is set, this does
|
/// If `metatable` is `None`, the metatable is removed (if no metatable is set, this does
|
||||||
/// nothing).
|
/// nothing).
|
||||||
pub fn set_metatable(&self, metatable: Option<Table>) {
|
pub fn set_metatable(&self, metatable: Option<Table>) -> Result<()> {
|
||||||
// Workaround to throw readonly error without returning Result
|
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
if self.is_readonly() {
|
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 lua = self.0.lua.lock();
|
||||||
let state = lua.state();
|
let ref_thread = lua.ref_thread();
|
||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(state);
|
if let Some(metatable) = &metatable {
|
||||||
assert_stack(state, 2);
|
ffi::lua_pushvalue(ref_thread, metatable.0.index);
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
|
||||||
if let Some(metatable) = metatable {
|
|
||||||
lua.push_ref(&metatable.0);
|
|
||||||
} else {
|
} 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.
|
/// Returns true if the table has metatable attached.
|
||||||
@@ -613,7 +756,7 @@ impl Table {
|
|||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [Lua manual]: http://www.lua.org/manual/5.4/manual.html#pdf-next
|
/// [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 {
|
TablePairs {
|
||||||
guard: self.0.lua.lock(),
|
guard: self.0.lua.lock(),
|
||||||
table: self,
|
table: self,
|
||||||
@@ -678,21 +821,30 @@ impl Table {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn sequence_values<V: FromLua>(&self) -> TableSequence<V> {
|
pub fn sequence_values<V: FromLua>(&self) -> TableSequence<'_, V> {
|
||||||
TableSequence {
|
TableSequence {
|
||||||
guard: self.0.lua.lock(),
|
guard: self.0.lua.lock(),
|
||||||
table: self,
|
table: self,
|
||||||
index: 1,
|
index: 1,
|
||||||
|
len: None,
|
||||||
_phantom: PhantomData,
|
_phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterates over the sequence part of the table, invoking the given closure on each value.
|
/// Iterates over the sequence part of the table, invoking the given closure on each value.
|
||||||
|
///
|
||||||
|
/// This methods is similar to [`Table::sequence_values`], but optimized for performance.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn for_each_value<V>(&self, mut f: impl FnMut(V) -> Result<()>) -> Result<()>
|
pub fn for_each_value<V: FromLua>(&self, f: impl FnMut(V) -> Result<()>) -> Result<()> {
|
||||||
where
|
self.for_each_value_by_len(None, f)
|
||||||
V: FromLua,
|
}
|
||||||
{
|
|
||||||
|
fn for_each_value_by_len<V: FromLua>(
|
||||||
|
&self,
|
||||||
|
len: impl Into<Option<usize>>,
|
||||||
|
mut f: impl FnMut(V) -> Result<()>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let len = len.into();
|
||||||
let lua = self.0.lua.lock();
|
let lua = self.0.lua.lock();
|
||||||
let state = lua.state();
|
let state = lua.state();
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -700,9 +852,14 @@ impl Table {
|
|||||||
check_stack(state, 4)?;
|
check_stack(state, 4)?;
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
lua.push_ref(&self.0);
|
||||||
let len = ffi::lua_rawlen(state, -1);
|
for i in 1.. {
|
||||||
for i in 1..=len {
|
if len.map(|len| i > len).unwrap_or(false) {
|
||||||
ffi::lua_rawgeti(state, -1, i as _);
|
break;
|
||||||
|
}
|
||||||
|
let t = ffi::lua_rawgeti(state, -1, i as _);
|
||||||
|
if len.is_none() && t == ffi::LUA_TNIL {
|
||||||
|
break;
|
||||||
|
}
|
||||||
f(V::from_stack(-1, &lua)?)?;
|
f(V::from_stack(-1, &lua)?)?;
|
||||||
ffi::lua_pop(state, 1);
|
ffi::lua_pop(state, 1);
|
||||||
}
|
}
|
||||||
@@ -735,8 +892,9 @@ impl Table {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
/// Checks if the table has the array metatable attached.
|
||||||
pub(crate) fn is_array(&self) -> bool {
|
#[cfg(feature = "serde")]
|
||||||
|
fn has_array_metatable(&self) -> bool {
|
||||||
let lua = self.0.lua.lock();
|
let lua = self.0.lua.lock();
|
||||||
let state = lua.state();
|
let state = lua.state();
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -752,6 +910,70 @@ impl Table {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If the table is an array, returns the number of non-nil elements and max index.
|
||||||
|
///
|
||||||
|
/// Returns `None` if the table is not an array.
|
||||||
|
///
|
||||||
|
/// This operation has O(n) complexity.
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
fn find_array_len(&self) -> Option<(usize, usize)> {
|
||||||
|
let lua = self.0.lua.lock();
|
||||||
|
let ref_thread = lua.ref_thread();
|
||||||
|
unsafe {
|
||||||
|
let _sg = StackGuard::new(ref_thread);
|
||||||
|
|
||||||
|
let (mut count, mut max_index) = (0, 0);
|
||||||
|
ffi::lua_pushnil(ref_thread);
|
||||||
|
while ffi::lua_next(ref_thread, self.0.index) != 0 {
|
||||||
|
if ffi::lua_type(ref_thread, -2) != ffi::LUA_TNUMBER {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let k = ffi::lua_tonumber(ref_thread, -2);
|
||||||
|
if k.trunc() != k || k < 1.0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
max_index = std::cmp::max(max_index, k as usize);
|
||||||
|
count += 1;
|
||||||
|
ffi::lua_pop(ref_thread, 1);
|
||||||
|
}
|
||||||
|
Some((count, max_index))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines if the table should be encoded as an array or a map.
|
||||||
|
///
|
||||||
|
/// The algorithm is the following:
|
||||||
|
/// 1. If `detect_mixed_tables` is enabled, iterate over all keys in the table checking is they
|
||||||
|
/// all are positive integers. If non-array key is found, return `None` (encode as map).
|
||||||
|
/// Otherwise check the sparsity of the array. Too sparse arrays are encoded as maps.
|
||||||
|
///
|
||||||
|
/// 2. If `detect_mixed_tables` is disabled, check if the table has a positive length or has the
|
||||||
|
/// array metatable. If so, encode as array. If the table is empty and
|
||||||
|
/// `encode_empty_tables_as_array` is enabled, encode as array.
|
||||||
|
///
|
||||||
|
/// Returns the length of the array if it should be encoded as an array.
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
pub(crate) fn encode_as_array(&self, options: crate::serde::de::Options) -> Option<usize> {
|
||||||
|
if options.detect_mixed_tables {
|
||||||
|
if let Some((len, max_idx)) = self.find_array_len() {
|
||||||
|
// If the array is too sparse, serialize it as a map instead
|
||||||
|
if len < 10 || len * 2 >= max_idx {
|
||||||
|
return Some(max_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let len = self.raw_len();
|
||||||
|
if len > 0 || self.has_array_metatable() {
|
||||||
|
return Some(len);
|
||||||
|
}
|
||||||
|
if options.encode_empty_tables_as_array && self.is_empty() {
|
||||||
|
return Some(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn check_readonly_write(&self, lua: &RawLua) -> Result<()> {
|
fn check_readonly_write(&self, lua: &RawLua) -> Result<()> {
|
||||||
@@ -871,10 +1093,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LuaType for Table {
|
|
||||||
const TYPE_ID: c_int = ffi::LUA_TTABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjectLike for Table {
|
impl ObjectLike for Table {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
|
fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
|
||||||
@@ -892,16 +1110,16 @@ impl ObjectLike for Table {
|
|||||||
R: FromLuaMulti,
|
R: FromLuaMulti,
|
||||||
{
|
{
|
||||||
// Convert table to a function and call via pcall that respects the `__call` metamethod.
|
// Convert table to a function and call via pcall that respects the `__call` metamethod.
|
||||||
Function(self.0.copy()).call(args)
|
Function(self.0.clone()).call(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
#[inline]
|
#[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
|
where
|
||||||
R: FromLuaMulti,
|
R: FromLuaMulti,
|
||||||
{
|
{
|
||||||
Function(self.0.copy()).call_async(args)
|
Function(self.0.clone()).call_async(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -913,7 +1131,7 @@ impl ObjectLike for Table {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[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
|
where
|
||||||
R: FromLuaMulti,
|
R: FromLuaMulti,
|
||||||
{
|
{
|
||||||
@@ -933,35 +1151,45 @@ impl ObjectLike for Table {
|
|||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
#[inline]
|
#[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
|
where
|
||||||
R: FromLuaMulti,
|
R: FromLuaMulti,
|
||||||
{
|
{
|
||||||
match self.get(name) {
|
match self.get(name) {
|
||||||
Ok(Value::Function(func)) => Either::Left(func.call_async(args)),
|
Ok(Value::Function(func)) => func.call_async(args),
|
||||||
Ok(val) => {
|
Ok(val) => {
|
||||||
let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
|
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]
|
#[inline]
|
||||||
fn to_string(&self) -> Result<StdString> {
|
fn to_string(&self) -> Result<String> {
|
||||||
Value::Table(Table(self.0.copy())).to_string()
|
Value::Table(Table(self.0.clone())).to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_value(&self) -> Value {
|
||||||
|
Value::Table(self.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn weak_lua(&self) -> &WeakLua {
|
||||||
|
&self.0.lua
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A wrapped [`Table`] with customized serialization behavior.
|
/// A wrapped [`Table`] with customized serialization behavior.
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
pub(crate) struct SerializableTable<'a> {
|
pub(crate) struct SerializableTable<'a> {
|
||||||
table: &'a Table,
|
table: &'a Table,
|
||||||
options: crate::serde::de::Options,
|
options: crate::serde::de::Options,
|
||||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
impl Serialize for Table {
|
impl Serialize for Table {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
|
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
|
||||||
@@ -969,7 +1197,7 @@ impl Serialize for Table {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
impl<'a> SerializableTable<'a> {
|
impl<'a> SerializableTable<'a> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
@@ -985,13 +1213,22 @@ impl<'a> SerializableTable<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
impl<V> TableSequence<'_, V> {
|
||||||
|
/// Sets the length (hint) of the sequence.
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
pub(crate) fn with_len(mut self, len: usize) -> Self {
|
||||||
|
self.len = Some(len);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
impl Serialize for SerializableTable<'_> {
|
impl Serialize for SerializableTable<'_> {
|
||||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
{
|
{
|
||||||
use crate::serde::de::{check_value_for_skip, MapPairs, RecursionGuard};
|
use crate::serde::de::{MapPairs, RecursionGuard, check_value_for_skip};
|
||||||
use crate::value::SerializableValue;
|
use crate::value::SerializableValue;
|
||||||
|
|
||||||
let convert_result = |res: Result<()>, serialize_err: Option<S::Error>| match res {
|
let convert_result = |res: Result<()>, serialize_err: Option<S::Error>| match res {
|
||||||
@@ -1006,14 +1243,10 @@ impl Serialize for SerializableTable<'_> {
|
|||||||
let _guard = RecursionGuard::new(self.table, visited);
|
let _guard = RecursionGuard::new(self.table, visited);
|
||||||
|
|
||||||
// Array
|
// Array
|
||||||
let len = self.table.raw_len();
|
if let Some(len) = self.table.encode_as_array(self.options) {
|
||||||
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 seq = serializer.serialize_seq(Some(len))?;
|
||||||
let mut serialize_err = None;
|
let mut serialize_err = None;
|
||||||
let res = self.table.for_each_value::<Value>(|value| {
|
let res = self.table.for_each_value_by_len::<Value>(len, |value| {
|
||||||
let skip = check_value_for_skip(&value, self.options, visited)
|
let skip = check_value_for_skip(&value, self.options, visited)
|
||||||
.map_err(|err| Error::SerializeError(err.to_string()))?;
|
.map_err(|err| Error::SerializeError(err.to_string()))?;
|
||||||
if skip {
|
if skip {
|
||||||
@@ -1023,7 +1256,7 @@ impl Serialize for SerializableTable<'_> {
|
|||||||
seq.serialize_element(&SerializableValue::new(&value, options, Some(visited)))
|
seq.serialize_element(&SerializableValue::new(&value, options, Some(visited)))
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
serialize_err = Some(err);
|
serialize_err = Some(err);
|
||||||
Error::SerializeError(StdString::new())
|
Error::SerializeError(String::new())
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
convert_result(res, serialize_err)?;
|
convert_result(res, serialize_err)?;
|
||||||
@@ -1048,7 +1281,7 @@ impl Serialize for SerializableTable<'_> {
|
|||||||
)
|
)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
serialize_err = Some(err);
|
serialize_err = Some(err);
|
||||||
Error::SerializeError(StdString::new())
|
Error::SerializeError(String::new())
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1137,13 +1370,11 @@ pub struct TableSequence<'a, V> {
|
|||||||
guard: LuaGuard,
|
guard: LuaGuard,
|
||||||
table: &'a Table,
|
table: &'a Table,
|
||||||
index: Integer,
|
index: Integer,
|
||||||
|
len: Option<usize>,
|
||||||
_phantom: PhantomData<V>,
|
_phantom: PhantomData<V>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V> Iterator for TableSequence<'_, V>
|
impl<V: FromLua> Iterator for TableSequence<'_, V> {
|
||||||
where
|
|
||||||
V: FromLua,
|
|
||||||
{
|
|
||||||
type Item = Result<V>;
|
type Item = Result<V>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
@@ -1157,7 +1388,7 @@ where
|
|||||||
|
|
||||||
lua.push_ref(&self.table.0);
|
lua.push_ref(&self.table.0);
|
||||||
match ffi::lua_rawgeti(state, -1, self.index) {
|
match ffi::lua_rawgeti(state, -1, self.index) {
|
||||||
ffi::LUA_TNIL => None,
|
ffi::LUA_TNIL if self.index as usize > self.len.unwrap_or(0) => None,
|
||||||
_ => {
|
_ => {
|
||||||
self.index += 1;
|
self.index += 1;
|
||||||
Some(V::from_stack(-1, lua))
|
Some(V::from_stack(-1, lua))
|
||||||
|
|||||||
+70
-35
@@ -6,11 +6,11 @@ use crate::function::Function;
|
|||||||
use crate::state::RawLua;
|
use crate::state::RawLua;
|
||||||
use crate::traits::{FromLuaMulti, IntoLuaMulti};
|
use crate::traits::{FromLuaMulti, IntoLuaMulti};
|
||||||
use crate::types::{LuaType, ValueRef};
|
use crate::types::{LuaType, ValueRef};
|
||||||
use crate::util::{check_stack, error_traceback_thread, pop_error, StackGuard};
|
use crate::util::{StackGuard, check_stack, error_traceback_thread, pop_error};
|
||||||
|
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
use crate::{
|
use crate::{
|
||||||
hook::{Debug, HookTriggers},
|
debug::{Debug, HookTriggers},
|
||||||
types::HookKind,
|
types::HookKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -86,13 +86,15 @@ unsafe impl Sync for Thread {}
|
|||||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||||
pub struct AsyncThread<R> {
|
pub struct AsyncThread<R> {
|
||||||
thread: Thread,
|
thread: Thread,
|
||||||
ret: PhantomData<R>,
|
ret: PhantomData<fn() -> R>,
|
||||||
recycle: bool,
|
recycle: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Thread {
|
impl Thread {
|
||||||
|
/// Returns reference to the Lua state that this thread is associated with.
|
||||||
|
#[doc(hidden)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn state(&self) -> *mut ffi::lua_State {
|
pub fn state(&self) -> *mut ffi::lua_State {
|
||||||
self.1
|
self.1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +156,6 @@ impl Thread {
|
|||||||
let thread_state = self.state();
|
let thread_state = self.state();
|
||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(state);
|
let _sg = StackGuard::new(state);
|
||||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
|
||||||
|
|
||||||
let nargs = args.push_into_stack_multi(&lua)?;
|
let nargs = args.push_into_stack_multi(&lua)?;
|
||||||
if nargs > 0 {
|
if nargs > 0 {
|
||||||
@@ -163,6 +164,7 @@ impl Thread {
|
|||||||
pushed_nargs += nargs;
|
pushed_nargs += nargs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||||
let (_, nresults) = self.resume_inner(&lua, pushed_nargs)?;
|
let (_, nresults) = self.resume_inner(&lua, pushed_nargs)?;
|
||||||
check_stack(state, nresults + 1)?;
|
check_stack(state, nresults + 1)?;
|
||||||
ffi::lua_xmove(thread_state, state, nresults);
|
ffi::lua_xmove(thread_state, state, nresults);
|
||||||
@@ -190,12 +192,12 @@ impl Thread {
|
|||||||
let thread_state = self.state();
|
let thread_state = self.state();
|
||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(state);
|
let _sg = StackGuard::new(state);
|
||||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
|
||||||
|
|
||||||
check_stack(state, 1)?;
|
check_stack(state, 1)?;
|
||||||
error.push_into_stack(&lua)?;
|
error.push_into_stack(&lua)?;
|
||||||
ffi::lua_xmove(state, thread_state, 1);
|
ffi::lua_xmove(state, thread_state, 1);
|
||||||
|
|
||||||
|
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||||
let (_, nresults) = self.resume_inner(&lua, ffi::LUA_RESUMEERROR)?;
|
let (_, nresults) = self.resume_inner(&lua, ffi::LUA_RESUMEERROR)?;
|
||||||
check_stack(state, nresults + 1)?;
|
check_stack(state, nresults + 1)?;
|
||||||
ffi::lua_xmove(thread_state, state, nresults);
|
ffi::lua_xmove(thread_state, state, nresults);
|
||||||
@@ -263,11 +265,13 @@ impl Thread {
|
|||||||
/// You can have multiple hooks for different threads.
|
/// You can have multiple hooks for different threads.
|
||||||
///
|
///
|
||||||
/// To remove a hook call [`Thread::remove_hook`].
|
/// To remove a hook call [`Thread::remove_hook`].
|
||||||
|
///
|
||||||
|
/// [`Lua::set_hook`]: crate::Lua::set_hook
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||||
pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F) -> Result<()>
|
pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F) -> Result<()>
|
||||||
where
|
where
|
||||||
F: Fn(&crate::Lua, Debug) -> Result<crate::VmState> + crate::MaybeSend + 'static,
|
F: Fn(&crate::Lua, &Debug) -> Result<crate::VmState> + crate::MaybeSend + 'static,
|
||||||
{
|
{
|
||||||
let lua = self.0.lua.lock();
|
let lua = self.0.lua.lock();
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -305,29 +309,10 @@ impl Thread {
|
|||||||
pub fn reset(&self, func: Function) -> Result<()> {
|
pub fn reset(&self, func: Function) -> Result<()> {
|
||||||
let lua = self.0.lua.lock();
|
let lua = self.0.lua.lock();
|
||||||
let thread_state = self.state();
|
let thread_state = self.state();
|
||||||
match self.status_inner(&lua) {
|
|
||||||
ThreadStatusInner::Running => return Err(Error::runtime("cannot reset a running thread")),
|
|
||||||
// Any Lua can reuse new or finished thread
|
|
||||||
ThreadStatusInner::New(_) => unsafe { ffi::lua_settop(thread_state, 0) },
|
|
||||||
ThreadStatusInner::Finished => {}
|
|
||||||
#[cfg(not(any(feature = "lua54", feature = "luau")))]
|
|
||||||
_ => return Err(Error::runtime("cannot reset non-finished thread")),
|
|
||||||
#[cfg(any(feature = "lua54", feature = "luau"))]
|
|
||||||
_ => 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);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
let status = self.status_inner(&lua);
|
||||||
|
self.reset_inner(status)?;
|
||||||
|
|
||||||
// Push function to the top of the thread stack
|
// Push function to the top of the thread stack
|
||||||
ffi::lua_xpush(lua.ref_thread(), thread_state, func.0.index);
|
ffi::lua_xpush(lua.ref_thread(), thread_state, func.0.index);
|
||||||
|
|
||||||
@@ -342,6 +327,42 @@ impl Thread {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 = "lua55", feature = "lua54", feature = "luau")))]
|
||||||
|
ThreadStatusInner::Yielded(_) | ThreadStatusInner::Error => {
|
||||||
|
Err(Error::runtime("cannot reset non-finished thread"))
|
||||||
|
}
|
||||||
|
#[cfg(any(feature = "lua55", 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(any(feature = "lua55", all(feature = "lua54", feature = "vendored")))]
|
||||||
|
let status = {
|
||||||
|
let lua = self.0.lua.lock();
|
||||||
|
ffi::lua_closethread(thread_state, lua.state())
|
||||||
|
};
|
||||||
|
#[cfg(any(feature = "lua55", 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.
|
/// Converts [`Thread`] to an [`AsyncThread`] which implements [`Future`] and [`Stream`] traits.
|
||||||
///
|
///
|
||||||
/// Only resumable threads can be converted to [`AsyncThread`].
|
/// Only resumable threads can be converted to [`AsyncThread`].
|
||||||
@@ -453,7 +474,6 @@ impl Thread {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(any(feature = "luau", doc))]
|
#[cfg(any(feature = "luau", doc))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn sandbox(&self) -> Result<()> {
|
pub fn sandbox(&self) -> Result<()> {
|
||||||
let lua = self.0.lua.lock();
|
let lua = self.0.lua.lock();
|
||||||
let state = lua.state();
|
let state = lua.state();
|
||||||
@@ -503,10 +523,25 @@ impl<R> AsyncThread<R> {
|
|||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
impl<R> Drop for AsyncThread<R> {
|
impl<R> Drop for AsyncThread<R> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
#[allow(clippy::collapsible_if)]
|
||||||
if self.recycle {
|
if self.recycle {
|
||||||
if let Some(lua) = self.thread.0.lua.try_lock() {
|
if let Some(lua) = self.thread.0.lua.try_lock() {
|
||||||
// For Lua 5.4 this also closes all pending to-be-closed variables
|
unsafe {
|
||||||
unsafe { lua.recycle_thread(&mut self.thread) };
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -570,7 +605,7 @@ impl<R: FromLuaMulti> Future for AsyncThread<R> {
|
|||||||
|
|
||||||
if status.is_yielded() {
|
if status.is_yielded() {
|
||||||
if !(nresults == 1 && is_poll_pending(thread_state)) {
|
if !(nresults == 1 && is_poll_pending(thread_state)) {
|
||||||
// Ignore value returned via yield()
|
// Ignore values returned via yield()
|
||||||
cx.waker().wake_by_ref();
|
cx.waker().wake_by_ref();
|
||||||
}
|
}
|
||||||
return Poll::Pending;
|
return Poll::Pending;
|
||||||
@@ -601,7 +636,7 @@ struct WakerGuard<'lua, 'a> {
|
|||||||
impl<'lua, 'a> WakerGuard<'lua, 'a> {
|
impl<'lua, 'a> WakerGuard<'lua, 'a> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(lua: &'lua RawLua, waker: &'a Waker) -> Result<WakerGuard<'lua, 'a>> {
|
pub fn new(lua: &'lua RawLua, waker: &'a Waker) -> Result<WakerGuard<'lua, 'a>> {
|
||||||
let prev = unsafe { lua.set_waker(NonNull::from(waker)) };
|
let prev = lua.set_waker(NonNull::from(waker));
|
||||||
Ok(WakerGuard {
|
Ok(WakerGuard {
|
||||||
lua,
|
lua,
|
||||||
prev,
|
prev,
|
||||||
@@ -613,7 +648,7 @@ impl<'lua, 'a> WakerGuard<'lua, 'a> {
|
|||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
impl Drop for WakerGuard<'_, '_> {
|
impl Drop for WakerGuard<'_, '_> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe { self.lua.set_waker(self.prev) };
|
self.lua.set_waker(self.prev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+48
-9
@@ -1,17 +1,16 @@
|
|||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
use std::string::String as StdString;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::multi::MultiValue;
|
use crate::multi::MultiValue;
|
||||||
use crate::private::Sealed;
|
use crate::private::Sealed;
|
||||||
use crate::state::{Lua, RawLua};
|
use crate::state::{Lua, RawLua, WeakLua};
|
||||||
use crate::types::MaybeSend;
|
use crate::types::MaybeSend;
|
||||||
use crate::util::{check_stack, short_type_name};
|
use crate::util::{check_stack, parse_lookup_path, short_type_name};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
use std::future::Future;
|
use {crate::function::AsyncCallFuture, std::future::Future};
|
||||||
|
|
||||||
/// Trait for types convertible to [`Value`].
|
/// Trait for types convertible to [`Value`].
|
||||||
pub trait IntoLua: Sized {
|
pub trait IntoLua: Sized {
|
||||||
@@ -162,7 +161,7 @@ pub trait ObjectLike: Sealed {
|
|||||||
/// arguments.
|
/// arguments.
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
#[cfg_attr(docsrs, doc(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
|
where
|
||||||
R: FromLuaMulti;
|
R: FromLuaMulti;
|
||||||
|
|
||||||
@@ -178,7 +177,7 @@ pub trait ObjectLike: Sealed {
|
|||||||
/// This might invoke the `__index` metamethod.
|
/// This might invoke the `__index` metamethod.
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
#[cfg_attr(docsrs, doc(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
|
where
|
||||||
R: FromLuaMulti;
|
R: FromLuaMulti;
|
||||||
|
|
||||||
@@ -196,14 +195,54 @@ pub trait ObjectLike: Sealed {
|
|||||||
/// This might invoke the `__index` metamethod.
|
/// This might invoke the `__index` metamethod.
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
#[cfg_attr(docsrs, doc(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
|
where
|
||||||
R: FromLuaMulti;
|
R: FromLuaMulti;
|
||||||
|
|
||||||
|
/// Look up a value by a path of keys.
|
||||||
|
///
|
||||||
|
/// The syntax is similar to accessing nested tables in Lua, with additional support for
|
||||||
|
/// `?` operator to perform safe navigation.
|
||||||
|
///
|
||||||
|
/// For example, the path `a[1].c` is equivalent to `table.a[1].c` in Lua.
|
||||||
|
/// With `?` operator, `a[1]?.c` is equivalent to `table.a[1] and table.a[1].c or nil` in Lua.
|
||||||
|
///
|
||||||
|
/// Bracket notation rules:
|
||||||
|
/// - `[123]` - integer keys
|
||||||
|
/// - `["string key"]` or `['string key']` - string keys (must be quoted)
|
||||||
|
/// - String keys support escape sequences: `\"`, `\'`, `\\`
|
||||||
|
fn get_path<V: FromLua>(&self, path: &str) -> Result<V> {
|
||||||
|
let mut current = self.to_value();
|
||||||
|
for (key, safe_nil) in parse_lookup_path(path)? {
|
||||||
|
current = match current {
|
||||||
|
Value::Table(table) => table.get::<Value>(key),
|
||||||
|
Value::UserData(ud) => ud.get::<Value>(key),
|
||||||
|
_ => {
|
||||||
|
let type_name = current.type_name();
|
||||||
|
let err = format!("attempt to index a {type_name} value with key '{key}'");
|
||||||
|
Err(Error::runtime(err))
|
||||||
|
}
|
||||||
|
}?;
|
||||||
|
if safe_nil && (current == Value::Nil || current == Value::NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let lua = self.weak_lua().lock();
|
||||||
|
V::from_lua(current, lua.lua())
|
||||||
|
}
|
||||||
|
|
||||||
/// Converts the object to a string in a human-readable format.
|
/// Converts the object to a string in a human-readable format.
|
||||||
///
|
///
|
||||||
/// This might invoke the `__tostring` metamethod.
|
/// This might invoke the `__tostring` metamethod.
|
||||||
fn to_string(&self) -> Result<StdString>;
|
fn to_string(&self) -> Result<String>;
|
||||||
|
|
||||||
|
/// Converts the object to a Lua value.
|
||||||
|
fn to_value(&self) -> Value;
|
||||||
|
|
||||||
|
/// Gets a reference to the associated Lua state.
|
||||||
|
#[doc(hidden)]
|
||||||
|
fn weak_lua(&self) -> &WeakLua;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait for types that can be used as Lua functions.
|
/// A trait for types that can be used as Lua functions.
|
||||||
@@ -299,7 +338,7 @@ impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
|
|||||||
|
|
||||||
pub(crate) trait ShortTypeName {
|
pub(crate) trait ShortTypeName {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn type_name() -> StdString {
|
fn type_name() -> String {
|
||||||
short_type_name::<Self>()
|
short_type_name::<Self>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-9
@@ -1,9 +1,9 @@
|
|||||||
use std::cell::UnsafeCell;
|
use std::cell::UnsafeCell;
|
||||||
use std::os::raw::{c_int, c_void};
|
use std::os::raw::{c_int, c_void};
|
||||||
|
|
||||||
use crate::error::Result;
|
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
use crate::hook::{Debug, HookTriggers};
|
use crate::debug::{Debug, HookTriggers};
|
||||||
|
use crate::error::Result;
|
||||||
use crate::state::{ExtraData, Lua, RawLua};
|
use crate::state::{ExtraData, Lua, RawLua};
|
||||||
|
|
||||||
// Re-export mutex wrappers
|
// Re-export mutex wrappers
|
||||||
@@ -20,6 +20,9 @@ pub use either::Either;
|
|||||||
pub use registry_key::RegistryKey;
|
pub use registry_key::RegistryKey;
|
||||||
pub(crate) use value_ref::ValueRef;
|
pub(crate) use value_ref::ValueRef;
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
pub(crate) use value_ref::ValueRefIndex;
|
||||||
|
|
||||||
/// Type of Lua integer numbers.
|
/// Type of Lua integer numbers.
|
||||||
pub type Integer = ffi::lua_Integer;
|
pub type Integer = ffi::lua_Integer;
|
||||||
/// Type of Lua floating point numbers.
|
/// Type of Lua floating point numbers.
|
||||||
@@ -35,10 +38,13 @@ unsafe impl Send for LightUserData {}
|
|||||||
unsafe impl Sync for LightUserData {}
|
unsafe impl Sync for LightUserData {}
|
||||||
|
|
||||||
#[cfg(feature = "send")]
|
#[cfg(feature = "send")]
|
||||||
pub(crate) type Callback = Box<dyn Fn(&RawLua, c_int) -> Result<c_int> + Send + 'static>;
|
type CallbackFn<'a> = dyn Fn(&RawLua, c_int) -> Result<c_int> + Send + 'a;
|
||||||
|
|
||||||
#[cfg(not(feature = "send"))]
|
#[cfg(not(feature = "send"))]
|
||||||
pub(crate) type Callback = Box<dyn Fn(&RawLua, c_int) -> Result<c_int> + 'static>;
|
type CallbackFn<'a> = dyn Fn(&RawLua, c_int) -> Result<c_int> + 'a;
|
||||||
|
|
||||||
|
pub(crate) type Callback = Box<CallbackFn<'static>>;
|
||||||
|
pub(crate) type CallbackPtr = *const CallbackFn<'static>;
|
||||||
|
|
||||||
pub(crate) type ScopedCallback<'s> = Box<dyn Fn(&RawLua, c_int) -> Result<c_int> + 's>;
|
pub(crate) type ScopedCallback<'s> = Box<dyn Fn(&RawLua, c_int) -> Result<c_int> + 's>;
|
||||||
|
|
||||||
@@ -61,7 +67,7 @@ pub(crate) type AsyncCallback =
|
|||||||
pub(crate) type AsyncCallbackUpvalue = Upvalue<AsyncCallback>;
|
pub(crate) type AsyncCallbackUpvalue = Upvalue<AsyncCallback>;
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[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 Lua VM action after executing interrupt or hook function.
|
/// Type to set next Lua VM action after executing interrupt or hook function.
|
||||||
pub enum VmState {
|
pub enum VmState {
|
||||||
@@ -79,10 +85,10 @@ pub(crate) enum HookKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "send", not(feature = "luau")))]
|
#[cfg(all(feature = "send", not(feature = "luau")))]
|
||||||
pub(crate) type HookCallback = XRc<dyn Fn(&Lua, Debug) -> Result<VmState> + Send>;
|
pub(crate) type HookCallback = XRc<dyn Fn(&Lua, &Debug) -> Result<VmState> + Send>;
|
||||||
|
|
||||||
#[cfg(all(not(feature = "send"), not(feature = "luau")))]
|
#[cfg(all(not(feature = "send"), not(feature = "luau")))]
|
||||||
pub(crate) type HookCallback = XRc<dyn Fn(&Lua, Debug) -> Result<VmState>>;
|
pub(crate) type HookCallback = XRc<dyn Fn(&Lua, &Debug) -> Result<VmState>>;
|
||||||
|
|
||||||
#[cfg(all(feature = "send", feature = "luau"))]
|
#[cfg(all(feature = "send", feature = "luau"))]
|
||||||
pub(crate) type InterruptCallback = XRc<dyn Fn(&Lua) -> Result<VmState> + Send>;
|
pub(crate) type InterruptCallback = XRc<dyn Fn(&Lua) -> Result<VmState> + Send>;
|
||||||
@@ -102,10 +108,12 @@ pub(crate) type ThreadCollectionCallback = XRc<dyn Fn(crate::LightUserData) + Se
|
|||||||
#[cfg(all(not(feature = "send"), feature = "luau"))]
|
#[cfg(all(not(feature = "send"), feature = "luau"))]
|
||||||
pub(crate) type ThreadCollectionCallback = XRc<dyn Fn(crate::LightUserData)>;
|
pub(crate) type ThreadCollectionCallback = XRc<dyn Fn(crate::LightUserData)>;
|
||||||
|
|
||||||
#[cfg(all(feature = "send", feature = "lua54"))]
|
#[cfg(feature = "send")]
|
||||||
|
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||||
pub(crate) type WarnCallback = XRc<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"))]
|
#[cfg(not(feature = "send"))]
|
||||||
|
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||||
pub(crate) type WarnCallback = XRc<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.
|
/// A trait that adds `Send` requirement if `send` feature is enabled.
|
||||||
@@ -114,6 +122,7 @@ pub trait MaybeSend: Send {}
|
|||||||
#[cfg(feature = "send")]
|
#[cfg(feature = "send")]
|
||||||
impl<T: Send> MaybeSend for T {}
|
impl<T: Send> MaybeSend for T {}
|
||||||
|
|
||||||
|
/// A trait that adds `Send` requirement if `send` feature is enabled.
|
||||||
#[cfg(not(feature = "send"))]
|
#[cfg(not(feature = "send"))]
|
||||||
pub trait MaybeSend {}
|
pub trait MaybeSend {}
|
||||||
#[cfg(not(feature = "send"))]
|
#[cfg(not(feature = "send"))]
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ impl AppData {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub(crate) fn borrow<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRef<T>> {
|
pub(crate) fn borrow<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRef<'_, T>> {
|
||||||
match self.try_borrow(guard) {
|
match self.try_borrow(guard) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(err) => panic!("already mutably borrowed: {err:?}"),
|
Err(err) => panic!("already mutably borrowed: {err:?}"),
|
||||||
@@ -53,7 +53,7 @@ impl AppData {
|
|||||||
pub(crate) fn try_borrow<T: 'static>(
|
pub(crate) fn try_borrow<T: 'static>(
|
||||||
&self,
|
&self,
|
||||||
guard: Option<LuaGuard>,
|
guard: Option<LuaGuard>,
|
||||||
) -> Result<Option<AppDataRef<T>>, BorrowError> {
|
) -> Result<Option<AppDataRef<'_, T>>, BorrowError> {
|
||||||
let data = unsafe { &*self.container.get() }
|
let data = unsafe { &*self.container.get() }
|
||||||
.get(&TypeId::of::<T>())
|
.get(&TypeId::of::<T>())
|
||||||
.map(|c| c.try_borrow())
|
.map(|c| c.try_borrow())
|
||||||
@@ -74,7 +74,7 @@ impl AppData {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub(crate) fn borrow_mut<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRefMut<T>> {
|
pub(crate) fn borrow_mut<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRefMut<'_, T>> {
|
||||||
match self.try_borrow_mut(guard) {
|
match self.try_borrow_mut(guard) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(err) => panic!("already borrowed: {err:?}"),
|
Err(err) => panic!("already borrowed: {err:?}"),
|
||||||
@@ -84,7 +84,7 @@ impl AppData {
|
|||||||
pub(crate) fn try_borrow_mut<T: 'static>(
|
pub(crate) fn try_borrow_mut<T: 'static>(
|
||||||
&self,
|
&self,
|
||||||
guard: Option<LuaGuard>,
|
guard: Option<LuaGuard>,
|
||||||
) -> Result<Option<AppDataRefMut<T>>, BorrowMutError> {
|
) -> Result<Option<AppDataRefMut<'_, T>>, BorrowMutError> {
|
||||||
let data = unsafe { &*self.container.get() }
|
let data = unsafe { &*self.container.get() }
|
||||||
.get(&TypeId::of::<T>())
|
.get(&TypeId::of::<T>())
|
||||||
.map(|c| c.try_borrow_mut())
|
.map(|c| c.try_borrow_mut())
|
||||||
|
|||||||
+2
-2
@@ -31,7 +31,7 @@ mod inner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn lock(&self) -> ReentrantMutexGuard<T> {
|
pub(crate) fn lock(&self) -> ReentrantMutexGuard<'_, T> {
|
||||||
ReentrantMutexGuard(&self.0)
|
ReentrantMutexGuard(&self.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ mod inner {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.0 .0
|
&self.0.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-23
@@ -1,22 +1,39 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::os::raw::{c_int, c_void};
|
use std::os::raw::{c_int, c_void};
|
||||||
|
|
||||||
|
use super::XRc;
|
||||||
use crate::state::{RawLua, WeakLua};
|
use crate::state::{RawLua, WeakLua};
|
||||||
|
|
||||||
/// A reference to a Lua (complex) value stored in the Lua auxiliary thread.
|
/// A reference to a Lua (complex) value stored in the Lua auxiliary thread.
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct ValueRef {
|
pub struct ValueRef {
|
||||||
pub(crate) lua: WeakLua,
|
pub(crate) lua: WeakLua,
|
||||||
|
// Keep index separate to avoid additional indirection when accessing it.
|
||||||
pub(crate) index: c_int,
|
pub(crate) index: c_int,
|
||||||
pub(crate) drop: bool,
|
// If `index_count` is `None`, the value does not need to be destroyed.
|
||||||
|
pub(crate) index_count: Option<ValueRefIndex>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A reference to a Lua value index in the auxiliary thread.
|
||||||
|
/// It's cheap to clone and can be used to track the number of references to a value.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct ValueRefIndex(pub(crate) XRc<c_int>);
|
||||||
|
|
||||||
|
impl From<c_int> for ValueRefIndex {
|
||||||
|
#[inline]
|
||||||
|
fn from(index: c_int) -> Self {
|
||||||
|
ValueRefIndex(XRc::new(index))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ValueRef {
|
impl ValueRef {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn new(lua: &RawLua, index: c_int) -> Self {
|
pub(crate) fn new(lua: &RawLua, index: impl Into<ValueRefIndex>) -> Self {
|
||||||
|
let index = index.into();
|
||||||
ValueRef {
|
ValueRef {
|
||||||
lua: lua.weak().clone(),
|
lua: lua.weak().clone(),
|
||||||
index,
|
index: *index.0,
|
||||||
drop: true,
|
index_count: Some(index),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,16 +42,6 @@ impl ValueRef {
|
|||||||
let lua = self.lua.lock();
|
let lua = self.lua.lock();
|
||||||
unsafe { ffi::lua_topointer(lua.ref_thread(), self.index) }
|
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 {
|
impl fmt::Debug for ValueRef {
|
||||||
@@ -43,17 +50,15 @@ impl fmt::Debug for ValueRef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for ValueRef {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
unsafe { self.lua.lock().clone_ref(self) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for ValueRef {
|
impl Drop for ValueRef {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if self.drop {
|
if let Some(ValueRefIndex(index)) = self.index_count.take() {
|
||||||
if let Some(lua) = self.lua.try_lock() {
|
// It's guaranteed that the inner value returns exactly once.
|
||||||
unsafe { lua.drop_ref(self) };
|
// This means in particular that the value is not dropped.
|
||||||
|
if XRc::into_inner(index).is_some()
|
||||||
|
&& let Some(lua) = self.lua.try_lock()
|
||||||
|
{
|
||||||
|
unsafe { lua.drop_ref(self) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+187
-86
@@ -3,22 +3,21 @@ use std::ffi::CStr;
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::os::raw::{c_char, c_void};
|
use std::os::raw::{c_char, c_void};
|
||||||
use std::string::String as StdString;
|
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::function::Function;
|
use crate::function::Function;
|
||||||
use crate::state::Lua;
|
use crate::state::Lua;
|
||||||
use crate::string::String;
|
use crate::string::LuaString;
|
||||||
use crate::table::{Table, TablePairs};
|
use crate::table::{Table, TablePairs};
|
||||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||||
use crate::types::{MaybeSend, ValueRef};
|
use crate::types::{MaybeSend, ValueRef};
|
||||||
use crate::util::{check_stack, get_userdata, push_string, take_userdata, StackGuard};
|
use crate::util::{StackGuard, check_stack, get_userdata, push_string, short_type_name, take_userdata};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
use {
|
use {
|
||||||
serde::ser::{self, Serialize, Serializer},
|
serde::ser::{self, Serialize, Serializer},
|
||||||
std::result::Result as StdResult,
|
std::result::Result as StdResult,
|
||||||
@@ -30,8 +29,8 @@ pub use r#ref::{UserDataRef, UserDataRefMut};
|
|||||||
pub use registry::UserDataRegistry;
|
pub use registry::UserDataRegistry;
|
||||||
pub(crate) use registry::{RawUserDataRegistry, UserDataProxy};
|
pub(crate) use registry::{RawUserDataRegistry, UserDataProxy};
|
||||||
pub(crate) use util::{
|
pub(crate) use util::{
|
||||||
borrow_userdata_scoped, borrow_userdata_scoped_mut, collect_userdata, init_userdata_metatable,
|
TypeIdHints, borrow_userdata_scoped, borrow_userdata_scoped_mut, collect_userdata,
|
||||||
TypeIdHints,
|
init_userdata_metatable,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Kinds of metamethods that can be overridden.
|
/// Kinds of metamethods that can be overridden.
|
||||||
@@ -56,32 +55,53 @@ pub enum MetaMethod {
|
|||||||
/// The unary minus (`-`) operator.
|
/// The unary minus (`-`) operator.
|
||||||
Unm,
|
Unm,
|
||||||
/// The floor division (//) operator.
|
/// The floor division (//) operator.
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))))]
|
#[cfg_attr(
|
||||||
|
docsrs,
|
||||||
|
doc(cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "luau")))
|
||||||
|
)]
|
||||||
IDiv,
|
IDiv,
|
||||||
/// The bitwise AND (&) operator.
|
/// The bitwise AND (&) operator.
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53"))))]
|
#[cfg_attr(
|
||||||
|
docsrs,
|
||||||
|
doc(cfg(any(feature = "lua55", feature = "lua54", feature = "lua53")))
|
||||||
|
)]
|
||||||
BAnd,
|
BAnd,
|
||||||
/// The bitwise OR (|) operator.
|
/// The bitwise OR (|) operator.
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53"))))]
|
#[cfg_attr(
|
||||||
|
docsrs,
|
||||||
|
doc(cfg(any(feature = "lua55", feature = "lua54", feature = "lua53")))
|
||||||
|
)]
|
||||||
BOr,
|
BOr,
|
||||||
/// The bitwise XOR (binary ~) operator.
|
/// The bitwise XOR (binary ~) operator.
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53"))))]
|
#[cfg_attr(
|
||||||
|
docsrs,
|
||||||
|
doc(cfg(any(feature = "lua55", feature = "lua54", feature = "lua53")))
|
||||||
|
)]
|
||||||
BXor,
|
BXor,
|
||||||
/// The bitwise NOT (unary ~) operator.
|
/// The bitwise NOT (unary ~) operator.
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53"))))]
|
#[cfg_attr(
|
||||||
|
docsrs,
|
||||||
|
doc(cfg(any(feature = "lua55", feature = "lua54", feature = "lua53")))
|
||||||
|
)]
|
||||||
BNot,
|
BNot,
|
||||||
/// The bitwise left shift (<<) operator.
|
/// The bitwise left shift (<<) operator.
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53"))))]
|
#[cfg_attr(
|
||||||
|
docsrs,
|
||||||
|
doc(cfg(any(feature = "lua55", feature = "lua54", feature = "lua53")))
|
||||||
|
)]
|
||||||
Shl,
|
Shl,
|
||||||
/// The bitwise right shift (>>) operator.
|
/// The bitwise right shift (>>) operator.
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53"))))]
|
#[cfg_attr(
|
||||||
|
docsrs,
|
||||||
|
doc(cfg(any(feature = "lua55", feature = "lua54", feature = "lua53")))
|
||||||
|
)]
|
||||||
Shr,
|
Shr,
|
||||||
/// The string concatenation operator `..`.
|
/// The string concatenation operator `..`.
|
||||||
Concat,
|
Concat,
|
||||||
@@ -106,10 +126,22 @@ pub enum MetaMethod {
|
|||||||
/// The `__pairs` metamethod.
|
/// The `__pairs` metamethod.
|
||||||
///
|
///
|
||||||
/// This is not an operator, but it will be called by the built-in `pairs` function.
|
/// This is not an operator, but it will be called by the built-in `pairs` function.
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit52"))]
|
#[cfg(any(
|
||||||
|
feature = "lua55",
|
||||||
|
feature = "lua54",
|
||||||
|
feature = "lua53",
|
||||||
|
feature = "lua52",
|
||||||
|
feature = "luajit52"
|
||||||
|
))]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
docsrs,
|
docsrs,
|
||||||
doc(cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit52")))
|
doc(cfg(any(
|
||||||
|
feature = "lua55",
|
||||||
|
feature = "lua54",
|
||||||
|
feature = "lua53",
|
||||||
|
feature = "lua52",
|
||||||
|
feature = "luajit52"
|
||||||
|
)))
|
||||||
)]
|
)]
|
||||||
Pairs,
|
Pairs,
|
||||||
/// The `__ipairs` metamethod.
|
/// The `__ipairs` metamethod.
|
||||||
@@ -135,8 +167,8 @@ pub enum MetaMethod {
|
|||||||
/// [documentation][lua_doc].
|
/// [documentation][lua_doc].
|
||||||
///
|
///
|
||||||
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#3.3.8
|
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#3.3.8
|
||||||
#[cfg(feature = "lua54")]
|
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua55", feature = "lua54"))))]
|
||||||
Close,
|
Close,
|
||||||
/// The `__name`/`__type` metafield.
|
/// The `__name`/`__type` metafield.
|
||||||
///
|
///
|
||||||
@@ -152,7 +184,7 @@ impl PartialEq<MetaMethod> for &str {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq<MetaMethod> for StdString {
|
impl PartialEq<MetaMethod> for String {
|
||||||
fn eq(&self, other: &MetaMethod) -> bool {
|
fn eq(&self, other: &MetaMethod) -> bool {
|
||||||
self == other.name()
|
self == other.name()
|
||||||
}
|
}
|
||||||
@@ -176,19 +208,19 @@ impl MetaMethod {
|
|||||||
MetaMethod::Pow => "__pow",
|
MetaMethod::Pow => "__pow",
|
||||||
MetaMethod::Unm => "__unm",
|
MetaMethod::Unm => "__unm",
|
||||||
|
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||||
MetaMethod::IDiv => "__idiv",
|
MetaMethod::IDiv => "__idiv",
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||||
MetaMethod::BAnd => "__band",
|
MetaMethod::BAnd => "__band",
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||||
MetaMethod::BOr => "__bor",
|
MetaMethod::BOr => "__bor",
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||||
MetaMethod::BXor => "__bxor",
|
MetaMethod::BXor => "__bxor",
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||||
MetaMethod::BNot => "__bnot",
|
MetaMethod::BNot => "__bnot",
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||||
MetaMethod::Shl => "__shl",
|
MetaMethod::Shl => "__shl",
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||||
MetaMethod::Shr => "__shr",
|
MetaMethod::Shr => "__shr",
|
||||||
|
|
||||||
MetaMethod::Concat => "__concat",
|
MetaMethod::Concat => "__concat",
|
||||||
@@ -201,14 +233,20 @@ impl MetaMethod {
|
|||||||
MetaMethod::Call => "__call",
|
MetaMethod::Call => "__call",
|
||||||
MetaMethod::ToString => "__tostring",
|
MetaMethod::ToString => "__tostring",
|
||||||
|
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit52"))]
|
#[cfg(any(
|
||||||
|
feature = "lua55",
|
||||||
|
feature = "lua54",
|
||||||
|
feature = "lua53",
|
||||||
|
feature = "lua52",
|
||||||
|
feature = "luajit52"
|
||||||
|
))]
|
||||||
MetaMethod::Pairs => "__pairs",
|
MetaMethod::Pairs => "__pairs",
|
||||||
#[cfg(any(feature = "lua52", feature = "luajit52"))]
|
#[cfg(any(feature = "lua52", feature = "luajit52"))]
|
||||||
MetaMethod::IPairs => "__ipairs",
|
MetaMethod::IPairs => "__ipairs",
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
MetaMethod::Iter => "__iter",
|
MetaMethod::Iter => "__iter",
|
||||||
|
|
||||||
#[cfg(feature = "lua54")]
|
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||||
MetaMethod::Close => "__close",
|
MetaMethod::Close => "__close",
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
@@ -240,6 +278,13 @@ impl AsRef<str> for MetaMethod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<MetaMethod> for String {
|
||||||
|
#[inline]
|
||||||
|
fn from(method: MetaMethod) -> Self {
|
||||||
|
method.name().to_owned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Method registry for [`UserData`] implementors.
|
/// Method registry for [`UserData`] implementors.
|
||||||
pub trait UserDataMethods<T> {
|
pub trait UserDataMethods<T> {
|
||||||
/// Add a regular method which accepts a `&T` as the first parameter.
|
/// Add a regular method which accepts a `&T` as the first parameter.
|
||||||
@@ -249,7 +294,7 @@ pub trait UserDataMethods<T> {
|
|||||||
///
|
///
|
||||||
/// If `add_meta_method` is used to set the `__index` metamethod, the `__index` metamethod will
|
/// If `add_meta_method` is used to set the `__index` metamethod, the `__index` metamethod will
|
||||||
/// be used as a fall-back if no regular method is found.
|
/// be used as a fall-back if no regular method is found.
|
||||||
fn add_method<M, A, R>(&mut self, name: impl ToString, method: M)
|
fn add_method<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||||
where
|
where
|
||||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||||
A: FromLuaMulti,
|
A: FromLuaMulti,
|
||||||
@@ -260,12 +305,35 @@ pub trait UserDataMethods<T> {
|
|||||||
/// Refer to [`add_method`] for more information about the implementation.
|
/// Refer to [`add_method`] for more information about the implementation.
|
||||||
///
|
///
|
||||||
/// [`add_method`]: UserDataMethods::add_method
|
/// [`add_method`]: UserDataMethods::add_method
|
||||||
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<String>, method: M)
|
||||||
where
|
where
|
||||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||||
A: FromLuaMulti,
|
A: FromLuaMulti,
|
||||||
R: IntoLuaMulti;
|
R: IntoLuaMulti;
|
||||||
|
|
||||||
|
/// Add a method which accepts `T` as the first parameter.
|
||||||
|
///
|
||||||
|
/// The userdata `T` will be moved out of the userdata container. This is useful for
|
||||||
|
/// methods that need to consume the userdata.
|
||||||
|
///
|
||||||
|
/// The method can be called only once per userdata instance, subsequent calls will result in a
|
||||||
|
/// [`Error::UserDataDestructed`] error.
|
||||||
|
#[doc(hidden)]
|
||||||
|
fn add_method_once<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||||
|
where
|
||||||
|
T: 'static,
|
||||||
|
M: Fn(&Lua, T, A) -> Result<R> + MaybeSend + 'static,
|
||||||
|
A: FromLuaMulti,
|
||||||
|
R: IntoLuaMulti,
|
||||||
|
{
|
||||||
|
let name = name.into();
|
||||||
|
let method_name = format!("{}.{name}", short_type_name::<T>());
|
||||||
|
self.add_function(name, move |lua, (ud, args): (AnyUserData, A)| {
|
||||||
|
let this = (ud.take()).map_err(|err| Error::bad_self_argument(&method_name, err))?;
|
||||||
|
method(lua, this, args)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Add an async method which accepts a `&T` as the first parameter and returns [`Future`].
|
/// Add an async method which accepts a `&T` as the first parameter and returns [`Future`].
|
||||||
///
|
///
|
||||||
/// Refer to [`add_method`] for more information about the implementation.
|
/// Refer to [`add_method`] for more information about the implementation.
|
||||||
@@ -273,7 +341,7 @@ pub trait UserDataMethods<T> {
|
|||||||
/// [`add_method`]: UserDataMethods::add_method
|
/// [`add_method`]: UserDataMethods::add_method
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
#[cfg_attr(docsrs, doc(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<String>, method: M)
|
||||||
where
|
where
|
||||||
T: 'static,
|
T: 'static,
|
||||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||||
@@ -288,7 +356,7 @@ pub trait UserDataMethods<T> {
|
|||||||
/// [`add_method`]: UserDataMethods::add_method
|
/// [`add_method`]: UserDataMethods::add_method
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
#[cfg_attr(docsrs, doc(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<String>, method: M)
|
||||||
where
|
where
|
||||||
T: 'static,
|
T: 'static,
|
||||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||||
@@ -296,12 +364,40 @@ pub trait UserDataMethods<T> {
|
|||||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||||
R: IntoLuaMulti;
|
R: IntoLuaMulti;
|
||||||
|
|
||||||
|
/// Add an async method which accepts a `T` as the first parameter and returns [`Future`].
|
||||||
|
///
|
||||||
|
/// The userdata `T` will be moved out of the userdata container. This is useful for
|
||||||
|
/// methods that need to consume the userdata.
|
||||||
|
///
|
||||||
|
/// The method can be called only once per userdata instance, subsequent calls will result in a
|
||||||
|
/// [`Error::UserDataDestructed`] error.
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||||
|
#[doc(hidden)]
|
||||||
|
fn add_async_method_once<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||||
|
where
|
||||||
|
T: 'static,
|
||||||
|
M: Fn(Lua, T, A) -> MR + MaybeSend + 'static,
|
||||||
|
A: FromLuaMulti,
|
||||||
|
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||||
|
R: IntoLuaMulti,
|
||||||
|
{
|
||||||
|
let name = name.into();
|
||||||
|
let method_name = format!("{}.{name}", short_type_name::<T>());
|
||||||
|
self.add_async_function(name, move |lua, (ud, args): (AnyUserData, A)| {
|
||||||
|
match (ud.take()).map_err(|err| Error::bad_self_argument(&method_name, err)) {
|
||||||
|
Ok(this) => either::Either::Left(method(lua, this, args)),
|
||||||
|
Err(err) => either::Either::Right(async move { Err(err) }),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Add a regular method as a function which accepts generic arguments.
|
/// Add a regular method as a function which accepts generic arguments.
|
||||||
///
|
///
|
||||||
/// The first argument will be a [`AnyUserData`] of type `T` if the method is called with Lua
|
/// The first argument will be a [`AnyUserData`] of type `T` if the method is called with Lua
|
||||||
/// method syntax: `my_userdata:my_method(arg1, arg2)`, or it is passed in as the first
|
/// method syntax: `my_userdata:my_method(arg1, arg2)`, or it is passed in as the first
|
||||||
/// argument: `my_userdata.my_method(my_userdata, arg1, arg2)`.
|
/// argument: `my_userdata.my_method(my_userdata, arg1, arg2)`.
|
||||||
fn add_function<F, A, R>(&mut self, name: impl ToString, function: F)
|
fn add_function<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||||
where
|
where
|
||||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||||
A: FromLuaMulti,
|
A: FromLuaMulti,
|
||||||
@@ -312,7 +408,7 @@ pub trait UserDataMethods<T> {
|
|||||||
/// This is a version of [`add_function`] that accepts a `FnMut` argument.
|
/// This is a version of [`add_function`] that accepts a `FnMut` argument.
|
||||||
///
|
///
|
||||||
/// [`add_function`]: UserDataMethods::add_function
|
/// [`add_function`]: UserDataMethods::add_function
|
||||||
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<String>, function: F)
|
||||||
where
|
where
|
||||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||||
A: FromLuaMulti,
|
A: FromLuaMulti,
|
||||||
@@ -326,7 +422,7 @@ pub trait UserDataMethods<T> {
|
|||||||
/// [`add_function`]: UserDataMethods::add_function
|
/// [`add_function`]: UserDataMethods::add_function
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
#[cfg_attr(docsrs, doc(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<String>, function: F)
|
||||||
where
|
where
|
||||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||||
A: FromLuaMulti,
|
A: FromLuaMulti,
|
||||||
@@ -341,7 +437,7 @@ pub trait UserDataMethods<T> {
|
|||||||
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
||||||
///
|
///
|
||||||
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
||||||
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<String>, method: M)
|
||||||
where
|
where
|
||||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||||
A: FromLuaMulti,
|
A: FromLuaMulti,
|
||||||
@@ -355,7 +451,7 @@ pub trait UserDataMethods<T> {
|
|||||||
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
||||||
///
|
///
|
||||||
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
||||||
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<String>, method: M)
|
||||||
where
|
where
|
||||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||||
A: FromLuaMulti,
|
A: FromLuaMulti,
|
||||||
@@ -371,7 +467,7 @@ pub trait UserDataMethods<T> {
|
|||||||
docsrs,
|
docsrs,
|
||||||
doc(cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau")))))
|
doc(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<String>, method: M)
|
||||||
where
|
where
|
||||||
T: 'static,
|
T: 'static,
|
||||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||||
@@ -387,7 +483,7 @@ pub trait UserDataMethods<T> {
|
|||||||
/// [`add_meta_method_mut`]: UserDataMethods::add_meta_method_mut
|
/// [`add_meta_method_mut`]: UserDataMethods::add_meta_method_mut
|
||||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||||
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<String>, method: M)
|
||||||
where
|
where
|
||||||
T: 'static,
|
T: 'static,
|
||||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||||
@@ -400,7 +496,7 @@ pub trait UserDataMethods<T> {
|
|||||||
/// Metamethods for binary operators can be triggered if either the left or right argument to
|
/// Metamethods for binary operators can be triggered if either the left or right argument to
|
||||||
/// the binary operator has a metatable, so the first argument here is not necessarily a
|
/// the binary operator has a metatable, so the first argument here is not necessarily a
|
||||||
/// userdata of type `T`.
|
/// userdata of type `T`.
|
||||||
fn add_meta_function<F, A, R>(&mut self, name: impl ToString, function: F)
|
fn add_meta_function<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||||
where
|
where
|
||||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||||
A: FromLuaMulti,
|
A: FromLuaMulti,
|
||||||
@@ -411,7 +507,7 @@ pub trait UserDataMethods<T> {
|
|||||||
/// This is a version of [`add_meta_function`] that accepts a `FnMut` argument.
|
/// This is a version of [`add_meta_function`] that accepts a `FnMut` argument.
|
||||||
///
|
///
|
||||||
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
||||||
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<String>, function: F)
|
||||||
where
|
where
|
||||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||||
A: FromLuaMulti,
|
A: FromLuaMulti,
|
||||||
@@ -427,7 +523,7 @@ pub trait UserDataMethods<T> {
|
|||||||
docsrs,
|
docsrs,
|
||||||
doc(cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau")))))
|
doc(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<String>, function: F)
|
||||||
where
|
where
|
||||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||||
A: FromLuaMulti,
|
A: FromLuaMulti,
|
||||||
@@ -446,7 +542,7 @@ pub trait UserDataFields<T> {
|
|||||||
///
|
///
|
||||||
/// If `add_meta_method` is used to set the `__index` metamethod, it will
|
/// If `add_meta_method` is used to set the `__index` metamethod, it will
|
||||||
/// be used as a fall-back if no regular field or method are found.
|
/// be used as a fall-back if no regular field or method are found.
|
||||||
fn add_field<V>(&mut self, name: impl ToString, value: V)
|
fn add_field<V>(&mut self, name: impl Into<String>, value: V)
|
||||||
where
|
where
|
||||||
V: IntoLua + 'static;
|
V: IntoLua + 'static;
|
||||||
|
|
||||||
@@ -457,7 +553,7 @@ pub trait UserDataFields<T> {
|
|||||||
///
|
///
|
||||||
/// If `add_meta_method` is used to set the `__index` metamethod, the `__index` metamethod will
|
/// If `add_meta_method` is used to set the `__index` metamethod, the `__index` metamethod will
|
||||||
/// be used as a fall-back if no regular field or method are found.
|
/// be used as a fall-back if no regular field or method are found.
|
||||||
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<String>, method: M)
|
||||||
where
|
where
|
||||||
M: Fn(&Lua, &T) -> Result<R> + MaybeSend + 'static,
|
M: Fn(&Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||||
R: IntoLua;
|
R: IntoLua;
|
||||||
@@ -470,21 +566,21 @@ pub trait UserDataFields<T> {
|
|||||||
///
|
///
|
||||||
/// If `add_meta_method` is used to set the `__newindex` metamethod, the `__newindex` metamethod
|
/// If `add_meta_method` is used to set the `__newindex` metamethod, the `__newindex` metamethod
|
||||||
/// will be used as a fall-back if no regular field is found.
|
/// will be used as a fall-back if no regular field is found.
|
||||||
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<String>, method: M)
|
||||||
where
|
where
|
||||||
M: FnMut(&Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
M: FnMut(&Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
||||||
A: FromLua;
|
A: FromLua;
|
||||||
|
|
||||||
/// Add a regular field getter as a function which accepts a generic [`AnyUserData`] of type `T`
|
/// Add a regular field getter as a function which accepts a generic [`AnyUserData`] of type `T`
|
||||||
/// argument.
|
/// argument.
|
||||||
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<String>, function: F)
|
||||||
where
|
where
|
||||||
F: Fn(&Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
|
F: Fn(&Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
|
||||||
R: IntoLua;
|
R: IntoLua;
|
||||||
|
|
||||||
/// Add a regular field setter as a function which accepts a generic [`AnyUserData`] of type `T`
|
/// Add a regular field setter as a function which accepts a generic [`AnyUserData`] of type `T`
|
||||||
/// first argument.
|
/// first argument.
|
||||||
fn add_field_function_set<F, A>(&mut self, name: impl ToString, function: F)
|
fn add_field_function_set<F, A>(&mut self, name: impl Into<String>, function: F)
|
||||||
where
|
where
|
||||||
F: FnMut(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
|
F: FnMut(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
|
||||||
A: FromLua;
|
A: FromLua;
|
||||||
@@ -497,7 +593,7 @@ pub trait UserDataFields<T> {
|
|||||||
///
|
///
|
||||||
/// `mlua` will trigger an error on an attempt to define a protected metamethod,
|
/// `mlua` will trigger an error on an attempt to define a protected metamethod,
|
||||||
/// like `__gc` or `__metatable`.
|
/// like `__gc` or `__metatable`.
|
||||||
fn add_meta_field<V>(&mut self, name: impl ToString, value: V)
|
fn add_meta_field<V>(&mut self, name: impl Into<String>, value: V)
|
||||||
where
|
where
|
||||||
V: IntoLua + 'static;
|
V: IntoLua + 'static;
|
||||||
|
|
||||||
@@ -509,7 +605,7 @@ pub trait UserDataFields<T> {
|
|||||||
///
|
///
|
||||||
/// `mlua` will trigger an error on an attempt to define a protected metamethod,
|
/// `mlua` will trigger an error on an attempt to define a protected metamethod,
|
||||||
/// like `__gc` or `__metatable`.
|
/// like `__gc` or `__metatable`.
|
||||||
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<String>, f: F)
|
||||||
where
|
where
|
||||||
F: FnOnce(&Lua) -> Result<R> + 'static,
|
F: FnOnce(&Lua) -> Result<R> + 'static,
|
||||||
R: IntoLua;
|
R: IntoLua;
|
||||||
@@ -621,6 +717,14 @@ impl AnyUserData {
|
|||||||
matches!(type_id, Some(type_id) if type_id == TypeId::of::<T>())
|
matches!(type_id, Some(type_id) if type_id == TypeId::of::<T>())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks whether the type of this userdata is a [proxy object] for `T`.
|
||||||
|
///
|
||||||
|
/// [proxy object]: crate::Lua::create_proxy
|
||||||
|
#[inline]
|
||||||
|
pub fn is_proxy<T: 'static>(&self) -> bool {
|
||||||
|
self.is::<UserDataProxy<T>>()
|
||||||
|
}
|
||||||
|
|
||||||
/// Borrow this userdata immutably if it is of type `T`.
|
/// Borrow this userdata immutably if it is of type `T`.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
@@ -683,22 +787,16 @@ impl AnyUserData {
|
|||||||
/// Keeps associated user values unchanged (they will be collected by Lua's GC).
|
/// Keeps associated user values unchanged (they will be collected by Lua's GC).
|
||||||
pub fn take<T: 'static>(&self) -> Result<T> {
|
pub fn take<T: 'static>(&self) -> Result<T> {
|
||||||
let lua = self.0.lua.lock();
|
let lua = self.0.lua.lock();
|
||||||
let state = lua.state();
|
match lua.get_userdata_ref_type_id(&self.0)? {
|
||||||
unsafe {
|
Some(type_id) if type_id == TypeId::of::<T>() => unsafe {
|
||||||
let _sg = StackGuard::new(state);
|
let ref_thread = lua.ref_thread();
|
||||||
check_stack(state, 2)?;
|
if (*get_userdata::<UserDataStorage<T>>(ref_thread, self.0.index)).has_exclusive_access() {
|
||||||
|
take_userdata::<UserDataStorage<T>>(ref_thread, self.0.index).into_inner()
|
||||||
let type_id = lua.push_userdata_ref(&self.0)?;
|
} else {
|
||||||
match type_id {
|
Err(Error::UserDataBorrowMutError)
|
||||||
Some(type_id) if type_id == TypeId::of::<T>() => {
|
|
||||||
if (*get_userdata::<UserDataStorage<T>>(state, -1)).has_exclusive_access() {
|
|
||||||
take_userdata::<UserDataStorage<T>>(state).into_inner()
|
|
||||||
} else {
|
|
||||||
Err(Error::UserDataBorrowMutError)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => Err(Error::UserDataTypeMismatch),
|
},
|
||||||
}
|
_ => Err(Error::UserDataTypeMismatch),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -887,16 +985,17 @@ impl AnyUserData {
|
|||||||
self.raw_metatable().map(UserDataMetatable)
|
self.raw_metatable().map(UserDataMetatable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a raw metatable of this [`AnyUserData`].
|
||||||
fn raw_metatable(&self) -> Result<Table> {
|
fn raw_metatable(&self) -> Result<Table> {
|
||||||
let lua = self.0.lua.lock();
|
let lua = self.0.lua.lock();
|
||||||
let state = lua.state();
|
let ref_thread = lua.ref_thread();
|
||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(state);
|
// Check that userdata is registered and not destructed
|
||||||
check_stack(state, 3)?;
|
// All registered userdata types have a non-empty metatable
|
||||||
|
let _type_id = lua.get_userdata_ref_type_id(&self.0)?;
|
||||||
|
|
||||||
lua.push_userdata_ref(&self.0)?;
|
ffi::lua_getmetatable(ref_thread, self.0.index);
|
||||||
ffi::lua_getmetatable(state, -1); // Checked that non-empty on the previous call
|
Ok(Table(lua.pop_ref_thread()))
|
||||||
Ok(Table(lua.pop_ref()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -919,8 +1018,10 @@ impl AnyUserData {
|
|||||||
lua.get_userdata_ref_type_id(&self.0).ok().flatten()
|
lua.get_userdata_ref_type_id(&self.0).ok().flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a type name of this `UserData` (from a metatable field).
|
/// Returns a type name of this userdata (from a metatable field).
|
||||||
pub(crate) fn type_name(&self) -> Result<Option<StdString>> {
|
///
|
||||||
|
/// If no type name is set, returns `None`.
|
||||||
|
pub fn type_name(&self) -> Result<Option<String>> {
|
||||||
let lua = self.0.lua.lock();
|
let lua = self.0.lua.lock();
|
||||||
let state = lua.state();
|
let state = lua.state();
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -937,7 +1038,7 @@ impl AnyUserData {
|
|||||||
ffi::luaL_getmetafield(state, -1, MetaMethod::Type.as_cstr().as_ptr())
|
ffi::luaL_getmetafield(state, -1, MetaMethod::Type.as_cstr().as_ptr())
|
||||||
};
|
};
|
||||||
match name_type {
|
match name_type {
|
||||||
ffi::LUA_TSTRING => Ok(Some(String(lua.pop_ref()).to_str()?.to_owned())),
|
ffi::LUA_TSTRING => Ok(Some(LuaString(lua.pop_ref()).to_str()?.to_owned())),
|
||||||
_ => Ok(None),
|
_ => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -963,7 +1064,7 @@ impl AnyUserData {
|
|||||||
|
|
||||||
/// Returns `true` if this [`AnyUserData`] is serializable (e.g. was created using
|
/// Returns `true` if this [`AnyUserData`] is serializable (e.g. was created using
|
||||||
/// [`Lua::create_ser_userdata`]).
|
/// [`Lua::create_ser_userdata`]).
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
pub(crate) fn is_serializable(&self) -> bool {
|
pub(crate) fn is_serializable(&self) -> bool {
|
||||||
let lua = self.0.lua.lock();
|
let lua = self.0.lua.lock();
|
||||||
let is_serializable = || unsafe {
|
let is_serializable = || unsafe {
|
||||||
@@ -1014,7 +1115,7 @@ impl UserDataMetatable {
|
|||||||
/// The pairs are wrapped in a [`Result`], since they are lazily converted to `V` type.
|
/// The pairs are wrapped in a [`Result`], since they are lazily converted to `V` type.
|
||||||
///
|
///
|
||||||
/// [`Result`]: crate::Result
|
/// [`Result`]: crate::Result
|
||||||
pub fn pairs<V: FromLua>(&self) -> UserDataMetatablePairs<V> {
|
pub fn pairs<V: FromLua>(&self) -> UserDataMetatablePairs<'_, V> {
|
||||||
UserDataMetatablePairs(self.0.pairs())
|
UserDataMetatablePairs(self.0.pairs())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1024,13 +1125,13 @@ impl UserDataMetatable {
|
|||||||
/// It skips restricted metamethods, such as `__gc` or `__metatable`.
|
/// It skips restricted metamethods, such as `__gc` or `__metatable`.
|
||||||
///
|
///
|
||||||
/// This struct is created by the [`UserDataMetatable::pairs`] method.
|
/// This struct is created by the [`UserDataMetatable::pairs`] method.
|
||||||
pub struct UserDataMetatablePairs<'a, V>(TablePairs<'a, StdString, V>);
|
pub struct UserDataMetatablePairs<'a, V>(TablePairs<'a, String, V>);
|
||||||
|
|
||||||
impl<V> Iterator for UserDataMetatablePairs<'_, V>
|
impl<V> Iterator for UserDataMetatablePairs<'_, V>
|
||||||
where
|
where
|
||||||
V: FromLua,
|
V: FromLua,
|
||||||
{
|
{
|
||||||
type Item = Result<(StdString, V)>;
|
type Item = Result<(String, V)>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
loop {
|
loop {
|
||||||
@@ -1047,7 +1148,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
impl Serialize for AnyUserData {
|
impl Serialize for AnyUserData {
|
||||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
@@ -1078,8 +1179,8 @@ impl AnyUserData {
|
|||||||
/// [`IntoLua`] trait.
|
/// [`IntoLua`] trait.
|
||||||
///
|
///
|
||||||
/// This function uses [`Lua::create_ser_any_userdata`] under the hood.
|
/// This function uses [`Lua::create_ser_any_userdata`] under the hood.
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||||
pub fn wrap_ser<T: Serialize + MaybeSend + 'static>(data: T) -> impl IntoLua {
|
pub fn wrap_ser<T: Serialize + MaybeSend + 'static>(data: T) -> impl IntoLua {
|
||||||
WrappedUserdata(move |lua| lua.create_ser_any_userdata(data))
|
WrappedUserdata(move |lua| lua.create_ser_any_userdata(data))
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-16
@@ -1,6 +1,6 @@
|
|||||||
use std::cell::{RefCell, UnsafeCell};
|
use std::cell::{RefCell, UnsafeCell};
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::ser::{Serialize, Serializer};
|
use serde::ser::{Serialize, Serializer};
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
@@ -9,10 +9,10 @@ use crate::types::XRc;
|
|||||||
use super::lock::{RawLock, UserDataLock};
|
use super::lock::{RawLock, UserDataLock};
|
||||||
use super::r#ref::{UserDataRef, UserDataRefMut};
|
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;
|
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;
|
type DynSerialize = dyn erased_serde::Serialize + Send;
|
||||||
|
|
||||||
pub(crate) enum UserDataStorage<T> {
|
pub(crate) enum UserDataStorage<T> {
|
||||||
@@ -24,7 +24,7 @@ pub(crate) enum UserDataStorage<T> {
|
|||||||
// It's stored inside a Lua VM and protected by the outer `ReentrantMutex`.
|
// It's stored inside a Lua VM and protected by the outer `ReentrantMutex`.
|
||||||
pub(crate) enum UserDataVariant<T> {
|
pub(crate) enum UserDataVariant<T> {
|
||||||
Default(XRc<UserDataCell<T>>),
|
Default(XRc<UserDataCell<T>>),
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
Serializable(XRc<UserDataCell<Box<DynSerialize>>>, bool), // bool is `is_sync`
|
Serializable(XRc<UserDataCell<Box<DynSerialize>>>, bool), // bool is `is_sync`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ impl<T> Clone for UserDataVariant<T> {
|
|||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Self::Default(inner) => Self::Default(XRc::clone(inner)),
|
Self::Default(inner) => Self::Default(XRc::clone(inner)),
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
Self::Serializable(inner, is_sync) => Self::Serializable(XRc::clone(inner), *is_sync),
|
Self::Serializable(inner, is_sync) => Self::Serializable(XRc::clone(inner), *is_sync),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,7 +79,7 @@ impl<T> UserDataVariant<T> {
|
|||||||
}
|
}
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Self::Default(inner) => XRc::into_inner(inner).unwrap().value.into_inner(),
|
Self::Default(inner) => XRc::into_inner(inner).unwrap().value.into_inner(),
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
Self::Serializable(inner, _) => unsafe {
|
Self::Serializable(inner, _) => unsafe {
|
||||||
let raw = Box::into_raw(XRc::into_inner(inner).unwrap().value.into_inner());
|
let raw = Box::into_raw(XRc::into_inner(inner).unwrap().value.into_inner());
|
||||||
*Box::from_raw(raw as *mut T)
|
*Box::from_raw(raw as *mut T)
|
||||||
@@ -91,7 +91,7 @@ impl<T> UserDataVariant<T> {
|
|||||||
fn strong_count(&self) -> usize {
|
fn strong_count(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Self::Default(inner) => XRc::strong_count(inner),
|
Self::Default(inner) => XRc::strong_count(inner),
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
Self::Serializable(inner, _) => XRc::strong_count(inner),
|
Self::Serializable(inner, _) => XRc::strong_count(inner),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,7 +100,7 @@ impl<T> UserDataVariant<T> {
|
|||||||
pub(super) fn raw_lock(&self) -> &RawLock {
|
pub(super) fn raw_lock(&self) -> &RawLock {
|
||||||
match self {
|
match self {
|
||||||
Self::Default(inner) => &inner.raw_lock,
|
Self::Default(inner) => &inner.raw_lock,
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
Self::Serializable(inner, _) => &inner.raw_lock,
|
Self::Serializable(inner, _) => &inner.raw_lock,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,13 +109,13 @@ impl<T> UserDataVariant<T> {
|
|||||||
pub(super) fn as_ptr(&self) -> *mut T {
|
pub(super) fn as_ptr(&self) -> *mut T {
|
||||||
match self {
|
match self {
|
||||||
Self::Default(inner) => inner.value.get(),
|
Self::Default(inner) => inner.value.get(),
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
Self::Serializable(inner, _) => unsafe { &mut **(inner.value.get() as *mut Box<T>) },
|
Self::Serializable(inner, _) => unsafe { &mut **(inner.value.get() as *mut Box<T>) },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
impl Serialize for UserDataStorage<()> {
|
impl Serialize for UserDataStorage<()> {
|
||||||
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
|
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
|
||||||
match self {
|
match self {
|
||||||
@@ -173,10 +173,10 @@ pub(crate) enum ScopedUserDataVariant<T> {
|
|||||||
impl<T> Drop for ScopedUserDataVariant<T> {
|
impl<T> Drop for ScopedUserDataVariant<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if let Self::Boxed(value) = self {
|
if let Self::Boxed(value) = self
|
||||||
if let Ok(value) = value.try_borrow_mut() {
|
&& let Ok(value) = value.try_borrow_mut()
|
||||||
unsafe { drop(Box::from_raw(*value)) };
|
{
|
||||||
}
|
unsafe { drop(Box::from_raw(*value)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,7 +197,7 @@ impl<T: 'static> UserDataStorage<T> {
|
|||||||
Self::Scoped(ScopedUserDataVariant::RefMut(RefCell::new(data)))
|
Self::Scoped(ScopedUserDataVariant::RefMut(RefCell::new(data)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn new_ser(data: T) -> Self
|
pub(crate) fn new_ser(data: T) -> Self
|
||||||
where
|
where
|
||||||
@@ -209,7 +209,7 @@ impl<T: 'static> UserDataStorage<T> {
|
|||||||
Self::Owned(variant)
|
Self::Owned(variant)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn is_serializable(&self) -> bool {
|
pub(crate) fn is_serializable(&self) -> bool {
|
||||||
matches!(self, Self::Owned(UserDataVariant::Serializable(..)))
|
matches!(self, Self::Owned(UserDataVariant::Serializable(..)))
|
||||||
|
|||||||
+25
-16
@@ -1,28 +1,27 @@
|
|||||||
use std::string::String as StdString;
|
use crate::Function;
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
|
use crate::state::WeakLua;
|
||||||
use crate::table::Table;
|
use crate::table::Table;
|
||||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike};
|
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike};
|
||||||
use crate::userdata::AnyUserData;
|
use crate::userdata::AnyUserData;
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use crate::Function;
|
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
use futures_util::future::{self, Either, Future};
|
use crate::function::AsyncCallFuture;
|
||||||
|
|
||||||
impl ObjectLike for AnyUserData {
|
impl ObjectLike for AnyUserData {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
|
fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
|
||||||
// `lua_gettable` method used under the hood can work with any Lua value
|
// `lua_gettable` method used under the hood can work with any Lua value
|
||||||
// that has `__index` metamethod
|
// that has `__index` metamethod
|
||||||
Table(self.0.copy()).get_protected(key)
|
Table(self.0.clone()).get_protected(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
|
fn set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
|
||||||
// `lua_settable` method used under the hood can work with any Lua value
|
// `lua_settable` method used under the hood can work with any Lua value
|
||||||
// that has `__newindex` metamethod
|
// that has `__newindex` metamethod
|
||||||
Table(self.0.copy()).set_protected(key, value)
|
Table(self.0.clone()).set_protected(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -30,16 +29,16 @@ impl ObjectLike for AnyUserData {
|
|||||||
where
|
where
|
||||||
R: FromLuaMulti,
|
R: FromLuaMulti,
|
||||||
{
|
{
|
||||||
Function(self.0.copy()).call(args)
|
Function(self.0.clone()).call(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
#[inline]
|
#[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
|
where
|
||||||
R: FromLuaMulti,
|
R: FromLuaMulti,
|
||||||
{
|
{
|
||||||
Function(self.0.copy()).call_async(args)
|
Function(self.0.clone()).call_async(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -51,7 +50,7 @@ impl ObjectLike for AnyUserData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[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
|
where
|
||||||
R: FromLuaMulti,
|
R: FromLuaMulti,
|
||||||
{
|
{
|
||||||
@@ -72,22 +71,32 @@ impl ObjectLike for AnyUserData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[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
|
where
|
||||||
R: FromLuaMulti,
|
R: FromLuaMulti,
|
||||||
{
|
{
|
||||||
match self.get(name) {
|
match self.get(name) {
|
||||||
Ok(Value::Function(func)) => Either::Left(func.call_async(args)),
|
Ok(Value::Function(func)) => func.call_async(args),
|
||||||
Ok(val) => {
|
Ok(val) => {
|
||||||
let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
|
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]
|
#[inline]
|
||||||
fn to_string(&self) -> Result<StdString> {
|
fn to_string(&self) -> Result<String> {
|
||||||
Value::UserData(AnyUserData(self.0.copy())).to_string()
|
Value::UserData(self.clone()).to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_value(&self) -> Value {
|
||||||
|
Value::UserData(self.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn weak_lua(&self) -> &WeakLua {
|
||||||
|
&self.0.lua
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-7
@@ -1,4 +1,4 @@
|
|||||||
use std::any::{type_name, TypeId};
|
use std::any::{TypeId, type_name};
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
use std::{fmt, mem};
|
use std::{fmt, mem};
|
||||||
@@ -63,7 +63,7 @@ impl<T> TryFrom<UserDataVariant<T>> for UserDataRef<T> {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_from(variant: UserDataVariant<T>) -> Result<Self> {
|
fn try_from(variant: UserDataVariant<T>) -> Result<Self> {
|
||||||
let guard = if !cfg!(feature = "send") || is_sync::<T>() {
|
let guard = if cfg!(not(feature = "send")) || is_sync::<T>() {
|
||||||
variant.raw_lock().try_lock_shared_guarded()
|
variant.raw_lock().try_lock_shared_guarded()
|
||||||
} else {
|
} else {
|
||||||
variant.raw_lock().try_lock_exclusive_guarded()
|
variant.raw_lock().try_lock_exclusive_guarded()
|
||||||
@@ -446,11 +446,11 @@ impl<T> DerefMut for UserDataRefMutInner<T> {
|
|||||||
fn try_value_to_userdata<T>(value: Value) -> Result<AnyUserData> {
|
fn try_value_to_userdata<T>(value: Value) -> Result<AnyUserData> {
|
||||||
match value {
|
match value {
|
||||||
Value::UserData(ud) => Ok(ud),
|
Value::UserData(ud) => Ok(ud),
|
||||||
_ => Err(Error::FromLuaConversionError {
|
_ => Err(Error::from_lua_conversion(
|
||||||
from: value.type_name(),
|
value.type_name(),
|
||||||
to: "userdata".to_string(),
|
"userdata",
|
||||||
message: Some(format!("expected userdata of type {}", type_name::<T>())),
|
format!("expected userdata of type {}", type_name::<T>()),
|
||||||
}),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+69
-48
@@ -4,15 +4,14 @@ use std::any::TypeId;
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
use std::string::String as StdString;
|
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::state::{Lua, LuaGuard};
|
use crate::state::{Lua, LuaGuard};
|
||||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||||
use crate::types::{Callback, MaybeSend};
|
use crate::types::{Callback, MaybeSend};
|
||||||
use crate::userdata::{
|
use crate::userdata::{
|
||||||
borrow_userdata_scoped, borrow_userdata_scoped_mut, AnyUserData, MetaMethod, TypeIdHints, UserData,
|
AnyUserData, MetaMethod, TypeIdHints, UserData, UserDataFields, UserDataMethods, UserDataStorage,
|
||||||
UserDataFields, UserDataMethods, UserDataStorage,
|
borrow_userdata_scoped, borrow_userdata_scoped_mut,
|
||||||
};
|
};
|
||||||
use crate::util::short_type_name;
|
use crate::util::short_type_name;
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
@@ -55,7 +54,10 @@ pub(crate) struct RawUserDataRegistry {
|
|||||||
|
|
||||||
pub(crate) destructor: ffi::lua_CFunction,
|
pub(crate) destructor: ffi::lua_CFunction,
|
||||||
pub(crate) type_id: Option<TypeId>,
|
pub(crate) type_id: Option<TypeId>,
|
||||||
pub(crate) type_name: StdString,
|
pub(crate) type_name: String,
|
||||||
|
|
||||||
|
#[cfg(feature = "luau")]
|
||||||
|
pub(crate) enable_namecall: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserDataType {
|
impl UserDataType {
|
||||||
@@ -100,6 +102,8 @@ impl<T> UserDataRegistry<T> {
|
|||||||
destructor: super::util::destroy_userdata_storage::<T>,
|
destructor: super::util::destroy_userdata_storage::<T>,
|
||||||
type_id: r#type.type_id(),
|
type_id: r#type.type_id(),
|
||||||
type_name: short_type_name::<T>(),
|
type_name: short_type_name::<T>(),
|
||||||
|
#[cfg(feature = "luau")]
|
||||||
|
enable_namecall: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
UserDataRegistry {
|
UserDataRegistry {
|
||||||
@@ -110,6 +114,23 @@ impl<T> UserDataRegistry<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enables support for the namecall optimization in Luau.
|
||||||
|
///
|
||||||
|
/// This enables methods resolution optimization in Luau for complex userdata types with methods
|
||||||
|
/// and field getters. When enabled, Luau will use a faster lookup path for method calls when a
|
||||||
|
/// specific syntax is used (e.g. `obj:method()`.
|
||||||
|
///
|
||||||
|
/// This optimization does not play well with async methods, custom `__index` metamethod and
|
||||||
|
/// field getters as functions. So, it is disabled by default.
|
||||||
|
///
|
||||||
|
/// Use with caution.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[cfg(feature = "luau")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||||
|
pub fn enable_namecall(&mut self) {
|
||||||
|
self.raw.enable_namecall = true;
|
||||||
|
}
|
||||||
|
|
||||||
fn box_method<M, A, R>(&self, name: &str, method: M) -> Callback
|
fn box_method<M, A, R>(&self, name: &str, method: M) -> Callback
|
||||||
where
|
where
|
||||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||||
@@ -346,7 +367,7 @@ impl<T> UserDataRegistry<T> {
|
|||||||
method: name.to_string(),
|
method: name.to_string(),
|
||||||
type_name: value.type_name(),
|
type_name: value.type_name(),
|
||||||
message: Some("expected nil, table or function".to_string()),
|
message: Some("expected nil, table or function".to_string()),
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -360,106 +381,106 @@ impl<T> UserDataRegistry<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns function name for the type `T`, without the module path
|
// Returns function name for the type `T`, without the module path
|
||||||
fn get_function_name<T>(name: &str) -> StdString {
|
fn get_function_name<T>(name: &str) -> String {
|
||||||
format!("{}.{name}", short_type_name::<T>())
|
format!("{}.{name}", short_type_name::<T>())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||||
fn add_field<V>(&mut self, name: impl ToString, value: V)
|
fn add_field<V>(&mut self, name: impl Into<String>, value: V)
|
||||||
where
|
where
|
||||||
V: IntoLua + 'static,
|
V: IntoLua + 'static,
|
||||||
{
|
{
|
||||||
let name = name.to_string();
|
let name = name.into();
|
||||||
self.raw.fields.push((name, value.into_lua(self.lua.lua())));
|
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<String>, method: M)
|
||||||
where
|
where
|
||||||
M: Fn(&Lua, &T) -> Result<R> + MaybeSend + 'static,
|
M: Fn(&Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||||
R: IntoLua,
|
R: IntoLua,
|
||||||
{
|
{
|
||||||
let name = name.to_string();
|
let name = name.into();
|
||||||
let callback = self.box_method(&name, move |lua, data, ()| method(lua, data));
|
let callback = self.box_method(&name, move |lua, data, ()| method(lua, data));
|
||||||
self.raw.field_getters.push((name, callback));
|
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<String>, method: M)
|
||||||
where
|
where
|
||||||
M: FnMut(&Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
M: FnMut(&Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
||||||
A: FromLua,
|
A: FromLua,
|
||||||
{
|
{
|
||||||
let name = name.to_string();
|
let name = name.into();
|
||||||
let callback = self.box_method_mut(&name, method);
|
let callback = self.box_method_mut(&name, method);
|
||||||
self.raw.field_setters.push((name, callback));
|
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<String>, function: F)
|
||||||
where
|
where
|
||||||
F: Fn(&Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
|
F: Fn(&Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
|
||||||
R: IntoLua,
|
R: IntoLua,
|
||||||
{
|
{
|
||||||
let name = name.to_string();
|
let name = name.into();
|
||||||
let callback = self.box_function(&name, function);
|
let callback = self.box_function(&name, function);
|
||||||
self.raw.field_getters.push((name, callback));
|
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<String>, mut function: F)
|
||||||
where
|
where
|
||||||
F: FnMut(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
|
F: FnMut(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
|
||||||
A: FromLua,
|
A: FromLua,
|
||||||
{
|
{
|
||||||
let name = name.to_string();
|
let name = name.into();
|
||||||
let callback = self.box_function_mut(&name, move |lua, (data, val)| function(lua, data, val));
|
let callback = self.box_function_mut(&name, move |lua, (data, val)| function(lua, data, val));
|
||||||
self.raw.field_setters.push((name, callback));
|
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<String>, value: V)
|
||||||
where
|
where
|
||||||
V: IntoLua + 'static,
|
V: IntoLua + 'static,
|
||||||
{
|
{
|
||||||
let lua = self.lua.lua();
|
let lua = self.lua.lua();
|
||||||
let name = name.to_string();
|
let name = name.into();
|
||||||
let field = Self::check_meta_field(lua, &name, value).and_then(|v| v.into_lua(lua));
|
let field = Self::check_meta_field(lua, &name, value).and_then(|v| v.into_lua(lua));
|
||||||
self.raw.meta_fields.push((name, field));
|
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<String>, f: F)
|
||||||
where
|
where
|
||||||
F: FnOnce(&Lua) -> Result<R> + 'static,
|
F: FnOnce(&Lua) -> Result<R> + 'static,
|
||||||
R: IntoLua,
|
R: IntoLua,
|
||||||
{
|
{
|
||||||
let lua = self.lua.lua();
|
let lua = self.lua.lua();
|
||||||
let name = name.to_string();
|
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)));
|
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));
|
self.raw.meta_fields.push((name, field));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||||
fn add_method<M, A, R>(&mut self, name: impl ToString, method: M)
|
fn add_method<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||||
where
|
where
|
||||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||||
A: FromLuaMulti,
|
A: FromLuaMulti,
|
||||||
R: IntoLuaMulti,
|
R: IntoLuaMulti,
|
||||||
{
|
{
|
||||||
let name = name.to_string();
|
let name = name.into();
|
||||||
let callback = self.box_method(&name, method);
|
let callback = self.box_method(&name, method);
|
||||||
self.raw.methods.push((name, callback));
|
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<String>, method: M)
|
||||||
where
|
where
|
||||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||||
A: FromLuaMulti,
|
A: FromLuaMulti,
|
||||||
R: IntoLuaMulti,
|
R: IntoLuaMulti,
|
||||||
{
|
{
|
||||||
let name = name.to_string();
|
let name = name.into();
|
||||||
let callback = self.box_method_mut(&name, method);
|
let callback = self.box_method_mut(&name, method);
|
||||||
self.raw.methods.push((name, callback));
|
self.raw.methods.push((name, callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[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<String>, method: M)
|
||||||
where
|
where
|
||||||
T: 'static,
|
T: 'static,
|
||||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||||
@@ -467,13 +488,13 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
|||||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||||
R: IntoLuaMulti,
|
R: IntoLuaMulti,
|
||||||
{
|
{
|
||||||
let name = name.to_string();
|
let name = name.into();
|
||||||
let callback = self.box_async_method(&name, method);
|
let callback = self.box_async_method(&name, method);
|
||||||
self.raw.async_methods.push((name, callback));
|
self.raw.async_methods.push((name, callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[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<String>, method: M)
|
||||||
where
|
where
|
||||||
T: 'static,
|
T: 'static,
|
||||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||||
@@ -481,70 +502,70 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
|||||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||||
R: IntoLuaMulti,
|
R: IntoLuaMulti,
|
||||||
{
|
{
|
||||||
let name = name.to_string();
|
let name = name.into();
|
||||||
let callback = self.box_async_method_mut(&name, method);
|
let callback = self.box_async_method_mut(&name, method);
|
||||||
self.raw.async_methods.push((name, callback));
|
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<String>, function: F)
|
||||||
where
|
where
|
||||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||||
A: FromLuaMulti,
|
A: FromLuaMulti,
|
||||||
R: IntoLuaMulti,
|
R: IntoLuaMulti,
|
||||||
{
|
{
|
||||||
let name = name.to_string();
|
let name = name.into();
|
||||||
let callback = self.box_function(&name, function);
|
let callback = self.box_function(&name, function);
|
||||||
self.raw.methods.push((name, callback));
|
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<String>, function: F)
|
||||||
where
|
where
|
||||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||||
A: FromLuaMulti,
|
A: FromLuaMulti,
|
||||||
R: IntoLuaMulti,
|
R: IntoLuaMulti,
|
||||||
{
|
{
|
||||||
let name = name.to_string();
|
let name = name.into();
|
||||||
let callback = self.box_function_mut(&name, function);
|
let callback = self.box_function_mut(&name, function);
|
||||||
self.raw.methods.push((name, callback));
|
self.raw.methods.push((name, callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[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<String>, function: F)
|
||||||
where
|
where
|
||||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||||
A: FromLuaMulti,
|
A: FromLuaMulti,
|
||||||
FR: Future<Output = Result<R>> + MaybeSend + 'static,
|
FR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||||
R: IntoLuaMulti,
|
R: IntoLuaMulti,
|
||||||
{
|
{
|
||||||
let name = name.to_string();
|
let name = name.into();
|
||||||
let callback = self.box_async_function(&name, function);
|
let callback = self.box_async_function(&name, function);
|
||||||
self.raw.async_methods.push((name, callback));
|
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<String>, method: M)
|
||||||
where
|
where
|
||||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||||
A: FromLuaMulti,
|
A: FromLuaMulti,
|
||||||
R: IntoLuaMulti,
|
R: IntoLuaMulti,
|
||||||
{
|
{
|
||||||
let name = name.to_string();
|
let name = name.into();
|
||||||
let callback = self.box_method(&name, method);
|
let callback = self.box_method(&name, method);
|
||||||
self.raw.meta_methods.push((name, callback));
|
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<String>, method: M)
|
||||||
where
|
where
|
||||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||||
A: FromLuaMulti,
|
A: FromLuaMulti,
|
||||||
R: IntoLuaMulti,
|
R: IntoLuaMulti,
|
||||||
{
|
{
|
||||||
let name = name.to_string();
|
let name = name.into();
|
||||||
let callback = self.box_method_mut(&name, method);
|
let callback = self.box_method_mut(&name, method);
|
||||||
self.raw.meta_methods.push((name, callback));
|
self.raw.meta_methods.push((name, callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
#[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<String>, method: M)
|
||||||
where
|
where
|
||||||
T: 'static,
|
T: 'static,
|
||||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||||
@@ -552,13 +573,13 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
|||||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||||
R: IntoLuaMulti,
|
R: IntoLuaMulti,
|
||||||
{
|
{
|
||||||
let name = name.to_string();
|
let name = name.into();
|
||||||
let callback = self.box_async_method(&name, method);
|
let callback = self.box_async_method(&name, method);
|
||||||
self.raw.async_meta_methods.push((name, callback));
|
self.raw.async_meta_methods.push((name, callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
#[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<String>, method: M)
|
||||||
where
|
where
|
||||||
T: 'static,
|
T: 'static,
|
||||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||||
@@ -566,42 +587,42 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
|||||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||||
R: IntoLuaMulti,
|
R: IntoLuaMulti,
|
||||||
{
|
{
|
||||||
let name = name.to_string();
|
let name = name.into();
|
||||||
let callback = self.box_async_method_mut(&name, method);
|
let callback = self.box_async_method_mut(&name, method);
|
||||||
self.raw.async_meta_methods.push((name, callback));
|
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<String>, function: F)
|
||||||
where
|
where
|
||||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||||
A: FromLuaMulti,
|
A: FromLuaMulti,
|
||||||
R: IntoLuaMulti,
|
R: IntoLuaMulti,
|
||||||
{
|
{
|
||||||
let name = name.to_string();
|
let name = name.into();
|
||||||
let callback = self.box_function(&name, function);
|
let callback = self.box_function(&name, function);
|
||||||
self.raw.meta_methods.push((name, callback));
|
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<String>, function: F)
|
||||||
where
|
where
|
||||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||||
A: FromLuaMulti,
|
A: FromLuaMulti,
|
||||||
R: IntoLuaMulti,
|
R: IntoLuaMulti,
|
||||||
{
|
{
|
||||||
let name = name.to_string();
|
let name = name.into();
|
||||||
let callback = self.box_function_mut(&name, function);
|
let callback = self.box_function_mut(&name, function);
|
||||||
self.raw.meta_methods.push((name, callback));
|
self.raw.meta_methods.push((name, callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
#[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<String>, function: F)
|
||||||
where
|
where
|
||||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||||
A: FromLuaMulti,
|
A: FromLuaMulti,
|
||||||
FR: Future<Output = Result<R>> + MaybeSend + 'static,
|
FR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||||
R: IntoLuaMulti,
|
R: IntoLuaMulti,
|
||||||
{
|
{
|
||||||
let name = name.to_string();
|
let name = name.into();
|
||||||
let callback = self.box_async_function(&name, function);
|
let callback = self.box_async_function(&name, function);
|
||||||
self.raw.async_meta_methods.push((name, callback));
|
self.raw.async_meta_methods.push((name, callback));
|
||||||
}
|
}
|
||||||
|
|||||||
+46
-4
@@ -4,8 +4,11 @@ use std::marker::PhantomData;
|
|||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use super::UserDataStorage;
|
use super::UserDataStorage;
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
|
use crate::types::CallbackPtr;
|
||||||
use crate::util::{get_userdata, rawget_field, rawset_field, take_userdata};
|
use crate::util::{get_userdata, rawget_field, rawset_field, take_userdata};
|
||||||
|
|
||||||
// This is a trick to check if a type is `Sync` or not.
|
// This is a trick to check if a type is `Sync` or not.
|
||||||
@@ -244,6 +247,7 @@ pub(crate) unsafe fn init_userdata_metatable(
|
|||||||
field_getters: Option<c_int>,
|
field_getters: Option<c_int>,
|
||||||
field_setters: Option<c_int>,
|
field_setters: Option<c_int>,
|
||||||
methods: Option<c_int>,
|
methods: Option<c_int>,
|
||||||
|
_methods_map: Option<FxHashMap<Vec<u8>, CallbackPtr>>, // Used only in Luau for `__namecall`
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if field_getters.is_some() || methods.is_some() {
|
if field_getters.is_some() || methods.is_some() {
|
||||||
// Push `__index` generator function
|
// Push `__index` generator function
|
||||||
@@ -267,6 +271,13 @@ pub(crate) unsafe fn init_userdata_metatable(
|
|||||||
}
|
}
|
||||||
|
|
||||||
rawset_field(state, metatable, "__index")?;
|
rawset_field(state, metatable, "__index")?;
|
||||||
|
|
||||||
|
#[cfg(feature = "luau")]
|
||||||
|
if let Some(methods_map) = _methods_map {
|
||||||
|
// In Luau we can speedup method calls by providing a dedicated `__namecall` metamethod
|
||||||
|
push_userdata_metatable_namecall(state, methods_map)?;
|
||||||
|
rawset_field(state, metatable, "__namecall")?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(field_setters) = field_setters {
|
if let Some(field_setters) = field_setters {
|
||||||
@@ -354,7 +365,7 @@ unsafe fn init_userdata_metatable_index(state: *mut ffi::lua_State) -> Result<()
|
|||||||
end
|
end
|
||||||
"#;
|
"#;
|
||||||
protect_lua!(state, 0, 1, |state| {
|
protect_lua!(state, 0, 1, |state| {
|
||||||
let ret = ffi::luaL_loadbuffer(state, code.as_ptr(), code.count_bytes(), cstr!("__mlua_index"));
|
let ret = ffi::luaL_loadbuffer(state, code.as_ptr(), code.count_bytes(), cstr!("=__mlua_index"));
|
||||||
if ret != ffi::LUA_OK {
|
if ret != ffi::LUA_OK {
|
||||||
ffi::lua_error(state);
|
ffi::lua_error(state);
|
||||||
}
|
}
|
||||||
@@ -405,7 +416,8 @@ unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Result
|
|||||||
end
|
end
|
||||||
"#;
|
"#;
|
||||||
protect_lua!(state, 0, 1, |state| {
|
protect_lua!(state, 0, 1, |state| {
|
||||||
let ret = ffi::luaL_loadbuffer(state, code.as_ptr(), code.count_bytes(), cstr!("__mlua_newindex"));
|
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 {
|
if ret != ffi::LUA_OK {
|
||||||
ffi::lua_error(state);
|
ffi::lua_error(state);
|
||||||
}
|
}
|
||||||
@@ -424,6 +436,36 @@ unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Result
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "luau")]
|
||||||
|
unsafe fn push_userdata_metatable_namecall(
|
||||||
|
state: *mut ffi::lua_State,
|
||||||
|
methods_map: FxHashMap<Vec<u8>, CallbackPtr>,
|
||||||
|
) -> Result<()> {
|
||||||
|
unsafe extern "C-unwind" fn namecall(state: *mut ffi::lua_State) -> c_int {
|
||||||
|
let name = ffi::lua_namecallatom(state, ptr::null_mut());
|
||||||
|
if name.is_null() {
|
||||||
|
ffi::luaL_error(state, cstr!("attempt to call an unknown method"));
|
||||||
|
}
|
||||||
|
let name_cs = std::ffi::CStr::from_ptr(name);
|
||||||
|
let methods_map = get_userdata::<FxHashMap<Vec<u8>, CallbackPtr>>(state, ffi::lua_upvalueindex(1));
|
||||||
|
let callback_ptr = match (*methods_map).get(name_cs.to_bytes()) {
|
||||||
|
Some(ptr) => *ptr,
|
||||||
|
#[rustfmt::skip]
|
||||||
|
None => ffi::luaL_error(state, cstr!("attempt to call an unknown method '%s'"), name),
|
||||||
|
};
|
||||||
|
crate::state::callback_error_ext(state, ptr::null_mut(), true, |extra, nargs| {
|
||||||
|
let rawlua = (*extra).raw_lua();
|
||||||
|
(*callback_ptr)(rawlua, nargs)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Automatic destructor is provided for any Luau userdata
|
||||||
|
crate::util::push_userdata(state, methods_map, true)?;
|
||||||
|
protect_lua!(state, 1, 1, |state| {
|
||||||
|
ffi::lua_pushcclosured(state, namecall, cstr!("__namecall"), 1);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// This method is called by Lua GC when it's time to collect the userdata.
|
// This method is called by Lua GC when it's time to collect the userdata.
|
||||||
//
|
//
|
||||||
// This method is usually used to collect internal userdata.
|
// This method is usually used to collect internal userdata.
|
||||||
@@ -454,9 +496,9 @@ pub(crate) unsafe extern "C" fn collect_userdata<T>(
|
|||||||
// It checks if the userdata is safe to destroy and sets the "destroyed" metatable
|
// It checks if the userdata is safe to destroy and sets the "destroyed" metatable
|
||||||
// to prevent further GC collection.
|
// to prevent further GC collection.
|
||||||
pub(super) unsafe extern "C-unwind" fn destroy_userdata_storage<T>(state: *mut ffi::lua_State) -> c_int {
|
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);
|
let ud = get_userdata::<UserDataStorage<T>>(state, 1);
|
||||||
if (*ud).is_safe_to_destroy() {
|
if (*ud).is_safe_to_destroy() {
|
||||||
take_userdata::<UserDataStorage<T>>(state);
|
take_userdata::<UserDataStorage<T>>(state, 1);
|
||||||
ffi::lua_pushboolean(state, 1);
|
ffi::lua_pushboolean(state, 1);
|
||||||
} else {
|
} else {
|
||||||
ffi::lua_pushboolean(state, 0);
|
ffi::lua_pushboolean(state, 0);
|
||||||
|
|||||||
+27
-15
@@ -2,15 +2,15 @@ use std::any::Any;
|
|||||||
use std::fmt::Write as _;
|
use std::fmt::Write as _;
|
||||||
use std::mem::MaybeUninit;
|
use std::mem::MaybeUninit;
|
||||||
use std::os::raw::{c_int, c_void};
|
use std::os::raw::{c_int, c_void};
|
||||||
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
use std::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::memory::MemoryState;
|
use crate::memory::MemoryState;
|
||||||
use crate::util::{
|
use crate::util::{
|
||||||
check_stack, get_internal_userdata, init_internal_metatable, push_internal_userdata, push_string,
|
DESTRUCTED_USERDATA_METATABLE, TypeKey, check_stack, get_internal_userdata, init_internal_metatable,
|
||||||
push_table, rawset_field, to_string, TypeKey, DESTRUCTED_USERDATA_METATABLE,
|
push_internal_userdata, push_string, push_table, rawset_field, to_string,
|
||||||
};
|
};
|
||||||
|
|
||||||
static WRAPPED_FAILURE_TYPE_KEY: u8 = 0;
|
static WRAPPED_FAILURE_TYPE_KEY: u8 = 0;
|
||||||
@@ -208,7 +208,7 @@ where
|
|||||||
F: FnOnce(*mut ffi::lua_State) -> R,
|
F: FnOnce(*mut ffi::lua_State) -> R,
|
||||||
R: Copy,
|
R: Copy,
|
||||||
{
|
{
|
||||||
let params = ffi::lua_touserdata(state, -1) as *mut Params<F, R>;
|
let params = ffi::lua_tolightuserdata(state, -1) as *mut Params<F, R>;
|
||||||
ffi::lua_pop(state, 1);
|
ffi::lua_pop(state, 1);
|
||||||
|
|
||||||
let f = (*params).function.take().unwrap();
|
let f = (*params).function.take().unwrap();
|
||||||
@@ -239,7 +239,7 @@ where
|
|||||||
|
|
||||||
ffi::lua_pushlightuserdata(state, &mut params as *mut Params<F, R> as *mut c_void);
|
ffi::lua_pushlightuserdata(state, &mut params as *mut Params<F, R> as *mut c_void);
|
||||||
let ret = ffi::lua_pcall(state, nargs + 1, nresults, stack_start + 1);
|
let ret = ffi::lua_pcall(state, nargs + 1, nresults, stack_start + 1);
|
||||||
ffi::lua_remove(state, stack_start + 1);
|
ffi::lua_remove(state, stack_start + 1); // remove error handler
|
||||||
|
|
||||||
if ret == ffi::LUA_OK {
|
if ret == ffi::LUA_OK {
|
||||||
// `LUA_OK` is only returned when the `do_call` function has completed successfully, so
|
// `LUA_OK` is only returned when the `do_call` function has completed successfully, so
|
||||||
@@ -349,7 +349,11 @@ pub(crate) unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(
|
|||||||
state,
|
state,
|
||||||
Some(|state| {
|
Some(|state| {
|
||||||
ffi::lua_pushcfunction(state, error_tostring);
|
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"));
|
||||||
}),
|
}),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -369,19 +373,19 @@ pub(crate) unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(
|
|||||||
"__mod",
|
"__mod",
|
||||||
"__pow",
|
"__pow",
|
||||||
"__unm",
|
"__unm",
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||||
"__idiv",
|
"__idiv",
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||||
"__band",
|
"__band",
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||||
"__bor",
|
"__bor",
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||||
"__bxor",
|
"__bxor",
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||||
"__bnot",
|
"__bnot",
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||||
"__shl",
|
"__shl",
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||||
"__shr",
|
"__shr",
|
||||||
"__concat",
|
"__concat",
|
||||||
"__len",
|
"__len",
|
||||||
@@ -392,13 +396,21 @@ pub(crate) unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(
|
|||||||
"__newindex",
|
"__newindex",
|
||||||
"__call",
|
"__call",
|
||||||
"__tostring",
|
"__tostring",
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit52"))]
|
#[cfg(any(
|
||||||
|
feature = "lua55",
|
||||||
|
feature = "lua54",
|
||||||
|
feature = "lua53",
|
||||||
|
feature = "lua52",
|
||||||
|
feature = "luajit52"
|
||||||
|
))]
|
||||||
"__pairs",
|
"__pairs",
|
||||||
#[cfg(any(feature = "lua53", feature = "lua52", feature = "luajit52"))]
|
#[cfg(any(feature = "lua53", feature = "lua52", feature = "luajit52"))]
|
||||||
"__ipairs",
|
"__ipairs",
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
"__iter",
|
"__iter",
|
||||||
#[cfg(feature = "lua54")]
|
#[cfg(feature = "luau")]
|
||||||
|
"__namecall",
|
||||||
|
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||||
"__close",
|
"__close",
|
||||||
] {
|
] {
|
||||||
ffi::lua_pushvalue(state, -1);
|
ffi::lua_pushvalue(state, -1);
|
||||||
|
|||||||
+48
-20
@@ -6,15 +6,16 @@ use std::{ptr, slice, str};
|
|||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
|
|
||||||
pub(crate) use error::{
|
pub(crate) use error::{
|
||||||
error_traceback, error_traceback_thread, init_error_registry, pop_error, protect_lua_call,
|
WrappedFailure, error_traceback, error_traceback_thread, init_error_registry, pop_error,
|
||||||
protect_lua_closure, WrappedFailure,
|
protect_lua_call, protect_lua_closure,
|
||||||
};
|
};
|
||||||
|
pub(crate) use path::parse_path as parse_lookup_path;
|
||||||
pub(crate) use short_names::short_type_name;
|
pub(crate) use short_names::short_type_name;
|
||||||
pub(crate) use types::TypeKey;
|
pub(crate) use types::TypeKey;
|
||||||
pub(crate) use userdata::{
|
pub(crate) use userdata::{
|
||||||
get_destructed_userdata_metatable, get_internal_metatable, get_internal_userdata, get_userdata,
|
DESTRUCTED_USERDATA_METATABLE, get_destructed_userdata_metatable, get_internal_metatable,
|
||||||
init_internal_metatable, push_internal_userdata, push_userdata, take_userdata,
|
get_internal_userdata, get_userdata, init_internal_metatable, push_internal_userdata, push_userdata,
|
||||||
DESTRUCTED_USERDATA_METATABLE,
|
take_userdata,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
@@ -88,7 +89,7 @@ impl Drop for StackGuard {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) unsafe fn push_string(state: *mut ffi::lua_State, s: &[u8], protect: bool) -> Result<()> {
|
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
|
// 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| {
|
protect_lua!(state, 0, 1, |state| {
|
||||||
ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len());
|
ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len());
|
||||||
})
|
})
|
||||||
@@ -98,18 +99,48 @@ pub(crate) unsafe fn push_string(state: *mut ffi::lua_State, s: &[u8], protect:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uses 3 (or 1 if unprotected) stack spaces, does not call checkstack.
|
||||||
|
#[cfg(feature = "lua55")]
|
||||||
|
pub(crate) unsafe fn push_external_string(
|
||||||
|
state: *mut ffi::lua_State,
|
||||||
|
mut bytes: Vec<u8>,
|
||||||
|
protect: bool,
|
||||||
|
) -> Result<()> {
|
||||||
|
bytes.push(0);
|
||||||
|
let s_len = bytes.len() - 1; // exclude null terminator
|
||||||
|
let s_ptr = bytes.as_ptr() as *const c_char;
|
||||||
|
let bytes_ud = Box::into_raw(Box::new(bytes));
|
||||||
|
|
||||||
|
unsafe extern "C" fn dealloc(ud: *mut c_void, _: *mut c_void, _: usize, _: usize) -> *mut c_void {
|
||||||
|
drop(Box::from_raw(ud as *mut Vec<u8>));
|
||||||
|
ptr::null_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
if protect {
|
||||||
|
let res = protect_lua!(state, 0, 1, move |state| {
|
||||||
|
ffi::lua_pushexternalstring(state, s_ptr, s_len, Some(dealloc), bytes_ud as *mut _);
|
||||||
|
});
|
||||||
|
if res.is_err() {
|
||||||
|
// Deallocate on error
|
||||||
|
drop(Box::from_raw(bytes_ud));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ffi::lua_pushexternalstring(state, s_ptr, s_len, Some(dealloc), bytes_ud as *mut _);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// Uses 3 stack spaces (when protect), does not call checkstack.
|
// Uses 3 stack spaces (when protect), does not call checkstack.
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) unsafe fn push_buffer(state: *mut ffi::lua_State, b: &[u8], protect: bool) -> Result<()> {
|
pub(crate) unsafe fn push_buffer(state: *mut ffi::lua_State, size: usize, protect: bool) -> Result<*mut u8> {
|
||||||
let data = if protect {
|
let data = if protect || size > const { 1024 * 1024 * 1024 } {
|
||||||
protect_lua!(state, 0, 1, |state| ffi::lua_newbuffer(state, b.len()))?
|
protect_lua!(state, 0, 1, |state| ffi::lua_newbuffer(state, size))?
|
||||||
} else {
|
} else {
|
||||||
ffi::lua_newbuffer(state, b.len())
|
ffi::lua_newbuffer(state, size)
|
||||||
};
|
};
|
||||||
let buf = slice::from_raw_parts_mut(data as *mut u8, b.len());
|
Ok(data as *mut u8)
|
||||||
buf.copy_from_slice(b);
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uses 3 stack spaces, does not call checkstack.
|
// Uses 3 stack spaces, does not call checkstack.
|
||||||
@@ -122,7 +153,7 @@ pub(crate) unsafe fn push_table(
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let narr: c_int = narr.try_into().unwrap_or(c_int::MAX);
|
let narr: c_int = narr.try_into().unwrap_or(c_int::MAX);
|
||||||
let nrec: c_int = nrec.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 << 26 } || nrec >= const { 1 << 26 } {
|
||||||
protect_lua!(state, 0, 1, |state| ffi::lua_createtable(state, narr, nrec))
|
protect_lua!(state, 0, 1, |state| ffi::lua_createtable(state, narr, nrec))
|
||||||
} else {
|
} else {
|
||||||
ffi::lua_createtable(state, narr, nrec);
|
ffi::lua_createtable(state, narr, nrec);
|
||||||
@@ -221,7 +252,7 @@ pub(crate) unsafe extern "C-unwind" fn safe_xpcall(state: *mut ffi::lua_State) -
|
|||||||
// Returns Lua main thread for Lua >= 5.2 or checks that the passed thread is main for Lua 5.1.
|
// Returns Lua main thread for Lua >= 5.2 or checks that the passed thread is main for Lua 5.1.
|
||||||
// Does not call lua_checkstack, uses 1 stack space.
|
// Does not call lua_checkstack, uses 1 stack space.
|
||||||
pub(crate) unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua_State> {
|
pub(crate) unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua_State> {
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||||
{
|
{
|
||||||
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_MAINTHREAD);
|
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_MAINTHREAD);
|
||||||
let main_state = ffi::lua_tothread(state, -1);
|
let main_state = ffi::lua_tothread(state, -1);
|
||||||
@@ -233,11 +264,7 @@ pub(crate) unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut f
|
|||||||
// Check the current state first
|
// Check the current state first
|
||||||
let is_main_state = ffi::lua_pushthread(state) == 1;
|
let is_main_state = ffi::lua_pushthread(state) == 1;
|
||||||
ffi::lua_pop(state, 1);
|
ffi::lua_pop(state, 1);
|
||||||
if is_main_state {
|
if is_main_state { Some(state) } else { None }
|
||||||
Some(state)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
Some(ffi::lua_mainthread(state))
|
Some(ffi::lua_mainthread(state))
|
||||||
@@ -329,6 +356,7 @@ pub(crate) fn linenumber_to_usize(n: c_int) -> Option<usize> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
|
mod path;
|
||||||
mod short_names;
|
mod short_names;
|
||||||
mod types;
|
mod types;
|
||||||
mod userdata;
|
mod userdata;
|
||||||
|
|||||||
@@ -0,0 +1,255 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
|
use std::fmt;
|
||||||
|
use std::iter::Peekable;
|
||||||
|
use std::str::CharIndices;
|
||||||
|
|
||||||
|
use crate::error::{Error, Result};
|
||||||
|
use crate::state::Lua;
|
||||||
|
use crate::traits::IntoLua;
|
||||||
|
use crate::types::Integer;
|
||||||
|
use crate::value::Value;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) enum PathKey<'a> {
|
||||||
|
Str(Cow<'a, str>),
|
||||||
|
Int(Integer),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for PathKey<'_> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
PathKey::Str(s) => write!(f, "{}", s),
|
||||||
|
PathKey::Int(i) => write!(f, "{}", i),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoLua for PathKey<'_> {
|
||||||
|
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||||
|
match self {
|
||||||
|
PathKey::Str(s) => Ok(Value::String(lua.create_string(s.as_ref())?)),
|
||||||
|
PathKey::Int(i) => Ok(Value::Integer(i)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parses a path like `a.b[3]?.c["d"]` into segments of `(key, safe_nil)`.
|
||||||
|
pub(crate) fn parse_path<'a>(path: &'a str) -> Result<Vec<(PathKey<'a>, bool)>> {
|
||||||
|
fn read_ident<'a>(path: &'a str, chars: &mut Peekable<CharIndices<'a>>) -> (Cow<'a, str>, bool) {
|
||||||
|
let mut safe_nil = false;
|
||||||
|
let start = chars.peek().map(|&(i, _)| i).unwrap_or(path.len());
|
||||||
|
let mut end = start;
|
||||||
|
while let Some(&(pos, c)) = chars.peek() {
|
||||||
|
if c == '.' || c == '?' || c.is_ascii_whitespace() || c == '[' {
|
||||||
|
if c == '?' {
|
||||||
|
safe_nil = true;
|
||||||
|
chars.next(); // consume '?'
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
end = pos + c.len_utf8();
|
||||||
|
chars.next();
|
||||||
|
}
|
||||||
|
(Cow::Borrowed(&path[start..end]), safe_nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut segments = Vec::new();
|
||||||
|
let mut chars = path.char_indices().peekable();
|
||||||
|
while let Some(&(pos, next)) = chars.peek() {
|
||||||
|
match next {
|
||||||
|
'.' => {
|
||||||
|
// Dot notation: identifier
|
||||||
|
chars.next();
|
||||||
|
let (key, safe_nil) = read_ident(path, &mut chars);
|
||||||
|
if key.is_empty() {
|
||||||
|
return Err(Error::runtime(format!("empty key in path at position {pos}")));
|
||||||
|
}
|
||||||
|
segments.push((PathKey::Str(key), safe_nil));
|
||||||
|
}
|
||||||
|
'[' => {
|
||||||
|
// Bracket notation: either integer or quoted string
|
||||||
|
chars.next();
|
||||||
|
let key = match chars.peek() {
|
||||||
|
Some(&(pos, c @ '0'..='9' | c @ '-')) => {
|
||||||
|
// Integer key
|
||||||
|
let negative = c == '-';
|
||||||
|
if negative {
|
||||||
|
chars.next(); // consume '-'
|
||||||
|
}
|
||||||
|
let mut num: Option<Integer> = None;
|
||||||
|
while let Some(&(_, c @ '0'..='9')) = chars.peek() {
|
||||||
|
let new_num = num
|
||||||
|
.unwrap_or(0)
|
||||||
|
.checked_mul(10)
|
||||||
|
.and_then(|n| n.checked_add((c as u8 - b'0') as Integer))
|
||||||
|
.ok_or_else(|| {
|
||||||
|
Error::runtime(format!("integer overflow in path at position {pos}"))
|
||||||
|
})?;
|
||||||
|
num = Some(new_num);
|
||||||
|
chars.next(); // consume digit
|
||||||
|
}
|
||||||
|
match num {
|
||||||
|
Some(n) if negative => PathKey::Int(-n),
|
||||||
|
Some(n) => PathKey::Int(n),
|
||||||
|
None => {
|
||||||
|
let err = format!("invalid integer in path at position {pos}");
|
||||||
|
return Err(Error::runtime(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some((_, '\'' | '"')) => {
|
||||||
|
// Quoted string
|
||||||
|
PathKey::Str(unquote_string(path, &mut chars)?)
|
||||||
|
}
|
||||||
|
Some((_, ']')) => {
|
||||||
|
return Err(Error::runtime(format!("empty key in path at position {pos}")));
|
||||||
|
}
|
||||||
|
Some((pos, c)) => {
|
||||||
|
let err = format!("unexpected character '{c}' in path at position {pos}");
|
||||||
|
return Err(Error::runtime(err));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(Error::runtime("unexpected end of path"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Expect closing bracket
|
||||||
|
let mut safe_nil = false;
|
||||||
|
match chars.next() {
|
||||||
|
Some((_, ']')) => {
|
||||||
|
// Check for optional safe-nil operator
|
||||||
|
if let Some(&(_, '?')) = chars.peek() {
|
||||||
|
safe_nil = true;
|
||||||
|
chars.next(); // consume '?'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some((pos, c)) => {
|
||||||
|
let err = format!("expected ']' in path at position {pos}, found '{c}'");
|
||||||
|
return Err(Error::runtime(err));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(Error::runtime("unexpected end of path"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
segments.push((key, safe_nil));
|
||||||
|
}
|
||||||
|
c if c.is_ascii_whitespace() => {
|
||||||
|
chars.next(); // Skip whitespace
|
||||||
|
}
|
||||||
|
_ if segments.is_empty() => {
|
||||||
|
// First segment without dot/bracket notation
|
||||||
|
let (key_cow, safe_nil) = read_ident(path, &mut chars);
|
||||||
|
if key_cow.is_empty() {
|
||||||
|
return Err(Error::runtime(format!("empty key in path at position {pos}")));
|
||||||
|
}
|
||||||
|
segments.push((PathKey::Str(key_cow), safe_nil));
|
||||||
|
}
|
||||||
|
c => {
|
||||||
|
let err = format!("unexpected character '{c}' in path at position {pos}");
|
||||||
|
return Err(Error::runtime(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(segments)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unquote_string<'a>(path: &'a str, chars: &mut Peekable<CharIndices<'a>>) -> Result<Cow<'a, str>> {
|
||||||
|
let (start_pos, first_quote) = chars.next().unwrap();
|
||||||
|
let mut result = String::new();
|
||||||
|
loop {
|
||||||
|
match chars.next() {
|
||||||
|
Some((pos, '\\')) => {
|
||||||
|
if result.is_empty() {
|
||||||
|
// First escape found, copy everything up to this point
|
||||||
|
result.push_str(&path[start_pos + 1..pos]);
|
||||||
|
}
|
||||||
|
match chars.next() {
|
||||||
|
Some((_, '\\')) => result.push('\\'),
|
||||||
|
Some((_, '"')) => result.push('"'),
|
||||||
|
Some((_, '\'')) => result.push('\''),
|
||||||
|
Some((_, other)) => {
|
||||||
|
result.push('\\');
|
||||||
|
result.push(other);
|
||||||
|
}
|
||||||
|
None => continue, // will be handled by outer loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some((pos, c)) if c == first_quote => {
|
||||||
|
if !result.is_empty() {
|
||||||
|
return Ok(Cow::Owned(result));
|
||||||
|
}
|
||||||
|
// No escapes, return borrowed slice
|
||||||
|
return Ok(Cow::Borrowed(&path[start_pos + 1..pos]));
|
||||||
|
}
|
||||||
|
Some((_, c)) => {
|
||||||
|
if !result.is_empty() {
|
||||||
|
result.push(c);
|
||||||
|
}
|
||||||
|
// If no escapes yet, continue tracking for potential borrowed slice
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let err = format!("unexpected end of string at position {start_pos}");
|
||||||
|
return Err(Error::runtime(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{PathKey, parse_path};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_path() {
|
||||||
|
// Test valid paths
|
||||||
|
let path = parse_path("a.b[3]?.c['d']").unwrap();
|
||||||
|
assert_eq!(path.len(), 5);
|
||||||
|
assert!(matches!(path[0], (PathKey::Str(ref s), false) if s == "a"));
|
||||||
|
assert!(matches!(path[1], (PathKey::Str(ref s), false) if s == "b"));
|
||||||
|
assert!(matches!(path[2], (PathKey::Int(3), true)));
|
||||||
|
assert!(matches!(path[3], (PathKey::Str(ref s), false) if s == "c"));
|
||||||
|
assert!(matches!(path[4], (PathKey::Str(ref s), false) if s == "d"));
|
||||||
|
|
||||||
|
// Test empty path
|
||||||
|
let path = parse_path("").unwrap();
|
||||||
|
assert_eq!(path.len(), 0);
|
||||||
|
let path = parse_path(" ").unwrap();
|
||||||
|
assert_eq!(path.len(), 0);
|
||||||
|
|
||||||
|
// Test invalid dot syntax
|
||||||
|
let err = parse_path("a..b").unwrap_err().to_string();
|
||||||
|
assert_eq!(err, "runtime error: empty key in path at position 1");
|
||||||
|
let err = parse_path("a.b.").unwrap_err().to_string();
|
||||||
|
assert_eq!(err, "runtime error: empty key in path at position 3");
|
||||||
|
|
||||||
|
// Test invalid bracket syntax
|
||||||
|
let err = parse_path("a[unclosed").unwrap_err().to_string();
|
||||||
|
assert_eq!(
|
||||||
|
err,
|
||||||
|
"runtime error: unexpected character 'u' in path at position 2"
|
||||||
|
);
|
||||||
|
let err = parse_path("a[]").unwrap_err().to_string();
|
||||||
|
assert_eq!(err, "runtime error: empty key in path at position 1");
|
||||||
|
let err = parse_path(r#"a["unclosed"#).unwrap_err().to_string();
|
||||||
|
assert_eq!(err, "runtime error: unexpected end of string at position 2");
|
||||||
|
let err = parse_path(r#"a["#).unwrap_err().to_string();
|
||||||
|
assert_eq!(err, "runtime error: unexpected end of path");
|
||||||
|
let err = parse_path(r#"a[123"#).unwrap_err().to_string();
|
||||||
|
assert_eq!(err, "runtime error: unexpected end of path");
|
||||||
|
let err = parse_path(r#"a['bla'123"#).unwrap_err().to_string();
|
||||||
|
assert_eq!(
|
||||||
|
err,
|
||||||
|
"runtime error: expected ']' in path at position 7, found '1'"
|
||||||
|
);
|
||||||
|
let err = parse_path(r#"a["bla"]x"#).unwrap_err().to_string();
|
||||||
|
assert_eq!(
|
||||||
|
err,
|
||||||
|
"runtime error: unexpected character 'x' in path at position 8"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test bad integers
|
||||||
|
let err = parse_path("a[99999999999999999999]").unwrap_err().to_string();
|
||||||
|
assert_eq!(err, "runtime error: integer overflow in path at position 2");
|
||||||
|
let err = parse_path("a[-]").unwrap_err().to_string();
|
||||||
|
assert_eq!(err, "runtime error: invalid integer in path at position 2");
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
-8
@@ -1,6 +1,6 @@
|
|||||||
//! Mostly copied from [bevy_utils]
|
//! Inspired by bevy's [disqualified]
|
||||||
//!
|
//!
|
||||||
//! [bevy_utils]: https://github.com/bevyengine/bevy/blob/main/crates/bevy_utils/src/short_names.rs
|
//! [disqualified]: https://github.com/bevyengine/disqualified/blob/main/src/short_name.rs
|
||||||
|
|
||||||
use std::any::type_name;
|
use std::any::type_name;
|
||||||
|
|
||||||
@@ -23,8 +23,7 @@ pub(crate) fn short_type_name<T: ?Sized>() -> String {
|
|||||||
while index < end_of_string {
|
while index < end_of_string {
|
||||||
let rest_of_string = full_name.get(index..end_of_string).unwrap_or_default();
|
let rest_of_string = full_name.get(index..end_of_string).unwrap_or_default();
|
||||||
|
|
||||||
// Collapse everything up to the next special character,
|
// Collapse everything up to the next special character, then skip over it
|
||||||
// then skip over it
|
|
||||||
if let Some(special_character_index) =
|
if let Some(special_character_index) =
|
||||||
rest_of_string.find(|c: char| [' ', '<', '>', '(', ')', '[', ']', ',', ';'].contains(&c))
|
rest_of_string.find(|c: char| [' ', '<', '>', '(', ')', '[', ']', ',', ';'].contains(&c))
|
||||||
{
|
{
|
||||||
@@ -32,11 +31,16 @@ pub(crate) fn short_type_name<T: ?Sized>() -> String {
|
|||||||
parsed_name += collapse_type_name(segment_to_collapse);
|
parsed_name += collapse_type_name(segment_to_collapse);
|
||||||
// Insert the special character
|
// Insert the special character
|
||||||
let special_character = &rest_of_string[special_character_index..=special_character_index];
|
let special_character = &rest_of_string[special_character_index..=special_character_index];
|
||||||
parsed_name.push_str(special_character);
|
parsed_name += special_character;
|
||||||
|
|
||||||
|
// Remove lifetimes like <'_> or <'_, '_, ...>
|
||||||
|
if parsed_name.ends_with("<'_>") || parsed_name.ends_with("<'_, ") {
|
||||||
|
_ = parsed_name.split_off(parsed_name.len() - 4);
|
||||||
|
}
|
||||||
|
|
||||||
match special_character {
|
match special_character {
|
||||||
">" | ")" | "]" if rest_of_string[special_character_index + 1..].starts_with("::") => {
|
">" | ")" | "]" if rest_of_string[special_character_index + 1..].starts_with("::") => {
|
||||||
parsed_name.push_str("::");
|
parsed_name += "::";
|
||||||
// Move the index past the "::"
|
// Move the index past the "::"
|
||||||
index += special_character_index + 3;
|
index += special_character_index + 3;
|
||||||
}
|
}
|
||||||
@@ -53,14 +57,18 @@ pub(crate) fn short_type_name<T: ?Sized>() -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn collapse_type_name(string: &str) -> &str {
|
fn collapse_type_name(segment: &str) -> &str {
|
||||||
string.rsplit("::").next().unwrap()
|
segment.rsplit("::").next().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::short_type_name;
|
use super::short_type_name;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
struct MyData<'a, 'b>(PhantomData<&'a &'b ()>);
|
||||||
|
struct MyDataT<'a, T>(PhantomData<&'a T>);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tests() {
|
fn tests() {
|
||||||
@@ -73,5 +81,7 @@ mod tests {
|
|||||||
"HashMap<String, Option<[i32; 3]>>"
|
"HashMap<String, Option<[i32; 3]>>"
|
||||||
);
|
);
|
||||||
assert_eq!(short_type_name::<dyn Fn(i32) -> i32>(), "dyn Fn(i32) -> i32");
|
assert_eq!(short_type_name::<dyn Fn(i32) -> i32>(), "dyn Fn(i32) -> i32");
|
||||||
|
assert_eq!(short_type_name::<MyDataT<&str>>(), "MyDataT<&str>");
|
||||||
|
assert_eq!(short_type_name::<(&MyData, [MyData])>(), "(MyData, [MyData])");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-20
@@ -3,7 +3,7 @@ use std::{mem, ptr};
|
|||||||
|
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::userdata::collect_userdata;
|
use crate::userdata::collect_userdata;
|
||||||
use crate::util::{check_stack, get_metatable_ptr, push_table, rawset_field, TypeKey};
|
use crate::util::{TypeKey, check_stack, get_metatable_ptr, push_table, rawset_field};
|
||||||
|
|
||||||
// Pushes the userdata and attaches a metatable with __gc method.
|
// Pushes the userdata and attaches a metatable with __gc method.
|
||||||
// Internally uses 3 stack spaces, does not call checkstack.
|
// Internally uses 3 stack spaces, does not call checkstack.
|
||||||
@@ -47,7 +47,7 @@ pub(crate) unsafe fn get_internal_metatable<T: TypeKey>(state: *mut ffi::lua_Sta
|
|||||||
// Uses 6 stack spaces and calls checkstack.
|
// Uses 6 stack spaces and calls checkstack.
|
||||||
pub(crate) unsafe fn init_internal_metatable<T: TypeKey>(
|
pub(crate) unsafe fn init_internal_metatable<T: TypeKey>(
|
||||||
state: *mut ffi::lua_State,
|
state: *mut ffi::lua_State,
|
||||||
customize_fn: Option<fn(*mut ffi::lua_State) -> Result<()>>,
|
customize_fn: Option<fn(*mut ffi::lua_State)>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
check_stack(state, 6)?;
|
check_stack(state, 6)?;
|
||||||
|
|
||||||
@@ -62,18 +62,18 @@ pub(crate) unsafe fn init_internal_metatable<T: TypeKey>(
|
|||||||
ffi::lua_pushboolean(state, 0);
|
ffi::lua_pushboolean(state, 0);
|
||||||
rawset_field(state, -2, "__metatable")?;
|
rawset_field(state, -2, "__metatable")?;
|
||||||
|
|
||||||
if let Some(f) = customize_fn {
|
|
||||||
f(state)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
protect_lua!(state, 1, 0, |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());
|
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, T::type_key());
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(())
|
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>(
|
pub(crate) unsafe fn get_internal_userdata<T: TypeKey>(
|
||||||
state: *mut ffi::lua_State,
|
state: *mut ffi::lua_State,
|
||||||
index: c_int,
|
index: c_int,
|
||||||
@@ -141,24 +141,27 @@ pub(crate) unsafe fn get_userdata<T>(state: *mut ffi::lua_State, index: c_int) -
|
|||||||
ud
|
ud
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pops the userdata off of the top of the stack and returns it to rust, invalidating the lua
|
/// Unwraps `T` from the Lua userdata and invalidating it by setting the special "destructed"
|
||||||
// userdata and gives it the special "destructed" userdata metatable. Userdata must not have been
|
/// metatable.
|
||||||
// previously invalidated, and this method does not check for this.
|
///
|
||||||
// Uses 1 extra stack space and does not call checkstack.
|
/// This method does not check that userdata is of type `T` and was not previously invalidated.
|
||||||
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
|
/// Uses 1 extra stack space, does not call checkstack.
|
||||||
// metamethods that trigger an error on access. We do this so that it will not be double
|
pub(crate) unsafe fn take_userdata<T>(state: *mut ffi::lua_State, idx: c_int) -> T {
|
||||||
// dropped, and also so that it cannot be used or identified as any particular userdata type
|
#[rustfmt::skip]
|
||||||
// after the first call to __gc.
|
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);
|
get_destructed_userdata_metatable(state);
|
||||||
ffi::lua_setmetatable(state, -2);
|
ffi::lua_setmetatable(state, idx);
|
||||||
let ud = get_userdata::<T>(state, -1);
|
let ud = get_userdata::<T>(state, idx);
|
||||||
|
|
||||||
// Update userdata tag to disable destructor and mark as destructed
|
// Update userdata tag to disable destructor and mark as destructed
|
||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
ffi::lua_setuserdatatag(state, -1, 1);
|
ffi::lua_setuserdatatag(state, idx, 1);
|
||||||
|
|
||||||
ffi::lua_pop(state, 1);
|
|
||||||
ptr::read(ud)
|
ptr::read(ud)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+50
-38
@@ -1,21 +1,20 @@
|
|||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
use std::string::String as StdString;
|
|
||||||
use std::{fmt, ptr, str};
|
use std::{fmt, ptr, str};
|
||||||
|
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::function::Function;
|
use crate::function::Function;
|
||||||
use crate::string::{BorrowedStr, String};
|
use crate::string::{BorrowedStr, LuaString};
|
||||||
use crate::table::Table;
|
use crate::table::Table;
|
||||||
use crate::thread::Thread;
|
use crate::thread::Thread;
|
||||||
use crate::types::{Integer, LightUserData, Number, ValueRef};
|
use crate::types::{Integer, LightUserData, Number, ValueRef};
|
||||||
use crate::userdata::AnyUserData;
|
use crate::userdata::AnyUserData;
|
||||||
use crate::util::{check_stack, StackGuard};
|
use crate::util::{StackGuard, check_stack};
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
use {
|
use {
|
||||||
crate::table::SerializableTable,
|
crate::table::SerializableTable,
|
||||||
rustc_hash::FxHashSet,
|
rustc_hash::FxHashSet,
|
||||||
@@ -28,9 +27,10 @@ use {
|
|||||||
/// The non-primitive variants (eg. string/table/function/thread/userdata) contain handle types
|
/// 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
|
/// 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.
|
/// `Lua` instances, and doing so will result in a panic.
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Default)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
/// The Lua value `nil`.
|
/// The Lua value `nil`.
|
||||||
|
#[default]
|
||||||
Nil,
|
Nil,
|
||||||
/// The Lua value `true` or `false`.
|
/// The Lua value `true` or `false`.
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
@@ -49,7 +49,7 @@ pub enum Value {
|
|||||||
/// An interned string, managed by Lua.
|
/// An interned string, managed by Lua.
|
||||||
///
|
///
|
||||||
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
|
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
|
||||||
String(String),
|
String(LuaString),
|
||||||
/// Reference to a Lua table.
|
/// Reference to a Lua table.
|
||||||
Table(Table),
|
Table(Table),
|
||||||
/// Reference to a Lua function (or closure).
|
/// Reference to a Lua function (or closure).
|
||||||
@@ -128,7 +128,7 @@ impl Value {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_pointer(&self) -> *const c_void {
|
pub fn to_pointer(&self) -> *const c_void {
|
||||||
match self {
|
match self {
|
||||||
Value::String(String(vref)) => {
|
Value::String(LuaString(vref)) => {
|
||||||
// In Lua < 5.4 (excluding Luau), string pointers are NULL
|
// In Lua < 5.4 (excluding Luau), string pointers are NULL
|
||||||
// Use alternative approach
|
// Use alternative approach
|
||||||
let lua = vref.lua.lock();
|
let lua = vref.lua.lock();
|
||||||
@@ -150,8 +150,8 @@ impl Value {
|
|||||||
///
|
///
|
||||||
/// This might invoke the `__tostring` metamethod for non-primitive types (eg. tables,
|
/// This might invoke the `__tostring` metamethod for non-primitive types (eg. tables,
|
||||||
/// functions).
|
/// functions).
|
||||||
pub fn to_string(&self) -> Result<StdString> {
|
pub fn to_string(&self) -> Result<String> {
|
||||||
unsafe fn invoke_to_string(vref: &ValueRef) -> Result<StdString> {
|
unsafe fn invoke_to_string(vref: &ValueRef) -> Result<String> {
|
||||||
let lua = vref.lua.lock();
|
let lua = vref.lua.lock();
|
||||||
let state = lua.state();
|
let state = lua.state();
|
||||||
let _guard = StackGuard::new(state);
|
let _guard = StackGuard::new(state);
|
||||||
@@ -161,7 +161,7 @@ impl Value {
|
|||||||
protect_lua!(state, 1, 1, fn(state) {
|
protect_lua!(state, 1, 1, fn(state) {
|
||||||
ffi::luaL_tolstring(state, -1, ptr::null_mut());
|
ffi::luaL_tolstring(state, -1, ptr::null_mut());
|
||||||
})?;
|
})?;
|
||||||
Ok(String(lua.pop_ref()).to_str()?.to_string())
|
Ok(LuaString(lua.pop_ref()).to_str()?.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
@@ -335,17 +335,17 @@ impl Value {
|
|||||||
self.as_number()
|
self.as_number()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the value is a Lua [`String`].
|
/// Returns `true` if the value is a [`LuaString`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_string(&self) -> bool {
|
pub fn is_string(&self) -> bool {
|
||||||
self.as_string().is_some()
|
self.as_string().is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cast the value to Lua [`String`].
|
/// Cast the value to a [`LuaString`].
|
||||||
///
|
///
|
||||||
/// If the value is a Lua [`String`], returns it or `None` otherwise.
|
/// If the value is a [`LuaString`], returns it or `None` otherwise.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_string(&self) -> Option<&String> {
|
pub fn as_string(&self) -> Option<&LuaString> {
|
||||||
match self {
|
match self {
|
||||||
Value::String(s) => Some(s),
|
Value::String(s) => Some(s),
|
||||||
_ => None,
|
_ => None,
|
||||||
@@ -354,18 +354,26 @@ impl Value {
|
|||||||
|
|
||||||
/// Cast the value to [`BorrowedStr`].
|
/// Cast the value to [`BorrowedStr`].
|
||||||
///
|
///
|
||||||
/// If the value is a Lua [`String`], try to convert it to [`BorrowedStr`] or return `None`
|
/// If the value is a [`LuaString`], try to convert it to [`BorrowedStr`] or return `None`
|
||||||
/// otherwise.
|
/// 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]
|
#[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())
|
self.as_string().and_then(|s| s.to_str().ok())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cast the value to [`StdString`].
|
/// Cast the value to [`String`].
|
||||||
///
|
///
|
||||||
/// If the value is a Lua [`String`], converts it to [`StdString`] or returns `None` otherwise.
|
/// If the value is a [`LuaString`], converts it to [`String`] 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]
|
#[inline]
|
||||||
pub fn as_string_lossy(&self) -> Option<StdString> {
|
pub fn as_string_lossy(&self) -> Option<String> {
|
||||||
self.as_string().map(|s| s.to_string_lossy())
|
self.as_string().map(|s| s.to_string_lossy())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,10 +489,9 @@ impl Value {
|
|||||||
/// Wrap reference to this Value into [`SerializableValue`].
|
/// Wrap reference to this Value into [`SerializableValue`].
|
||||||
///
|
///
|
||||||
/// This allows customizing serialization behavior using serde.
|
/// This allows customizing serialization behavior using serde.
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
#[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)
|
SerializableValue::new(self, Default::default(), None)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -572,12 +579,6 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Value {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Value {
|
impl fmt::Debug for Value {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
if fmt.alternate() {
|
if fmt.alternate() {
|
||||||
@@ -630,8 +631,8 @@ impl PartialEq for Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A wrapped [`Value`] with customized serialization behavior.
|
/// A wrapped [`Value`] with customized serialization behavior.
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||||
pub struct SerializableValue<'a> {
|
pub struct SerializableValue<'a> {
|
||||||
value: &'a Value,
|
value: &'a Value,
|
||||||
options: crate::serde::de::Options,
|
options: crate::serde::de::Options,
|
||||||
@@ -639,7 +640,7 @@ pub struct SerializableValue<'a> {
|
|||||||
visited: Option<Rc<RefCell<FxHashSet<*const c_void>>>>,
|
visited: Option<Rc<RefCell<FxHashSet<*const c_void>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
impl Serialize for Value {
|
impl Serialize for Value {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
|
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
|
||||||
@@ -647,7 +648,7 @@ impl Serialize for Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
impl<'a> SerializableValue<'a> {
|
impl<'a> SerializableValue<'a> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
@@ -676,7 +677,7 @@ impl<'a> SerializableValue<'a> {
|
|||||||
///
|
///
|
||||||
/// Default: **true**
|
/// Default: **true**
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn deny_unsupported_types(mut self, enabled: bool) -> Self {
|
pub fn deny_unsupported_types(mut self, enabled: bool) -> Self {
|
||||||
self.options.deny_unsupported_types = enabled;
|
self.options.deny_unsupported_types = enabled;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -687,7 +688,7 @@ impl<'a> SerializableValue<'a> {
|
|||||||
///
|
///
|
||||||
/// Default: **true**
|
/// Default: **true**
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn deny_recursive_tables(mut self, enabled: bool) -> Self {
|
pub fn deny_recursive_tables(mut self, enabled: bool) -> Self {
|
||||||
self.options.deny_recursive_tables = enabled;
|
self.options.deny_recursive_tables = enabled;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -696,7 +697,7 @@ impl<'a> SerializableValue<'a> {
|
|||||||
///
|
///
|
||||||
/// Default: **false**
|
/// Default: **false**
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn sort_keys(mut self, enabled: bool) -> Self {
|
pub fn sort_keys(mut self, enabled: bool) -> Self {
|
||||||
self.options.sort_keys = enabled;
|
self.options.sort_keys = enabled;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -705,13 +706,24 @@ impl<'a> SerializableValue<'a> {
|
|||||||
///
|
///
|
||||||
/// Default: **false**
|
/// Default: **false**
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn encode_empty_tables_as_array(mut self, enabled: bool) -> Self {
|
pub fn encode_empty_tables_as_array(mut self, enabled: bool) -> Self {
|
||||||
self.options.encode_empty_tables_as_array = enabled;
|
self.options.encode_empty_tables_as_array = enabled;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If true, enable detection of mixed tables.
|
||||||
|
///
|
||||||
|
/// A mixed table is a table that has both array-like and map-like entries or several borders.
|
||||||
|
///
|
||||||
|
/// Default: **false**
|
||||||
|
#[must_use]
|
||||||
|
pub fn detect_mixed_tables(mut self, enabled: bool) -> Self {
|
||||||
|
self.options.detect_mixed_tables = enabled;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
impl Serialize for SerializableValue<'_> {
|
impl Serialize for SerializableValue<'_> {
|
||||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
|
|||||||
+2
-3
@@ -1,6 +1,6 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::ser::{Serialize, SerializeTupleStruct, Serializer};
|
use serde::ser::{Serialize, SerializeTupleStruct, Serializer};
|
||||||
|
|
||||||
/// A Luau vector type.
|
/// A Luau vector type.
|
||||||
@@ -38,7 +38,6 @@ impl Vector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new vector with all components set to `0.0`.
|
/// Creates a new vector with all components set to `0.0`.
|
||||||
#[doc(hidden)]
|
|
||||||
pub const fn zero() -> Self {
|
pub const fn zero() -> Self {
|
||||||
Self([0.0; Self::SIZE])
|
Self([0.0; Self::SIZE])
|
||||||
}
|
}
|
||||||
@@ -66,7 +65,7 @@ impl Vector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serde")]
|
||||||
impl Serialize for Vector {
|
impl Serialize for Vector {
|
||||||
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
|
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
|
||||||
let mut ts = serializer.serialize_tuple_struct("Vector", Self::SIZE)?;
|
let mut ts = serializer.serialize_tuple_struct("Vector", Self::SIZE)?;
|
||||||
|
|||||||
+10
-10
@@ -1,23 +1,23 @@
|
|||||||
[lua54]
|
[lua55]
|
||||||
features = "lua54,vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
features = "lua55,vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||||
|
|
||||||
[lua54_non_send]
|
[lua55_non_send]
|
||||||
features = "lua54,vendored,async,serialize,macros,anyhow,userdata-wrappers"
|
features = "lua55,vendored,async,serde,macros,anyhow,userdata-wrappers"
|
||||||
|
|
||||||
[lua54_with_memory_limit]
|
[lua55_with_memory_limit]
|
||||||
features = "lua54,vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
features = "lua55,vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||||
rustflags = "--cfg force_memory_limit"
|
rustflags = "--cfg force_memory_limit"
|
||||||
|
|
||||||
[lua51]
|
[lua51]
|
||||||
features = "lua51,vendored,async,send,serialize,macros"
|
features = "lua51,vendored,async,send,serde,macros"
|
||||||
|
|
||||||
[lua51_with_memory_limit]
|
[lua51_with_memory_limit]
|
||||||
features = "lua51,vendored,async,send,serialize,macros"
|
features = "lua51,vendored,async,send,serde,macros"
|
||||||
rustflags = "--cfg force_memory_limit"
|
rustflags = "--cfg force_memory_limit"
|
||||||
|
|
||||||
[luau]
|
[luau]
|
||||||
features = "luau,async,send,serialize,macros"
|
features = "luau,async,send,serde,macros"
|
||||||
|
|
||||||
[luau_with_memory_limit]
|
[luau_with_memory_limit]
|
||||||
features = "luau,async,send,serialize,macros"
|
features = "luau,async,send,serde,macros"
|
||||||
rustflags = "--cfg force_memory_limit"
|
rustflags = "--cfg force_memory_limit"
|
||||||
|
|||||||
+104
-11
@@ -1,6 +1,5 @@
|
|||||||
#![cfg(feature = "async")]
|
#![cfg(feature = "async")]
|
||||||
|
|
||||||
use std::string::String as StdString;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@@ -8,8 +7,8 @@ use futures_util::stream::TryStreamExt;
|
|||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use mlua::{
|
use mlua::{
|
||||||
Error, Function, Lua, LuaOptions, MultiValue, ObjectLike, Result, StdLib, Table, UserData,
|
Error, Function, Lua, LuaOptions, MultiValue, ObjectLike, Result, StdLib, Table, ThreadStatus, UserData,
|
||||||
UserDataMethods, Value,
|
UserDataMethods, UserDataRef, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
@@ -40,7 +39,7 @@ async fn test_async_function() -> Result<()> {
|
|||||||
async fn test_async_function_wrap() -> Result<()> {
|
async fn test_async_function_wrap() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|
||||||
let f = Function::wrap_async(|s: StdString| async move {
|
let f = Function::wrap_async(|s: String| async move {
|
||||||
tokio::task::yield_now().await;
|
tokio::task::yield_now().await;
|
||||||
Ok(s)
|
Ok(s)
|
||||||
});
|
});
|
||||||
@@ -68,7 +67,7 @@ async fn test_async_function_wrap() -> Result<()> {
|
|||||||
async fn test_async_function_wrap_raw() -> Result<()> {
|
async fn test_async_function_wrap_raw() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|
||||||
let f = Function::wrap_raw_async(|s: StdString| async move {
|
let f = Function::wrap_raw_async(|s: String| async move {
|
||||||
tokio::task::yield_now().await;
|
tokio::task::yield_now().await;
|
||||||
s
|
s
|
||||||
});
|
});
|
||||||
@@ -249,7 +248,7 @@ async fn test_async_return_async_closure() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "lua54")]
|
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_async_lua54_to_be_closed() -> Result<()> {
|
async fn test_async_lua54_to_be_closed() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
@@ -386,7 +385,7 @@ async fn test_async_table_object_like() -> Result<()> {
|
|||||||
table.get::<i64>("val")
|
table.get::<i64>("val")
|
||||||
})?,
|
})?,
|
||||||
)?;
|
)?;
|
||||||
table.set_metatable(Some(metatable));
|
table.set_metatable(Some(metatable))?;
|
||||||
assert_eq!(table.call_async::<i64>(()).await.unwrap(), 15);
|
assert_eq!(table.call_async::<i64>(()).await.unwrap(), 15);
|
||||||
|
|
||||||
match table.call_async_method::<()>("non_existent", ()).await {
|
match table.call_async_method::<()>("non_existent", ()).await {
|
||||||
@@ -423,9 +422,9 @@ async fn test_async_thread_pool() -> Result<()> {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_async_userdata() -> Result<()> {
|
async fn test_async_userdata() -> Result<()> {
|
||||||
struct MyUserData(u64);
|
struct MyUserdata(u64);
|
||||||
|
|
||||||
impl UserData for MyUserData {
|
impl UserData for MyUserdata {
|
||||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||||
methods.add_async_method("get_value", |_, data, ()| async move {
|
methods.add_async_method("get_value", |_, data, ()| async move {
|
||||||
sleep_ms(10).await;
|
sleep_ms(10).await;
|
||||||
@@ -438,6 +437,11 @@ async fn test_async_userdata() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
|
methods.add_async_method_once("take_value", |_, data, ()| async move {
|
||||||
|
sleep_ms(10).await;
|
||||||
|
Ok(data.0)
|
||||||
|
});
|
||||||
|
|
||||||
methods.add_async_function("sleep", |_, n| async move {
|
methods.add_async_function("sleep", |_, n| async move {
|
||||||
sleep_ms(n).await;
|
sleep_ms(n).await;
|
||||||
Ok(format!("elapsed:{}ms", n))
|
Ok(format!("elapsed:{}ms", n))
|
||||||
@@ -479,7 +483,7 @@ async fn test_async_userdata() -> Result<()> {
|
|||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
let globals = lua.globals();
|
let globals = lua.globals();
|
||||||
|
|
||||||
let userdata = lua.create_userdata(MyUserData(11))?;
|
let userdata = lua.create_userdata(MyUserdata(11))?;
|
||||||
globals.set("userdata", &userdata)?;
|
globals.set("userdata", &userdata)?;
|
||||||
|
|
||||||
lua.load(
|
lua.load(
|
||||||
@@ -518,6 +522,21 @@ async fn test_async_userdata() -> Result<()> {
|
|||||||
#[cfg(not(any(feature = "lua51", feature = "luau")))]
|
#[cfg(not(any(feature = "lua51", feature = "luau")))]
|
||||||
assert_eq!(userdata.call_async::<String>(()).await?, "elapsed:24ms");
|
assert_eq!(userdata.call_async::<String>(()).await?, "elapsed:24ms");
|
||||||
|
|
||||||
|
// Take value
|
||||||
|
let userdata2 = lua.create_userdata(MyUserdata(0))?;
|
||||||
|
globals.set("userdata2", userdata2)?;
|
||||||
|
lua.load("assert(userdata:take_value() == 24)")
|
||||||
|
.exec_async()
|
||||||
|
.await?;
|
||||||
|
match lua.load("userdata2.take_value(userdata)").exec_async().await {
|
||||||
|
Err(Error::CallbackError { cause, .. }) => {
|
||||||
|
let err = cause.to_string();
|
||||||
|
assert!(err.contains("bad argument `self` to `MyUserdata.take_value`"));
|
||||||
|
assert!(err.contains("userdata has been destructed"));
|
||||||
|
}
|
||||||
|
r => panic!("expected Err(CallbackError), got {r:?}"),
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,6 +566,7 @@ async fn test_async_thread_error() -> Result<()> {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_async_terminate() -> Result<()> {
|
async fn test_async_terminate() -> Result<()> {
|
||||||
|
// Future captures `Lua` instance and dropped all together
|
||||||
let mutex = Arc::new(Mutex::new(0u32));
|
let mutex = Arc::new(Mutex::new(0u32));
|
||||||
{
|
{
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
@@ -565,6 +585,17 @@ async fn test_async_terminate() -> Result<()> {
|
|||||||
}
|
}
|
||||||
assert!(mutex.try_lock().is_ok());
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -598,6 +629,36 @@ async fn test_async_task() -> Result<()> {
|
|||||||
Ok(())
|
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]
|
#[tokio::test]
|
||||||
#[cfg(not(feature = "luau"))]
|
#[cfg(not(feature = "luau"))]
|
||||||
async fn test_async_hook() -> Result<()> {
|
async fn test_async_hook() -> Result<()> {
|
||||||
@@ -608,7 +669,7 @@ async fn test_async_hook() -> Result<()> {
|
|||||||
static HOOK_CALLED: AtomicBool = AtomicBool::new(false);
|
static HOOK_CALLED: AtomicBool = AtomicBool::new(false);
|
||||||
lua.set_global_hook(mlua::HookTriggers::new().every_line(), move |_, _| {
|
lua.set_global_hook(mlua::HookTriggers::new().every_line(), move |_, _| {
|
||||||
if !HOOK_CALLED.swap(true, Ordering::Relaxed) {
|
if !HOOK_CALLED.swap(true, Ordering::Relaxed) {
|
||||||
#[cfg(any(feature = "lu53", feature = "lua54"))]
|
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||||
return Ok(mlua::VmState::Yield);
|
return Ok(mlua::VmState::Yield);
|
||||||
}
|
}
|
||||||
Ok(mlua::VmState::Continue)
|
Ok(mlua::VmState::Continue)
|
||||||
@@ -625,3 +686,35 @@ async fn test_async_hook() -> Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_async_yield_with() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
let func = lua.create_async_function(|lua, (mut a, mut b): (i32, i32)| async move {
|
||||||
|
let zero = lua.yield_with::<MultiValue>(()).await?;
|
||||||
|
assert!(zero.is_empty());
|
||||||
|
let one = lua.yield_with::<MultiValue>(a + b).await?;
|
||||||
|
assert_eq!(one.len(), 1);
|
||||||
|
|
||||||
|
for _ in 0..3 {
|
||||||
|
(a, b) = lua.yield_with((a + b, a * b)).await?;
|
||||||
|
}
|
||||||
|
Ok((0, 0))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let thread = lua.create_thread(func)?;
|
||||||
|
|
||||||
|
let zero = thread.resume::<MultiValue>((2, 3))?; // function arguments
|
||||||
|
assert!(zero.is_empty());
|
||||||
|
let one = thread.resume::<i32>(())?; // value of "zero" is passed here
|
||||||
|
assert_eq!(one, 5);
|
||||||
|
|
||||||
|
assert_eq!(thread.resume::<(i32, i32)>(1)?, (5, 6)); // value of "one" is passed here
|
||||||
|
assert_eq!(thread.resume::<(i32, i32)>((10, 11))?, (21, 110));
|
||||||
|
assert_eq!(thread.resume::<(i32, i32)>((11, 12))?, (23, 132));
|
||||||
|
assert_eq!(thread.resume::<(i32, i32)>((12, 13))?, (0, 0));
|
||||||
|
assert_eq!(thread.status(), ThreadStatus::Finished);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
+68
-2
@@ -1,5 +1,7 @@
|
|||||||
#![cfg(feature = "luau")]
|
#![cfg(feature = "luau")]
|
||||||
|
|
||||||
|
use std::io::{Read, Seek, SeekFrom, Write};
|
||||||
|
|
||||||
use mlua::{Lua, Result, Value};
|
use mlua::{Lua, Result, Value};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -41,7 +43,7 @@ fn test_buffer() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "range end index 14 out of range for slice of length 13")]
|
#[should_panic(expected = "out of range for slice of length 13")]
|
||||||
fn test_buffer_out_of_bounds_read() {
|
fn test_buffer_out_of_bounds_read() {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
let buf = lua.create_buffer(b"hello, world!").unwrap();
|
let buf = lua.create_buffer(b"hello, world!").unwrap();
|
||||||
@@ -49,9 +51,73 @@ fn test_buffer_out_of_bounds_read() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "range end index 16 out of range for slice of length 13")]
|
#[should_panic(expected = "out of range for slice of length 13")]
|
||||||
fn test_buffer_out_of_bounds_write() {
|
fn test_buffer_out_of_bounds_write() {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
let buf = lua.create_buffer(b"hello, world!").unwrap();
|
let buf = lua.create_buffer(b"hello, world!").unwrap();
|
||||||
buf.write_bytes(14, b"!!");
|
buf.write_bytes(14, b"!!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn create_large_buffer() {
|
||||||
|
let lua = Lua::new();
|
||||||
|
let err = lua.create_buffer_with_capacity(1_073_741_824 + 1).unwrap_err(); // 1GB
|
||||||
|
assert!(err.to_string().contains("memory allocation error"));
|
||||||
|
|
||||||
|
// Normal buffer is okay
|
||||||
|
let buf = lua.create_buffer_with_capacity(1024 * 1024).unwrap();
|
||||||
|
assert_eq!(buf.len(), 1024 * 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_buffer_cursor() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
let mut cursor = lua.create_buffer(b"hello, world")?.cursor();
|
||||||
|
|
||||||
|
let mut data = Vec::new();
|
||||||
|
cursor.read_to_end(&mut data)?;
|
||||||
|
assert_eq!(data, b"hello, world");
|
||||||
|
|
||||||
|
// No more data to read
|
||||||
|
let mut one = [0u8; 1];
|
||||||
|
assert_eq!(cursor.read(&mut one)?, 0);
|
||||||
|
|
||||||
|
// Seek to start
|
||||||
|
cursor.seek(SeekFrom::Start(0))?;
|
||||||
|
cursor.read_exact(&mut one)?;
|
||||||
|
assert_eq!(one, [b'h']);
|
||||||
|
|
||||||
|
// Seek to end -5
|
||||||
|
cursor.seek(SeekFrom::End(-5))?;
|
||||||
|
let mut five = [0u8; 5];
|
||||||
|
cursor.read_exact(&mut five)?;
|
||||||
|
assert_eq!(&five, b"world");
|
||||||
|
|
||||||
|
// Seek to current -1
|
||||||
|
cursor.seek(SeekFrom::Current(-1))?;
|
||||||
|
cursor.read_exact(&mut one)?;
|
||||||
|
assert_eq!(one, [b'd']);
|
||||||
|
|
||||||
|
// Invalid seek
|
||||||
|
assert!(cursor.seek(SeekFrom::Current(-100)).is_err());
|
||||||
|
assert!(cursor.seek(SeekFrom::End(1)).is_err());
|
||||||
|
|
||||||
|
// Write data
|
||||||
|
let buf = lua.create_buffer_with_capacity(100)?;
|
||||||
|
cursor = buf.clone().cursor();
|
||||||
|
|
||||||
|
cursor.write_all(b"hello, ...")?;
|
||||||
|
cursor.seek(SeekFrom::Current(-3))?;
|
||||||
|
cursor.write_all(b"Rust!")?;
|
||||||
|
|
||||||
|
assert_eq!(&buf.read_bytes::<12>(0), b"hello, Rust!");
|
||||||
|
|
||||||
|
// Writing beyond the end of the buffer does nothing
|
||||||
|
cursor.seek(SeekFrom::End(0))?;
|
||||||
|
assert_eq!(cursor.write(b".")?, 0);
|
||||||
|
|
||||||
|
// Flush is no-op
|
||||||
|
cursor.flush()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
+10
-12
@@ -21,6 +21,7 @@ fn test_chunk_methods() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(not(target_os = "wasi"))]
|
||||||
fn test_chunk_path() -> Result<()> {
|
fn test_chunk_path() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|
||||||
@@ -119,12 +120,11 @@ fn test_compiler() -> Result<()> {
|
|||||||
.set_debug_level(2)
|
.set_debug_level(2)
|
||||||
.set_type_info_level(1)
|
.set_type_info_level(1)
|
||||||
.set_coverage_level(2)
|
.set_coverage_level(2)
|
||||||
.set_vector_lib("vector")
|
.set_vector_ctor("vector.new")
|
||||||
.set_vector_ctor("new")
|
|
||||||
.set_vector_type("vector")
|
.set_vector_type("vector")
|
||||||
.set_mutable_globals(vec!["mutable_global"])
|
.set_mutable_globals(["mutable_global"])
|
||||||
.set_userdata_types(vec!["MyUserdata"])
|
.set_userdata_types(["MyUserdata"])
|
||||||
.set_disabled_builtins(vec!["tostring"]);
|
.set_disabled_builtins(["tostring"]);
|
||||||
|
|
||||||
assert!(compiler.compile("return tostring(vector.new(1, 2, 3))").is_ok());
|
assert!(compiler.compile("return tostring(vector.new(1, 2, 3))").is_ok());
|
||||||
|
|
||||||
@@ -142,16 +142,14 @@ fn test_compiler() -> Result<()> {
|
|||||||
#[cfg(feature = "luau")]
|
#[cfg(feature = "luau")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_compiler_library_constants() {
|
fn test_compiler_library_constants() {
|
||||||
use mlua::{CompileConstant, Compiler, Vector};
|
use mlua::{Compiler, Vector};
|
||||||
|
|
||||||
let compiler = Compiler::new()
|
let compiler = Compiler::new()
|
||||||
.set_optimization_level(2)
|
.set_optimization_level(2)
|
||||||
.set_library_constants(vec![
|
.add_library_constant("mylib.const_bool", true)
|
||||||
("mylib", "const_bool", CompileConstant::Boolean(true)),
|
.add_library_constant("mylib.const_num", 123.0)
|
||||||
("mylib", "const_num", CompileConstant::Number(123.0)),
|
.add_library_constant("mylib.const_vec", Vector::zero())
|
||||||
("mylib", "const_vec", CompileConstant::Vector(Vector::zero())),
|
.add_library_constant("mylib.const_str", "value1");
|
||||||
("mylib", "const_str", "value1".into()),
|
|
||||||
]);
|
|
||||||
|
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
lua.set_compiler(compiler);
|
lua.set_compiler(compiler);
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
error[E0596]: cannot borrow `s` as mutable, as it is a captured variable in a `Fn` closure
|
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:49
|
--> tests/compile/async_any_userdata_method.rs:9:49
|
||||||
|
|
|
|
||||||
9 | reg.add_async_method("t", |_, this, ()| async {
|
8 | let mut s = &s;
|
||||||
| ^^^^^ cannot borrow as mutable
|
| ----- `s` declared here, outside the closure
|
||||||
|
9 | reg.add_async_method("t", |_, this, ()| async {
|
||||||
|
| ------------- ^^^^^ cannot borrow as mutable
|
||||||
|
| |
|
||||||
|
| in this closure
|
||||||
10 | s = &*this;
|
10 | s = &*this;
|
||||||
| - mutable borrow occurs due to use of `s` in closure
|
| - mutable borrow occurs due to use of `s` in closure
|
||||||
|
|
||||||
error[E0373]: async block may outlive the current function, but it borrows `this`, which is owned by the current function
|
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
|
--> tests/compile/async_any_userdata_method.rs:9:49
|
||||||
|
|
|
|
||||||
9 | reg.add_async_method("t", |_, this, ()| async {
|
9 | reg.add_async_method("t", |_, this, ()| async {
|
||||||
| ^^^^^ may outlive borrowed value `this`
|
| ^^^^^ may outlive borrowed value `this`
|
||||||
10 | s = &*this;
|
10 | s = &*this;
|
||||||
| ---- `this` is borrowed here
|
| ---- `this` is borrowed here
|
||||||
@@ -17,7 +21,7 @@ error[E0373]: async block may outlive the current function, but it borrows `this
|
|||||||
note: async block is returned here
|
note: async block is returned here
|
||||||
--> tests/compile/async_any_userdata_method.rs:9:49
|
--> tests/compile/async_any_userdata_method.rs:9:49
|
||||||
|
|
|
|
||||||
9 | reg.add_async_method("t", |_, this, ()| async {
|
9 | reg.add_async_method("t", |_, this, ()| async {
|
||||||
| _________________________________________________^
|
| _________________________________________________^
|
||||||
10 | | s = &*this;
|
10 | | s = &*this;
|
||||||
11 | | Ok(())
|
11 | | Ok(())
|
||||||
@@ -25,13 +29,13 @@ note: async block is returned here
|
|||||||
| |_________^
|
| |_________^
|
||||||
help: to force the async block to take ownership of `this` (and any other referenced variables), use the `move` keyword
|
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 {
|
9 | reg.add_async_method("t", |_, this, ()| async move {
|
||||||
| ++++
|
| ++++
|
||||||
|
|
||||||
error: lifetime may not live long enough
|
error: lifetime may not live long enough
|
||||||
--> tests/compile/async_any_userdata_method.rs:9:49
|
--> tests/compile/async_any_userdata_method.rs:9:49
|
||||||
|
|
|
|
||||||
9 | reg.add_async_method("t", |_, this, ()| async {
|
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`
|
| | | return type of closure `{async block@$DIR/tests/compile/async_any_userdata_method.rs:9:49: 9:54}` contains a lifetime `'2`
|
||||||
@@ -46,22 +50,28 @@ error: lifetime may not live long enough
|
|||||||
error[E0597]: `s` does not live long enough
|
error[E0597]: `s` does not live long enough
|
||||||
--> tests/compile/async_any_userdata_method.rs:8:21
|
--> tests/compile/async_any_userdata_method.rs:8:21
|
||||||
|
|
|
|
||||||
7 | let s = String::new();
|
7 | let s = String::new();
|
||||||
| - binding `s` declared here
|
| - binding `s` declared here
|
||||||
8 | let mut s = &s;
|
8 | let mut s = &s;
|
||||||
| ^^ borrowed value does not live long enough
|
| ^^ borrowed value does not live long enough
|
||||||
9 | / reg.add_async_method("t", |_, this, ()| async {
|
9 | / reg.add_async_method("t", |_, this, ()| async {
|
||||||
10 | | s = &*this;
|
10 | | s = &*this;
|
||||||
11 | | Ok(())
|
11 | | Ok(())
|
||||||
12 | | });
|
12 | | });
|
||||||
| |__________- argument requires that `s` is borrowed for `'static`
|
| |__________- argument requires that `s` is borrowed for `'static`
|
||||||
13 | })
|
13 | })
|
||||||
| - `s` dropped here while still borrowed
|
| - `s` dropped here while still borrowed
|
||||||
|
|
|
||||||
|
note: requirement that the value outlives `'static` introduced here
|
||||||
|
--> src/userdata.rs
|
||||||
|
|
|
||||||
|
| M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||||
|
| ^^^^^^^
|
||||||
|
|
||||||
error[E0373]: closure may outlive the current function, but it borrows `s`, which is owned by the current function
|
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
|
--> tests/compile/async_any_userdata_method.rs:9:35
|
||||||
|
|
|
|
||||||
9 | reg.add_async_method("t", |_, this, ()| async {
|
9 | reg.add_async_method("t", |_, this, ()| async {
|
||||||
| ^^^^^^^^^^^^^ may outlive borrowed value `s`
|
| ^^^^^^^^^^^^^ may outlive borrowed value `s`
|
||||||
10 | s = &*this;
|
10 | s = &*this;
|
||||||
| - `s` is borrowed here
|
| - `s` is borrowed here
|
||||||
@@ -69,12 +79,12 @@ error[E0373]: closure may outlive the current function, but it borrows `s`, whic
|
|||||||
note: function requires argument type to outlive `'static`
|
note: function requires argument type to outlive `'static`
|
||||||
--> tests/compile/async_any_userdata_method.rs:9:9
|
--> tests/compile/async_any_userdata_method.rs:9:9
|
||||||
|
|
|
|
||||||
9 | / reg.add_async_method("t", |_, this, ()| async {
|
9 | / reg.add_async_method("t", |_, this, ()| async {
|
||||||
10 | | s = &*this;
|
10 | | s = &*this;
|
||||||
11 | | Ok(())
|
11 | | Ok(())
|
||||||
12 | | });
|
12 | | });
|
||||||
| |__________^
|
| |__________^
|
||||||
help: to force the closure to take ownership of `s` (and any other referenced variables), use the `move` keyword
|
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, ()| async {
|
9 | reg.add_async_method("t", move |_, this, ()| async {
|
||||||
| ++++
|
| ++++
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
error: lifetime may not live long enough
|
error: lifetime may not live long enough
|
||||||
--> tests/compile/async_nonstatic_userdata.rs:9:13
|
--> tests/compile/async_nonstatic_userdata.rs:9:13
|
||||||
|
|
|
|
||||||
7 | impl UserData for MyUserData<'_> {
|
7 | impl UserData for MyUserData<'_> {
|
||||||
| -- lifetime `'1` appears in the `impl`'s self type
|
| -- lifetime `'1` appears in the `impl`'s self type
|
||||||
8 | fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
8 | fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||||
9 | / methods.add_async_method("print", |_, data, ()| async move {
|
9 | / methods.add_async_method("print", |_, data, ()| async move {
|
||||||
10 | | println!("{}", data.0);
|
10 | | println!("{}", data.0);
|
||||||
11 | | Ok(())
|
11 | | Ok(())
|
||||||
12 | | });
|
12 | | });
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
error[E0277]: the type `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
error[E0277]: the type `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||||
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
||||||
|
|
|
|
||||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||||
| |
|
| |
|
||||||
| required by a bound introduced by this call
|
| required by a bound introduced by this call
|
||||||
|
|
|
|
||||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::raw::RawLua>`, which is required by `{closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:20}: UnwindSafe`
|
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::raw::RawLua>`
|
||||||
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
|
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
|
||||||
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
--> $CARGO/lock_api-$VERSION/src/remutex.rs
|
||||||
|
|
|
|
||||||
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
||||||
@@ -20,7 +20,7 @@ note: required because it appears within the type `alloc::sync::ArcInner<lock_ap
|
|||||||
note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>`
|
note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>`
|
||||||
--> $RUST/core/src/marker.rs
|
--> $RUST/core/src/marker.rs
|
||||||
|
|
|
|
||||||
| pub struct PhantomData<T: ?Sized>;
|
| pub struct PhantomData<T: PointeeSized>;
|
||||||
| ^^^^^^^^^^^
|
| ^^^^^^^^^^^
|
||||||
note: required because it appears within the type `Arc<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
note: required because it appears within the type `Arc<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||||
--> $RUST/alloc/src/sync.rs
|
--> $RUST/alloc/src/sync.rs
|
||||||
@@ -44,27 +44,27 @@ note: required by a bound in `std::panic::catch_unwind`
|
|||||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||||
| ^^^^^^^^^^ required by this bound in `catch_unwind`
|
| ^^^^^^^^^^ required by this bound in `catch_unwind`
|
||||||
|
|
||||||
error[E0277]: the type `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
error[E0277]: the type `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||||
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
||||||
|
|
|
|
||||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||||
| |
|
| |
|
||||||
| required by a bound introduced by this call
|
| required by a bound introduced by this call
|
||||||
|
|
|
|
||||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<usize>`, which is required by `{closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:20}: UnwindSafe`
|
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<usize>`
|
||||||
note: required because it appears within the type `Cell<usize>`
|
note: required because it appears within the type `Cell<usize>`
|
||||||
--> $RUST/core/src/cell.rs
|
--> $RUST/core/src/cell.rs
|
||||||
|
|
|
|
||||||
| pub struct Cell<T: ?Sized> {
|
| pub struct Cell<T: ?Sized> {
|
||||||
| ^^^^
|
| ^^^^
|
||||||
note: required because it appears within the type `lock_api::remutex::RawReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId>`
|
note: required because it appears within the type `lock_api::remutex::RawReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId>`
|
||||||
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
--> $CARGO/lock_api-$VERSION/src/remutex.rs
|
||||||
|
|
|
|
||||||
| pub struct RawReentrantMutex<R, G> {
|
| pub struct RawReentrantMutex<R, G> {
|
||||||
| ^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^
|
||||||
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
|
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
|
||||||
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
--> $CARGO/lock_api-$VERSION/src/remutex.rs
|
||||||
|
|
|
|
||||||
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
||||||
@@ -76,7 +76,7 @@ note: required because it appears within the type `alloc::sync::ArcInner<lock_ap
|
|||||||
note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>`
|
note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>`
|
||||||
--> $RUST/core/src/marker.rs
|
--> $RUST/core/src/marker.rs
|
||||||
|
|
|
|
||||||
| pub struct PhantomData<T: ?Sized>;
|
| pub struct PhantomData<T: PointeeSized>;
|
||||||
| ^^^^^^^^^^^
|
| ^^^^^^^^^^^
|
||||||
note: required because it appears within the type `Arc<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
note: required because it appears within the type `Arc<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||||
--> $RUST/alloc/src/sync.rs
|
--> $RUST/alloc/src/sync.rs
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ error[E0277]: `Rc<Cell<i32>>` cannot be sent between threads safely
|
|||||||
| | within this `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`
|
| | within this `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`
|
||||||
| required by a bound introduced by this call
|
| required by a bound introduced by this call
|
||||||
|
|
|
|
||||||
= help: within `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`, the trait `Send` is not implemented for `Rc<Cell<i32>>`, which is required by `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}: MaybeSend`
|
= help: within `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`, the trait `Send` is not implemented for `Rc<Cell<i32>>`
|
||||||
note: required because it's used within this closure
|
note: required because it's used within this closure
|
||||||
--> tests/compile/non_send.rs:11:25
|
--> tests/compile/non_send.rs:11:25
|
||||||
|
|
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
error[E0277]: the type `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
error[E0277]: the type `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||||
|
|
|
|
||||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||||
| |
|
| |
|
||||||
| required by a bound introduced by this call
|
| required by a bound introduced by this call
|
||||||
|
|
|
|
||||||
= help: within `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::raw::RawLua>`, which is required by `{closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:25}: UnwindSafe`
|
= help: within `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::raw::RawLua>`
|
||||||
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
|
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
|
||||||
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
--> $CARGO/lock_api-$VERSION/src/remutex.rs
|
||||||
|
|
|
|
||||||
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
||||||
@@ -23,16 +23,16 @@ note: required because it appears within the type `std::sync::Weak<lock_api::rem
|
|||||||
|
|
|
|
||||||
| pub struct Weak<
|
| pub struct Weak<
|
||||||
| ^^^^
|
| ^^^^
|
||||||
note: required because it appears within the type `mlua::state::WeakLua`
|
note: required because it appears within the type `WeakLua`
|
||||||
--> src/state.rs
|
--> src/state.rs
|
||||||
|
|
|
|
||||||
| pub(crate) struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
|
| pub struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
|
||||||
| ^^^^^^^
|
| ^^^^^^^
|
||||||
note: required because it appears within the type `mlua::types::ValueRef`
|
note: required because it appears within the type `mlua::types::value_ref::ValueRef`
|
||||||
--> src/types.rs
|
--> src/types/value_ref.rs
|
||||||
|
|
|
|
||||||
| pub(crate) struct ValueRef {
|
| pub struct ValueRef {
|
||||||
| ^^^^^^^^
|
| ^^^^^^^^
|
||||||
note: required because it appears within the type `LuaTable`
|
note: required because it appears within the type `LuaTable`
|
||||||
--> src/table.rs
|
--> src/table.rs
|
||||||
|
|
|
|
||||||
@@ -49,27 +49,27 @@ note: required by a bound in `std::panic::catch_unwind`
|
|||||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||||
| ^^^^^^^^^^ required by this bound in `catch_unwind`
|
| ^^^^^^^^^^ required by this bound in `catch_unwind`
|
||||||
|
|
||||||
error[E0277]: the type `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
error[E0277]: the type `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||||
|
|
|
|
||||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||||
| |
|
| |
|
||||||
| required by a bound introduced by this call
|
| required by a bound introduced by this call
|
||||||
|
|
|
|
||||||
= help: within `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<usize>`, which is required by `{closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:25}: UnwindSafe`
|
= help: within `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<usize>`
|
||||||
note: required because it appears within the type `Cell<usize>`
|
note: required because it appears within the type `Cell<usize>`
|
||||||
--> $RUST/core/src/cell.rs
|
--> $RUST/core/src/cell.rs
|
||||||
|
|
|
|
||||||
| pub struct Cell<T: ?Sized> {
|
| pub struct Cell<T: ?Sized> {
|
||||||
| ^^^^
|
| ^^^^
|
||||||
note: required because it appears within the type `lock_api::remutex::RawReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId>`
|
note: required because it appears within the type `lock_api::remutex::RawReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId>`
|
||||||
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
--> $CARGO/lock_api-$VERSION/src/remutex.rs
|
||||||
|
|
|
|
||||||
| pub struct RawReentrantMutex<R, G> {
|
| pub struct RawReentrantMutex<R, G> {
|
||||||
| ^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^
|
||||||
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
|
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
|
||||||
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
--> $CARGO/lock_api-$VERSION/src/remutex.rs
|
||||||
|
|
|
|
||||||
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
||||||
@@ -84,16 +84,16 @@ note: required because it appears within the type `std::sync::Weak<lock_api::rem
|
|||||||
|
|
|
|
||||||
| pub struct Weak<
|
| pub struct Weak<
|
||||||
| ^^^^
|
| ^^^^
|
||||||
note: required because it appears within the type `mlua::state::WeakLua`
|
note: required because it appears within the type `WeakLua`
|
||||||
--> src/state.rs
|
--> src/state.rs
|
||||||
|
|
|
|
||||||
| pub(crate) struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
|
| pub struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
|
||||||
| ^^^^^^^
|
| ^^^^^^^
|
||||||
note: required because it appears within the type `mlua::types::ValueRef`
|
note: required because it appears within the type `mlua::types::value_ref::ValueRef`
|
||||||
--> src/types.rs
|
--> src/types/value_ref.rs
|
||||||
|
|
|
|
||||||
| pub(crate) struct ValueRef {
|
| pub struct ValueRef {
|
||||||
| ^^^^^^^^
|
| ^^^^^^^^
|
||||||
note: required because it appears within the type `LuaTable`
|
note: required because it appears within the type `LuaTable`
|
||||||
--> src/table.rs
|
--> src/table.rs
|
||||||
|
|
|
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
error[E0373]: closure may outlive the current function, but it borrows `inner`, which is owned by the current function
|
error[E0373]: closure may outlive the current function, but it borrows `inner`, which is owned by the current function
|
||||||
--> tests/compile/scope_callback_capture.rs:7:43
|
--> tests/compile/scope_callback_capture.rs:7:43
|
||||||
|
|
|
|
||||||
5 | lua.scope(|scope| {
|
5 | lua.scope(|scope| {
|
||||||
| ----- has type `&'1 mut mlua::scope::Scope<'1, '_>`
|
| ----- has type `&'1 mlua::Scope<'1, '_>`
|
||||||
6 | let mut inner: Option<Table> = None;
|
6 | let mut inner: Option<Table> = None;
|
||||||
7 | let f = scope.create_function_mut(|_, t: Table| {
|
7 | let f = scope.create_function_mut(|_, t: Table| {
|
||||||
| ^^^^^^^^^^^^^ may outlive borrowed value `inner`
|
| ^^^^^^^^^^^^^ may outlive borrowed value `inner`
|
||||||
8 | inner = Some(t);
|
8 | inner = Some(t);
|
||||||
| ----- `inner` is borrowed here
|
| ----- `inner` is borrowed here
|
||||||
|
|
|
|
||||||
note: function requires argument type to outlive `'1`
|
note: function requires argument type to outlive `'1`
|
||||||
--> tests/compile/scope_callback_capture.rs:7:17
|
--> tests/compile/scope_callback_capture.rs:7:17
|
||||||
|
|
|
|
||||||
7 | let f = scope.create_function_mut(|_, t: Table| {
|
7 | let f = scope.create_function_mut(|_, t: Table| {
|
||||||
| _________________^
|
| _________________^
|
||||||
8 | | inner = Some(t);
|
8 | | inner = Some(t);
|
||||||
9 | | Ok(())
|
9 | | Ok(())
|
||||||
10 | | })?;
|
10 | | })?;
|
||||||
| |__________^
|
| |__________^
|
||||||
help: to force the closure to take ownership of `inner` (and any other referenced variables), use the `move` keyword
|
help: to force the closure to take ownership of `inner` (and any other referenced variables), use the `move` keyword
|
||||||
|
|
|
|
||||||
7 | let f = scope.create_function_mut(move |_, t: Table| {
|
7 | let f = scope.create_function_mut(move |_, t: Table| {
|
||||||
| ++++
|
| ++++
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
error[E0373]: closure may outlive the current function, but it borrows `test.field`, which is owned by the current function
|
error[E0373]: closure may outlive the current function, but it borrows `test.field`, which is owned by the current function
|
||||||
--> tests/compile/scope_invariance.rs:13:39
|
--> tests/compile/scope_invariance.rs:13:39
|
||||||
|
|
|
|
||||||
9 | lua.scope(|scope| {
|
9 | lua.scope(|scope| {
|
||||||
| ----- has type `&'1 mut mlua::scope::Scope<'1, '_>`
|
| ----- has type `&'1 mlua::Scope<'1, '_>`
|
||||||
...
|
...
|
||||||
13 | scope.create_function_mut(|_, ()| {
|
13 | scope.create_function_mut(|_, ()| {
|
||||||
| ^^^^^^^ may outlive borrowed value `test.field`
|
| ^^^^^^^ may outlive borrowed value `test.field`
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ error[E0499]: cannot borrow `i` as mutable more than once at a time
|
|||||||
--> tests/compile/scope_mutable_aliasing.rs:12:51
|
--> tests/compile/scope_mutable_aliasing.rs:12:51
|
||||||
|
|
|
|
||||||
10 | lua.scope(|scope| {
|
10 | lua.scope(|scope| {
|
||||||
| ----- has type `&mut mlua::scope::Scope<'_, '1>`
|
| ----- has type `&mlua::Scope<'_, '1>`
|
||||||
11 | let _a = scope.create_userdata(MyUserData(&mut i)).unwrap();
|
11 | let _a = scope.create_userdata(MyUserData(&mut i)).unwrap();
|
||||||
| -----------------------------------------
|
| -----------------------------------------
|
||||||
| | |
|
| | |
|
||||||
@@ -10,3 +10,9 @@ error[E0499]: cannot borrow `i` as mutable more than once at a time
|
|||||||
| argument requires that `i` is borrowed for `'1`
|
| argument requires that `i` is borrowed for `'1`
|
||||||
12 | let _b = scope.create_userdata(MyUserData(&mut i)).unwrap();
|
12 | let _b = scope.create_userdata(MyUserData(&mut i)).unwrap();
|
||||||
| ^^^^^^ second mutable borrow occurs here
|
| ^^^^^^ second mutable borrow occurs here
|
||||||
|
|
|
||||||
|
note: requirement that the value outlives `'1` introduced here
|
||||||
|
--> src/scope.rs
|
||||||
|
|
|
||||||
|
| T: UserData + 'env,
|
||||||
|
| ^^^^
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ error[E0597]: `ibad` does not live long enough
|
|||||||
--> tests/compile/scope_userdata_borrow.rs:15:46
|
--> tests/compile/scope_userdata_borrow.rs:15:46
|
||||||
|
|
|
|
||||||
11 | lua.scope(|scope| {
|
11 | lua.scope(|scope| {
|
||||||
| ----- has type `&mut mlua::scope::Scope<'_, '1>`
|
| ----- has type `&mlua::Scope<'_, '1>`
|
||||||
...
|
...
|
||||||
14 | let ibad = 42;
|
14 | let ibad = 42;
|
||||||
| ---- binding `ibad` declared here
|
| ---- binding `ibad` declared here
|
||||||
@@ -13,3 +13,9 @@ error[E0597]: `ibad` does not live long enough
|
|||||||
| argument requires that `ibad` is borrowed for `'1`
|
| argument requires that `ibad` is borrowed for `'1`
|
||||||
16 | };
|
16 | };
|
||||||
| - `ibad` dropped here while still borrowed
|
| - `ibad` dropped here while still borrowed
|
||||||
|
|
|
||||||
|
note: requirement that the value outlives `'1` introduced here
|
||||||
|
--> src/scope.rs
|
||||||
|
|
|
||||||
|
| T: UserData + 'env,
|
||||||
|
| ^^^^
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user