mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
73 Commits
| 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 |
@@ -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
|
||||
+50
-31
@@ -9,12 +9,12 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
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:
|
||||
- os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
target: aarch64-apple-darwin
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
steps:
|
||||
@@ -31,35 +31,19 @@ jobs:
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow,userdata-wrappers,send"
|
||||
shell: bash
|
||||
- name: Build ${{ matrix.lua }} pkg-config
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
if: ${{ matrix.os == 'ubuntu-latest' && matrix.lua != 'lua55' }}
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends liblua5.4-dev liblua5.3-dev liblua5.2-dev liblua5.1-0-dev libluajit-5.1-dev
|
||||
cargo build --features "${{ matrix.lua }}"
|
||||
|
||||
build_aarch64_cross_macos:
|
||||
name: Cross-compile to aarch64-apple-darwin
|
||||
runs-on: macos-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,serde,macros,anyhow,userdata-wrappers"
|
||||
|
||||
build_aarch64_cross_ubuntu:
|
||||
name: Cross-compile to aarch64-unknown-linux-gnu
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
lua: [lua55, lua54, lua53, lua52, lua51, luajit]
|
||||
steps:
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
@@ -81,7 +65,7 @@ jobs:
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51]
|
||||
lua: [lua55, lua54, lua53, lua52, lua51]
|
||||
steps:
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
@@ -105,12 +89,12 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
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:
|
||||
- os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
target: aarch64-apple-darwin
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
steps:
|
||||
@@ -126,8 +110,8 @@ jobs:
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow,userdata-wrappers"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow,userdata-wrappers,send"
|
||||
shell: bash
|
||||
- name: Run compile tests (macos lua54)
|
||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua54' }}
|
||||
- name: Run compile tests (macos lua55)
|
||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua55' }}
|
||||
run: |
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" --tests -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serde,macros" --tests -- --ignored
|
||||
@@ -141,7 +125,7 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
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:
|
||||
- os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
@@ -168,7 +152,7 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
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:
|
||||
- os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
@@ -194,12 +178,12 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust: [stable]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
lua: [lua55, lua54, lua53, lua52, lua51, luajit]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
target: aarch64-apple-darwin
|
||||
steps:
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
@@ -240,7 +224,7 @@ jobs:
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luau]
|
||||
lua: [lua55, lua54, lua53, lua52, lua51, luau]
|
||||
steps:
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
@@ -256,6 +240,41 @@ jobs:
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored"
|
||||
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:
|
||||
name: Rustfmt
|
||||
runs-on: ubuntu-latest
|
||||
@@ -271,7 +290,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
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:
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
name: Typos Check
|
||||
name: Spelling Check
|
||||
on:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CLICOLOR: 1
|
||||
|
||||
jobs:
|
||||
run:
|
||||
spelling:
|
||||
name: Spell Check with Typos
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Actions Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
uses: actions/checkout@main
|
||||
- name: Check spelling
|
||||
uses: crate-ci/typos@master
|
||||
uses: crate-ci/typos@v1.42.1
|
||||
with:
|
||||
config: ./typos.toml
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
## 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
|
||||
|
||||
+14
-13
@@ -1,9 +1,9 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.11.4" # 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>"]
|
||||
rust-version = "1.79.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.88"
|
||||
edition = "2024"
|
||||
repository = "https://github.com/mlua-rs/mlua"
|
||||
documentation = "https://docs.rs/mlua"
|
||||
readme = "README.md"
|
||||
@@ -11,12 +11,12 @@ keywords = ["lua", "luajit", "luau", "async", "scripting"]
|
||||
categories = ["api-bindings", "asynchronous"]
|
||||
license = "MIT"
|
||||
description = """
|
||||
High level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and 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.
|
||||
"""
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["lua54", "vendored", "async", "send", "serde", "macros"]
|
||||
features = ["lua55", "vendored", "async", "send", "serde", "macros"]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
@@ -26,6 +26,7 @@ members = [
|
||||
]
|
||||
|
||||
[features]
|
||||
lua55 = ["ffi/lua55"]
|
||||
lua54 = ["ffi/lua54"]
|
||||
lua53 = ["ffi/lua53"]
|
||||
lua52 = ["ffi/lua52"]
|
||||
@@ -60,24 +61,24 @@ erased-serde = { version = "0.4", optional = true }
|
||||
serde-value = { version = "0.7", optional = true }
|
||||
parking_lot = { version = "0.12", features = ["arc_lock"] }
|
||||
anyhow = { version = "1.0", optional = true }
|
||||
rustversion = "1.0"
|
||||
libc = "0.2"
|
||||
|
||||
ffi = { package = "mlua-sys", version = "0.8.3", path = "mlua-sys" }
|
||||
ffi = { package = "mlua-sys", version = "0.10.0", path = "mlua-sys" }
|
||||
|
||||
[dev-dependencies]
|
||||
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"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0", features = ["arbitrary_precision"] }
|
||||
maplit = "1.0"
|
||||
tempfile = "3"
|
||||
static_assertions = "1.0"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
||||
[target.'cfg(not(target_family = "wasm"))'.dev-dependencies]
|
||||
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"] }
|
||||
tempfile = "3"
|
||||
criterion = { version = "0.7", features = ["async_tokio"] }
|
||||
rustyline = "17.0"
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
`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.
|
||||
|
||||
Started as an `rlua` fork, `mlua` supports Lua 5.4, 5.3, 5.2, 5.1 (including LuaJIT) and [Luau] and allows writing native Lua modules in Rust as well as using Lua in a standalone mode.
|
||||
Started as 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.
|
||||
|
||||
`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).
|
||||
|
||||
@@ -36,6 +36,7 @@ WebAssembly (WASM) is supported through the `wasm32-unknown-emscripten` target f
|
||||
`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.
|
||||
|
||||
* `lua55`: enable Lua [5.5] support
|
||||
* `lua54`: enable Lua [5.4] support
|
||||
* `lua53`: enable Lua [5.3] support
|
||||
* `lua52`: enable Lua [5.2] support
|
||||
@@ -55,6 +56,7 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
|
||||
* `anyhow`: enable `anyhow::Error` conversion into Lua
|
||||
* `userdata-wrappers`: opt into `impl UserData` for `Rc<T>`/`Arc<T>`/`Rc<RefCell<T>>`/`Arc<Mutex<T>>` where `T: UserData`
|
||||
|
||||
[5.5]: https://www.lua.org/manual/5.5/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.2]: https://www.lua.org/manual/5.2/manual.html
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
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::task;
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ use hyper::body::Incoming;
|
||||
use hyper_util::client::legacy::Client as HyperClient;
|
||||
use hyper_util::rt::TokioExecutor;
|
||||
|
||||
use mlua::{chunk, ExternalResult, Lua, Result, UserData, UserDataMethods};
|
||||
use mlua::{ExternalResult, Lua, Result, UserData, UserDataMethods, chunk};
|
||||
|
||||
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")]
|
||||
async fn main() -> Result<()> {
|
||||
|
||||
@@ -11,7 +11,7 @@ use hyper::{Request, Response};
|
||||
use hyper_util::rt::TokioIo;
|
||||
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
|
||||
struct LuaRequest(SocketAddr, Request<Incoming>);
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::net::SocketAddr;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
|
||||
use mlua::{chunk, BString, Function, Lua, UserData, UserDataMethods};
|
||||
use mlua::{BString, Function, Lua, UserData, UserDataMethods, chunk};
|
||||
|
||||
struct LuaTcpStream(TcpStream);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::f32;
|
||||
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<()> {
|
||||
// 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]
|
||||
|
||||
[features]
|
||||
lua55 = ["mlua/lua55"]
|
||||
lua54 = ["mlua/lua54"]
|
||||
lua53 = ["mlua/lua53"]
|
||||
lua52 = ["mlua/lua52"]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use mlua::{chunk, Lua, MetaMethod, Result, UserData};
|
||||
use mlua::{Lua, MetaMethod, Result, UserData, chunk};
|
||||
|
||||
#[derive(Default)]
|
||||
struct Rectangle {
|
||||
|
||||
+9
-7
@@ -1,9 +1,9 @@
|
||||
[package]
|
||||
name = "mlua-sys"
|
||||
version = "0.8.3"
|
||||
version = "0.10.0"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
rust-version = "1.71"
|
||||
edition = "2021"
|
||||
rust-version = "1.88"
|
||||
edition = "2024"
|
||||
repository = "https://github.com/mlua-rs/mlua"
|
||||
documentation = "https://docs.rs/mlua-sys"
|
||||
readme = "README.md"
|
||||
@@ -12,14 +12,15 @@ license = "MIT"
|
||||
links = "lua"
|
||||
build = "build/main.rs"
|
||||
description = """
|
||||
Low level (FFI) bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and 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]
|
||||
features = ["lua54", "vendored"]
|
||||
features = ["lua55", "vendored"]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[features]
|
||||
lua55 = []
|
||||
lua54 = []
|
||||
lua53 = []
|
||||
lua52 = []
|
||||
@@ -34,14 +35,15 @@ external = []
|
||||
module = []
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
cfg-if = "1.0"
|
||||
pkg-config = "0.3.17"
|
||||
lua-src = { version = ">= 548.1.0, < 548.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 }
|
||||
luau0-src = { version = "0.15.6", optional = true }
|
||||
luau0-src = { version = "0.18.0", optional = true }
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(raw_dylib)'] }
|
||||
|
||||
+2
-1
@@ -1,8 +1,9 @@
|
||||
# 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.
|
||||
|
||||
[LuaJIT]: https://github.com/LuaJIT/LuaJIT
|
||||
[Luau]: https://github.com/luau-lang/luau
|
||||
[mlua]: https://crates.io/crates/mlua
|
||||
|
||||
@@ -31,18 +31,16 @@ pub fn probe_lua() {
|
||||
|
||||
// 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")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) =
|
||||
("5.4", "5.5", ["lua5.4", "lua-5.4", "lua54"], "5.4");
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("5.4", "5.5", ["lua5.4", "lua-5.4", "lua54"], "5.4");
|
||||
#[cfg(feature = "lua53")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) =
|
||||
("5.3", "5.4", ["lua5.3", "lua-5.3", "lua53"], "5.3");
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("5.3", "5.4", ["lua5.3", "lua-5.3", "lua53"], "5.3");
|
||||
#[cfg(feature = "lua52")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) =
|
||||
("5.2", "5.3", ["lua5.2", "lua-5.2", "lua52"], "5.2");
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("5.2", "5.3", ["lua5.2", "lua-5.2", "lua52"], "5.2");
|
||||
#[cfg(feature = "lua51")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) =
|
||||
("5.1", "5.2", ["lua5.1", "lua-5.1", "lua51"], "5.1");
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("5.1", "5.2", ["lua5.1", "lua-5.1", "lua51"], "5.1");
|
||||
#[cfg(feature = "luajit")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("2.0.4", "2.2", [], "JIT");
|
||||
|
||||
@@ -54,9 +52,7 @@ pub fn probe_lua() {
|
||||
|
||||
if lua.is_err() {
|
||||
for pkg in alt_probe {
|
||||
lua = pkg_config::Config::new()
|
||||
.cargo_metadata(true)
|
||||
.probe(pkg);
|
||||
lua = pkg_config::Config::new().cargo_metadata(true).probe(pkg);
|
||||
|
||||
if lua.is_ok() {
|
||||
break;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
pub fn probe_lua() {
|
||||
#[cfg(feature = "lua55")]
|
||||
let artifacts = lua_src::Build::new().build(lua_src::Lua55);
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
let artifacts = lua_src::Build::new().build(lua_src::Lua54);
|
||||
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
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");
|
||||
} 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");
|
||||
} 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");
|
||||
} 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");
|
||||
} 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");
|
||||
} 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");
|
||||
} else {
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+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(clippy::missing_safety_doc)]
|
||||
@@ -8,6 +8,9 @@
|
||||
|
||||
use std::os::raw::c_int;
|
||||
|
||||
#[cfg(any(feature = "lua55", doc))]
|
||||
pub use lua55::*;
|
||||
|
||||
#[cfg(any(feature = "lua54", doc))]
|
||||
pub use lua54::*;
|
||||
|
||||
@@ -23,7 +26,7 @@ pub use lua51::*;
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
pub use luau::*;
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
#[doc(hidden)]
|
||||
pub const LUA_MAX_UPVALUES: c_int = 255;
|
||||
|
||||
@@ -40,14 +43,22 @@ pub const LUA_MAX_UPVALUES: c_int = 200;
|
||||
#[doc(hidden)]
|
||||
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. This value is used to
|
||||
// add fast paths for low alignment values.
|
||||
#[cfg(any(
|
||||
// The minimum alignment guaranteed by the architecture.
|
||||
// Copied from https://github.com/rust-lang/rust/blob/main/library/std/src/sys/alloc/mod.rs
|
||||
#[doc(hidden)]
|
||||
#[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 = "arm",
|
||||
target_arch = "m68k",
|
||||
target_arch = "csky",
|
||||
target_arch = "loongarch32",
|
||||
target_arch = "mips",
|
||||
target_arch = "mips32r6",
|
||||
target_arch = "powerpc",
|
||||
@@ -55,12 +66,11 @@ pub const LUA_TRACEBACK_STACK: c_int = 11;
|
||||
target_arch = "sparc",
|
||||
target_arch = "wasm32",
|
||||
target_arch = "hexagon",
|
||||
all(target_arch = "riscv32", not(any(target_os = "espidf", target_os = "zkvm"))),
|
||||
all(target_arch = "xtensa", not(target_os = "espidf")),
|
||||
))]
|
||||
#[doc(hidden)]
|
||||
pub const SYS_MIN_ALIGN: usize = 8;
|
||||
#[cfg(any(
|
||||
target_arch = "riscv32",
|
||||
target_arch = "xtensa",
|
||||
)) {
|
||||
8
|
||||
} else if cfg!(any(
|
||||
target_arch = "x86_64",
|
||||
target_arch = "aarch64",
|
||||
target_arch = "arm64ec",
|
||||
@@ -71,20 +81,19 @@ pub const SYS_MIN_ALIGN: usize = 8;
|
||||
target_arch = "sparc64",
|
||||
target_arch = "riscv64",
|
||||
target_arch = "wasm64",
|
||||
))]
|
||||
#[doc(hidden)]
|
||||
pub const SYS_MIN_ALIGN: usize = 16;
|
||||
// The allocator on the esp-idf and zkvm platforms guarantee 4 byte alignment.
|
||||
#[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;
|
||||
)) {
|
||||
16
|
||||
} else {
|
||||
panic!("no value for SYS_MIN_ALIGN")
|
||||
};
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
#[cfg(any(feature = "lua55", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua55")))]
|
||||
pub mod lua55;
|
||||
|
||||
#[cfg(any(feature = "lua54", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
||||
pub mod lua54;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
//!
|
||||
//! 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::{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_LEVELS2: c_int = 10; // size of the second part of the stack
|
||||
const COMPAT53_LEVELS1: c_int = 10; // size of the first 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 {
|
||||
let mut ar: lua_Debug = mem::zeroed();
|
||||
@@ -54,11 +55,7 @@ unsafe fn compat53_checkmode(
|
||||
while *st != 0 && *st != c {
|
||||
st = st.offset(1);
|
||||
}
|
||||
if *st == c {
|
||||
st
|
||||
} else {
|
||||
ptr::null()
|
||||
}
|
||||
if *st == c { st } else { ptr::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)
|
||||
return 1;
|
||||
} else if compat53_findfield(L, objidx, level - 1) != 0 {
|
||||
// try recursively
|
||||
lua_remove(L, -2); // remove table (but keep name)
|
||||
lua_pushliteral(L, c".");
|
||||
lua_insert(L, -2); // place '.' between the two names
|
||||
lua_concat(L, 3);
|
||||
// stack: lib_name, lib_table, field_name (top)
|
||||
lua_pushliteral(L, c"."); // place '.' between the two names
|
||||
lua_replace(L, -3); // (in the slot occupied by table)
|
||||
lua_concat(L, 3); // lib_name.field_name
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -101,13 +97,20 @@ unsafe fn compat53_findfield(L: *mut lua_State, objidx: c_int, level: c_int) ->
|
||||
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);
|
||||
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);
|
||||
luaL_checkstack(L, 6, cstr!("not enough stack")); // slots for 'findfield'
|
||||
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_pop(L, 2); // remove pushed values
|
||||
lua_settop(L, top + 1); // remove pushed values
|
||||
1
|
||||
} else {
|
||||
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) {
|
||||
if *(*ar).namewhat != b'\0' as c_char {
|
||||
// is there a name?
|
||||
lua_pushfstring(L, cstr!("function '%s'"), (*ar).name);
|
||||
unsafe fn compat53_pushfuncname(L: *mut lua_State, L1: *mut lua_State, ar: *mut lua_Debug) {
|
||||
// try first a global name
|
||||
if compat53_pushglobalfuncname(L, L1, ar) != 0 {
|
||||
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 {
|
||||
// main?
|
||||
lua_pushliteral(L, c"main chunk");
|
||||
} else if *(*ar).what == b'C' as c_char {
|
||||
if compat53_pushglobalfuncname(L, ar) != 0 {
|
||||
lua_pushfstring(L, cstr!("function '%s'"), lua_tostring(L, -1));
|
||||
lua_remove(L, -2); // remove name
|
||||
} else {
|
||||
lua_pushliteral(L, c"?");
|
||||
}
|
||||
} else if *(*ar).what != b'C' as c_char {
|
||||
// for Lua functions, use <file:line>
|
||||
let short_src = (*ar).short_src.as_ptr();
|
||||
lua_pushfstring(L, cstr!("function <%s:%d>"), short_src, (*ar).linedefined);
|
||||
} else {
|
||||
lua_pushfstring(
|
||||
L,
|
||||
cstr!("function <%s:%d>"),
|
||||
(*ar).short_src.as_ptr(),
|
||||
(*ar).linedefined,
|
||||
);
|
||||
lua_pushliteral(L, c"?");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,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 top = lua_gettop(L);
|
||||
let numlevels = compat53_countlevels(L1);
|
||||
let mark = if numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2 {
|
||||
COMPAT53_LEVELS1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
#[rustfmt::skip]
|
||||
let mut limit = if numlevels - level > COMPAT53_LEVELS1 + COMPAT53_LEVELS2 { COMPAT53_LEVELS1 } else { -1 };
|
||||
|
||||
if !msg.is_null() {
|
||||
lua_pushfstring(L, cstr!("%s\n"), msg);
|
||||
}
|
||||
lua_pushliteral(L, c"stack traceback:");
|
||||
while lua_getstack(L1, level, &mut ar) != 0 {
|
||||
level += 1;
|
||||
if level == mark {
|
||||
if limit == 0 {
|
||||
// too many levels?
|
||||
lua_pushliteral(L, c"\n\t..."); // add a '...'
|
||||
level = numlevels - COMPAT53_LEVELS2; // and skip to last ones
|
||||
let n = numlevels - level - COMPAT53_LEVELS2;
|
||||
// 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 {
|
||||
lua_getinfo(L1, cstr!("Slnt"), &mut ar);
|
||||
lua_pushfstring(L, cstr!("\n\t%s:"), ar.short_src.as_ptr());
|
||||
if ar.currentline > 0 {
|
||||
lua_pushfstring(L, cstr!("%d:"), ar.currentline);
|
||||
lua_getinfo(L1, cstr!("Sln"), &mut ar);
|
||||
if *ar.what != b't' as c_char {
|
||||
if ar.currentline <= 0 {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
// Deprecated from 5.3: luaL_checkint, luaL_optint, luaL_checklong, luaL_optlong
|
||||
|
||||
#[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))
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// TODO: lua_strlen
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_strlen(L: *mut lua_State, i: c_int) -> usize {
|
||||
lua_objlen(L, i)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_isfunction(L: *mut lua_State, n: c_int) -> c_int {
|
||||
|
||||
@@ -125,7 +125,7 @@ pub unsafe fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int {
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int {
|
||||
let n = n.try_into().expect("cannot convert index to lua_Integer");
|
||||
let n = n.try_into().expect("cannot convert index to c_int");
|
||||
lua_rawgeti_(L, idx, n);
|
||||
lua_type(L, -1)
|
||||
}
|
||||
@@ -153,7 +153,7 @@ pub unsafe fn lua_seti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) {
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer) {
|
||||
let n = n.try_into().expect("cannot convert index from lua_Integer");
|
||||
let n = n.try_into().expect("cannot convert index to c_int");
|
||||
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_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;
|
||||
-> *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;
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
// luaL_opt would be implemented here but it is undocumented, so it's omitted
|
||||
|
||||
#[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())
|
||||
}
|
||||
|
||||
#[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`.
|
||||
|
||||
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};
|
||||
|
||||
@@ -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_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;
|
||||
-> *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;
|
||||
@@ -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_opt would be implemented here but it is undocumented, so it's omitted
|
||||
|
||||
#[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())
|
||||
}
|
||||
|
||||
#[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`.
|
||||
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::ptr;
|
||||
use std::os::raw::{c_char, c_double, c_int, c_long, c_void};
|
||||
use std::{mem, ptr};
|
||||
|
||||
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_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;
|
||||
-> *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;
|
||||
@@ -91,7 +91,7 @@ unsafe extern "C-unwind" {
|
||||
|
||||
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(
|
||||
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);
|
||||
}
|
||||
|
||||
// luaL_opt would be implemented here but it is undocumented, so it's omitted
|
||||
|
||||
#[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())
|
||||
@@ -188,6 +186,96 @@ pub unsafe fn luaL_loadbufferenv(
|
||||
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;
|
||||
+54
-41
@@ -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_LEVELS2: c_int = 10; // size of the second part of the stack
|
||||
const COMPAT53_LEVELS1: c_int = 10; // size of the first 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 {
|
||||
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)
|
||||
return 1;
|
||||
} else if compat53_findfield(L, objidx, level - 1) != 0 {
|
||||
// try recursively
|
||||
lua_remove(L, -2); // remove table (but keep name)
|
||||
lua_pushliteral(L, c".");
|
||||
lua_insert(L, -2); // place '.' between the two names
|
||||
lua_concat(L, 3);
|
||||
// stack: lib_name, lib_table, field_name (top)
|
||||
lua_pushliteral(L, c"."); // place '.' between the two names
|
||||
lua_replace(L, -3); // (in the slot occupied by table)
|
||||
lua_concat(L, 3); // lib_name.field_name
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -54,14 +53,25 @@ unsafe fn compat53_findfield(L: *mut lua_State, objidx: c_int, level: c_int) ->
|
||||
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);
|
||||
// push function
|
||||
lua_getinfo(L, level, cstr!("f"), ar);
|
||||
lua_getinfo(L1, level, cstr!("f"), ar); // push function
|
||||
lua_xmove(L1, L, 1); // and move onto L
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||
luaL_checkstack(L, 6, cstr!("not enough stack")); // slots for 'findfield'
|
||||
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_pop(L, 2); // remove pushed values
|
||||
lua_settop(L, top + 1); // remove pushed values
|
||||
1
|
||||
} else {
|
||||
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() {
|
||||
// is there a 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_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 {
|
||||
lua_pushliteral(L, c"?");
|
||||
}
|
||||
@@ -190,9 +203,7 @@ pub unsafe fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_in
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int {
|
||||
let abs_i = lua_absindex(L, idx);
|
||||
lua_pushlightuserdata(L, p as *mut c_void);
|
||||
lua_rawget(L, abs_i)
|
||||
lua_rawgetptagged(L, idx, p, 0)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -226,11 +237,7 @@ pub unsafe fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer) {
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void) {
|
||||
let abs_i = lua_absindex(L, idx);
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
|
||||
lua_pushlightuserdata(L, p as *mut c_void);
|
||||
lua_insert(L, -2);
|
||||
lua_rawset(L, abs_i);
|
||||
lua_rawsetptagged(L, idx, p, 0)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -452,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) {
|
||||
let mut ar: lua_Debug = mem::zeroed();
|
||||
let top = lua_gettop(L);
|
||||
let numlevels = lua_stackdepth(L);
|
||||
let mark = if numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2 {
|
||||
COMPAT53_LEVELS1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
#[rustfmt::skip]
|
||||
let mut limit = if numlevels - level > COMPAT53_LEVELS1 + COMPAT53_LEVELS2 { COMPAT53_LEVELS1 } else { -1 };
|
||||
|
||||
let mut buf: luaL_Strbuf = mem::zeroed();
|
||||
luaL_buffinit(L, &mut buf);
|
||||
|
||||
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:");
|
||||
while lua_getinfo(L1, level, cstr!(""), &mut ar) != 0 {
|
||||
if level + 1 == mark {
|
||||
luaL_addstring(&mut buf, cstr!("stack traceback:"));
|
||||
while lua_getinfo(L1, level, cstr!("sln"), &mut ar) != 0 {
|
||||
if limit == 0 {
|
||||
// too many levels?
|
||||
lua_pushliteral(L, c"\n\t..."); // add a '...'
|
||||
level = numlevels - COMPAT53_LEVELS2; // and skip to last ones
|
||||
let n = numlevels - level - COMPAT53_LEVELS2;
|
||||
// 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 {
|
||||
lua_getinfo(L1, level, cstr!("sln"), &mut ar);
|
||||
lua_pushfstring(L, cstr!("\n\t%s:"), ar.short_src);
|
||||
luaL_addstring(&mut buf, cstr!("\n\t"));
|
||||
luaL_addstring(&mut buf, ar.short_src);
|
||||
luaL_addstring(&mut buf, cstr!(":"));
|
||||
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 ");
|
||||
compat53_pushfuncname(L, level, &mut ar);
|
||||
lua_concat(L, lua_gettop(L) - top);
|
||||
luaL_addstring(&mut buf, cstr!(" in "));
|
||||
compat53_pushfuncname(L, L1, level, &mut ar);
|
||||
luaL_addvalue(&mut buf);
|
||||
}
|
||||
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 {
|
||||
@@ -544,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) {
|
||||
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, cstr!("_LOADED"));
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
|
||||
if lua_getfield(L, -1, modname) == LUA_TNIL {
|
||||
lua_pop(L, 1);
|
||||
lua_pushcfunction(L, openf);
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
use std::os::raw::{c_char, c_float, c_int, c_void};
|
||||
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)]
|
||||
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;
|
||||
|
||||
#[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
|
||||
#[link_name = "luaL_sandbox"]
|
||||
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())
|
||||
}
|
||||
|
||||
// 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)]
|
||||
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);
|
||||
}
|
||||
|
||||
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_ERRMEM: c_int = 4;
|
||||
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.
|
||||
#[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_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_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_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_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;
|
||||
@@ -203,6 +220,7 @@ unsafe extern "C-unwind" {
|
||||
pub fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
#[link_name = "lua_rawgeti"]
|
||||
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_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_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);
|
||||
#[link_name = "lua_rawseti"]
|
||||
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_setfenv(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
|
||||
@@ -249,6 +269,12 @@ unsafe extern "C-unwind" {
|
||||
pub fn lua_isyieldable(L: *mut lua_State) -> c_int;
|
||||
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_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
|
||||
}
|
||||
|
||||
//
|
||||
@@ -285,7 +311,7 @@ unsafe extern "C-unwind" {
|
||||
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_rawiter(L: *mut lua_State, idx: c_int, iter: c_int) -> c_int;
|
||||
pub fn lua_concat(L: *mut lua_State, n: c_int);
|
||||
// TODO: lua_encodepointer
|
||||
pub fn lua_encodepointer(L: *mut lua_State, p: usize) -> usize;
|
||||
pub fn lua_clock() -> c_double;
|
||||
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>);
|
||||
@@ -296,6 +322,7 @@ unsafe extern "C-unwind" {
|
||||
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_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;
|
||||
}
|
||||
|
||||
@@ -355,7 +382,10 @@ pub unsafe fn lua_newuserdata_t<T>(L: *mut lua_State, data: T) -> *mut T {
|
||||
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)]
|
||||
pub unsafe fn lua_isfunction(L: *mut lua_State, n: c_int) -> c_int {
|
||||
@@ -545,4 +575,9 @@ unsafe extern "C" {
|
||||
unsafe extern "C" {
|
||||
pub fn luau_setfflag(name: *const c_char, value: c_int) -> c_int;
|
||||
pub fn lua_getmetatablepointer(L: *mut lua_State, idx: c_int) -> *const c_void;
|
||||
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,6 +23,16 @@ pub enum luarequire_WriteResult {
|
||||
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)]
|
||||
pub struct luarequire_Configuration {
|
||||
// Returns whether requires are permitted from the given chunkname.
|
||||
@@ -48,6 +58,29 @@ pub struct luarequire_Configuration {
|
||||
path: *const c_char,
|
||||
) -> 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.
|
||||
pub to_parent:
|
||||
unsafe extern "C-unwind" fn(L: *mut lua_State, ctx: *mut c_void) -> luarequire_NavigateResult,
|
||||
@@ -90,13 +123,14 @@ pub struct luarequire_Configuration {
|
||||
size_out: *mut usize,
|
||||
) -> luarequire_WriteResult,
|
||||
|
||||
// Returns whether a configuration file is present in the current context.
|
||||
// If not, require-by-string will call to_parent until either a configuration file is present or
|
||||
// Returns whether a configuration file is present in the current context, and if so, its syntax.
|
||||
// If not present, require-by-string will call to_parent until either a configuration file is present or
|
||||
// NAVIGATE_FAILURE is returned (at root).
|
||||
pub is_config_present: unsafe extern "C-unwind" fn(L: *mut lua_State, ctx: *mut c_void) -> bool,
|
||||
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 is_config_present
|
||||
// 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.
|
||||
@@ -111,9 +145,10 @@ pub struct luarequire_Configuration {
|
||||
) -> luarequire_WriteResult,
|
||||
>,
|
||||
|
||||
// Provides the contents of the configuration file in the current context. This function is only called
|
||||
// if is_config_present returns true. If this function pointer is set, get_alias must not be set. Opting
|
||||
// in to this function pointer enables parsing configuration files internally.
|
||||
// Provides the contents of the configuration file in the current context.
|
||||
// This function is only called if get_config_status does not return CONFIG_ABSENT. If this function
|
||||
// pointer is set, get_alias must not be set. Opting in to this function pointer enables parsing
|
||||
// configuration files internally.
|
||||
pub get_config: Option<
|
||||
unsafe extern "C-unwind" fn(
|
||||
L: *mut lua_State,
|
||||
@@ -124,6 +159,13 @@ pub struct luarequire_Configuration {
|
||||
) -> 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
|
||||
// stack.
|
||||
// Returning -1 directs the requiring thread to yield. In this case, this thread should be resumed with
|
||||
|
||||
+3
-3
@@ -97,7 +97,7 @@ 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 lua = self.0.0.lua.lock();
|
||||
let data = self.0.as_slice(&lua);
|
||||
if self.1 == data.len() {
|
||||
return Ok(0);
|
||||
@@ -111,7 +111,7 @@ impl io::Read for BufferCursor {
|
||||
|
||||
impl io::Write for BufferCursor {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let lua = self.0 .0.lua.lock();
|
||||
let lua = self.0.0.lua.lock();
|
||||
let data = self.0.as_slice_mut(&lua);
|
||||
if self.1 == data.len() {
|
||||
return Ok(0);
|
||||
@@ -129,7 +129,7 @@ impl io::Write for BufferCursor {
|
||||
|
||||
impl io::Seek for BufferCursor {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
|
||||
let lua = self.0 .0.lua.lock();
|
||||
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,
|
||||
|
||||
+67
-71
@@ -4,7 +4,6 @@ use std::ffi::CString;
|
||||
use std::io::Result as IoResult;
|
||||
use std::panic::Location;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
@@ -20,7 +19,7 @@ pub trait AsChunk {
|
||||
/// Returns optional chunk name
|
||||
///
|
||||
/// See [`Chunk::set_name`] for possible name prefixes.
|
||||
fn name(&self) -> Option<StdString> {
|
||||
fn name(&self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -52,13 +51,13 @@ impl AsChunk for &str {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChunk for StdString {
|
||||
impl AsChunk for String {
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Owned(self.clone().into_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChunk for &StdString {
|
||||
impl AsChunk for &String {
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>>
|
||||
where
|
||||
Self: 'a,
|
||||
@@ -92,7 +91,7 @@ impl AsChunk for &Vec<u8> {
|
||||
}
|
||||
|
||||
impl AsChunk for &Path {
|
||||
fn name(&self) -> Option<StdString> {
|
||||
fn name(&self) -> Option<String> {
|
||||
Some(format!("@{}", self.display()))
|
||||
}
|
||||
|
||||
@@ -102,7 +101,7 @@ impl AsChunk for &Path {
|
||||
}
|
||||
|
||||
impl AsChunk for PathBuf {
|
||||
fn name(&self) -> Option<StdString> {
|
||||
fn name(&self) -> Option<String> {
|
||||
Some(format!("@{}", self.display()))
|
||||
}
|
||||
|
||||
@@ -112,7 +111,7 @@ impl AsChunk for PathBuf {
|
||||
}
|
||||
|
||||
impl<C: AsChunk + ?Sized> AsChunk for Box<C> {
|
||||
fn name(&self) -> Option<StdString> {
|
||||
fn name(&self) -> Option<String> {
|
||||
(**self).name()
|
||||
}
|
||||
|
||||
@@ -136,7 +135,7 @@ impl<C: AsChunk + ?Sized> AsChunk for Box<C> {
|
||||
#[must_use = "`Chunk`s do nothing unless one of `exec`, `eval`, `call`, or `into_function` are called on them"]
|
||||
pub struct Chunk<'a> {
|
||||
pub(crate) lua: WeakLua,
|
||||
pub(crate) name: StdString,
|
||||
pub(crate) name: String,
|
||||
pub(crate) env: Result<Option<Table>>,
|
||||
pub(crate) mode: Option<ChunkMode>,
|
||||
pub(crate) source: IoResult<Cow<'a, [u8]>>,
|
||||
@@ -160,7 +159,7 @@ pub enum CompileConstant {
|
||||
Boolean(bool),
|
||||
Number(crate::Number),
|
||||
Vector(crate::Vector),
|
||||
String(StdString),
|
||||
String(String),
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
@@ -192,7 +191,7 @@ impl From<&str> for CompileConstant {
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
type LibraryMemberConstantMap = HashMap<(StdString, StdString), CompileConstant>;
|
||||
type LibraryMemberConstantMap = HashMap<(String, String), CompileConstant>;
|
||||
|
||||
/// Luau compiler
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
@@ -203,14 +202,14 @@ pub struct Compiler {
|
||||
debug_level: u8,
|
||||
type_info_level: u8,
|
||||
coverage_level: u8,
|
||||
vector_lib: Option<StdString>,
|
||||
vector_ctor: Option<StdString>,
|
||||
vector_type: Option<StdString>,
|
||||
mutable_globals: Vec<StdString>,
|
||||
userdata_types: Vec<StdString>,
|
||||
libraries_with_known_members: Vec<StdString>,
|
||||
vector_lib: Option<String>,
|
||||
vector_ctor: Option<String>,
|
||||
vector_type: Option<String>,
|
||||
mutable_globals: Vec<String>,
|
||||
userdata_types: Vec<String>,
|
||||
libraries_with_known_members: Vec<String>,
|
||||
library_constants: Option<LibraryMemberConstantMap>,
|
||||
disabled_builtins: Vec<StdString>,
|
||||
disabled_builtins: Vec<String>,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
@@ -294,7 +293,7 @@ impl Compiler {
|
||||
/// To set the library and method name, use the `lib.ctor` format.
|
||||
#[doc(hidden)]
|
||||
#[must_use]
|
||||
pub fn set_vector_ctor(mut self, ctor: impl Into<StdString>) -> Self {
|
||||
pub fn set_vector_ctor(mut self, ctor: impl Into<String>) -> Self {
|
||||
let ctor = ctor.into();
|
||||
let lib_ctor = ctor.split_once('.');
|
||||
self.vector_lib = lib_ctor.as_ref().map(|&(lib, _)| lib.to_owned());
|
||||
@@ -307,7 +306,7 @@ impl Compiler {
|
||||
/// Sets alternative vector type name for type tables, in addition to default type `vector`.
|
||||
#[doc(hidden)]
|
||||
#[must_use]
|
||||
pub fn set_vector_type(mut self, r#type: impl Into<StdString>) -> Self {
|
||||
pub fn set_vector_type(mut self, r#type: impl Into<String>) -> Self {
|
||||
self.vector_type = Some(r#type.into());
|
||||
self
|
||||
}
|
||||
@@ -316,7 +315,7 @@ impl Compiler {
|
||||
///
|
||||
/// It disables the import optimization for fields accessed through it.
|
||||
#[must_use]
|
||||
pub fn add_mutable_global(mut self, global: impl Into<StdString>) -> Self {
|
||||
pub fn add_mutable_global(mut self, global: impl Into<String>) -> Self {
|
||||
self.mutable_globals.push(global.into());
|
||||
self
|
||||
}
|
||||
@@ -325,21 +324,21 @@ impl Compiler {
|
||||
///
|
||||
/// It disables the import optimization for fields accessed through these.
|
||||
#[must_use]
|
||||
pub fn set_mutable_globals<S: Into<StdString>>(mut self, globals: impl IntoIterator<Item = 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
|
||||
}
|
||||
|
||||
/// Adds a userdata type to the list that will be included in the type information.
|
||||
#[must_use]
|
||||
pub fn add_userdata_type(mut self, r#type: impl Into<StdString>) -> Self {
|
||||
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.
|
||||
#[must_use]
|
||||
pub fn set_userdata_types<S: Into<StdString>>(mut self, types: impl IntoIterator<Item = 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
|
||||
}
|
||||
@@ -366,24 +365,21 @@ impl Compiler {
|
||||
self.libraries_with_known_members.push(lib.clone());
|
||||
}
|
||||
self.library_constants
|
||||
.get_or_insert_with(HashMap::new)
|
||||
.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<StdString>) -> Self {
|
||||
pub fn add_disabled_builtin(mut self, builtin: impl Into<String>) -> Self {
|
||||
self.disabled_builtins.push(builtin.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a list of builtins that should be disabled.
|
||||
#[must_use]
|
||||
pub fn set_disabled_builtins<S: Into<StdString>>(
|
||||
mut self,
|
||||
builtins: impl IntoIterator<Item = S>,
|
||||
) -> Self {
|
||||
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
|
||||
}
|
||||
@@ -477,11 +473,11 @@ impl Compiler {
|
||||
options.mutableGlobals = mutable_globals_ptr;
|
||||
options.userdataTypes = userdata_types_ptr;
|
||||
options.librariesWithKnownMembers = libraries_with_known_members_ptr;
|
||||
if let Some(map) = self.library_constants.as_ref() {
|
||||
if !self.libraries_with_known_members.is_empty() {
|
||||
LIBRARY_MEMBER_CONSTANT_MAP.with_borrow_mut(|gmap| *gmap = map.clone());
|
||||
options.libraryMemberConstantCallback = Some(library_member_constant_callback);
|
||||
}
|
||||
if let Some(map) = self.library_constants.as_ref()
|
||||
&& !self.libraries_with_known_members.is_empty()
|
||||
{
|
||||
LIBRARY_MEMBER_CONSTANT_MAP.with_borrow_mut(|gmap| *gmap = map.clone());
|
||||
options.libraryMemberConstantCallback = Some(library_member_constant_callback);
|
||||
}
|
||||
options.disabledBuiltins = disabled_builtins_ptr;
|
||||
ffi::luau_compile(source.as_ref(), options)
|
||||
@@ -490,7 +486,7 @@ impl Compiler {
|
||||
if bytecode.first() == Some(&0) {
|
||||
// The rest of the bytecode is the error message starting with `:`
|
||||
// See https://github.com/luau-lang/luau/blob/0.640/Compiler/src/Compiler.cpp#L4336
|
||||
let message = StdString::from_utf8_lossy(&bytecode[2..]).into_owned();
|
||||
let message = String::from_utf8_lossy(&bytecode[2..]).into_owned();
|
||||
return Err(Error::SyntaxError {
|
||||
incomplete_input: message.ends_with("<eof>"),
|
||||
message,
|
||||
@@ -513,7 +509,7 @@ impl Chunk<'_> {
|
||||
/// - `@` - file path (when truncation is needed, the end of the file path is kept, as this is
|
||||
/// more useful for identifying the file)
|
||||
/// - `=` - custom chunk name (when truncation is needed, the beginning of the name is kept)
|
||||
pub fn set_name(mut self, name: impl Into<StdString>) -> Self {
|
||||
pub fn set_name(mut self, name: impl Into<String>) -> Self {
|
||||
self.name = name.into();
|
||||
self
|
||||
}
|
||||
@@ -662,19 +658,19 @@ impl Chunk<'_> {
|
||||
///
|
||||
/// It does nothing if the chunk is already binary or invalid.
|
||||
fn compile(&mut self) {
|
||||
if let Ok(ref source) = self.source {
|
||||
if self.detect_mode() == ChunkMode::Text {
|
||||
#[cfg(feature = "luau")]
|
||||
if let Ok(data) = self.compiler.get_or_insert_with(Default::default).compile(source) {
|
||||
self.source = Ok(Cow::Owned(data));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
}
|
||||
#[cfg(not(feature = "luau"))]
|
||||
if let Ok(func) = self.lua.lock().load_chunk(None, None, None, source.as_ref()) {
|
||||
let data = func.dump(false);
|
||||
self.source = Ok(Cow::Owned(data));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
}
|
||||
if let Ok(ref source) = self.source
|
||||
&& self.detect_mode() == ChunkMode::Text
|
||||
{
|
||||
#[cfg(feature = "luau")]
|
||||
if let Ok(data) = self.compiler.get_or_insert_default().compile(source) {
|
||||
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()) {
|
||||
let data = func.dump(false);
|
||||
self.source = Ok(Cow::Owned(data));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -687,33 +683,33 @@ impl Chunk<'_> {
|
||||
|
||||
// Try to fetch compiled chunk from cache
|
||||
let mut text_source = None;
|
||||
if let Ok(ref source) = self.source {
|
||||
if self.detect_mode() == ChunkMode::Text {
|
||||
let lua = self.lua.lock();
|
||||
if let Some(cache) = lua.priv_app_data_ref::<ChunksCache>() {
|
||||
if let Some(data) = cache.0.get(source.as_ref()) {
|
||||
self.source = Ok(Cow::Owned(data.clone()));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
return self;
|
||||
}
|
||||
}
|
||||
text_source = Some(source.as_ref().to_vec());
|
||||
if let Ok(ref source) = self.source
|
||||
&& self.detect_mode() == ChunkMode::Text
|
||||
{
|
||||
let lua = self.lua.lock();
|
||||
if let Some(cache) = lua.priv_app_data_ref::<ChunksCache>()
|
||||
&& let Some(data) = cache.0.get(source.as_ref())
|
||||
{
|
||||
self.source = Ok(Cow::Owned(data.clone()));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
return self;
|
||||
}
|
||||
text_source = Some(source.as_ref().to_vec());
|
||||
}
|
||||
|
||||
// Compile and cache the chunk
|
||||
if let Some(text_source) = text_source {
|
||||
self.compile();
|
||||
if let Ok(ref binary_source) = self.source {
|
||||
if self.detect_mode() == ChunkMode::Binary {
|
||||
let lua = self.lua.lock();
|
||||
if let Some(mut cache) = lua.priv_app_data_mut::<ChunksCache>() {
|
||||
cache.0.insert(text_source, binary_source.to_vec());
|
||||
} else {
|
||||
let mut cache = ChunksCache(HashMap::new());
|
||||
cache.0.insert(text_source, binary_source.to_vec());
|
||||
lua.set_priv_app_data(cache);
|
||||
};
|
||||
if let Ok(ref binary_source) = self.source
|
||||
&& self.detect_mode() == ChunkMode::Binary
|
||||
{
|
||||
let lua = self.lua.lock();
|
||||
if let Some(mut cache) = lua.priv_app_data_mut::<ChunksCache>() {
|
||||
cache.0.insert(text_source, binary_source.to_vec());
|
||||
} else {
|
||||
let mut cache = ChunksCache(HashMap::new());
|
||||
cache.0.insert(text_source, binary_source.to_vec());
|
||||
lua.set_priv_app_data(cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -761,7 +757,7 @@ impl Chunk<'_> {
|
||||
ChunkMode::Text
|
||||
}
|
||||
|
||||
fn convert_name(name: StdString) -> Result<CString> {
|
||||
fn convert_name(name: String) -> Result<CString> {
|
||||
CString::new(name).map_err(|err| Error::runtime(format!("invalid name: {err}")))
|
||||
}
|
||||
|
||||
|
||||
+138
-195
@@ -4,16 +4,15 @@ use std::ffi::{CStr, CString, OsStr, OsString};
|
||||
use std::hash::{BuildHasher, Hash};
|
||||
use std::os::raw::c_int;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::string::String as StdString;
|
||||
use std::{mem, slice, str};
|
||||
|
||||
use bstr::{BStr, BString, ByteSlice, ByteVec};
|
||||
use bstr::{BStr, BString, ByteVec};
|
||||
use num_traits::cast;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::string::{BorrowedBytes, BorrowedStr, String};
|
||||
use crate::string::{BorrowedBytes, BorrowedStr, LuaString};
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::traits::{FromLua, IntoLua, ShortTypeName as _};
|
||||
@@ -47,14 +46,14 @@ impl FromLua for Value {
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for String {
|
||||
impl IntoLua for LuaString {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for &String {
|
||||
impl IntoLua for &LuaString {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(self.clone()))
|
||||
@@ -67,16 +66,12 @@ impl IntoLua for &String {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for String {
|
||||
impl FromLua for LuaString {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<String> {
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<LuaString> {
|
||||
let ty = value.type_name();
|
||||
lua.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "string".to_string(),
|
||||
message: Some("expected string or number".to_string()),
|
||||
})
|
||||
.ok_or_else(|| Error::from_lua_conversion(ty, "string", "expected string or number".to_string()))
|
||||
}
|
||||
|
||||
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);
|
||||
if type_id == ffi::LUA_TSTRING {
|
||||
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
|
||||
Self::from_lua(lua.stack_value(idx, Some(type_id)), lua.lua())
|
||||
@@ -119,7 +114,7 @@ impl IntoLua for &BorrowedStr<'_> {
|
||||
|
||||
impl FromLua for BorrowedStr<'_> {
|
||||
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 buf = unsafe { mem::transmute::<&str, &'static str>(buf) };
|
||||
let borrow = Cow::Owned(s);
|
||||
@@ -127,7 +122,7 @@ impl FromLua for BorrowedStr<'_> {
|
||||
}
|
||||
|
||||
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 buf = unsafe { mem::transmute::<&str, &'static str>(buf) };
|
||||
let borrow = Cow::Owned(s);
|
||||
@@ -163,7 +158,7 @@ impl IntoLua for &BorrowedBytes<'_> {
|
||||
|
||||
impl FromLua for BorrowedBytes<'_> {
|
||||
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 buf = unsafe { mem::transmute::<&[u8], &'static [u8]>(buf) };
|
||||
let borrow = Cow::Owned(s);
|
||||
@@ -171,7 +166,7 @@ impl FromLua for BorrowedBytes<'_> {
|
||||
}
|
||||
|
||||
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 buf = unsafe { mem::transmute::<&[u8], &'static [u8]>(buf) };
|
||||
let borrow = Cow::Owned(s);
|
||||
@@ -204,11 +199,7 @@ impl FromLua for Table {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Table> {
|
||||
match value {
|
||||
Value::Table(table) => Ok(table),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "table".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(value.type_name(), "table", None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,11 +229,7 @@ impl FromLua for Function {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Function> {
|
||||
match value {
|
||||
Value::Function(table) => Ok(table),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "function".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(value.type_name(), "function", None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -272,11 +259,7 @@ impl FromLua for Thread {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Thread> {
|
||||
match value {
|
||||
Value::Thread(t) => Ok(t),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "thread".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(value.type_name(), "thread", None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,11 +289,7 @@ impl FromLua for AnyUserData {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<AnyUserData> {
|
||||
match value {
|
||||
Value::UserData(ud) => Ok(ud),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "userdata".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(value.type_name(), "userdata", None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -428,11 +407,11 @@ impl FromLua for LightUserData {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
match value {
|
||||
Value::LightUserData(ud) => Ok(ud),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "lightuserdata".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(
|
||||
value.type_name(),
|
||||
"lightuserdata",
|
||||
None,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -451,11 +430,7 @@ impl FromLua for crate::Vector {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
match value {
|
||||
Value::Vector(v) => Ok(v),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "vector".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(value.type_name(), "vector", None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -488,37 +463,41 @@ impl FromLua for crate::Buffer {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
match value {
|
||||
Value::Buffer(buf) => Ok(buf),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "buffer".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(value.type_name(), "buffer", None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for StdString {
|
||||
impl IntoLua for String {
|
||||
#[inline]
|
||||
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)?))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for StdString {
|
||||
impl FromLua for String {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
Ok(lua
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: Self::type_name(),
|
||||
message: Some("expected string or number".to_string()),
|
||||
.ok_or_else(|| {
|
||||
Error::from_lua_conversion(ty, Self::type_name(), "expected string or number".to_string())
|
||||
})?
|
||||
.to_str()?
|
||||
.to_owned())
|
||||
@@ -534,11 +513,7 @@ impl FromLua for StdString {
|
||||
let bytes = slice::from_raw_parts(data as *const u8, size);
|
||||
return str::from_utf8(bytes)
|
||||
.map(|s| s.to_owned())
|
||||
.map_err(|e| Error::FromLuaConversionError {
|
||||
from: "string",
|
||||
to: Self::type_name(),
|
||||
message: Some(e.to_string()),
|
||||
});
|
||||
.map_err(|e| Error::from_lua_conversion("string", Self::type_name(), e.to_string()));
|
||||
}
|
||||
// Fallback to default
|
||||
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();
|
||||
Ok(lua
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: Self::type_name(),
|
||||
message: Some("expected string or number".to_string()),
|
||||
.ok_or_else(|| {
|
||||
Error::from_lua_conversion(ty, Self::type_name(), "expected string or number".to_string())
|
||||
})?
|
||||
.to_str()?
|
||||
.to_owned()
|
||||
@@ -591,6 +564,11 @@ impl FromLua for Box<str> {
|
||||
impl IntoLua for CString {
|
||||
#[inline]
|
||||
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())?))
|
||||
}
|
||||
}
|
||||
@@ -599,21 +577,12 @@ impl FromLua for CString {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
let string = lua
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: Self::type_name(),
|
||||
message: Some("expected string or number".to_string()),
|
||||
})?;
|
||||
|
||||
let string = lua.coerce_string(value)?.ok_or_else(|| {
|
||||
Error::from_lua_conversion(ty, Self::type_name(), "expected string or number".to_string())
|
||||
})?;
|
||||
match CStr::from_bytes_with_nul(&string.as_bytes_with_nul()) {
|
||||
Ok(s) => Ok(s.into()),
|
||||
Err(_) => Err(Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: Self::type_name(),
|
||||
message: Some("invalid C-style string".to_string()),
|
||||
}),
|
||||
Err(err) => Err(Error::from_lua_conversion(ty, Self::type_name(), err.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -635,6 +604,11 @@ impl IntoLua for Cow<'_, CStr> {
|
||||
impl IntoLua for BString {
|
||||
#[inline]
|
||||
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)?))
|
||||
}
|
||||
}
|
||||
@@ -648,10 +622,8 @@ impl FromLua for BString {
|
||||
Value::Buffer(buf) => Ok(buf.to_vec().into()),
|
||||
_ => Ok((*lua
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: Self::type_name(),
|
||||
message: Some("expected string or number".to_string()),
|
||||
.ok_or_else(|| {
|
||||
Error::from_lua_conversion(ty, Self::type_name(), "expected string or number".to_string())
|
||||
})?
|
||||
.as_bytes())
|
||||
.into()),
|
||||
@@ -702,23 +674,22 @@ impl FromLua for OsString {
|
||||
let bs = BString::from_lua(value, lua)?;
|
||||
Vec::from(bs)
|
||||
.into_os_string()
|
||||
.map_err(|err| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "OsString".into(),
|
||||
message: Some(err.to_string()),
|
||||
})
|
||||
.map_err(|err| Error::from_lua_conversion(ty, "OsString", err.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for &OsStr {
|
||||
#[cfg(unix)]
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
let s = <[u8]>::from_os_str(self).ok_or_else(|| Error::ToLuaConversionError {
|
||||
from: "OsStr".into(),
|
||||
to: "string",
|
||||
message: Some("invalid utf-8 encoding".into()),
|
||||
})?;
|
||||
Ok(Value::String(lua.create_string(s)?))
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
Ok(Value::String(lua.create_string(self.as_bytes())?))
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
#[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> {
|
||||
let ty = value.type_name();
|
||||
match value {
|
||||
Value::Integer(i) => {
|
||||
cast(i)
|
||||
.and_then(char::from_u32)
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "char".to_string(),
|
||||
message: Some("integer out of range when converting to char".to_string()),
|
||||
})
|
||||
}
|
||||
Value::Integer(i) => cast(i).and_then(char::from_u32).ok_or_else(|| {
|
||||
let msg = "integer out of range when converting to char";
|
||||
Error::from_lua_conversion(ty, "char", msg.to_string())
|
||||
}),
|
||||
Value::String(s) => {
|
||||
let str = s.to_str()?;
|
||||
let mut str_iter = str.chars();
|
||||
match (str_iter.next(), str_iter.next()) {
|
||||
(Some(char), None) => Ok(char),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "char".to_string(),
|
||||
message: Some(
|
||||
"expected string to have exactly one char when converting to char".to_string(),
|
||||
),
|
||||
}),
|
||||
_ => {
|
||||
let msg = "expected string to have exactly one char when converting to char";
|
||||
Err(Error::from_lua_conversion(ty, "char", msg.to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: Self::type_name(),
|
||||
message: Some("expected string or integer".to_string()),
|
||||
}),
|
||||
_ => {
|
||||
let msg = "expected string or integer";
|
||||
Err(Error::from_lua_conversion(ty, Self::type_name(), msg.to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -834,24 +796,14 @@ macro_rules! lua_convert_int {
|
||||
if let Some(i) = lua.coerce_integer(value.clone())? {
|
||||
cast(i)
|
||||
} else {
|
||||
cast(
|
||||
lua.coerce_number(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: stringify!($x).to_string(),
|
||||
message: Some(
|
||||
"expected number or string coercible to number".to_string(),
|
||||
),
|
||||
})?,
|
||||
)
|
||||
cast(lua.coerce_number(value)?.ok_or_else(|| {
|
||||
let msg = "expected number or string coercible to number";
|
||||
Error::from_lua_conversion(ty, stringify!($x), msg.to_string())
|
||||
})?)
|
||||
}
|
||||
}
|
||||
})
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: stringify!($x).to_string(),
|
||||
message: Some("out of range".to_owned()),
|
||||
})
|
||||
.ok_or_else(|| Error::from_lua_conversion(ty, stringify!($x), "out of range".to_string()))
|
||||
}
|
||||
|
||||
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 i = ffi::lua_tointegerx(state, idx, &mut ok);
|
||||
if ok != 0 {
|
||||
return cast(i).ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: "integer",
|
||||
to: stringify!($x).to_string(),
|
||||
message: Some("out of range".to_owned()),
|
||||
return cast(i).ok_or_else(|| {
|
||||
Error::from_lua_conversion("integer", stringify!($x), "out of range".to_string())
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -901,13 +851,10 @@ macro_rules! lua_convert_float {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
lua.coerce_number(value)?
|
||||
.map(|n| n as $x)
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: stringify!($x).to_string(),
|
||||
message: Some("expected number or string coercible to number".to_string()),
|
||||
})
|
||||
lua.coerce_number(value)?.map(|n| n as $x).ok_or_else(|| {
|
||||
let msg = "expected number or string coercible to number";
|
||||
Error::from_lua_conversion(ty, stringify!($x), msg.to_string())
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
@@ -967,18 +914,16 @@ where
|
||||
},
|
||||
Value::Table(table) => {
|
||||
let vec = table.sequence_values().collect::<Result<Vec<_>>>()?;
|
||||
vec.try_into()
|
||||
.map_err(|vec: Vec<T>| Error::FromLuaConversionError {
|
||||
from: "table",
|
||||
to: Self::type_name(),
|
||||
message: Some(format!("expected table of length {N}, got {}", vec.len())),
|
||||
})
|
||||
vec.try_into().map_err(|vec: Vec<T>| {
|
||||
let msg = format!("expected table of length {N}, got {}", vec.len());
|
||||
Error::from_lua_conversion("table", Self::type_name(), msg)
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
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> {
|
||||
match value {
|
||||
Value::Table(table) => table.sequence_values().collect(),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: Self::type_name(),
|
||||
message: Some("expected table".to_string()),
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(
|
||||
value.type_name(),
|
||||
Self::type_name(),
|
||||
"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> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
if let Value::Table(table) = value {
|
||||
table.pairs().collect()
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: Self::type_name(),
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
match value {
|
||||
Value::Table(table) => table.pairs().collect(),
|
||||
_ => Err(Error::from_lua_conversion(
|
||||
value.type_name(),
|
||||
Self::type_name(),
|
||||
"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> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
if let Value::Table(table) = value {
|
||||
table.pairs().collect()
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: Self::type_name(),
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
match value {
|
||||
Value::Table(table) => table.pairs().collect(),
|
||||
_ => Err(Error::from_lua_conversion(
|
||||
value.type_name(),
|
||||
Self::type_name(),
|
||||
"expected table".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1077,11 +1020,11 @@ impl<T: Eq + Hash + FromLua, S: BuildHasher + Default> FromLua for HashSet<T, S>
|
||||
match value {
|
||||
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(),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: Self::type_name(),
|
||||
message: Some("expected table".to_string()),
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(
|
||||
value.type_name(),
|
||||
Self::type_name(),
|
||||
"expected table".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1101,11 +1044,11 @@ impl<T: Ord + FromLua> FromLua for BTreeSet<T> {
|
||||
match value {
|
||||
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(),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: Self::type_name(),
|
||||
message: Some("expected table".to_string()),
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(
|
||||
value.type_name(),
|
||||
Self::type_name(),
|
||||
"expected table".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1175,11 +1118,11 @@ impl<L: FromLua, R: FromLua> FromLua for Either<L, R> {
|
||||
// Try the right type
|
||||
Err(_) => match R::from_lua(value, lua).map(Either::Right) {
|
||||
Ok(r) => Ok(r),
|
||||
Err(_) => Err(Error::FromLuaConversionError {
|
||||
from: value_type_name,
|
||||
to: Self::type_name(),
|
||||
message: None,
|
||||
}),
|
||||
Err(_) => Err(Error::from_lua_conversion(
|
||||
value_type_name,
|
||||
Self::type_name(),
|
||||
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) {
|
||||
Ok(r) => Ok(r),
|
||||
Err(_) => {
|
||||
let value_type_name = CStr::from_ptr(ffi::luaL_typename(lua.state(), idx));
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: value_type_name.to_str().unwrap(),
|
||||
to: Self::type_name(),
|
||||
message: None,
|
||||
})
|
||||
let state = lua.state();
|
||||
let from_type_name = CStr::from_ptr(ffi::lua_typename(state, ffi::lua_type(state, idx)))
|
||||
.to_str()
|
||||
.unwrap_or("unknown");
|
||||
let err = Error::from_lua_conversion(from_type_name, Self::type_name(), None);
|
||||
Err(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
+34
-27
@@ -1,3 +1,9 @@
|
||||
//! 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::os::raw::c_int;
|
||||
|
||||
@@ -5,7 +11,7 @@ use ffi::{lua_Debug, lua_State};
|
||||
|
||||
use crate::function::Function;
|
||||
use crate::state::RawLua;
|
||||
use crate::util::{assert_stack, linenumber_to_usize, ptr_to_lossy_str, ptr_to_str, StackGuard};
|
||||
use crate::util::{StackGuard, assert_stack, linenumber_to_usize, ptr_to_lossy_str, ptr_to_str};
|
||||
|
||||
/// Contains information about currently executing Lua code.
|
||||
///
|
||||
@@ -133,12 +139,6 @@ impl<'a> Debug<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[deprecated(note = "Use `current_line` instead")]
|
||||
pub fn curr_line(&self) -> i32 {
|
||||
self.current_line().map(|n| n as i32).unwrap_or(-1)
|
||||
}
|
||||
|
||||
/// Corresponds to the `l` "what" mask. Returns the current line.
|
||||
pub fn current_line(&self) -> Option<usize> {
|
||||
unsafe {
|
||||
@@ -159,10 +159,10 @@ impl<'a> Debug<'a> {
|
||||
|
||||
/// Corresponds to the `t` "what" mask. Returns true if the hook is in a function tail call,
|
||||
/// false otherwise.
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(feature = "lua54", feature = "lua53", feature = "lua52")))
|
||||
doc(cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52")))
|
||||
)]
|
||||
pub fn is_tail_call(&self) -> bool {
|
||||
unsafe {
|
||||
@@ -190,15 +190,15 @@ impl<'a> Debug<'a> {
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let stack = DebugStack {
|
||||
num_ups: (*self.ar).nups as _,
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
num_upvalues: (*self.ar).nups as _,
|
||||
#[cfg(not(any(feature = "lua51", feature = "luajit")))]
|
||||
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).isvararg != 0,
|
||||
};
|
||||
#[cfg(feature = "luau")]
|
||||
let stack = DebugStack {
|
||||
num_ups: (*self.ar).nupvals,
|
||||
num_upvalues: (*self.ar).nupvals,
|
||||
num_params: (*self.ar).nparams,
|
||||
is_vararg: (*self.ar).isvararg != 0,
|
||||
};
|
||||
@@ -208,6 +208,8 @@ impl<'a> Debug<'a> {
|
||||
}
|
||||
|
||||
/// 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)]
|
||||
pub enum DebugEvent {
|
||||
Call,
|
||||
@@ -218,6 +220,9 @@ pub enum DebugEvent {
|
||||
Unknown(c_int),
|
||||
}
|
||||
|
||||
/// Contains the name information of a function in the call stack.
|
||||
///
|
||||
/// Returned by the [`Debug::names`] method.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DebugNames<'a> {
|
||||
/// A (reasonable) name of the function (`None` if the name cannot be found).
|
||||
@@ -228,6 +233,9 @@ pub struct DebugNames<'a> {
|
||||
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)]
|
||||
pub struct DebugSource<'a> {
|
||||
/// Source of the chunk that created the function.
|
||||
@@ -243,23 +251,20 @@ pub struct DebugSource<'a> {
|
||||
pub what: &'static str,
|
||||
}
|
||||
|
||||
/// Contains stack information about a function in the call stack.
|
||||
///
|
||||
/// Returned by the [`Debug::stack`] method.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct DebugStack {
|
||||
/// Number of upvalues.
|
||||
pub num_ups: u8,
|
||||
/// Number of parameters.
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau")))
|
||||
)]
|
||||
/// 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 vararg function.
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau")))
|
||||
)]
|
||||
/// 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,
|
||||
}
|
||||
|
||||
@@ -337,6 +342,7 @@ impl HookTriggers {
|
||||
}
|
||||
|
||||
// Compute the mask to pass to `lua_sethook`.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) const fn mask(&self) -> c_int {
|
||||
let mut mask: c_int = 0;
|
||||
if self.on_calls {
|
||||
@@ -356,6 +362,7 @@ impl HookTriggers {
|
||||
|
||||
// Returns the `count` parameter to pass to `lua_sethook`, if applicable. Otherwise, zero is
|
||||
// returned.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) const fn count(&self) -> c_int {
|
||||
match self.every_nth_instruction {
|
||||
Some(n) => n as c_int,
|
||||
|
||||
+30
-41
@@ -4,7 +4,6 @@ use std::io::Error as IoError;
|
||||
use std::net::AddrParseError;
|
||||
use std::result::Result as StdResult;
|
||||
use std::str::Utf8Error;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::private::Sealed;
|
||||
@@ -22,7 +21,7 @@ pub enum Error {
|
||||
/// Syntax error while parsing Lua source code.
|
||||
SyntaxError {
|
||||
/// 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.
|
||||
///
|
||||
/// 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.
|
||||
/// Among other things, this includes invoking operators on wrong types (such as calling or
|
||||
/// indexing a `nil` value).
|
||||
RuntimeError(StdString),
|
||||
RuntimeError(String),
|
||||
/// Lua memory error, aka `LUA_ERRMEM`
|
||||
///
|
||||
/// The Lua VM returns this error when the allocator does not return the requested memory, aka
|
||||
/// it is an out-of-memory error.
|
||||
MemoryError(StdString),
|
||||
MemoryError(String),
|
||||
/// Lua garbage collector error, aka `LUA_ERRGCMM`.
|
||||
///
|
||||
/// The Lua VM returns this error when there is an error running a `__gc` metamethod.
|
||||
#[cfg(any(feature = "lua53", feature = "lua52", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua53", feature = "lua52"))))]
|
||||
GarbageCollectorError(StdString),
|
||||
GarbageCollectorError(String),
|
||||
/// Potentially unsafe action in safe mode.
|
||||
SafetyError(StdString),
|
||||
SafetyError(String),
|
||||
/// Memory control is not available.
|
||||
///
|
||||
/// 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).
|
||||
BadArgument {
|
||||
/// Function that was called.
|
||||
to: Option<StdString>,
|
||||
to: Option<String>,
|
||||
/// Argument position (usually starts from 1).
|
||||
pos: usize,
|
||||
/// Argument name.
|
||||
name: Option<StdString>,
|
||||
name: Option<String>,
|
||||
/// Underlying error returned when converting argument to a Lua value.
|
||||
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.
|
||||
FromLuaConversionError {
|
||||
/// 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.
|
||||
to: String,
|
||||
/// A string containing more detailed error information.
|
||||
message: Option<StdString>,
|
||||
message: Option<String>,
|
||||
},
|
||||
/// [`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`).
|
||||
///
|
||||
/// [`MetaMethod`]: crate::MetaMethod
|
||||
MetaMethodRestricted(StdString),
|
||||
MetaMethodRestricted(String),
|
||||
/// A [`MetaMethod`] (eg. `__index` or `__newindex`) has invalid type.
|
||||
///
|
||||
/// [`MetaMethod`]: crate::MetaMethod
|
||||
MetaMethodTypeError {
|
||||
/// Name of the metamethod.
|
||||
method: StdString,
|
||||
method: String,
|
||||
/// Passed value type.
|
||||
type_name: &'static str,
|
||||
/// A string containing more detailed error information.
|
||||
message: Option<StdString>,
|
||||
message: Option<String>,
|
||||
},
|
||||
/// 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.
|
||||
CallbackError {
|
||||
/// Lua call stack backtrace.
|
||||
traceback: StdString,
|
||||
traceback: String,
|
||||
/// Original error returned by the Rust code.
|
||||
cause: Arc<Error>,
|
||||
},
|
||||
@@ -185,11 +175,11 @@ pub enum Error {
|
||||
/// Serialization error.
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
SerializeError(StdString),
|
||||
SerializeError(String),
|
||||
/// Deserialization error.
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
DeserializeError(StdString),
|
||||
DeserializeError(String),
|
||||
/// A custom error.
|
||||
///
|
||||
/// This can be used for returning user-defined errors from callbacks.
|
||||
@@ -201,7 +191,7 @@ pub enum Error {
|
||||
/// An error with additional context.
|
||||
WithContext {
|
||||
/// A string containing additional context.
|
||||
context: StdString,
|
||||
context: String,
|
||||
/// Underlying error.
|
||||
cause: Arc<Error>,
|
||||
},
|
||||
@@ -225,7 +215,7 @@ impl fmt::Display for Error {
|
||||
}
|
||||
Error::SafetyError(msg) => {
|
||||
write!(fmt, "safety error: {msg}")
|
||||
},
|
||||
}
|
||||
Error::MemoryControlNotAvailable => {
|
||||
write!(fmt, "memory control is not available")
|
||||
}
|
||||
@@ -238,10 +228,7 @@ impl fmt::Display for Error {
|
||||
fmt,
|
||||
"out of Lua stack, too many arguments to a Lua function or too many return values from a callback"
|
||||
),
|
||||
Error::BindError => write!(
|
||||
fmt,
|
||||
"too many arguments to Function::bind"
|
||||
),
|
||||
Error::BindError => write!(fmt, "too many arguments to Function::bind"),
|
||||
Error::BadArgument { to, pos, name, cause } => {
|
||||
if let Some(name) = name {
|
||||
write!(fmt, "bad argument `{name}`")?;
|
||||
@@ -252,13 +239,6 @@ impl fmt::Display for Error {
|
||||
write!(fmt, " to `{to}`")?;
|
||||
}
|
||||
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 } => {
|
||||
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::UserDataBorrowMutError => write!(fmt, "error mutably borrowing userdata"),
|
||||
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}")?;
|
||||
match message {
|
||||
None => Ok(()),
|
||||
@@ -286,7 +270,11 @@ impl fmt::Display for Error {
|
||||
Error::CallbackError { cause, traceback } => {
|
||||
// Trace errors down to the root
|
||||
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;
|
||||
full_traceback = Some(traceback2);
|
||||
}
|
||||
@@ -312,11 +300,11 @@ impl fmt::Display for Error {
|
||||
#[cfg(feature = "serde")]
|
||||
Error::SerializeError(err) => {
|
||||
write!(fmt, "serialize error: {err}")
|
||||
},
|
||||
}
|
||||
#[cfg(feature = "serde")]
|
||||
Error::DeserializeError(err) => {
|
||||
write!(fmt, "deserialize error: {err}")
|
||||
},
|
||||
}
|
||||
Error::ExternalError(err) => err.fmt(fmt),
|
||||
Error::WithContext { context, cause } => {
|
||||
writeln!(fmt, "{context}")?;
|
||||
@@ -394,6 +382,7 @@ impl Error {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn from_lua_conversion(
|
||||
from: &'static str,
|
||||
to: impl ToString,
|
||||
|
||||
+123
-12
@@ -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::os::raw::{c_int, c_void};
|
||||
use std::{mem, ptr, slice};
|
||||
@@ -8,7 +89,7 @@ use crate::table::Table;
|
||||
use crate::traits::{FromLuaMulti, IntoLua, IntoLuaMulti, LuaNativeFn, LuaNativeFnMut};
|
||||
use crate::types::{Callback, LuaType, MaybeSend, ValueRef};
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, linenumber_to_usize, pop_error, ptr_to_lossy_str, ptr_to_str, StackGuard,
|
||||
StackGuard, assert_stack, check_stack, linenumber_to_usize, pop_error, ptr_to_lossy_str, ptr_to_str,
|
||||
};
|
||||
use crate::value::Value;
|
||||
|
||||
@@ -18,7 +99,7 @@ use {
|
||||
crate::traits::LuaNativeAsyncFn,
|
||||
crate::types::AsyncCallback,
|
||||
std::future::{self, Future},
|
||||
std::pin::{pin, Pin},
|
||||
std::pin::{Pin, pin},
|
||||
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
|
||||
#[derive(Clone, Debug)]
|
||||
#[non_exhaustive]
|
||||
pub struct FunctionInfo {
|
||||
/// A (reasonable) name of the function (`None` if the name cannot be found).
|
||||
pub name: Option<String>,
|
||||
@@ -50,6 +132,16 @@ pub struct FunctionInfo {
|
||||
pub line_defined: Option<usize>,
|
||||
/// The line number where the definition of the function ends (not set by Luau).
|
||||
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.
|
||||
@@ -276,7 +368,7 @@ impl Function {
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
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 {
|
||||
// Traverse upvalues until we find the _ENV one
|
||||
match ffi::lua_getupvalue(state, -1, i) {
|
||||
@@ -316,7 +408,7 @@ impl Function {
|
||||
lua.push_ref(&env.0);
|
||||
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 {
|
||||
match ffi::lua_getupvalue(state, -1, i) {
|
||||
s if s.is_null() => return Ok(false),
|
||||
@@ -343,7 +435,8 @@ impl 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
|
||||
pub fn info(&self) -> FunctionInfo {
|
||||
@@ -355,11 +448,16 @@ impl Function {
|
||||
|
||||
let mut ar: ffi::lua_Debug = mem::zeroed();
|
||||
lua.push_ref(&self.0);
|
||||
|
||||
#[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")]
|
||||
let res = ffi::lua_getinfo(state, -1, cstr!("sn"), &mut ar);
|
||||
mlua_assert!(res != 0, "lua_getinfo failed with `>Sn`");
|
||||
let res = ffi::lua_getinfo(state, -1, cstr!("snau"), &mut ar);
|
||||
#[cfg(feature = "luau")]
|
||||
mlua_assert!(res != 0, "lua_getinfo failed with `snau`");
|
||||
|
||||
FunctionInfo {
|
||||
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),
|
||||
#[cfg(feature = "luau")]
|
||||
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,
|
||||
buf: *const c_void,
|
||||
buf_len: usize,
|
||||
data: *mut c_void,
|
||||
data_ptr: *mut c_void,
|
||||
) -> c_int {
|
||||
let data = &mut *(data as *mut Vec<u8>);
|
||||
let buf = slice::from_raw_parts(buf as *const u8, buf_len);
|
||||
data.extend_from_slice(buf);
|
||||
// If `data` is null, then it's a signal that write is finished.
|
||||
if !data_ptr.is_null() && buf_len > 0 {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -653,7 +762,9 @@ impl LuaType for Function {
|
||||
const TYPE_ID: c_int = ffi::LUA_TFUNCTION;
|
||||
}
|
||||
|
||||
/// Future for asynchronous function calls.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct AsyncCallFuture<R: FromLuaMulti>(Result<AsyncThread<R>>);
|
||||
|
||||
|
||||
+8
-11
@@ -66,7 +66,6 @@
|
||||
// warnings at all.
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(not(send), allow(clippy::arc_with_non_send_sync))]
|
||||
#![allow(clippy::ptr_eq)]
|
||||
#![allow(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
#[macro_use]
|
||||
@@ -75,9 +74,7 @@ mod macros;
|
||||
mod buffer;
|
||||
mod chunk;
|
||||
mod conversion;
|
||||
mod debug;
|
||||
mod error;
|
||||
mod function;
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
mod luau;
|
||||
mod memory;
|
||||
@@ -86,7 +83,6 @@ mod scope;
|
||||
mod state;
|
||||
mod stdlib;
|
||||
mod string;
|
||||
mod table;
|
||||
mod thread;
|
||||
mod traits;
|
||||
mod types;
|
||||
@@ -95,21 +91,23 @@ mod util;
|
||||
mod value;
|
||||
mod vector;
|
||||
|
||||
pub mod debug;
|
||||
pub mod function;
|
||||
pub mod prelude;
|
||||
pub mod table;
|
||||
|
||||
pub use bstr::BString;
|
||||
pub use ffi::{self, lua_CFunction, lua_State};
|
||||
|
||||
pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
|
||||
pub use crate::debug::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack};
|
||||
pub use crate::error::{Error, ErrorContext, ExternalError, ExternalResult, Result};
|
||||
pub use crate::function::{Function, FunctionInfo};
|
||||
pub use crate::function::Function;
|
||||
pub use crate::multi::{MultiValue, Variadic};
|
||||
pub use crate::scope::Scope;
|
||||
pub use crate::state::{GCMode, Lua, LuaOptions, WeakLua};
|
||||
pub use crate::stdlib::StdLib;
|
||||
pub use crate::string::{BorrowedBytes, BorrowedStr, String};
|
||||
pub use crate::table::{Table, TablePairs, TableSequence};
|
||||
pub use crate::string::{BorrowedBytes, BorrowedStr, LuaString, LuaString as String};
|
||||
pub use crate::table::Table;
|
||||
pub use crate::thread::{Thread, ThreadStatus};
|
||||
pub use crate::traits::{
|
||||
FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, LuaNativeFn, LuaNativeFnMut, ObjectLike,
|
||||
@@ -131,8 +129,7 @@ pub use crate::debug::HookTriggers;
|
||||
pub use crate::{
|
||||
buffer::Buffer,
|
||||
chunk::{CompileConstant, Compiler},
|
||||
function::CoverageInfo,
|
||||
luau::{NavigateError, Require, TextRequirer},
|
||||
luau::{HeapDump, NavigateError, Require, TextRequirer},
|
||||
vector::Vector,
|
||||
};
|
||||
|
||||
@@ -143,7 +140,7 @@ pub use crate::{thread::AsyncThread, traits::LuaNativeAsyncFn};
|
||||
#[cfg(feature = "serde")]
|
||||
#[doc(inline)]
|
||||
pub use crate::{
|
||||
serde::{de::Options as DeserializeOptions, ser::Options as SerializeOptions, LuaSerdeExt},
|
||||
serde::{LuaSerdeExt, de::Options as DeserializeOptions, ser::Options as SerializeOptions},
|
||||
value::SerializableValue,
|
||||
};
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
+56
-3
@@ -1,14 +1,15 @@
|
||||
use std::ffi::CStr;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::os::raw::c_int;
|
||||
use std::ptr;
|
||||
|
||||
use crate::chunk::ChunkMode;
|
||||
use crate::error::Result;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::{callback_error_ext, ExtraData, Lua};
|
||||
use crate::state::{ExtraData, Lua, callback_error_ext};
|
||||
use crate::traits::{FromLuaMulti, IntoLua};
|
||||
use crate::types::MaybeSend;
|
||||
|
||||
pub use heap_dump::HeapDump;
|
||||
pub use require::{NavigateError, Require, TextRequirer};
|
||||
|
||||
// Since Luau has some missing standard functions, we re-implement them here
|
||||
@@ -22,6 +23,56 @@ impl Lua {
|
||||
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<()> {
|
||||
let globals = self.globals();
|
||||
|
||||
@@ -96,4 +147,6 @@ unsafe extern "C-unwind" fn lua_loadstring(state: *mut ffi::lua_State) -> c_int
|
||||
})
|
||||
}
|
||||
|
||||
mod heap_dump;
|
||||
mod json;
|
||||
mod require;
|
||||
|
||||
+57
-274
@@ -1,22 +1,21 @@
|
||||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::ffi::CStr;
|
||||
use std::io::Result as IoResult;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
use std::result::Result as StdResult;
|
||||
use std::{env, fmt, fs, mem, ptr};
|
||||
use std::{fmt, mem, ptr};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::{callback_error_ext, Lua};
|
||||
use crate::state::{Lua, callback_error_ext};
|
||||
use crate::table::Table;
|
||||
use crate::types::MaybeSend;
|
||||
|
||||
// TODO: Rename to FsRequirer
|
||||
pub use fs::TextRequirer;
|
||||
|
||||
/// An error that can occur during navigation in the Luau `require-by-string` system.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum NavigateError {
|
||||
Ambiguous,
|
||||
@@ -50,9 +49,10 @@ impl From<Error> for NavigateError {
|
||||
#[cfg(feature = "luau")]
|
||||
type WriteResult = ffi::luarequire_WriteResult;
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
type ConfigStatus = ffi::luarequire_ConfigStatus;
|
||||
|
||||
/// A trait for handling modules loading and navigation in the Luau `require-by-string` system.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub trait Require {
|
||||
/// Returns `true` if "require" is permitted for the given chunk name.
|
||||
fn is_require_allowed(&self, chunk_name: &str) -> bool;
|
||||
@@ -73,7 +73,7 @@ pub trait Require {
|
||||
/// Navigate to the given child directory.
|
||||
fn to_child(&mut self, name: &str) -> StdResult<(), NavigateError>;
|
||||
|
||||
/// Returns whether the context is currently pointing at a module
|
||||
/// Returns whether the context is currently pointing at a module.
|
||||
fn has_module(&self) -> bool;
|
||||
|
||||
/// Provides a cache key representing the current module.
|
||||
@@ -103,226 +103,31 @@ impl fmt::Debug for dyn Require {
|
||||
}
|
||||
}
|
||||
|
||||
/// The standard implementation of Luau `require-by-string` navigation.
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TextRequirer {
|
||||
/// An absolute path to the current Luau module (not mapped to a physical file)
|
||||
abs_path: PathBuf,
|
||||
/// A relative path to the current Luau module (not mapped to a physical file)
|
||||
rel_path: PathBuf,
|
||||
/// A physical path to the current Luau module, which is a file or a directory with an
|
||||
/// `init.lua(u)` file
|
||||
resolved_path: Option<PathBuf>,
|
||||
struct Context {
|
||||
require: Box<dyn Require>,
|
||||
config_cache: Option<IoResult<Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl TextRequirer {
|
||||
/// The prefix used for chunk names in the require system.
|
||||
/// Only chunk names starting with this prefix are allowed to be used in `require`.
|
||||
const CHUNK_PREFIX: &str = "@";
|
||||
|
||||
/// The file extensions that are considered valid for Luau modules.
|
||||
const FILE_EXTENSIONS: &[&str] = &["luau", "lua"];
|
||||
|
||||
/// Creates a new `TextRequirer` instance.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn normalize_chunk_name(chunk_name: &str) -> &str {
|
||||
if let Some((path, line)) = chunk_name.rsplit_once(':') {
|
||||
if line.parse::<u32>().is_ok() {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
chunk_name
|
||||
}
|
||||
|
||||
// Normalizes the path by removing unnecessary components
|
||||
fn normalize_path(path: &Path) -> PathBuf {
|
||||
let mut components = VecDeque::new();
|
||||
|
||||
for comp in path.components() {
|
||||
match comp {
|
||||
Component::Prefix(..) | Component::RootDir => {
|
||||
components.push_back(comp);
|
||||
}
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir => {
|
||||
if matches!(components.back(), None | Some(Component::ParentDir)) {
|
||||
components.push_back(Component::ParentDir);
|
||||
} else if matches!(components.back(), Some(Component::Normal(..))) {
|
||||
components.pop_back();
|
||||
}
|
||||
}
|
||||
Component::Normal(..) => components.push_back(comp),
|
||||
}
|
||||
}
|
||||
|
||||
if matches!(components.front(), None | Some(Component::Normal(..))) {
|
||||
components.push_front(Component::CurDir);
|
||||
}
|
||||
|
||||
// Join the components back together
|
||||
components.into_iter().collect()
|
||||
}
|
||||
|
||||
/// Resolve a Luau module path to a physical file or directory.
|
||||
///
|
||||
/// Empty directories without init files are considered valid as "intermediate" directories.
|
||||
fn resolve_module(path: &Path) -> StdResult<Option<PathBuf>, NavigateError> {
|
||||
let mut found_path = None;
|
||||
|
||||
if path.components().next_back() != Some(Component::Normal("init".as_ref())) {
|
||||
let current_ext = (path.extension().and_then(|s| s.to_str()))
|
||||
.map(|s| format!("{s}."))
|
||||
.unwrap_or_default();
|
||||
for ext in Self::FILE_EXTENSIONS {
|
||||
let candidate = path.with_extension(format!("{current_ext}{ext}"));
|
||||
if candidate.is_file() && found_path.replace(candidate).is_some() {
|
||||
return Err(NavigateError::Ambiguous);
|
||||
}
|
||||
}
|
||||
}
|
||||
if path.is_dir() {
|
||||
for component in Self::FILE_EXTENSIONS.iter().map(|ext| format!("init.{ext}")) {
|
||||
let candidate = path.join(component);
|
||||
if candidate.is_file() && found_path.replace(candidate).is_some() {
|
||||
return Err(NavigateError::Ambiguous);
|
||||
}
|
||||
}
|
||||
|
||||
if found_path.is_none() {
|
||||
// Directories without init files are considered valid "intermediate" path
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(found_path.ok_or(NavigateError::NotFound)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl Require for TextRequirer {
|
||||
fn is_require_allowed(&self, chunk_name: &str) -> bool {
|
||||
chunk_name.starts_with(Self::CHUNK_PREFIX)
|
||||
}
|
||||
|
||||
fn reset(&mut self, chunk_name: &str) -> StdResult<(), NavigateError> {
|
||||
if !chunk_name.starts_with(Self::CHUNK_PREFIX) {
|
||||
return Err(NavigateError::NotFound);
|
||||
}
|
||||
let chunk_name = Self::normalize_chunk_name(&chunk_name[1..]);
|
||||
let chunk_path = Self::normalize_path(chunk_name.as_ref());
|
||||
|
||||
if chunk_path.extension() == Some("rs".as_ref()) {
|
||||
// Special case for Rust source files, reset to the current directory
|
||||
let chunk_filename = chunk_path.file_name().unwrap();
|
||||
let cwd = env::current_dir().map_err(|_| NavigateError::NotFound)?;
|
||||
self.abs_path = Self::normalize_path(&cwd.join(chunk_filename));
|
||||
self.rel_path = ([Component::CurDir, Component::Normal(chunk_filename)].into_iter()).collect();
|
||||
self.resolved_path = None;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if chunk_path.is_absolute() {
|
||||
let resolved_path = Self::resolve_module(&chunk_path)?;
|
||||
self.abs_path = chunk_path.clone();
|
||||
self.rel_path = chunk_path;
|
||||
self.resolved_path = resolved_path;
|
||||
} else {
|
||||
// Relative path
|
||||
let cwd = env::current_dir().map_err(|_| NavigateError::NotFound)?;
|
||||
let abs_path = Self::normalize_path(&cwd.join(&chunk_path));
|
||||
let resolved_path = Self::resolve_module(&abs_path)?;
|
||||
self.abs_path = abs_path;
|
||||
self.rel_path = chunk_path;
|
||||
self.resolved_path = resolved_path;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn jump_to_alias(&mut self, path: &str) -> StdResult<(), NavigateError> {
|
||||
let path = Self::normalize_path(path.as_ref());
|
||||
let resolved_path = Self::resolve_module(&path)?;
|
||||
|
||||
self.abs_path = path.clone();
|
||||
self.rel_path = path;
|
||||
self.resolved_path = resolved_path;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn to_parent(&mut self) -> StdResult<(), NavigateError> {
|
||||
let mut abs_path = self.abs_path.clone();
|
||||
if !abs_path.pop() {
|
||||
// It's important to return `NotFound` if we reached the root, as it's a "recoverable" error if we
|
||||
// cannot go beyond the root directory.
|
||||
// Luau "require-by-string` has a special logic to search for config file to resolve aliases.
|
||||
return Err(NavigateError::NotFound);
|
||||
}
|
||||
let mut rel_parent = self.rel_path.clone();
|
||||
rel_parent.pop();
|
||||
let resolved_path = Self::resolve_module(&abs_path)?;
|
||||
|
||||
self.abs_path = abs_path;
|
||||
self.rel_path = Self::normalize_path(&rel_parent);
|
||||
self.resolved_path = resolved_path;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn to_child(&mut self, name: &str) -> StdResult<(), NavigateError> {
|
||||
let abs_path = self.abs_path.join(name);
|
||||
let rel_path = self.rel_path.join(name);
|
||||
let resolved_path = Self::resolve_module(&abs_path)?;
|
||||
|
||||
self.abs_path = abs_path;
|
||||
self.rel_path = rel_path;
|
||||
self.resolved_path = resolved_path;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn has_module(&self) -> bool {
|
||||
(self.resolved_path.as_deref())
|
||||
.map(Path::is_file)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn cache_key(&self) -> String {
|
||||
self.resolved_path.as_deref().unwrap().display().to_string()
|
||||
}
|
||||
|
||||
fn has_config(&self) -> bool {
|
||||
self.abs_path.is_dir() && self.abs_path.join(".luaurc").is_file()
|
||||
}
|
||||
|
||||
fn config(&self) -> IoResult<Vec<u8>> {
|
||||
fs::read(self.abs_path.join(".luaurc"))
|
||||
}
|
||||
|
||||
fn loader(&self, lua: &Lua) -> Result<Function> {
|
||||
let name = format!("@{}", self.rel_path.display());
|
||||
lua.load(self.resolved_path.as_deref().unwrap())
|
||||
.set_name(name)
|
||||
.into_function()
|
||||
}
|
||||
}
|
||||
|
||||
struct Context(Box<dyn Require>);
|
||||
|
||||
impl Deref for Context {
|
||||
type Target = dyn Require;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.0
|
||||
&*self.require
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Context {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut *self.0
|
||||
&mut *self.require
|
||||
}
|
||||
}
|
||||
|
||||
impl Context {
|
||||
fn new(require: impl Require + MaybeSend + 'static) -> Self {
|
||||
Context {
|
||||
require: Box::new(require),
|
||||
config_cache: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,9 +252,18 @@ pub(super) unsafe extern "C-unwind" fn init_config(config: *mut ffi::luarequire_
|
||||
write_to_buffer(buffer, buffer_size, size_out, cache_key.as_bytes())
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn is_config_present(state: *mut ffi::lua_State, ctx: *mut c_void) -> bool {
|
||||
let this = try_borrow!(state, ctx);
|
||||
this.has_config()
|
||||
unsafe extern "C-unwind" fn get_config_status(
|
||||
state: *mut ffi::lua_State,
|
||||
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-unwind" fn get_config(
|
||||
@@ -459,8 +273,10 @@ pub(super) unsafe extern "C-unwind" fn init_config(config: *mut ffi::luarequire_
|
||||
buffer_size: usize,
|
||||
size_out: *mut usize,
|
||||
) -> WriteResult {
|
||||
let this = try_borrow!(state, ctx);
|
||||
let config = callback_error_ext(state, ptr::null_mut(), true, move |_, _| Ok(this.config()?));
|
||||
let mut this = try_borrow_mut!(state, ctx);
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -483,18 +299,33 @@ pub(super) unsafe extern "C-unwind" fn init_config(config: *mut ffi::luarequire_
|
||||
(*config).is_require_allowed = is_require_allowed;
|
||||
(*config).reset = reset;
|
||||
(*config).jump_to_alias = jump_to_alias;
|
||||
(*config).to_alias_override = None;
|
||||
(*config).to_alias_fallback = None;
|
||||
(*config).to_parent = to_parent;
|
||||
(*config).to_child = to_child;
|
||||
(*config).is_module_present = is_module_present;
|
||||
(*config).get_chunkname = get_chunkname;
|
||||
(*config).get_loadname = get_loadname;
|
||||
(*config).get_cache_key = get_cache_key;
|
||||
(*config).is_config_present = is_config_present;
|
||||
(*config).get_config_status = get_config_status;
|
||||
(*config).get_alias = None;
|
||||
(*config).get_config = Some(get_config);
|
||||
(*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
|
||||
#[cfg(feature = "luau")]
|
||||
unsafe fn write_to_buffer(
|
||||
@@ -545,7 +376,7 @@ pub(super) fn create_require_function<R: Require + MaybeSend + 'static>(
|
||||
|
||||
let (get_cache_key, find_current_file, proxyrequire, registered_modules, loader_cache) = unsafe {
|
||||
lua.exec_raw::<(Function, Function, Function, Table, Table)>((), move |state| {
|
||||
let context = Context(Box::new(require));
|
||||
let context = 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_pushcfunctiond(state, find_current_file, cstr!("find_current_file"));
|
||||
@@ -637,52 +468,4 @@ pub(super) fn create_require_function<R: Require + MaybeSend + 'static>(
|
||||
.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}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
mod fs;
|
||||
|
||||
@@ -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"))]
|
||||
#[rustversion::since(1.85)]
|
||||
#[inline]
|
||||
#[allow(clippy::incompatible_msrv)]
|
||||
pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
|
||||
let mut mem_state = ptr::null_mut();
|
||||
if !ptr::fn_addr_eq(ffi::lua_getallocf(state, &mut mem_state), ALLOCATOR) {
|
||||
@@ -39,17 +37,6 @@ impl 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]
|
||||
pub(crate) fn used_memory(&self) -> 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
|
||||
#[cfg(any(feature = "lua52", feature = "lua53", feature = "lua54"))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn relax_limit_with(_state: *mut ffi::lua_State, f: impl FnOnce()) {
|
||||
f();
|
||||
|
||||
+18
-1
@@ -1,4 +1,4 @@
|
||||
use std::collections::{vec_deque, VecDeque};
|
||||
use std::collections::{VecDeque, vec_deque};
|
||||
use std::iter::FromIterator;
|
||||
use std::mem;
|
||||
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 {
|
||||
#[inline]
|
||||
fn from_lua_multi(values: MultiValue, _: &Lua) -> Result<Self> {
|
||||
|
||||
+7
-8
@@ -5,16 +5,16 @@ pub use crate::{
|
||||
AnyUserData as LuaAnyUserData, BorrowedBytes as LuaBorrowedBytes, BorrowedStr as LuaBorrowedStr,
|
||||
Chunk as LuaChunk, Either as LuaEither, Error as LuaError, ErrorContext as LuaErrorContext,
|
||||
ExternalError as LuaExternalError, ExternalResult as LuaExternalResult, FromLua, FromLuaMulti,
|
||||
Function as LuaFunction, FunctionInfo as LuaFunctionInfo, GCMode as LuaGCMode, Integer as LuaInteger,
|
||||
IntoLua, IntoLuaMulti, LightUserData as LuaLightUserData, Lua, LuaNativeFn, LuaNativeFnMut, LuaOptions,
|
||||
Function as LuaFunction, GCMode as LuaGCMode, Integer as LuaInteger, IntoLua, IntoLuaMulti,
|
||||
LightUserData as LuaLightUserData, Lua, LuaNativeFn, LuaNativeFnMut, LuaOptions, LuaString,
|
||||
MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber,
|
||||
ObjectLike as LuaObjectLike, RegistryKey as LuaRegistryKey, Result as LuaResult, StdLib as LuaStdLib,
|
||||
String as LuaString, Table as LuaTable, TablePairs as LuaTablePairs, TableSequence as LuaTableSequence,
|
||||
Thread as LuaThread, ThreadStatus as LuaThreadStatus, UserData as LuaUserData,
|
||||
Table as LuaTable, Thread as LuaThread, ThreadStatus as LuaThreadStatus, UserData as LuaUserData,
|
||||
UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable,
|
||||
UserDataMethods as LuaUserDataMethods, UserDataRef as LuaUserDataRef,
|
||||
UserDataRefMut as LuaUserDataRefMut, UserDataRegistry as LuaUserDataRegistry, Value as LuaValue,
|
||||
Variadic as LuaVariadic, VmState as LuaVmState, WeakLua,
|
||||
Variadic as LuaVariadic, VmState as LuaVmState, WeakLua, function::FunctionInfo as LuaFunctionInfo,
|
||||
table::TablePairs as LuaTablePairs, table::TableSequence as LuaTableSequence,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
@@ -24,9 +24,8 @@ pub use crate::HookTriggers as LuaHookTriggers;
|
||||
#[cfg(feature = "luau")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{
|
||||
CompileConstant as LuaCompileConstant, CoverageInfo as LuaCoverageInfo,
|
||||
NavigateError as LuaNavigateError, Require as LuaRequire, TextRequirer as LuaTextRequirer,
|
||||
Vector as LuaVector,
|
||||
CompileConstant as LuaCompileConstant, NavigateError as LuaNavigateError, Require as LuaRequire,
|
||||
TextRequirer as LuaTextRequirer, Vector as LuaVector,
|
||||
};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ use crate::state::{Lua, LuaGuard, RawLua};
|
||||
use crate::traits::{FromLuaMulti, IntoLuaMulti};
|
||||
use crate::types::{Callback, CallbackUpvalue, ScopedCallback, ValueRef};
|
||||
use crate::userdata::{AnyUserData, UserData, UserDataRegistry, UserDataStorage};
|
||||
use crate::util::{self, check_stack, get_metatable_ptr, get_userdata, take_userdata, StackGuard};
|
||||
use crate::util::{self, StackGuard, check_stack, get_metatable_ptr, get_userdata, take_userdata};
|
||||
|
||||
/// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and
|
||||
/// callbacks that are not required to be `Send` or `'static`.
|
||||
|
||||
+3
-4
@@ -4,7 +4,6 @@ use std::cell::RefCell;
|
||||
use std::os::raw::c_void;
|
||||
use std::rc::Rc;
|
||||
use std::result::Result as StdResult;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use rustc_hash::FxHashSet;
|
||||
use serde::de::{self, IntoDeserializer};
|
||||
@@ -243,14 +242,14 @@ impl<'de> serde::Deserializer<'de> for Deserializer {
|
||||
Value::Table(table) => {
|
||||
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() {
|
||||
Some(v) => v?,
|
||||
None => {
|
||||
return Err(de::Error::invalid_value(
|
||||
de::Unexpected::Map,
|
||||
&"map with a single key",
|
||||
))
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -621,7 +620,7 @@ impl<'de> de::MapAccess<'de> for MapDeserializer<'_> {
|
||||
}
|
||||
|
||||
struct EnumDeserializer {
|
||||
variant: StdString,
|
||||
variant: String,
|
||||
value: Option<Value>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
//! Serialize a Rust data structure into Lua value.
|
||||
|
||||
use serde::{ser, Serialize};
|
||||
use serde::{Serialize, ser};
|
||||
|
||||
use super::LuaSerdeExt;
|
||||
use crate::error::{Error, Result};
|
||||
@@ -531,10 +531,10 @@ impl ser::SerializeStruct for SerializeStruct<'_> {
|
||||
Some(table @ Value::Table(_)) => Ok(table),
|
||||
Some(value @ Value::String(_)) if self.options.detect_serde_json_arbitrary_precision => {
|
||||
let number_s = value.to_string()?;
|
||||
if number_s.contains(['.', 'e', 'E']) {
|
||||
if let Ok(number) = number_s.parse().map(Value::Number) {
|
||||
return Ok(number);
|
||||
}
|
||||
if number_s.contains(['.', 'e', 'E'])
|
||||
&& let Ok(number) = number_s.parse().map(Value::Number)
|
||||
{
|
||||
return Ok(number);
|
||||
}
|
||||
Ok(number_s
|
||||
.parse()
|
||||
|
||||
+173
-84
@@ -15,7 +15,7 @@ use crate::memory::MemoryState;
|
||||
use crate::multi::MultiValue;
|
||||
use crate::scope::Scope;
|
||||
use crate::stdlib::StdLib;
|
||||
use crate::string::String;
|
||||
use crate::string::LuaString;
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
@@ -24,7 +24,7 @@ use crate::types::{
|
||||
ReentrantMutexGuard, RegistryKey, VmState, XRc, XWeak,
|
||||
};
|
||||
use crate::userdata::{AnyUserData, UserData, UserDataProxy, UserDataRegistry, UserDataStorage};
|
||||
use crate::util::{assert_stack, check_stack, protect_lua_closure, push_string, rawset_field, StackGuard};
|
||||
use crate::util::{StackGuard, assert_stack, check_stack, protect_lua_closure, push_string, rawset_field};
|
||||
use crate::value::{Nil, Value};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
@@ -73,8 +73,8 @@ pub(crate) struct LuaGuard(ArcReentrantMutexGuard<RawLua>);
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum GCMode {
|
||||
Incremental,
|
||||
#[cfg(feature = "lua54")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua55", feature = "lua54"))))]
|
||||
Generational,
|
||||
}
|
||||
|
||||
@@ -249,7 +249,7 @@ impl Lua {
|
||||
ffi::luaL_loadstring 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_setglobal as _);
|
||||
@@ -337,6 +337,39 @@ impl 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.
|
||||
///
|
||||
/// Use the [`StdLib`] flags to specify the libraries you want to load.
|
||||
@@ -382,7 +415,7 @@ impl Lua {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
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 {
|
||||
self.exec_raw::<Option<Table>>((), |state| {
|
||||
ffi::lua_getfield(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_PRELOAD_TABLE);
|
||||
@@ -814,15 +847,14 @@ impl Lua {
|
||||
}
|
||||
|
||||
/// Sets the warning function to be used by Lua to emit warnings.
|
||||
#[cfg(feature = "lua54")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua55", feature = "lua54"))))]
|
||||
pub fn set_warning_function<F>(&self, callback: F)
|
||||
where
|
||||
F: Fn(&Lua, &str, bool) -> Result<()> + MaybeSend + 'static,
|
||||
{
|
||||
use std::ffi::CStr;
|
||||
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) {
|
||||
let extra = ud as *mut ExtraData;
|
||||
@@ -832,7 +864,7 @@ impl Lua {
|
||||
if XRc::strong_count(&warn_callback) > 2 {
|
||||
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)
|
||||
});
|
||||
}
|
||||
@@ -847,8 +879,8 @@ impl Lua {
|
||||
/// Removes warning function previously set by `set_warning_function`.
|
||||
///
|
||||
/// This function has no effect if a warning function was not previously set.
|
||||
#[cfg(feature = "lua54")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua55", feature = "lua54"))))]
|
||||
pub fn remove_warning_function(&self) {
|
||||
let lua = self.lock();
|
||||
unsafe {
|
||||
@@ -861,8 +893,8 @@ impl Lua {
|
||||
///
|
||||
/// A message in a call with `incomplete` set to `true` should be continued in
|
||||
/// another call to this function.
|
||||
#[cfg(feature = "lua54")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua55", feature = "lua54"))))]
|
||||
pub fn warning(&self, msg: impl AsRef<str>, incomplete: bool) {
|
||||
let msg = msg.as_ref();
|
||||
let mut bytes = vec![0; msg.len() + 1];
|
||||
@@ -875,7 +907,7 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets information about the interpreter runtime stack at a given level.
|
||||
/// Gets information about the interpreter runtime stack at the given level.
|
||||
///
|
||||
/// This function calls callback `f`, passing the [`Debug`] structure that can be used to get
|
||||
/// information about the function executing at a given level.
|
||||
@@ -899,6 +931,26 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the amount of memory (in bytes) currently used inside this Lua state.
|
||||
pub fn used_memory(&self) -> usize {
|
||||
let lua = self.lock();
|
||||
@@ -934,7 +986,13 @@ impl Lua {
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
let lua = self.lock();
|
||||
unsafe { ffi::lua_gc(lua.main_state(), ffi::LUA_GCISRUNNING, 0) != 0 }
|
||||
@@ -999,8 +1057,12 @@ impl Lua {
|
||||
let lua = self.lock();
|
||||
let state = lua.main_state();
|
||||
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);
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
return ffi::lua_gc(state, ffi::LUA_GCSETGOAL, pause);
|
||||
}
|
||||
@@ -1014,7 +1076,18 @@ impl Lua {
|
||||
/// [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 {
|
||||
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.
|
||||
@@ -1056,9 +1129,16 @@ impl Lua {
|
||||
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")]
|
||||
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 {
|
||||
ffi::LUA_GCINC => GCMode::Incremental,
|
||||
ffi::LUA_GCGEN => GCMode::Generational,
|
||||
@@ -1072,11 +1152,19 @@ impl Lua {
|
||||
/// 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
|
||||
#[cfg(feature = "lua54")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
||||
#[cfg(any(feature = "lua55", 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 {
|
||||
let lua = self.lock();
|
||||
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) };
|
||||
match prev_mode {
|
||||
ffi::LUA_GCGEN => GCMode::Generational,
|
||||
@@ -1116,10 +1204,10 @@ impl Lua {
|
||||
#[doc(hidden)]
|
||||
#[allow(clippy::result_unit_err)]
|
||||
pub fn set_fflag(name: &str, enabled: bool) -> StdResult<(), ()> {
|
||||
if let Ok(name) = std::ffi::CString::new(name) {
|
||||
if unsafe { ffi::luau_setfflag(name.as_ptr(), enabled as c_int) != 0 } {
|
||||
return Ok(());
|
||||
}
|
||||
if let Ok(name) = std::ffi::CString::new(name)
|
||||
&& unsafe { ffi::luau_setfflag(name.as_ptr(), enabled as c_int) != 0 }
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
Err(())
|
||||
}
|
||||
@@ -1159,8 +1247,19 @@ impl Lua {
|
||||
/// Lua strings can be arbitrary `[u8]` data including embedded nulls, so in addition to `&str`
|
||||
/// and `&String`, you can also pass plain `&[u8]` here.
|
||||
#[inline]
|
||||
pub fn create_string(&self, s: impl AsRef<[u8]>) -> Result<String> {
|
||||
unsafe { self.lock().create_string(s) }
|
||||
pub fn create_string(&self, s: impl AsRef<[u8]>) -> Result<LuaString> {
|
||||
unsafe { self.lock().create_string(s.as_ref()) }
|
||||
}
|
||||
|
||||
/// 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.
|
||||
@@ -1297,7 +1396,12 @@ impl Lua {
|
||||
/// 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> {
|
||||
let lua = self.lock();
|
||||
if cfg!(any(feature = "lua54", feature = "lua53", feature = "lua52")) {
|
||||
if cfg!(any(
|
||||
feature = "lua55",
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52"
|
||||
)) {
|
||||
ffi::lua_pushcfunction(lua.ref_thread(), func);
|
||||
return Ok(Function(lua.pop_ref_thread()));
|
||||
}
|
||||
@@ -1494,7 +1598,27 @@ impl Lua {
|
||||
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.
|
||||
///
|
||||
@@ -1521,44 +1645,13 @@ impl Lua {
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
|
||||
match T::TYPE_ID {
|
||||
ffi::LUA_TBOOLEAN => {
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
if lua.push_primitive_type::<T>() {
|
||||
match metatable {
|
||||
Some(metatable) => lua.push_ref(&metatable.0),
|
||||
None => ffi::lua_pushnil(state),
|
||||
}
|
||||
ffi::LUA_TLIGHTUSERDATA => {
|
||||
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,
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
}
|
||||
match metatable {
|
||||
Some(metatable) => lua.push_ref(&metatable.0),
|
||||
None => ffi::lua_pushnil(state),
|
||||
}
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1569,7 +1662,7 @@ impl Lua {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
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);
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
ffi::lua_pushvalue(state, ffi::LUA_GLOBALSINDEX);
|
||||
@@ -1601,7 +1694,7 @@ impl Lua {
|
||||
|
||||
lua.push_ref(&globals.0);
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
#[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);
|
||||
@@ -1647,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
|
||||
/// number.
|
||||
pub fn coerce_string(&self, v: Value) -> Result<Option<String>> {
|
||||
pub fn coerce_string(&self, v: Value) -> Result<Option<LuaString>> {
|
||||
Ok(match v {
|
||||
Value::String(s) => Some(s),
|
||||
v => unsafe {
|
||||
@@ -1665,7 +1758,7 @@ impl Lua {
|
||||
})?
|
||||
};
|
||||
if !res.is_null() {
|
||||
Some(String(lua.pop_ref()))
|
||||
Some(LuaString(lua.pop_ref()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -1691,11 +1784,7 @@ impl Lua {
|
||||
lua.push_value(&v)?;
|
||||
let mut isint = 0;
|
||||
let i = ffi::lua_tointegerx(state, -1, &mut isint);
|
||||
if isint == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(i)
|
||||
}
|
||||
if isint == 0 { None } else { Some(i) }
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1717,11 +1806,7 @@ impl Lua {
|
||||
lua.push_value(&v)?;
|
||||
let mut isnum = 0;
|
||||
let n = ffi::lua_tonumberx(state, -1, &mut isnum);
|
||||
if isnum == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(n)
|
||||
}
|
||||
if isnum == 0 { None } else { Some(n) }
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -2089,7 +2174,7 @@ impl Lua {
|
||||
|
||||
/// Suspends the current async function, returning the provided arguments to caller.
|
||||
///
|
||||
/// This function is similar to [`coroutine.yield`] but allow yeilding Rust functions
|
||||
/// 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).
|
||||
@@ -2167,9 +2252,13 @@ impl Lua {
|
||||
None => unsafe {
|
||||
let lua = self.lock();
|
||||
let state = lua.state();
|
||||
let _sg = StackGuard::with_top(state, 0);
|
||||
let nvals = ffi::lua_gettop(state);
|
||||
Poll::Ready(R::from_stack_multi(nvals, &lua))
|
||||
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
|
||||
@@ -2197,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")?;
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
let searchers: Table = package.get("loaders")?;
|
||||
|
||||
+7
-3
@@ -14,7 +14,7 @@ use crate::state::RawLua;
|
||||
use crate::stdlib::StdLib;
|
||||
use crate::types::{AppData, ReentrantMutex, XRc};
|
||||
use crate::userdata::RawUserDataRegistry;
|
||||
use crate::util::{get_internal_metatable, push_internal_userdata, TypeKey, WrappedFailure};
|
||||
use crate::util::{TypeKey, WrappedFailure, get_internal_metatable, push_internal_userdata};
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
use crate::chunk::Compiler;
|
||||
@@ -77,7 +77,7 @@ pub(crate) struct ExtraData {
|
||||
pub(super) hook_callback: Option<crate::types::HookCallback>,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(super) hook_triggers: crate::debug::HookTriggers,
|
||||
#[cfg(feature = "lua54")]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
pub(super) warn_callback: Option<crate::types::WarnCallback>,
|
||||
#[cfg(feature = "luau")]
|
||||
pub(super) interrupt_callback: Option<crate::types::InterruptCallback>,
|
||||
@@ -94,6 +94,8 @@ pub(crate) struct ExtraData {
|
||||
pub(super) compiler: Option<Compiler>,
|
||||
#[cfg(feature = "luau-jit")]
|
||||
pub(super) enable_jit: bool,
|
||||
#[cfg(feature = "luau")]
|
||||
pub(crate) mem_categories: Vec<std::ffi::CString>,
|
||||
}
|
||||
|
||||
impl Drop for ExtraData {
|
||||
@@ -180,7 +182,7 @@ impl ExtraData {
|
||||
hook_callback: None,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
hook_triggers: Default::default(),
|
||||
#[cfg(feature = "lua54")]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
warn_callback: None,
|
||||
#[cfg(feature = "luau")]
|
||||
interrupt_callback: None,
|
||||
@@ -196,6 +198,8 @@ impl ExtraData {
|
||||
enable_jit: true,
|
||||
#[cfg(feature = "luau")]
|
||||
running_gc: false,
|
||||
#[cfg(feature = "luau")]
|
||||
mem_categories: vec![std::ffi::CString::new("main").unwrap()],
|
||||
}));
|
||||
|
||||
// Store it in the registry
|
||||
|
||||
+131
-58
@@ -10,26 +10,26 @@ use std::sync::Arc;
|
||||
use crate::chunk::ChunkMode;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::memory::{MemoryState, ALLOCATOR};
|
||||
use crate::memory::{ALLOCATOR, MemoryState};
|
||||
use crate::state::util::callback_error_ext;
|
||||
use crate::stdlib::StdLib;
|
||||
use crate::string::String;
|
||||
use crate::string::LuaString;
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::traits::IntoLua;
|
||||
use crate::types::{
|
||||
AppDataRef, AppDataRefMut, Callback, CallbackUpvalue, DestructedUserdata, Integer, LightUserData,
|
||||
MaybeSend, ReentrantMutex, RegistryKey, ValueRef, XRc,
|
||||
LuaType, MaybeSend, ReentrantMutex, RegistryKey, ValueRef, XRc,
|
||||
};
|
||||
use crate::userdata::{
|
||||
init_userdata_metatable, AnyUserData, MetaMethod, RawUserDataRegistry, UserData, UserDataRegistry,
|
||||
UserDataStorage,
|
||||
AnyUserData, MetaMethod, RawUserDataRegistry, UserData, UserDataRegistry, UserDataStorage,
|
||||
init_userdata_metatable,
|
||||
};
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, get_destructed_userdata_metatable, get_internal_userdata, get_main_state,
|
||||
get_metatable_ptr, get_userdata, init_error_registry, init_internal_metatable, pop_error,
|
||||
push_internal_userdata, push_string, push_table, push_userdata, rawset_field, safe_pcall, safe_xpcall,
|
||||
short_type_name, StackGuard, WrappedFailure,
|
||||
StackGuard, WrappedFailure, assert_stack, check_stack, get_destructed_userdata_metatable,
|
||||
get_internal_userdata, get_main_state, get_metatable_ptr, get_userdata, init_error_registry,
|
||||
init_internal_metatable, pop_error, push_internal_userdata, push_string, push_table, push_userdata,
|
||||
rawset_field, safe_pcall, safe_xpcall, short_type_name,
|
||||
};
|
||||
use crate::value::{Nil, Value};
|
||||
|
||||
@@ -122,6 +122,12 @@ impl RawLua {
|
||||
|
||||
pub(super) unsafe fn new(libs: StdLib, options: &LuaOptions) -> XRc<ReentrantMutex<Self>> {
|
||||
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);
|
||||
// If state is null then switch to Lua internal allocator
|
||||
if state.is_null() {
|
||||
@@ -153,7 +159,7 @@ impl RawLua {
|
||||
(|| -> Result<()> {
|
||||
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);
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
ffi::lua_pushvalue(state, ffi::LUA_GLOBALSINDEX);
|
||||
@@ -416,7 +422,7 @@ impl RawLua {
|
||||
VmState::Yield => {
|
||||
// Only count and line events can yield
|
||||
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 {
|
||||
ffi::lua_yield(state, 0);
|
||||
}
|
||||
@@ -510,17 +516,34 @@ impl RawLua {
|
||||
}
|
||||
|
||||
/// 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();
|
||||
if self.unlikely_memory_error() {
|
||||
push_string(state, s.as_ref(), false)?;
|
||||
return Ok(String(self.pop_ref()));
|
||||
push_string(state, s, false)?;
|
||||
return Ok(LuaString(self.pop_ref()));
|
||||
}
|
||||
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
push_string(state, s.as_ref(), true)?;
|
||||
Ok(String(self.pop_ref()))
|
||||
push_string(state, s, true)?;
|
||||
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")]
|
||||
@@ -658,25 +681,65 @@ impl RawLua {
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) unsafe fn recycle_thread(&self, thread: &mut Thread) {
|
||||
let extra = &mut *self.extra.get();
|
||||
if extra.thread_pool.len() < extra.thread_pool.capacity() {
|
||||
if let Some(index) = thread.0.index_count.take() {
|
||||
extra.thread_pool.push(index);
|
||||
}
|
||||
if extra.thread_pool.len() < extra.thread_pool.capacity()
|
||||
&& let Some(index) = thread.0.index_count.take()
|
||||
{
|
||||
extra.thread_pool.push(index);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
ffi::LUA_TLIGHTUSERDATA => {
|
||||
ffi::lua_pushlightuserdata(self.state(), ptr::null_mut());
|
||||
}
|
||||
ffi::LUA_TNUMBER => {
|
||||
ffi::lua_pushnumber(self.state(), 0.);
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::LUA_TVECTOR => {
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
ffi::lua_pushvector(self.state(), 0., 0., 0.);
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
ffi::lua_pushvector(self.state(), 0., 0., 0., 0.);
|
||||
}
|
||||
ffi::LUA_TSTRING => {
|
||||
ffi::lua_pushstring(self.state(), b"\0" as *const u8 as *const _);
|
||||
}
|
||||
ffi::LUA_TFUNCTION => {
|
||||
unsafe extern "C-unwind" fn func(_state: *mut ffi::lua_State) -> c_int {
|
||||
0
|
||||
}
|
||||
ffi::lua_pushcfunction(self.state(), func);
|
||||
}
|
||||
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.
|
||||
///
|
||||
/// Uses up to 2 stack spaces to push a single value, does not call `checkstack`.
|
||||
#[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)
|
||||
}
|
||||
|
||||
/// Pushes a `Value` (by reference) onto the Lua stack.
|
||||
///
|
||||
/// 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();
|
||||
match value {
|
||||
Value::Nil => ffi::lua_pushnil(state),
|
||||
@@ -710,7 +773,8 @@ impl RawLua {
|
||||
/// Pops a value from the Lua stack.
|
||||
///
|
||||
/// 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);
|
||||
ffi::lua_pop(self.state(), 1);
|
||||
value
|
||||
@@ -728,7 +792,7 @@ impl RawLua {
|
||||
|
||||
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 => {
|
||||
if ffi::lua_isinteger(state, idx) != 0 {
|
||||
Value::Integer(ffi::lua_tointeger(state, idx))
|
||||
@@ -760,7 +824,7 @@ impl RawLua {
|
||||
|
||||
ffi::LUA_TSTRING => {
|
||||
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 => {
|
||||
@@ -859,7 +923,7 @@ impl RawLua {
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
ffi::lua_xpush(self.ref_thread(), state, ExtraData::ERROR_TRACEBACK_IDX);
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -1062,7 +1126,7 @@ impl RawLua {
|
||||
#[cfg(feature = "luau")]
|
||||
if registry.enable_namecall {
|
||||
let map: &mut rustc_hash::FxHashMap<_, crate::types::CallbackPtr> =
|
||||
methods_map.get_or_insert_with(Default::default);
|
||||
methods_map.get_or_insert_default();
|
||||
for (k, m) in ®istry.methods {
|
||||
map.insert(k.as_bytes().to_vec(), &**m);
|
||||
}
|
||||
@@ -1155,10 +1219,11 @@ impl RawLua {
|
||||
Ok(type_id) => Ok(type_id),
|
||||
Err(Error::UserDataTypeMismatch) if ffi::lua_type(state, idx) != ffi::LUA_TUSERDATA => {
|
||||
// Report `FromLuaConversionError` instead
|
||||
let idx_type_name = CStr::from_ptr(ffi::luaL_typename(state, idx));
|
||||
let idx_type_name = idx_type_name.to_str().unwrap();
|
||||
let type_name = CStr::from_ptr(ffi::lua_typename(state, ffi::lua_type(state, idx)))
|
||||
.to_str()
|
||||
.unwrap_or("unknown");
|
||||
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),
|
||||
}
|
||||
@@ -1239,7 +1304,13 @@ impl RawLua {
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) fn create_async_callback(&self, func: AsyncCallback) -> Result<Function> {
|
||||
// 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 {
|
||||
if !(*self.extra.get()).libs.contains(StdLib::COROUTINE) {
|
||||
load_std_libs(self.main_state(), StdLib::COROUTINE)?;
|
||||
@@ -1247,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,
|
||||
// so the first upvalue is always valid
|
||||
let upvalue = get_userdata::<AsyncCallbackUpvalue>(state, ffi::lua_upvalueindex(1));
|
||||
@@ -1261,37 +1332,31 @@ impl RawLua {
|
||||
let extra = XRc::clone(&(*upvalue).extra);
|
||||
let protect = !rawlua.unlikely_memory_error();
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn poll_future(state: *mut ffi::lua_State) -> c_int {
|
||||
let upvalue = get_userdata::<AsyncPollUpvalue>(state, ffi::lua_upvalueindex(1));
|
||||
callback_error_ext(state, (*upvalue).extra.get(), true, |extra, nargs| {
|
||||
// Future is always passed in the first argument
|
||||
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)
|
||||
// The lock must be already held as the future is polled
|
||||
let rawlua = (*extra).raw_lua();
|
||||
|
||||
if nargs == 1 && ffi::lua_tolightuserdata(state, -1) == Lua::poll_terminate().0 {
|
||||
if nargs == 2 && ffi::lua_tolightuserdata(state, -1) == Lua::poll_terminate().0 {
|
||||
// Destroy the future and terminate the Lua thread
|
||||
(*upvalue).data.take();
|
||||
(*future).data.take();
|
||||
ffi::lua_pushinteger(state, -1);
|
||||
return Ok(1);
|
||||
}
|
||||
|
||||
let fut = &mut (*upvalue).data;
|
||||
let fut = &mut (*future).data;
|
||||
let mut ctx = Context::from_waker(rawlua.waker());
|
||||
match fut.as_mut().map(|fut| fut.as_mut().poll(&mut ctx)) {
|
||||
Some(Poll::Pending) => {
|
||||
let fut_nvals = ffi::lua_gettop(state);
|
||||
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);
|
||||
@@ -1326,7 +1391,7 @@ impl RawLua {
|
||||
}
|
||||
|
||||
let state = self.state();
|
||||
let get_poll = unsafe {
|
||||
let get_future = unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
@@ -1336,10 +1401,10 @@ impl RawLua {
|
||||
push_internal_userdata(state, upvalue, protect)?;
|
||||
if protect {
|
||||
protect_lua!(state, 1, 1, fn(state) {
|
||||
ffi::lua_pushcclosure(state, call_callback, 1);
|
||||
ffi::lua_pushcclosure(state, get_future_callback, 1);
|
||||
})?;
|
||||
} else {
|
||||
ffi::lua_pushcclosure(state, call_callback, 1);
|
||||
ffi::lua_pushcclosure(state, get_future_callback, 1);
|
||||
}
|
||||
|
||||
Function(self.pop_ref())
|
||||
@@ -1358,15 +1423,17 @@ impl RawLua {
|
||||
let coroutine = lua.globals().get::<Table>("coroutine")?;
|
||||
|
||||
// Prepare environment for the async poller
|
||||
let env = lua.create_table_with_capacity(0, 3)?;
|
||||
env.set("get_poll", get_poll)?;
|
||||
let env = lua.create_table_with_capacity(0, 4)?;
|
||||
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("unpack", unsafe { lua.create_c_function(unpack)? })?;
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
local poll = get_poll(...)
|
||||
local nres, res, res2 = poll()
|
||||
local poll, yield = poll, yield
|
||||
local future = get_future(...)
|
||||
local nres, res, res2 = poll(future)
|
||||
while true do
|
||||
-- Poll::Ready branch, `nres` is the number of results
|
||||
if nres ~= nil then
|
||||
@@ -1390,13 +1457,13 @@ impl RawLua {
|
||||
-- `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(yield(res))
|
||||
nres, res, res2 = poll(future, yield(res))
|
||||
elseif res2 == 0 then
|
||||
nres, res, res2 = poll(yield())
|
||||
nres, res, res2 = poll(future, yield())
|
||||
elseif res2 == 1 then
|
||||
nres, res, res2 = poll(yield(res))
|
||||
nres, res, res2 = poll(future, yield(res))
|
||||
else
|
||||
nres, res, res2 = poll(yield(unpack(res, res2)))
|
||||
nres, res, res2 = poll(future, yield(unpack(res, res2)))
|
||||
end
|
||||
end
|
||||
"#,
|
||||
@@ -1456,7 +1523,13 @@ unsafe fn load_std_libs(state: *mut ffi::lua_State, libs: StdLib) -> Result<()>
|
||||
#[cfg(feature = "luajit")]
|
||||
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) {
|
||||
requiref(state, ffi::LUA_COLIBNAME, ffi::luaopen_coroutine, 1)?;
|
||||
@@ -1480,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)?;
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||
{
|
||||
if libs.contains(StdLib::UTF8) {
|
||||
requiref(state, ffi::LUA_UTF8LIBNAME, ffi::luaopen_utf8, 1)?;
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
use std::os::raw::c_int;
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
use std::panic::{AssertUnwindSafe, catch_unwind};
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
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);
|
||||
|
||||
|
||||
+19
-4
@@ -6,10 +6,22 @@ pub struct StdLib(u32);
|
||||
|
||||
impl StdLib {
|
||||
/// [`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(
|
||||
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);
|
||||
|
||||
@@ -28,8 +40,11 @@ impl StdLib {
|
||||
pub const STRING: StdLib = StdLib(1 << 4);
|
||||
|
||||
/// [`utf8`](https://www.lua.org/manual/5.4/manual.html#6.5) library
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(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 = "lua55", feature = "lua54", feature = "lua53", feature = "luau")))
|
||||
)]
|
||||
pub const UTF8: StdLib = StdLib(1 << 5);
|
||||
|
||||
/// [`bit`](https://www.lua.org/manual/5.2/manual.html#6.7) library
|
||||
|
||||
+40
-44
@@ -2,7 +2,6 @@ use std::borrow::{Borrow, Cow};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::Deref;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::string::String as StdString;
|
||||
use std::{cmp, fmt, slice, str};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
@@ -21,23 +20,23 @@ use {
|
||||
///
|
||||
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
|
||||
#[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.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use mlua::{Lua, Result, String};
|
||||
/// # use mlua::{Lua, LuaString, Result};
|
||||
/// # fn main() -> Result<()> {
|
||||
/// # let lua = Lua::new();
|
||||
/// let globals = lua.globals();
|
||||
///
|
||||
/// let version: String = globals.get("_VERSION")?;
|
||||
/// let version: LuaString = globals.get("_VERSION")?;
|
||||
/// 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());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
@@ -47,11 +46,11 @@ impl String {
|
||||
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].
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// [U+FFFD]: std::char::REPLACEMENT_CHARACTER
|
||||
@@ -70,11 +69,11 @@ impl String {
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_string_lossy(&self) -> StdString {
|
||||
StdString::from_utf8_lossy(&self.as_bytes()).into_owned()
|
||||
pub fn to_string_lossy(&self) -> String {
|
||||
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.
|
||||
///
|
||||
/// This may perform lossy conversion.
|
||||
@@ -92,10 +91,10 @@ impl String {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use mlua::{Lua, Result, String};
|
||||
/// # use mlua::{Lua, LuaString, Result};
|
||||
/// # fn main() -> Result<()> {
|
||||
/// # 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_eq!(non_utf8.as_bytes(), &b"test\xff"[..]);
|
||||
/// # Ok(())
|
||||
@@ -135,7 +134,7 @@ impl String {
|
||||
(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.
|
||||
///
|
||||
@@ -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 {
|
||||
let bytes = self.as_bytes();
|
||||
// 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.
|
||||
//
|
||||
// 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
|
||||
// `AsRef<str>`, which collides with this impl. Requiring `AsRef<str>` would fix that, but limit us
|
||||
// in other ways.
|
||||
impl<T> PartialEq<T> for String
|
||||
impl<T> PartialEq<T> for LuaString
|
||||
where
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
@@ -176,43 +175,43 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for String {
|
||||
fn eq(&self, other: &String) -> bool {
|
||||
impl PartialEq for LuaString {
|
||||
fn eq(&self, other: &LuaString) -> bool {
|
||||
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
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
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 {
|
||||
fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
|
||||
impl PartialOrd for LuaString {
|
||||
fn partial_cmp(&self, other: &LuaString) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for String {
|
||||
fn cmp(&self, other: &String) -> cmp::Ordering {
|
||||
impl Ord for LuaString {
|
||||
fn cmp(&self, other: &LuaString) -> cmp::Ordering {
|
||||
self.as_bytes().cmp(&other.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for String {
|
||||
impl Hash for LuaString {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.as_bytes().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for String {
|
||||
impl Serialize for LuaString {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
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<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
@@ -237,7 +236,7 @@ impl fmt::Display for Display<'_> {
|
||||
pub struct BorrowedStr<'a> {
|
||||
// `buf` points to a readonly memory managed by Lua
|
||||
pub(crate) buf: &'a str,
|
||||
pub(crate) borrow: Cow<'a, String>,
|
||||
pub(crate) borrow: Cow<'a, LuaString>,
|
||||
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;
|
||||
|
||||
#[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 buf = str::from_utf8(buf).map_err(|e| Error::FromLuaConversionError {
|
||||
from: "string",
|
||||
to: "&str".to_string(),
|
||||
message: Some(e.to_string()),
|
||||
})?;
|
||||
let buf =
|
||||
str::from_utf8(buf).map_err(|e| Error::from_lua_conversion("string", "&str", e.to_string()))?;
|
||||
Ok(Self { buf, borrow, _lua })
|
||||
}
|
||||
}
|
||||
@@ -321,7 +317,7 @@ impl<'a> TryFrom<&'a String> for BorrowedStr<'a> {
|
||||
pub struct BorrowedBytes<'a> {
|
||||
// `buf` points to a readonly memory managed by Lua
|
||||
pub(crate) buf: &'a [u8],
|
||||
pub(crate) borrow: Cow<'a, String>,
|
||||
pub(crate) borrow: Cow<'a, LuaString>,
|
||||
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]
|
||||
fn from(value: &'a String) -> Self {
|
||||
fn from(value: &'a LuaString) -> Self {
|
||||
let (buf, _lua) = unsafe { value.to_slice() };
|
||||
let borrow = Cow::Borrowed(value);
|
||||
Self { buf, borrow, _lua }
|
||||
@@ -400,7 +396,7 @@ impl<'a> From<&'a String> for BorrowedBytes<'a> {
|
||||
|
||||
struct WrappedString<T: AsRef<[u8]>>(T);
|
||||
|
||||
impl String {
|
||||
impl LuaString {
|
||||
/// Wraps bytes, returning an opaque type that implements [`IntoLua`] trait.
|
||||
///
|
||||
/// 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;
|
||||
}
|
||||
|
||||
@@ -424,9 +420,9 @@ mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_any!(String: Send);
|
||||
static_assertions::assert_not_impl_any!(LuaString: Send);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(String: Send, Sync);
|
||||
static_assertions::assert_impl_all!(LuaString: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(BorrowedBytes: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
|
||||
+174
-20
@@ -1,15 +1,173 @@
|
||||
//! 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::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::string::String as StdString;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::{LuaGuard, RawLua, WeakLua};
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike};
|
||||
use crate::types::{Integer, LuaType, ValueRef};
|
||||
use crate::util::{assert_stack, check_stack, get_metatable_ptr, StackGuard};
|
||||
use crate::types::{Integer, ValueRef};
|
||||
use crate::util::{StackGuard, assert_stack, check_stack, get_metatable_ptr};
|
||||
use crate::value::{Nil, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
@@ -226,15 +384,15 @@ impl Table {
|
||||
// Compare using `__eq` metamethod if exists
|
||||
// First, check the self for the metamethod.
|
||||
// If self does not define it, then check the other table.
|
||||
if let Some(mt) = self.metatable() {
|
||||
if mt.contains_key("__eq")? {
|
||||
return mt.get::<Function>("__eq")?.call((self, other));
|
||||
}
|
||||
if let Some(mt) = self.metatable()
|
||||
&& let Some(eq_func) = mt.get::<Option<Function>>("__eq")?
|
||||
{
|
||||
return eq_func.call((self, other));
|
||||
}
|
||||
if let Some(mt) = other.metatable() {
|
||||
if mt.contains_key("__eq")? {
|
||||
return mt.get::<Function>("__eq")?.call((self, other));
|
||||
}
|
||||
if let Some(mt) = other.metatable()
|
||||
&& let Some(eq_func) = mt.get::<Option<Function>>("__eq")?
|
||||
{
|
||||
return eq_func.call((self, other));
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
@@ -935,10 +1093,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaType for Table {
|
||||
const TYPE_ID: c_int = ffi::LUA_TTABLE;
|
||||
}
|
||||
|
||||
impl ObjectLike for Table {
|
||||
#[inline]
|
||||
fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
|
||||
@@ -1012,7 +1166,7 @@ impl ObjectLike for Table {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_string(&self) -> Result<StdString> {
|
||||
fn to_string(&self) -> Result<String> {
|
||||
Value::Table(Table(self.0.clone())).to_string()
|
||||
}
|
||||
|
||||
@@ -1074,7 +1228,7 @@ impl Serialize for SerializableTable<'_> {
|
||||
where
|
||||
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;
|
||||
|
||||
let convert_result = |res: Result<()>, serialize_err: Option<S::Error>| match res {
|
||||
@@ -1102,7 +1256,7 @@ impl Serialize for SerializableTable<'_> {
|
||||
seq.serialize_element(&SerializableValue::new(&value, options, Some(visited)))
|
||||
.map_err(|err| {
|
||||
serialize_err = Some(err);
|
||||
Error::SerializeError(StdString::new())
|
||||
Error::SerializeError(String::new())
|
||||
})
|
||||
});
|
||||
convert_result(res, serialize_err)?;
|
||||
@@ -1127,7 +1281,7 @@ impl Serialize for SerializableTable<'_> {
|
||||
)
|
||||
.map_err(|err| {
|
||||
serialize_err = Some(err);
|
||||
Error::SerializeError(StdString::new())
|
||||
Error::SerializeError(String::new())
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
+6
-5
@@ -6,7 +6,7 @@ use crate::function::Function;
|
||||
use crate::state::RawLua;
|
||||
use crate::traits::{FromLuaMulti, IntoLuaMulti};
|
||||
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"))]
|
||||
use crate::{
|
||||
@@ -336,22 +336,22 @@ impl Thread {
|
||||
}
|
||||
ThreadStatusInner::Running => Err(Error::runtime("cannot reset a running thread")),
|
||||
ThreadStatusInner::Finished => Ok(()),
|
||||
#[cfg(not(any(feature = "lua54", feature = "luau")))]
|
||||
#[cfg(not(any(feature = "lua55", feature = "lua54", feature = "luau")))]
|
||||
ThreadStatusInner::Yielded(_) | ThreadStatusInner::Error => {
|
||||
Err(Error::runtime("cannot reset non-finished thread"))
|
||||
}
|
||||
#[cfg(any(feature = "lua54", feature = "luau"))]
|
||||
#[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(all(feature = "lua54", feature = "vendored"))]
|
||||
#[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(feature = "lua54")]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
if status != ffi::LUA_OK {
|
||||
return Err(pop_error(thread_state, status));
|
||||
}
|
||||
@@ -523,6 +523,7 @@ impl<R> AsyncThread<R> {
|
||||
#[cfg(feature = "async")]
|
||||
impl<R> Drop for AsyncThread<R> {
|
||||
fn drop(&mut self) {
|
||||
#[allow(clippy::collapsible_if)]
|
||||
if self.recycle {
|
||||
if let Some(lua) = self.thread.0.lua.try_lock() {
|
||||
unsafe {
|
||||
|
||||
+2
-3
@@ -1,5 +1,4 @@
|
||||
use std::os::raw::c_int;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
@@ -236,7 +235,7 @@ pub trait ObjectLike: Sealed {
|
||||
/// Converts the object to a string in a human-readable format.
|
||||
///
|
||||
/// 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;
|
||||
@@ -339,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 {
|
||||
#[inline(always)]
|
||||
fn type_name() -> StdString {
|
||||
fn type_name() -> String {
|
||||
short_type_name::<Self>()
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -108,10 +108,12 @@ pub(crate) type ThreadCollectionCallback = XRc<dyn Fn(crate::LightUserData) + Se
|
||||
#[cfg(all(not(feature = "send"), feature = "luau"))]
|
||||
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>;
|
||||
|
||||
#[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<()>>;
|
||||
|
||||
/// A trait that adds `Send` requirement if `send` feature is enabled.
|
||||
@@ -120,6 +122,7 @@ pub trait MaybeSend: Send {}
|
||||
#[cfg(feature = "send")]
|
||||
impl<T: Send> MaybeSend for T {}
|
||||
|
||||
/// A trait that adds `Send` requirement if `send` feature is enabled.
|
||||
#[cfg(not(feature = "send"))]
|
||||
pub trait MaybeSend {}
|
||||
#[cfg(not(feature = "send"))]
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ mod inner {
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0 .0
|
||||
&self.0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,10 +55,10 @@ impl Drop for ValueRef {
|
||||
if let Some(ValueRefIndex(index)) = self.index_count.take() {
|
||||
// It's guaranteed that the inner value returns exactly once.
|
||||
// This means in particular that the value is not dropped.
|
||||
if XRc::into_inner(index).is_some() {
|
||||
if let Some(lua) = self.lua.try_lock() {
|
||||
unsafe { lua.drop_ref(self) };
|
||||
}
|
||||
if XRc::into_inner(index).is_some()
|
||||
&& let Some(lua) = self.lua.try_lock()
|
||||
{
|
||||
unsafe { lua.drop_ref(self) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+159
-60
@@ -3,16 +3,15 @@ use std::ffi::CStr;
|
||||
use std::fmt;
|
||||
use std::hash::Hash;
|
||||
use std::os::raw::{c_char, c_void};
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::Lua;
|
||||
use crate::string::String;
|
||||
use crate::string::LuaString;
|
||||
use crate::table::{Table, TablePairs};
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
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;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
@@ -30,8 +29,8 @@ pub use r#ref::{UserDataRef, UserDataRefMut};
|
||||
pub use registry::UserDataRegistry;
|
||||
pub(crate) use registry::{RawUserDataRegistry, UserDataProxy};
|
||||
pub(crate) use util::{
|
||||
borrow_userdata_scoped, borrow_userdata_scoped_mut, collect_userdata, init_userdata_metatable,
|
||||
TypeIdHints,
|
||||
TypeIdHints, borrow_userdata_scoped, borrow_userdata_scoped_mut, collect_userdata,
|
||||
init_userdata_metatable,
|
||||
};
|
||||
|
||||
/// Kinds of metamethods that can be overridden.
|
||||
@@ -56,32 +55,53 @@ pub enum MetaMethod {
|
||||
/// The unary minus (`-`) operator.
|
||||
Unm,
|
||||
/// The floor division (//) operator.
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(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 = "lua55", feature = "lua54", feature = "lua53", feature = "luau")))
|
||||
)]
|
||||
IDiv,
|
||||
/// The bitwise AND (&) operator.
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53"))))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(feature = "lua55", feature = "lua54", feature = "lua53")))
|
||||
)]
|
||||
BAnd,
|
||||
/// The bitwise OR (|) operator.
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53"))))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(feature = "lua55", feature = "lua54", feature = "lua53")))
|
||||
)]
|
||||
BOr,
|
||||
/// The bitwise XOR (binary ~) operator.
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53"))))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(feature = "lua55", feature = "lua54", feature = "lua53")))
|
||||
)]
|
||||
BXor,
|
||||
/// The bitwise NOT (unary ~) operator.
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53"))))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(feature = "lua55", feature = "lua54", feature = "lua53")))
|
||||
)]
|
||||
BNot,
|
||||
/// The bitwise left shift (<<) operator.
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53"))))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(feature = "lua55", feature = "lua54", feature = "lua53")))
|
||||
)]
|
||||
Shl,
|
||||
/// The bitwise right shift (>>) operator.
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53"))))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(feature = "lua55", feature = "lua54", feature = "lua53")))
|
||||
)]
|
||||
Shr,
|
||||
/// The string concatenation operator `..`.
|
||||
Concat,
|
||||
@@ -106,10 +126,22 @@ pub enum MetaMethod {
|
||||
/// The `__pairs` metamethod.
|
||||
///
|
||||
/// 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(
|
||||
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,
|
||||
/// The `__ipairs` metamethod.
|
||||
@@ -135,8 +167,8 @@ pub enum MetaMethod {
|
||||
/// [documentation][lua_doc].
|
||||
///
|
||||
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#3.3.8
|
||||
#[cfg(feature = "lua54")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua55", feature = "lua54"))))]
|
||||
Close,
|
||||
/// 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 {
|
||||
self == other.name()
|
||||
}
|
||||
@@ -176,19 +208,19 @@ impl MetaMethod {
|
||||
MetaMethod::Pow => "__pow",
|
||||
MetaMethod::Unm => "__unm",
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||
MetaMethod::IDiv => "__idiv",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||
MetaMethod::BAnd => "__band",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||
MetaMethod::BOr => "__bor",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||
MetaMethod::BXor => "__bxor",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||
MetaMethod::BNot => "__bnot",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||
MetaMethod::Shl => "__shl",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||
MetaMethod::Shr => "__shr",
|
||||
|
||||
MetaMethod::Concat => "__concat",
|
||||
@@ -201,14 +233,20 @@ impl MetaMethod {
|
||||
MetaMethod::Call => "__call",
|
||||
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",
|
||||
#[cfg(any(feature = "lua52", feature = "luajit52"))]
|
||||
MetaMethod::IPairs => "__ipairs",
|
||||
#[cfg(feature = "luau")]
|
||||
MetaMethod::Iter => "__iter",
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
MetaMethod::Close => "__close",
|
||||
|
||||
#[rustfmt::skip]
|
||||
@@ -240,7 +278,7 @@ impl AsRef<str> for MetaMethod {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MetaMethod> for StdString {
|
||||
impl From<MetaMethod> for String {
|
||||
#[inline]
|
||||
fn from(method: MetaMethod) -> Self {
|
||||
method.name().to_owned()
|
||||
@@ -256,7 +294,7 @@ pub trait UserDataMethods<T> {
|
||||
///
|
||||
/// 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.
|
||||
fn add_method<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_method<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -267,12 +305,35 @@ pub trait UserDataMethods<T> {
|
||||
/// Refer to [`add_method`] for more information about the implementation.
|
||||
///
|
||||
/// [`add_method`]: UserDataMethods::add_method
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
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`].
|
||||
///
|
||||
/// Refer to [`add_method`] for more information about the implementation.
|
||||
@@ -280,7 +341,7 @@ pub trait UserDataMethods<T> {
|
||||
/// [`add_method`]: UserDataMethods::add_method
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -295,7 +356,7 @@ pub trait UserDataMethods<T> {
|
||||
/// [`add_method`]: UserDataMethods::add_method
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -303,12 +364,40 @@ pub trait UserDataMethods<T> {
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
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.
|
||||
///
|
||||
/// 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
|
||||
/// argument: `my_userdata.my_method(my_userdata, arg1, arg2)`.
|
||||
fn add_function<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_function<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -319,7 +408,7 @@ pub trait UserDataMethods<T> {
|
||||
/// This is a version of [`add_function`] that accepts a `FnMut` argument.
|
||||
///
|
||||
/// [`add_function`]: UserDataMethods::add_function
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -333,7 +422,7 @@ pub trait UserDataMethods<T> {
|
||||
/// [`add_function`]: UserDataMethods::add_function
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -348,7 +437,7 @@ pub trait UserDataMethods<T> {
|
||||
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
||||
///
|
||||
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -362,7 +451,7 @@ pub trait UserDataMethods<T> {
|
||||
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
||||
///
|
||||
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -378,7 +467,7 @@ pub trait UserDataMethods<T> {
|
||||
docsrs,
|
||||
doc(cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau")))))
|
||||
)]
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -394,7 +483,7 @@ pub trait UserDataMethods<T> {
|
||||
/// [`add_meta_method_mut`]: UserDataMethods::add_meta_method_mut
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -407,7 +496,7 @@ pub trait UserDataMethods<T> {
|
||||
/// Metamethods for binary operators can be triggered if either the left or right argument to
|
||||
/// the binary operator has a metatable, so the first argument here is not necessarily a
|
||||
/// userdata of type `T`.
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -418,7 +507,7 @@ pub trait UserDataMethods<T> {
|
||||
/// This is a version of [`add_meta_function`] that accepts a `FnMut` argument.
|
||||
///
|
||||
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -434,7 +523,7 @@ pub trait UserDataMethods<T> {
|
||||
docsrs,
|
||||
doc(cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau")))))
|
||||
)]
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -453,7 +542,7 @@ pub trait UserDataFields<T> {
|
||||
///
|
||||
/// 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.
|
||||
fn add_field<V>(&mut self, name: impl Into<StdString>, value: V)
|
||||
fn add_field<V>(&mut self, name: impl Into<String>, value: V)
|
||||
where
|
||||
V: IntoLua + 'static;
|
||||
|
||||
@@ -464,7 +553,7 @@ pub trait UserDataFields<T> {
|
||||
///
|
||||
/// If `add_meta_method` is used to set the `__index` metamethod, the `__index` metamethod will
|
||||
/// be used as a fall-back if no regular field or method are found.
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua;
|
||||
@@ -477,21 +566,21 @@ pub trait UserDataFields<T> {
|
||||
///
|
||||
/// If `add_meta_method` is used to set the `__newindex` metamethod, the `__newindex` metamethod
|
||||
/// will be used as a fall-back if no regular field is found.
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua;
|
||||
|
||||
/// Add a regular field getter as a function which accepts a generic [`AnyUserData`] of type `T`
|
||||
/// argument.
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua;
|
||||
|
||||
/// Add a regular field setter as a function which accepts a generic [`AnyUserData`] of type `T`
|
||||
/// first argument.
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua;
|
||||
@@ -504,7 +593,7 @@ pub trait UserDataFields<T> {
|
||||
///
|
||||
/// `mlua` will trigger an error on an attempt to define a protected metamethod,
|
||||
/// like `__gc` or `__metatable`.
|
||||
fn add_meta_field<V>(&mut self, name: impl Into<StdString>, value: V)
|
||||
fn add_meta_field<V>(&mut self, name: impl Into<String>, value: V)
|
||||
where
|
||||
V: IntoLua + 'static;
|
||||
|
||||
@@ -516,7 +605,7 @@ pub trait UserDataFields<T> {
|
||||
///
|
||||
/// `mlua` will trigger an error on an attempt to define a protected metamethod,
|
||||
/// like `__gc` or `__metatable`.
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl Into<StdString>, f: F)
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl Into<String>, f: F)
|
||||
where
|
||||
F: FnOnce(&Lua) -> Result<R> + 'static,
|
||||
R: IntoLua;
|
||||
@@ -628,6 +717,14 @@ impl AnyUserData {
|
||||
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`.
|
||||
///
|
||||
/// # Errors
|
||||
@@ -921,8 +1018,10 @@ impl AnyUserData {
|
||||
lua.get_userdata_ref_type_id(&self.0).ok().flatten()
|
||||
}
|
||||
|
||||
/// Returns a type name of this `UserData` (from a metatable field).
|
||||
pub(crate) fn type_name(&self) -> Result<Option<StdString>> {
|
||||
/// Returns a type name of this userdata (from a metatable field).
|
||||
///
|
||||
/// If no type name is set, returns `None`.
|
||||
pub fn type_name(&self) -> Result<Option<String>> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
@@ -939,7 +1038,7 @@ impl AnyUserData {
|
||||
ffi::luaL_getmetafield(state, -1, MetaMethod::Type.as_cstr().as_ptr())
|
||||
};
|
||||
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),
|
||||
}
|
||||
}
|
||||
@@ -1026,13 +1125,13 @@ impl UserDataMetatable {
|
||||
/// It skips restricted metamethods, such as `__gc` or `__metatable`.
|
||||
///
|
||||
/// 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>
|
||||
where
|
||||
V: FromLua,
|
||||
{
|
||||
type Item = Result<(StdString, V)>;
|
||||
type Item = Result<(String, V)>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
|
||||
@@ -173,10 +173,10 @@ pub(crate) enum ScopedUserDataVariant<T> {
|
||||
impl<T> Drop for ScopedUserDataVariant<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
if let Self::Boxed(value) = self {
|
||||
if let Ok(value) = value.try_borrow_mut() {
|
||||
unsafe { drop(Box::from_raw(*value)) };
|
||||
}
|
||||
if let Self::Boxed(value) = self
|
||||
&& let Ok(value) = value.try_borrow_mut()
|
||||
{
|
||||
unsafe { drop(Box::from_raw(*value)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::Function;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::WeakLua;
|
||||
use crate::table::Table;
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike};
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::value::Value;
|
||||
use crate::Function;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use crate::function::AsyncCallFuture;
|
||||
@@ -88,7 +86,7 @@ impl ObjectLike for AnyUserData {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_string(&self) -> Result<StdString> {
|
||||
fn to_string(&self) -> Result<String> {
|
||||
Value::UserData(self.clone()).to_string()
|
||||
}
|
||||
|
||||
|
||||
+6
-6
@@ -1,4 +1,4 @@
|
||||
use std::any::{type_name, TypeId};
|
||||
use std::any::{TypeId, type_name};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::c_int;
|
||||
use std::{fmt, mem};
|
||||
@@ -446,11 +446,11 @@ impl<T> DerefMut for UserDataRefMutInner<T> {
|
||||
fn try_value_to_userdata<T>(value: Value) -> Result<AnyUserData> {
|
||||
match value {
|
||||
Value::UserData(ud) => Ok(ud),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "userdata".to_string(),
|
||||
message: Some(format!("expected userdata of type {}", type_name::<T>())),
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(
|
||||
value.type_name(),
|
||||
"userdata",
|
||||
format!("expected userdata of type {}", type_name::<T>()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+26
-27
@@ -4,15 +4,14 @@ use std::any::TypeId;
|
||||
use std::cell::RefCell;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_void;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::{Lua, LuaGuard};
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
use crate::types::{Callback, MaybeSend};
|
||||
use crate::userdata::{
|
||||
borrow_userdata_scoped, borrow_userdata_scoped_mut, AnyUserData, MetaMethod, TypeIdHints, UserData,
|
||||
UserDataFields, UserDataMethods, UserDataStorage,
|
||||
AnyUserData, MetaMethod, TypeIdHints, UserData, UserDataFields, UserDataMethods, UserDataStorage,
|
||||
borrow_userdata_scoped, borrow_userdata_scoped_mut,
|
||||
};
|
||||
use crate::util::short_type_name;
|
||||
use crate::value::Value;
|
||||
@@ -55,7 +54,7 @@ pub(crate) struct RawUserDataRegistry {
|
||||
|
||||
pub(crate) destructor: ffi::lua_CFunction,
|
||||
pub(crate) type_id: Option<TypeId>,
|
||||
pub(crate) type_name: StdString,
|
||||
pub(crate) type_name: String,
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
pub(crate) enable_namecall: bool,
|
||||
@@ -368,7 +367,7 @@ impl<T> UserDataRegistry<T> {
|
||||
method: name.to_string(),
|
||||
type_name: value.type_name(),
|
||||
message: Some("expected nil, table or function".to_string()),
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -382,12 +381,12 @@ impl<T> UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
// 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>())
|
||||
}
|
||||
|
||||
impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
fn add_field<V>(&mut self, name: impl Into<StdString>, value: V)
|
||||
fn add_field<V>(&mut self, name: impl Into<String>, value: V)
|
||||
where
|
||||
V: IntoLua + 'static,
|
||||
{
|
||||
@@ -395,7 +394,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
self.raw.fields.push((name, value.into_lua(self.lua.lua())));
|
||||
}
|
||||
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua,
|
||||
@@ -405,7 +404,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
self.raw.field_getters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua,
|
||||
@@ -415,7 +414,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
self.raw.field_setters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua,
|
||||
@@ -425,7 +424,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
self.raw.field_getters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl Into<StdString>, mut function: F)
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl Into<String>, mut function: F)
|
||||
where
|
||||
F: FnMut(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua,
|
||||
@@ -435,7 +434,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
self.raw.field_setters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_field<V>(&mut self, name: impl Into<StdString>, value: V)
|
||||
fn add_meta_field<V>(&mut self, name: impl Into<String>, value: V)
|
||||
where
|
||||
V: IntoLua + 'static,
|
||||
{
|
||||
@@ -445,7 +444,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
self.raw.meta_fields.push((name, field));
|
||||
}
|
||||
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl Into<StdString>, f: F)
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl Into<String>, f: F)
|
||||
where
|
||||
F: FnOnce(&Lua) -> Result<R> + 'static,
|
||||
R: IntoLua,
|
||||
@@ -458,7 +457,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
fn add_method<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_method<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -469,7 +468,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
self.raw.methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -481,7 +480,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -495,7 +494,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -508,7 +507,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
self.raw.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_function<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_function<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -519,7 +518,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
self.raw.methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -531,7 +530,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -543,7 +542,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
self.raw.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -554,7 +553,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
self.raw.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -566,7 +565,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -580,7 +579,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -593,7 +592,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
self.raw.async_meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -604,7 +603,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
self.raw.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -616,7 +615,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
|
||||
+20
-14
@@ -2,15 +2,15 @@ use std::any::Any;
|
||||
use std::fmt::Write as _;
|
||||
use std::mem::MaybeUninit;
|
||||
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::sync::Arc;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::memory::MemoryState;
|
||||
use crate::util::{
|
||||
check_stack, get_internal_userdata, init_internal_metatable, push_internal_userdata, push_string,
|
||||
push_table, rawset_field, to_string, TypeKey, DESTRUCTED_USERDATA_METATABLE,
|
||||
DESTRUCTED_USERDATA_METATABLE, TypeKey, check_stack, get_internal_userdata, init_internal_metatable,
|
||||
push_internal_userdata, push_string, push_table, rawset_field, to_string,
|
||||
};
|
||||
|
||||
static WRAPPED_FAILURE_TYPE_KEY: u8 = 0;
|
||||
@@ -208,7 +208,7 @@ where
|
||||
F: FnOnce(*mut ffi::lua_State) -> R,
|
||||
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);
|
||||
|
||||
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);
|
||||
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 {
|
||||
// `LUA_OK` is only returned when the `do_call` function has completed successfully, so
|
||||
@@ -373,19 +373,19 @@ pub(crate) unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(
|
||||
"__mod",
|
||||
"__pow",
|
||||
"__unm",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||
"__idiv",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||
"__band",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||
"__bor",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||
"__bxor",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||
"__bnot",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||
"__shl",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||
"__shr",
|
||||
"__concat",
|
||||
"__len",
|
||||
@@ -396,7 +396,13 @@ pub(crate) unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(
|
||||
"__newindex",
|
||||
"__call",
|
||||
"__tostring",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit52"))]
|
||||
#[cfg(any(
|
||||
feature = "lua55",
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "luajit52"
|
||||
))]
|
||||
"__pairs",
|
||||
#[cfg(any(feature = "lua53", feature = "lua52", feature = "luajit52"))]
|
||||
"__ipairs",
|
||||
@@ -404,7 +410,7 @@ pub(crate) unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(
|
||||
"__iter",
|
||||
#[cfg(feature = "luau")]
|
||||
"__namecall",
|
||||
#[cfg(feature = "lua54")]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
"__close",
|
||||
] {
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
|
||||
+39
-11
@@ -6,16 +6,16 @@ use std::{ptr, slice, str};
|
||||
use crate::error::{Error, Result};
|
||||
|
||||
pub(crate) use error::{
|
||||
error_traceback, error_traceback_thread, init_error_registry, pop_error, protect_lua_call,
|
||||
protect_lua_closure, WrappedFailure,
|
||||
WrappedFailure, error_traceback, error_traceback_thread, init_error_registry, pop_error,
|
||||
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 types::TypeKey;
|
||||
pub(crate) use userdata::{
|
||||
get_destructed_userdata_metatable, get_internal_metatable, get_internal_userdata, get_userdata,
|
||||
init_internal_metatable, push_internal_userdata, push_userdata, take_userdata,
|
||||
DESTRUCTED_USERDATA_METATABLE,
|
||||
DESTRUCTED_USERDATA_METATABLE, get_destructed_userdata_metatable, get_internal_metatable,
|
||||
get_internal_userdata, get_userdata, init_internal_metatable, push_internal_userdata, push_userdata,
|
||||
take_userdata,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
@@ -99,6 +99,38 @@ 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.
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline(always)]
|
||||
@@ -220,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.
|
||||
// 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> {
|
||||
#[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);
|
||||
let main_state = ffi::lua_tothread(state, -1);
|
||||
@@ -232,11 +264,7 @@ pub(crate) unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut f
|
||||
// Check the current state first
|
||||
let is_main_state = ffi::lua_pushthread(state) == 1;
|
||||
ffi::lua_pop(state, 1);
|
||||
if is_main_state {
|
||||
Some(state)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
if is_main_state { Some(state) } else { None }
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
Some(ffi::lua_mainthread(state))
|
||||
|
||||
+1
-1
@@ -196,7 +196,7 @@ fn unquote_string<'a>(path: &'a str, chars: &mut Peekable<CharIndices<'a>>) -> R
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{parse_path, PathKey};
|
||||
use super::{PathKey, parse_path};
|
||||
|
||||
#[test]
|
||||
fn test_parse_path() {
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::{mem, ptr};
|
||||
|
||||
use crate::error::Result;
|
||||
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.
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
|
||||
+15
-16
@@ -1,19 +1,18 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashSet;
|
||||
use std::os::raw::c_void;
|
||||
use std::string::String as StdString;
|
||||
use std::{fmt, ptr, str};
|
||||
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::string::{BorrowedStr, String};
|
||||
use crate::string::{BorrowedStr, LuaString};
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::types::{Integer, LightUserData, Number, ValueRef};
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::util::{check_stack, StackGuard};
|
||||
use crate::util::{StackGuard, check_stack};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use {
|
||||
@@ -50,7 +49,7 @@ pub enum Value {
|
||||
/// An interned string, managed by Lua.
|
||||
///
|
||||
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
|
||||
String(String),
|
||||
String(LuaString),
|
||||
/// Reference to a Lua table.
|
||||
Table(Table),
|
||||
/// Reference to a Lua function (or closure).
|
||||
@@ -129,7 +128,7 @@ impl Value {
|
||||
#[inline]
|
||||
pub fn to_pointer(&self) -> *const c_void {
|
||||
match self {
|
||||
Value::String(String(vref)) => {
|
||||
Value::String(LuaString(vref)) => {
|
||||
// In Lua < 5.4 (excluding Luau), string pointers are NULL
|
||||
// Use alternative approach
|
||||
let lua = vref.lua.lock();
|
||||
@@ -151,8 +150,8 @@ impl Value {
|
||||
///
|
||||
/// This might invoke the `__tostring` metamethod for non-primitive types (eg. tables,
|
||||
/// functions).
|
||||
pub fn to_string(&self) -> Result<StdString> {
|
||||
unsafe fn invoke_to_string(vref: &ValueRef) -> Result<StdString> {
|
||||
pub fn to_string(&self) -> Result<String> {
|
||||
unsafe fn invoke_to_string(vref: &ValueRef) -> Result<String> {
|
||||
let lua = vref.lua.lock();
|
||||
let state = lua.state();
|
||||
let _guard = StackGuard::new(state);
|
||||
@@ -162,7 +161,7 @@ impl Value {
|
||||
protect_lua!(state, 1, 1, fn(state) {
|
||||
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 {
|
||||
@@ -336,17 +335,17 @@ impl Value {
|
||||
self.as_number()
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a Lua [`String`].
|
||||
/// Returns `true` if the value is a [`LuaString`].
|
||||
#[inline]
|
||||
pub fn is_string(&self) -> bool {
|
||||
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]
|
||||
pub fn as_string(&self) -> Option<&String> {
|
||||
pub fn as_string(&self) -> Option<&LuaString> {
|
||||
match self {
|
||||
Value::String(s) => Some(s),
|
||||
_ => None,
|
||||
@@ -355,7 +354,7 @@ impl Value {
|
||||
|
||||
/// 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.
|
||||
#[deprecated(
|
||||
since = "0.11.0",
|
||||
@@ -366,15 +365,15 @@ impl Value {
|
||||
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]
|
||||
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())
|
||||
}
|
||||
|
||||
|
||||
+6
-6
@@ -1,11 +1,11 @@
|
||||
[lua54]
|
||||
features = "lua54,vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||
[lua55]
|
||||
features = "lua55,vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||
|
||||
[lua54_non_send]
|
||||
features = "lua54,vendored,async,serde,macros,anyhow,userdata-wrappers"
|
||||
[lua55_non_send]
|
||||
features = "lua55,vendored,async,serde,macros,anyhow,userdata-wrappers"
|
||||
|
||||
[lua54_with_memory_limit]
|
||||
features = "lua54,vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||
[lua55_with_memory_limit]
|
||||
features = "lua55,vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||
rustflags = "--cfg force_memory_limit"
|
||||
|
||||
[lua51]
|
||||
|
||||
+27
-8
@@ -1,6 +1,5 @@
|
||||
#![cfg(feature = "async")]
|
||||
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -40,7 +39,7 @@ async fn test_async_function() -> Result<()> {
|
||||
async fn test_async_function_wrap() -> Result<()> {
|
||||
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;
|
||||
Ok(s)
|
||||
});
|
||||
@@ -68,7 +67,7 @@ async fn test_async_function_wrap() -> Result<()> {
|
||||
async fn test_async_function_wrap_raw() -> Result<()> {
|
||||
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;
|
||||
s
|
||||
});
|
||||
@@ -249,7 +248,7 @@ async fn test_async_return_async_closure() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
#[tokio::test]
|
||||
async fn test_async_lua54_to_be_closed() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -423,9 +422,9 @@ async fn test_async_thread_pool() -> Result<()> {
|
||||
|
||||
#[tokio::test]
|
||||
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) {
|
||||
methods.add_async_method("get_value", |_, data, ()| async move {
|
||||
sleep_ms(10).await;
|
||||
@@ -438,6 +437,11 @@ async fn test_async_userdata() -> Result<()> {
|
||||
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 {
|
||||
sleep_ms(n).await;
|
||||
Ok(format!("elapsed:{}ms", n))
|
||||
@@ -479,7 +483,7 @@ async fn test_async_userdata() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
let userdata = lua.create_userdata(MyUserData(11))?;
|
||||
let userdata = lua.create_userdata(MyUserdata(11))?;
|
||||
globals.set("userdata", &userdata)?;
|
||||
|
||||
lua.load(
|
||||
@@ -518,6 +522,21 @@ async fn test_async_userdata() -> Result<()> {
|
||||
#[cfg(not(any(feature = "lua51", feature = "luau")))]
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -650,7 +669,7 @@ async fn test_async_hook() -> Result<()> {
|
||||
static HOOK_CALLED: AtomicBool = AtomicBool::new(false);
|
||||
lua.set_global_hook(mlua::HookTriggers::new().every_line(), move |_, _| {
|
||||
if !HOOK_CALLED.swap(true, Ordering::Relaxed) {
|
||||
#[cfg(any(feature = "lu53", feature = "lua54"))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||
return Ok(mlua::VmState::Yield);
|
||||
}
|
||||
Ok(mlua::VmState::Continue)
|
||||
|
||||
@@ -21,6 +21,7 @@ fn test_chunk_methods() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_os = "wasi"))]
|
||||
fn test_chunk_path() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
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
|
||||
|
|
||||
9 | reg.add_async_method("t", |_, this, ()| async {
|
||||
| ^^^^^ cannot borrow as mutable
|
||||
8 | let mut s = &s;
|
||||
| ----- `s` declared here, outside the closure
|
||||
9 | reg.add_async_method("t", |_, this, ()| async {
|
||||
| ------------- ^^^^^ cannot borrow as mutable
|
||||
| |
|
||||
| in this closure
|
||||
10 | s = &*this;
|
||||
| - 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
|
||||
--> 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`
|
||||
10 | s = &*this;
|
||||
| ---- `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
|
||||
--> 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;
|
||||
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
|
||||
|
|
||||
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
|
||||
--> 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`
|
||||
@@ -46,22 +50,28 @@ error: lifetime may not live long enough
|
||||
error[E0597]: `s` does not live long enough
|
||||
--> tests/compile/async_any_userdata_method.rs:8:21
|
||||
|
|
||||
7 | let s = String::new();
|
||||
7 | let s = String::new();
|
||||
| - binding `s` declared here
|
||||
8 | let mut s = &s;
|
||||
8 | let mut s = &s;
|
||||
| ^^ 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;
|
||||
11 | | Ok(())
|
||||
12 | | });
|
||||
| |__________- argument requires that `s` is borrowed for `'static`
|
||||
13 | })
|
||||
| - `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
|
||||
--> 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`
|
||||
10 | s = &*this;
|
||||
| - `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`
|
||||
--> 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;
|
||||
11 | | Ok(())
|
||||
12 | | });
|
||||
| |__________^
|
||||
help: to force the closure to take ownership of `s` (and any other referenced variables), use the `move` keyword
|
||||
|
|
||||
9 | reg.add_async_method("t", move |_, this, ()| async {
|
||||
9 | reg.add_async_method("t", move |_, this, ()| async {
|
||||
| ++++
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
error: lifetime may not live long enough
|
||||
--> 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
|
||||
8 | fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
9 | / methods.add_async_method("print", |_, data, ()| async move {
|
||||
8 | fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
9 | / methods.add_async_method("print", |_, data, ()| async move {
|
||||
10 | | println!("{}", data.0);
|
||||
11 | | Ok(())
|
||||
12 | | });
|
||||
|
||||
@@ -1,28 +1,32 @@
|
||||
error[E0277]: the type `UnsafeCell<*mut lua_State>` 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
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<*mut lua_State>` 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
|
||||
|
|
||||
= help: within `mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<*mut lua_State>`
|
||||
note: required because it appears within the type `Cell<*mut lua_State>`
|
||||
--> $RUST/core/src/cell.rs
|
||||
= 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>`
|
||||
--> $CARGO/lock_api-$VERSION/src/remutex.rs
|
||||
|
|
||||
| pub struct Cell<T: ?Sized> {
|
||||
| ^^^^
|
||||
note: required because it appears within the type `mlua::state::raw::RawLua`
|
||||
--> src/state/raw.rs
|
||||
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct RawLua {
|
||||
| ^^^^^^
|
||||
note: required because it appears within the type `mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>`
|
||||
--> src/types/sync.rs
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
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
|
||||
|
|
||||
| pub(crate) struct ReentrantMutex<T>(T);
|
||||
| ^^^^^^^^^^^^^^
|
||||
= note: required for `Rc<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>` to implement `RefUnwindSafe`
|
||||
| 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>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct Arc<
|
||||
| ^^^
|
||||
note: required because it appears within the type `Lua`
|
||||
--> src/state.rs
|
||||
|
|
||||
@@ -40,27 +44,45 @@ note: required by a bound in `std::panic::catch_unwind`
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ^^^^^^^^^^ required by this bound in `catch_unwind`
|
||||
|
||||
error[E0277]: the type `UnsafeCell<mlua::state::extra::ExtraData>` 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
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::extra::ExtraData>` 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
|
||||
|
|
||||
= help: the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::extra::ExtraData>`
|
||||
= note: required for `Rc<UnsafeCell<mlua::state::extra::ExtraData>>` to implement `RefUnwindSafe`
|
||||
note: required because it appears within the type `mlua::state::raw::RawLua`
|
||||
--> src/state/raw.rs
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<usize>`
|
||||
note: required because it appears within the type `Cell<usize>`
|
||||
--> $RUST/core/src/cell.rs
|
||||
|
|
||||
| pub struct RawLua {
|
||||
| ^^^^^^
|
||||
note: required because it appears within the type `mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>`
|
||||
--> src/types/sync.rs
|
||||
| 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>`
|
||||
--> $CARGO/lock_api-$VERSION/src/remutex.rs
|
||||
|
|
||||
| pub(crate) struct ReentrantMutex<T>(T);
|
||||
| ^^^^^^^^^^^^^^
|
||||
= note: required for `Rc<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>` to implement `RefUnwindSafe`
|
||||
| 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>`
|
||||
--> $CARGO/lock_api-$VERSION/src/remutex.rs
|
||||
|
|
||||
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
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
|
||||
|
|
||||
| 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>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct Arc<
|
||||
| ^^^
|
||||
note: required because it appears within the type `Lua`
|
||||
--> src/state.rs
|
||||
|
|
||||
|
||||
@@ -1,143 +1,86 @@
|
||||
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<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
|
||||
|
|
||||
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<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
|
||||
|
|
||||
= help: within `rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<usize>`
|
||||
= 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>`
|
||||
--> $CARGO/lock_api-$VERSION/src/remutex.rs
|
||||
|
|
||||
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
= note: required for `NonNull<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `std::sync::Weak<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct Weak<
|
||||
| ^^^^
|
||||
note: required because it appears within the type `WeakLua`
|
||||
--> src/state.rs
|
||||
|
|
||||
| pub struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
|
||||
| ^^^^^^^
|
||||
note: required because it appears within the type `mlua::types::value_ref::ValueRef`
|
||||
--> src/types/value_ref.rs
|
||||
|
|
||||
| pub struct ValueRef {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `LuaTable`
|
||||
--> src/table.rs
|
||||
|
|
||||
| pub struct Table(pub(crate) ValueRef);
|
||||
| ^^^^^
|
||||
note: required because it's used within this closure
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ^^^^^^^
|
||||
note: required by a bound in `std::panic::catch_unwind`
|
||||
--> $RUST/std/src/panic.rs
|
||||
|
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ^^^^^^^^^^ required by this bound in `catch_unwind`
|
||||
|
||||
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
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `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
|
||||
|
|
||||
= 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>`
|
||||
--> $RUST/core/src/cell.rs
|
||||
|
|
||||
| pub struct Cell<T: ?Sized> {
|
||||
| ^^^^
|
||||
note: required because it appears within the type `rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
note: required because it appears within the type `lock_api::remutex::RawReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId>`
|
||||
--> $CARGO/lock_api-$VERSION/src/remutex.rs
|
||||
|
|
||||
| struct RcInner<T: ?Sized> {
|
||||
| ^^^^^^^
|
||||
= note: required for `NonNull<rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `std::rc::Weak<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| pub struct Weak<
|
||||
| ^^^^
|
||||
note: required because it appears within the type `WeakLua`
|
||||
--> src/state.rs
|
||||
|
|
||||
| pub struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
|
||||
| ^^^^^^^
|
||||
note: required because it appears within the type `mlua::types::value_ref::ValueRef`
|
||||
--> src/types/value_ref.rs
|
||||
|
|
||||
| pub struct ValueRef {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `LuaTable`
|
||||
--> src/table.rs
|
||||
|
|
||||
| pub struct Table(pub(crate) ValueRef);
|
||||
| ^^^^^
|
||||
note: required because it's used within this closure
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ^^^^^^^
|
||||
note: required by a bound in `std::panic::catch_unwind`
|
||||
--> $RUST/std/src/panic.rs
|
||||
|
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ^^^^^^^^^^ required by this bound in `catch_unwind`
|
||||
|
||||
error[E0277]: the type `UnsafeCell<*mut lua_State>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<*mut lua_State>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: within `rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<*mut lua_State>`
|
||||
note: required because it appears within the type `Cell<*mut lua_State>`
|
||||
--> $RUST/core/src/cell.rs
|
||||
|
|
||||
| pub struct Cell<T: ?Sized> {
|
||||
| ^^^^
|
||||
note: required because it appears within the type `mlua::state::raw::RawLua`
|
||||
--> src/state/raw.rs
|
||||
|
|
||||
| pub struct RawLua {
|
||||
| ^^^^^^
|
||||
note: required because it appears within the type `mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>`
|
||||
--> src/types/sync.rs
|
||||
|
|
||||
| pub(crate) struct ReentrantMutex<T>(T);
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| struct RcInner<T: ?Sized> {
|
||||
| ^^^^^^^
|
||||
= note: required for `NonNull<rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `std::rc::Weak<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| pub struct Weak<
|
||||
| ^^^^
|
||||
note: required because it appears within the type `WeakLua`
|
||||
--> src/state.rs
|
||||
|
|
||||
| pub struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
|
||||
| ^^^^^^^
|
||||
note: required because it appears within the type `mlua::types::value_ref::ValueRef`
|
||||
--> src/types/value_ref.rs
|
||||
|
|
||||
| pub struct ValueRef {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `LuaTable`
|
||||
--> src/table.rs
|
||||
|
|
||||
| pub struct Table(pub(crate) ValueRef);
|
||||
| ^^^^^
|
||||
note: required because it's used within this closure
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ^^^^^^^
|
||||
note: required by a bound in `std::panic::catch_unwind`
|
||||
--> $RUST/std/src/panic.rs
|
||||
|
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ^^^^^^^^^^ required by this bound in `catch_unwind`
|
||||
|
||||
error[E0277]: the type `UnsafeCell<mlua::state::extra::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::extra::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::extra::ExtraData>`
|
||||
= note: required for `Rc<UnsafeCell<mlua::state::extra::ExtraData>>` to implement `RefUnwindSafe`
|
||||
note: required because it appears within the type `mlua::state::raw::RawLua`
|
||||
--> src/state/raw.rs
|
||||
|
|
||||
| pub struct RawLua {
|
||||
| ^^^^^^
|
||||
note: required because it appears within the type `mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>`
|
||||
--> src/types/sync.rs
|
||||
|
|
||||
| pub(crate) struct ReentrantMutex<T>(T);
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| struct RcInner<T: ?Sized> {
|
||||
| ^^^^^^^
|
||||
= note: required for `NonNull<rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `std::rc::Weak<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
| 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>`
|
||||
--> $CARGO/lock_api-$VERSION/src/remutex.rs
|
||||
|
|
||||
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
= note: required for `NonNull<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `std::sync::Weak<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct Weak<
|
||||
| ^^^^
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
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
|
||||
|
|
||||
5 | lua.scope(|scope| {
|
||||
5 | lua.scope(|scope| {
|
||||
| ----- has type `&'1 mlua::Scope<'1, '_>`
|
||||
6 | let mut inner: Option<Table> = None;
|
||||
7 | let f = scope.create_function_mut(|_, t: Table| {
|
||||
6 | let mut inner: Option<Table> = None;
|
||||
7 | let f = scope.create_function_mut(|_, t: Table| {
|
||||
| ^^^^^^^^^^^^^ may outlive borrowed value `inner`
|
||||
8 | inner = Some(t);
|
||||
8 | inner = Some(t);
|
||||
| ----- `inner` is borrowed here
|
||||
|
|
||||
note: function requires argument type to outlive `'1`
|
||||
--> 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);
|
||||
9 | | Ok(())
|
||||
8 | | inner = Some(t);
|
||||
9 | | Ok(())
|
||||
10 | | })?;
|
||||
| |__________^
|
||||
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,7 +1,7 @@
|
||||
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
|
||||
|
|
||||
9 | lua.scope(|scope| {
|
||||
9 | lua.scope(|scope| {
|
||||
| ----- has type `&'1 mlua::Scope<'1, '_>`
|
||||
...
|
||||
13 | scope.create_function_mut(|_, ()| {
|
||||
|
||||
@@ -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`
|
||||
12 | let _b = scope.create_userdata(MyUserData(&mut i)).unwrap();
|
||||
| ^^^^^^ second mutable borrow occurs here
|
||||
|
|
||||
note: requirement that the value outlives `'1` introduced here
|
||||
--> src/scope.rs
|
||||
|
|
||||
| T: UserData + 'env,
|
||||
| ^^^^
|
||||
|
||||
@@ -13,3 +13,9 @@ error[E0597]: `ibad` does not live long enough
|
||||
| argument requires that `ibad` is borrowed for `'1`
|
||||
16 | };
|
||||
| - `ibad` dropped here while still borrowed
|
||||
|
|
||||
note: requirement that the value outlives `'1` introduced here
|
||||
--> src/scope.rs
|
||||
|
|
||||
| T: UserData + 'env,
|
||||
| ^^^^
|
||||
|
||||
+17
-13
@@ -49,7 +49,7 @@ fn test_string_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// From stack
|
||||
let f = lua.create_function(|_, s: mlua::String| Ok(s))?;
|
||||
let f = lua.create_function(|_, s: mlua::LuaString| Ok(s))?;
|
||||
let s = f.call::<String>("hello, world!")?;
|
||||
assert_eq!(s, "hello, world!");
|
||||
|
||||
@@ -708,9 +708,10 @@ fn test_either_from_lua() -> Result<()> {
|
||||
},
|
||||
err => panic!("expected `Error::BadArgument`, got {err:?}"),
|
||||
}
|
||||
assert!(err
|
||||
.to_string()
|
||||
.starts_with("bad argument #1: error converting Lua string to Either<i32, Table>"),);
|
||||
assert!(
|
||||
err.to_string()
|
||||
.starts_with("bad argument #1: error converting Lua string to Either<i32, Table>"),
|
||||
);
|
||||
}
|
||||
err => panic!("expected `Error::CallbackError`, got {err:?}"),
|
||||
}
|
||||
@@ -736,15 +737,18 @@ fn test_char_from_lua() -> Result<()> {
|
||||
assert_eq!(lua.convert::<char>("A")?, 'A');
|
||||
assert_eq!(lua.convert::<char>(65)?, 'A');
|
||||
assert_eq!(lua.convert::<char>(128175)?, '💯');
|
||||
assert!(lua
|
||||
.convert::<char>(5456324)
|
||||
.is_err_and(|e| e.to_string().contains("integer out of range")));
|
||||
assert!(lua
|
||||
.convert::<char>("hello")
|
||||
.is_err_and(|e| e.to_string().contains("expected string to have exactly one char")));
|
||||
assert!(lua
|
||||
.convert::<char>(HashMap::<String, String>::new())
|
||||
.is_err_and(|e| e.to_string().contains("expected string or integer")));
|
||||
assert!(
|
||||
lua.convert::<char>(5456324)
|
||||
.is_err_and(|e| e.to_string().contains("integer out of range"))
|
||||
);
|
||||
assert!(
|
||||
lua.convert::<char>("hello")
|
||||
.is_err_and(|e| e.to_string().contains("expected string to have exactly one char"))
|
||||
);
|
||||
assert!(
|
||||
lua.convert::<char>(HashMap::<String, String>::new())
|
||||
.is_err_and(|e| e.to_string().contains("expected string or integer"))
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+25
-6
@@ -1,4 +1,4 @@
|
||||
use mlua::{Error, Function, Lua, Result, String, Table, Variadic};
|
||||
use mlua::{Error, Function, Lua, LuaString, Result, Table, Variadic};
|
||||
|
||||
#[test]
|
||||
fn test_function_call() -> Result<()> {
|
||||
@@ -194,6 +194,25 @@ fn test_function_info() -> Result<()> {
|
||||
assert_eq!(print_info.what, "C");
|
||||
assert_eq!(print_info.line_defined, None);
|
||||
|
||||
// Function with upvalues and params
|
||||
#[cfg(not(any(feature = "lua51", feature = "luajit")))]
|
||||
{
|
||||
let func_with_upvalues = lua
|
||||
.load(
|
||||
r#"
|
||||
local x, y = ...
|
||||
return function(a, ...)
|
||||
return a*x + y
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.call::<Function>((10, 20))?;
|
||||
let func_with_upvalues_info = func_with_upvalues.info();
|
||||
assert_eq!(func_with_upvalues_info.num_upvalues, 2);
|
||||
assert_eq!(func_with_upvalues_info.num_params, 1);
|
||||
assert_eq!(func_with_upvalues_info.is_vararg, true);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -248,7 +267,7 @@ fn test_function_coverage() -> Result<()> {
|
||||
|
||||
assert_eq!(
|
||||
report[0],
|
||||
mlua::CoverageInfo {
|
||||
mlua::function::CoverageInfo {
|
||||
function: None,
|
||||
line_defined: 1,
|
||||
depth: 0,
|
||||
@@ -257,7 +276,7 @@ fn test_function_coverage() -> Result<()> {
|
||||
);
|
||||
assert_eq!(
|
||||
report[1],
|
||||
mlua::CoverageInfo {
|
||||
mlua::function::CoverageInfo {
|
||||
function: Some("abc".into()),
|
||||
line_defined: 4,
|
||||
depth: 1,
|
||||
@@ -266,7 +285,7 @@ fn test_function_coverage() -> Result<()> {
|
||||
);
|
||||
assert_eq!(
|
||||
report[2],
|
||||
mlua::CoverageInfo {
|
||||
mlua::function::CoverageInfo {
|
||||
function: None,
|
||||
line_defined: 12,
|
||||
depth: 1,
|
||||
@@ -275,7 +294,7 @@ fn test_function_coverage() -> Result<()> {
|
||||
);
|
||||
assert_eq!(
|
||||
report[3],
|
||||
mlua::CoverageInfo {
|
||||
mlua::function::CoverageInfo {
|
||||
function: None,
|
||||
line_defined: 13,
|
||||
depth: 2,
|
||||
@@ -324,7 +343,7 @@ fn test_function_deep_clone() -> Result<()> {
|
||||
fn test_function_wrap() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = Function::wrap(|s: String, n| Ok(s.to_str().unwrap().repeat(n)));
|
||||
let f = Function::wrap(|s: LuaString, n| Ok(s.to_str().unwrap().repeat(n)));
|
||||
lua.globals().set("f", f)?;
|
||||
lua.load(r#"assert(f("hello", 2) == "hellohello")"#)
|
||||
.exec()
|
||||
|
||||
+1
-1
@@ -274,7 +274,7 @@ fn test_hook_yield() -> Result<()> {
|
||||
|
||||
co.set_hook(HookTriggers::EVERY_LINE, move |_lua, _debug| Ok(VmState::Yield))?;
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))]
|
||||
{
|
||||
assert!(co.resume::<()>(()).is_ok());
|
||||
assert!(co.resume::<()>(()).is_ok());
|
||||
|
||||
+67
-2
@@ -3,8 +3,8 @@
|
||||
use std::cell::Cell;
|
||||
use std::fmt::Debug;
|
||||
use std::os::raw::c_void;
|
||||
use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicU64, Ordering};
|
||||
|
||||
use mlua::{
|
||||
Compiler, Error, Function, Lua, LuaOptions, Result, StdLib, Table, ThreadStatus, Value, Vector, VmState,
|
||||
@@ -448,7 +448,7 @@ fn test_loadstring() -> Result<()> {
|
||||
assert_eq!(f.call::<i32>(())?, 123);
|
||||
|
||||
let err = lua
|
||||
.load(r#"loadstring("retur 123", "chunk")"#)
|
||||
.load(r#"loadstring("retur 123", "chunk")"#) // typos:ignore
|
||||
.exec()
|
||||
.err()
|
||||
.unwrap();
|
||||
@@ -470,5 +470,70 @@ fn test_typeof_error() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_memory_category() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.set_memory_category("main").unwrap();
|
||||
|
||||
// Invalid category names should be rejected
|
||||
let err = lua.set_memory_category("invalid$");
|
||||
assert!(err.is_err());
|
||||
|
||||
for i in 0..254 {
|
||||
let name = format!("category_{}", i);
|
||||
lua.set_memory_category(&name).unwrap();
|
||||
}
|
||||
// 255th category should fail
|
||||
let err = lua.set_memory_category("category_254");
|
||||
assert!(err.is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_heap_dump() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Assign a new memory category and create few objects
|
||||
lua.set_memory_category("test_category")?;
|
||||
let _t = lua.create_table()?;
|
||||
let _ud = lua.create_any_userdata("hello, world")?;
|
||||
|
||||
let dump = lua.heap_dump()?;
|
||||
|
||||
assert!(dump.size() > 0);
|
||||
let size_by_category = dump.size_by_category();
|
||||
assert_eq!(size_by_category.len(), 2);
|
||||
assert!(size_by_category.contains_key("test_category"));
|
||||
assert!(size_by_category["main"] < dump.size());
|
||||
|
||||
// Check size by type within the category
|
||||
let size_by_type = dump.size_by_type(Some("test_category"));
|
||||
assert!(!size_by_type.is_empty());
|
||||
assert!(size_by_type.contains_key("table"));
|
||||
assert!(size_by_type.contains_key("userdata"));
|
||||
// Try non-existent category
|
||||
let size_by_type2 = dump.size_by_type(Some("non_existent_category"));
|
||||
assert!(size_by_type2.is_empty());
|
||||
// Remove category filter
|
||||
let size_by_type_all = dump.size_by_type(None);
|
||||
assert!(size_by_type.len() < size_by_type_all.len());
|
||||
|
||||
// Check size by userdata type within the category
|
||||
let size_by_udtype = dump.size_by_userdata(Some("test_category"));
|
||||
assert_eq!(size_by_udtype.len(), 1);
|
||||
assert!(size_by_udtype.contains_key("&str"));
|
||||
assert_eq!(size_by_udtype["&str"].0, 1);
|
||||
// Try non-existent category
|
||||
let size_by_udtype2 = dump.size_by_userdata(Some("non_existent_category"));
|
||||
assert!(size_by_udtype2.is_empty());
|
||||
// Remove category filter
|
||||
let size_by_udtype_all = dump.size_by_userdata(None);
|
||||
assert!(size_by_udtype.len() < size_by_udtype_all.len());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[path = "luau/require.rs"]
|
||||
mod require;
|
||||
|
||||
+70
-25
@@ -1,7 +1,7 @@
|
||||
use std::io::Result as IoResult;
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
use mlua::{Error, IntoLua, Lua, MultiValue, NavigateError, Require, Result, TextRequirer, Value};
|
||||
use mlua::{Error, FromLua, IntoLua, Lua, MultiValue, NavigateError, Require, Result, TextRequirer, Value};
|
||||
|
||||
fn run_require(lua: &Lua, path: impl IntoLua) -> Result<Value> {
|
||||
lua.load(r#"return require(...)"#).call(path)
|
||||
@@ -11,9 +11,14 @@ fn run_require_pcall(lua: &Lua, path: impl IntoLua) -> Result<MultiValue> {
|
||||
lua.load(r#"return pcall(require, ...)"#).call(path)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn get_value<V: FromLua>(value: &Value, key: impl IntoLua) -> V {
|
||||
value.as_table().unwrap().get(key).unwrap()
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn get_str(value: &Value, key: impl IntoLua) -> String {
|
||||
value.as_table().unwrap().get::<String>(key).unwrap()
|
||||
get_value(value, key)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -37,8 +42,10 @@ fn test_require_errors() {
|
||||
// Pass non-string to require
|
||||
let res = run_require(&lua, true);
|
||||
assert!(res.is_err());
|
||||
assert!((res.unwrap_err().to_string())
|
||||
.contains("bad argument #1 to 'require' (string expected, got boolean)"));
|
||||
assert!(
|
||||
(res.unwrap_err().to_string())
|
||||
.contains("bad argument #1 to 'require' (string expected, got boolean)")
|
||||
);
|
||||
|
||||
// Require from loadstring
|
||||
let res = lua
|
||||
@@ -47,6 +54,16 @@ fn test_require_errors() {
|
||||
assert!(res.is_err());
|
||||
assert!((res.unwrap_err().to_string()).contains("require is not supported in this context"));
|
||||
|
||||
// RequireAliasThatDoesNotExist
|
||||
let res = run_require(&lua, "@this.alias.does.not.exist");
|
||||
assert!(res.is_err());
|
||||
assert!((res.unwrap_err().to_string()).contains("@this.alias.does.not.exist is not a valid alias"));
|
||||
|
||||
// IllegalAlias
|
||||
let res = run_require(&lua, "@");
|
||||
assert!(res.is_err());
|
||||
assert!((res.unwrap_err().to_string()).contains("@ is not a valid alias"));
|
||||
|
||||
// Test throwing mlua::Error
|
||||
struct MyRequire(TextRequirer);
|
||||
|
||||
@@ -154,8 +171,10 @@ fn test_require_without_config() {
|
||||
"./tests/luau/require/without_config/ambiguous_file_requirer",
|
||||
);
|
||||
assert!(res.is_err());
|
||||
assert!((res.unwrap_err().to_string())
|
||||
.contains("could not resolve child component \"dependency\" (ambiguous)"));
|
||||
assert!(
|
||||
(res.unwrap_err().to_string())
|
||||
.contains("could not resolve child component \"dependency\" (ambiguous)")
|
||||
);
|
||||
|
||||
// RequireWithDirectoryAmbiguity
|
||||
let res = run_require(
|
||||
@@ -163,48 +182,74 @@ fn test_require_without_config() {
|
||||
"./tests/luau/require/without_config/ambiguous_directory_requirer",
|
||||
);
|
||||
assert!(res.is_err());
|
||||
assert!((res.unwrap_err().to_string())
|
||||
.contains("could not resolve child component \"dependency\" (ambiguous)"));
|
||||
assert!(
|
||||
(res.unwrap_err().to_string())
|
||||
.contains("could not resolve child component \"dependency\" (ambiguous)")
|
||||
);
|
||||
|
||||
// CheckCachedResult
|
||||
let res = run_require(&lua, "./tests/luau/require/without_config/validate_cache").unwrap();
|
||||
assert!(res.is_table());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_require_with_config() {
|
||||
fn test_require_with_config_inner(r#type: &str) {
|
||||
let lua = Lua::new();
|
||||
|
||||
let base_path = format!("./tests/luau/require/{type}");
|
||||
|
||||
// RequirePathWithAlias
|
||||
let res = run_require(&lua, "./tests/luau/require/with_config/src/alias_requirer").unwrap();
|
||||
let res = run_require(&lua, format!("{base_path}/src/alias_requirer")).unwrap();
|
||||
assert_eq!("result from dependency", get_str(&res, 1));
|
||||
|
||||
// RequirePathWithAlias (case-insensitive)
|
||||
let res2 = run_require(&lua, "./tests/luau/require/with_config/src/alias_requirer_uc").unwrap();
|
||||
let res2 = run_require(&lua, format!("{base_path}/src/alias_requirer_uc")).unwrap();
|
||||
assert_eq!("result from dependency", get_str(&res2, 1));
|
||||
assert_eq!(res.to_pointer(), res2.to_pointer());
|
||||
|
||||
// RequirePathWithParentAlias
|
||||
let res = run_require(&lua, "./tests/luau/require/with_config/src/parent_alias_requirer").unwrap();
|
||||
let res = run_require(&lua, format!("{base_path}/src/parent_alias_requirer")).unwrap();
|
||||
assert_eq!("result from other_dependency", get_str(&res, 1));
|
||||
|
||||
// RequirePathWithAliasPointingToDirectory
|
||||
let res = run_require(
|
||||
&lua,
|
||||
"./tests/luau/require/with_config/src/directory_alias_requirer",
|
||||
)
|
||||
.unwrap();
|
||||
let res = run_require(&lua, format!("{base_path}/src/directory_alias_requirer")).unwrap();
|
||||
assert_eq!("result from subdirectory_dependency", get_str(&res, 1));
|
||||
|
||||
// RequireAliasThatDoesNotExist
|
||||
let res = run_require(&lua, "@this.alias.does.not.exist");
|
||||
assert!(res.is_err());
|
||||
assert!((res.unwrap_err().to_string()).contains("@this.alias.does.not.exist is not a valid alias"));
|
||||
// RequireChainedAliasesSuccess
|
||||
let res = run_require(
|
||||
&lua,
|
||||
format!("{base_path}/chained_aliases/subdirectory/successful_requirer"),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!("result from inner_dependency", get_str(&get_value(&res, 1), 1));
|
||||
assert_eq!("result from outer_dependency", get_str(&get_value(&res, 2), 1));
|
||||
|
||||
// IllegalAlias
|
||||
let res = run_require(&lua, "@");
|
||||
// RequireChainedAliasesFailureCyclic
|
||||
let res = run_require(
|
||||
&lua,
|
||||
format!("{base_path}/chained_aliases/subdirectory/failing_requirer_cyclic"),
|
||||
);
|
||||
assert!(res.is_err());
|
||||
assert!((res.unwrap_err().to_string()).contains("@ is not a valid alias"));
|
||||
let err_msg = "error requiring module \"@cyclicentry\": detected alias cycle (@cyclic1 -> @cyclic2 -> @cyclic3 -> @cyclic1)";
|
||||
assert!(res.unwrap_err().to_string().contains(err_msg));
|
||||
|
||||
// RequireChainedAliasesFailureMissing
|
||||
let res = run_require(
|
||||
&lua,
|
||||
format!("{base_path}/chained_aliases/subdirectory/failing_requirer_missing"),
|
||||
);
|
||||
assert!(res.is_err());
|
||||
let err_msg = "error requiring module \"@brokenchain\": @missing is not a valid alias";
|
||||
assert!(res.unwrap_err().to_string().contains(err_msg));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_require_with_config() {
|
||||
test_require_with_config_inner("with_config");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_require_with_config_luau() {
|
||||
test_require_with_config_inner("with_config_luau");
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(windows)))]
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"aliases":{
|
||||
"outer": "./",
|
||||
"cyclicentry": "@cyclic1",
|
||||
"cyclic1": "@cyclic2",
|
||||
"cyclic2": "@cyclic3",
|
||||
"cyclic3": "@cyclic1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
return {"result from outer_dependency"}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"aliases":{
|
||||
"passthroughinner": "./inner_dependency",
|
||||
"passthroughouter": "@outer",
|
||||
"dep": "@passthroughinner",
|
||||
"outerdep": "@outer/outer_dependency",
|
||||
"outerdir": "@passthroughouter",
|
||||
"brokenchain": "@missing"
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
return require("@cyclicentry")
|
||||
+1
@@ -0,0 +1 @@
|
||||
return require("@brokenchain")
|
||||
@@ -0,0 +1 @@
|
||||
return {"result from inner_dependency"}
|
||||
@@ -0,0 +1,7 @@
|
||||
local result = {}
|
||||
|
||||
table.insert(result, require("@dep"))
|
||||
table.insert(result, require("@outerdep"))
|
||||
table.insert(result, require("@outerdir/outer_dependency"))
|
||||
|
||||
return result
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user