diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml
index 503db17d..f64ab1d3 100644
--- a/.github/workflows/coverage.yml
+++ b/.github/workflows/coverage.yml
@@ -6,18 +6,18 @@ jobs:
name: coverage
runs-on: ubuntu-latest
container:
- image: xd009642/tarpaulin
+ image: xd009642/tarpaulin:develop-nightly
options: --security-opt seccomp=unconfined
steps:
- name: Checkout repository
- uses: actions/checkout@v2
+ uses: actions/checkout@main
- - name: Generate code coverage
+ - name: Generate coverage report
run: |
- cargo tarpaulin --verbose --features lua54,vendored,async,send,serialize,macros --out xml --exclude-files benches --exclude-files build --exclude-files mlua_derive --exclude-files src/ffi --exclude-files tests
+ cargo +nightly tarpaulin --verbose --out xml --tests --exclude-files benches/* --exclude-files mlua-sys/src/*/*
- - name: Upload to codecov.io
- uses: codecov/codecov-action@v1
+ - name: Upload report to codecov.io
+ uses: codecov/codecov-action@v4
with:
token: ${{secrets.CODECOV_TOKEN}}
fail_ci_if_error: false
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 00000000..662a04a3
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,68 @@
+name: Documentation (main)
+
+on:
+ push:
+ branches: [main]
+ 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 '
+
+
+
+ Redirecting to mlua documentation
+
+
+
+
+ Redirecting to mlua documentation...
+
+ ' > 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
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 10133f04..b67adccb 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -7,98 +7,79 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
- os: [ubuntu-20.04, macos-latest, windows-latest]
+ os: [ubuntu-latest, macos-latest, windows-latest]
rust: [stable]
- lua: [lua54, lua53, lua52, lua51, luajit, luau]
+ lua: [lua55, lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
include:
- - os: ubuntu-20.04
- target: x86_64-unknown-linux-gnu
- - os: macos-latest
- target: x86_64-apple-darwin
- - os: windows-latest
- target: x86_64-pc-windows-msvc
+ - os: ubuntu-latest
+ target: x86_64-unknown-linux-gnu
+ - os: macos-latest
+ target: aarch64-apple-darwin
+ - os: windows-latest
+ target: x86_64-pc-windows-msvc
steps:
- - uses: actions/checkout@v2
- - uses: actions-rs/toolchain@v1
- with:
- toolchain: ${{ matrix.rust }}
- target: ${{ matrix.target }}
- override: true
- - uses: Swatinem/rust-cache@v1
- - name: Build ${{ matrix.lua }} vendored
- run: |
- cargo build --features "${{ matrix.lua }},vendored"
- cargo build --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
- shell: bash
- - name: Build ${{ matrix.lua }} pkg-config
- if: ${{ matrix.os == 'ubuntu-20.04' && matrix.lua != 'lua54' }}
- run: |
- sudo apt-get update
- sudo apt-get install -y --no-install-recommends liblua5.3-dev liblua5.2-dev liblua5.1-0-dev libluajit-5.1-dev
- cargo build --features "${{ matrix.lua }}"
-
- 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@v2
- - uses: actions-rs/toolchain@v1
- with:
- toolchain: stable
- target: aarch64-apple-darwin
- override: true
- - name: Cross-compile
- run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
+ - uses: actions/checkout@main
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ toolchain: ${{ matrix.rust }}
+ target: ${{ matrix.target }}
+ - uses: Swatinem/rust-cache@v2
+ - name: Build ${{ matrix.lua }} vendored
+ run: |
+ cargo build --features "${{ matrix.lua }},vendored"
+ cargo build --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow,userdata-wrappers"
+ cargo build --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow,userdata-wrappers,send"
+ shell: bash
+ - name: Build ${{ matrix.lua }} pkg-config
+ if: ${{ matrix.os == 'ubuntu-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_ubuntu:
name: Cross-compile to aarch64-unknown-linux-gnu
- runs-on: ubuntu-20.04
+ 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@v2
- - uses: actions-rs/toolchain@v1
- with:
- toolchain: stable
- target: aarch64-unknown-linux-gnu
- override: true
- - name: Install ARM compiler toolchain
- run: |
- sudo apt-get update
- sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross
- shell: bash
- - name: Cross-compile
- run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
- shell: bash
+ - uses: actions/checkout@main
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ toolchain: stable
+ target: aarch64-unknown-linux-gnu
+ - name: Install ARM compiler toolchain
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross
+ shell: bash
+ - name: Cross-compile
+ run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serde,macros,anyhow,userdata-wrappers"
+ shell: bash
build_armv7_cross_ubuntu:
name: Cross-compile to armv7-unknown-linux-gnueabihf
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
needs: build
strategy:
matrix:
- lua: [lua54, lua53, lua52, lua51]
+ lua: [lua55, lua54, lua53, lua52, lua51]
steps:
- - uses: actions/checkout@v2
- - uses: actions-rs/toolchain@v1
- with:
- toolchain: stable
- target: armv7-unknown-linux-gnueabihf
- override: true
- - name: Install ARM compiler toolchain
- run: |
- sudo apt-get update
- sudo apt-get install -y --no-install-recommends gcc-arm-linux-gnueabihf libc-dev-armhf-cross
- shell: bash
- - name: Cross-compile
- run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
- shell: bash
+ - uses: actions/checkout@main
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ toolchain: stable
+ target: armv7-unknown-linux-gnueabihf
+ - name: Install ARM compiler toolchain
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y --no-install-recommends gcc-arm-linux-gnueabihf libc-dev-armhf-cross
+ shell: bash
+ - name: Cross-compile
+ run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serde,macros,anyhow,userdata-wrappers"
+ shell: bash
test:
name: Test
@@ -106,35 +87,35 @@ jobs:
needs: build
strategy:
matrix:
- os: [ubuntu-20.04, macos-latest, windows-latest]
+ os: [ubuntu-latest, macos-latest, windows-latest]
rust: [stable, nightly]
- lua: [lua54, lua53, lua52, lua51, luajit, luajit52, luau]
+ lua: [lua55, lua54, lua53, lua52, lua51, luajit, luajit52, luau, luau-jit, luau-vector4]
include:
- - os: ubuntu-20.04
- target: x86_64-unknown-linux-gnu
- - os: macos-latest
- target: x86_64-apple-darwin
- - os: windows-latest
- target: x86_64-pc-windows-msvc
+ - os: ubuntu-latest
+ target: x86_64-unknown-linux-gnu
+ - os: macos-latest
+ target: aarch64-apple-darwin
+ - os: windows-latest
+ target: x86_64-pc-windows-msvc
steps:
- - uses: actions/checkout@v2
- - uses: actions-rs/toolchain@v1
- with:
- toolchain: ${{ matrix.rust }}
- target: ${{ matrix.target }}
- override: true
- - uses: Swatinem/rust-cache@v1
- - name: Run ${{ matrix.lua }} tests
- run: |
- cargo test --features "${{ matrix.lua }},vendored"
- cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
- shell: bash
- - name: Run compile tests (macos lua54)
- if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua54' }}
- run: |
- TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" -- --ignored
- TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot" -- --ignored
- shell: bash
+ - uses: actions/checkout@main
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ toolchain: ${{ matrix.rust }}
+ target: ${{ matrix.target }}
+ - uses: Swatinem/rust-cache@v2
+ - name: Run ${{ matrix.lua }} tests
+ run: |
+ cargo test --features "${{ matrix.lua }},vendored"
+ 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 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
+ shell: bash
test_with_sanitizer:
name: Test with address sanitizer
@@ -142,25 +123,52 @@ jobs:
needs: build
strategy:
matrix:
- os: [ubuntu-20.04]
+ os: [ubuntu-latest]
rust: [nightly]
- lua: [lua54, lua53, lua52, lua51, luajit, luau]
+ lua: [lua55, lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
include:
- - os: ubuntu-20.04
- target: x86_64-unknown-linux-gnu
+ - os: ubuntu-latest
+ target: x86_64-unknown-linux-gnu
steps:
- - uses: actions/checkout@v2
- - uses: actions-rs/toolchain@v1
- with:
- toolchain: ${{ matrix.rust }}
- target: ${{ matrix.target }}
- override: true
- - uses: Swatinem/rust-cache@v1
- - name: Run ${{ matrix.lua }} tests with address sanitizer
- run: |
- RUSTFLAGS="-Z sanitizer=address" \
- cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
- shell: bash
+ - uses: actions/checkout@main
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ toolchain: ${{ matrix.rust }}
+ target: ${{ matrix.target }}
+ - uses: Swatinem/rust-cache@v2
+ - name: Run ${{ matrix.lua }} tests with address sanitizer
+ run: |
+ cargo test --tests --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
+ cargo test --tests --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow,userdata-wrappers,send" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
+ shell: bash
+ env:
+ RUSTFLAGS: -Z sanitizer=address
+
+ test_with_memory_limit:
+ name: Test with memory limit
+ runs-on: ${{ matrix.os }}
+ needs: build
+ strategy:
+ matrix:
+ os: [ubuntu-latest]
+ rust: [nightly]
+ lua: [lua55, lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
+ include:
+ - os: ubuntu-latest
+ target: x86_64-unknown-linux-gnu
+ steps:
+ - uses: actions/checkout@main
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ toolchain: ${{ matrix.rust }}
+ target: ${{ matrix.target }}
+ - uses: Swatinem/rust-cache@v2
+ - name: Run ${{ matrix.lua }} tests with forced memory limit
+ run: |
+ cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serde,macros,anyhow,userdata-wrappers"
+ shell: bash
+ env:
+ RUSTFLAGS: --cfg=force_memory_limit
test_modules:
name: Test modules
@@ -168,27 +176,26 @@ jobs:
needs: build
strategy:
matrix:
- os: [ubuntu-20.04, macos-latest]
+ os: [ubuntu-latest, macos-latest]
rust: [stable]
- lua: [lua54, lua53, lua52, lua51, luajit]
+ lua: [lua55, lua54, lua53, lua52, lua51, luajit]
include:
- - os: ubuntu-20.04
- target: x86_64-unknown-linux-gnu
- - os: macos-latest
- target: x86_64-apple-darwin
+ - os: ubuntu-latest
+ target: x86_64-unknown-linux-gnu
+ - os: macos-latest
+ target: aarch64-apple-darwin
steps:
- - uses: actions/checkout@v2
- - uses: actions-rs/toolchain@v1
- with:
- toolchain: ${{ matrix.rust }}
- target: ${{ matrix.target }}
- override: true
- - uses: Swatinem/rust-cache@v1
- - name: Run ${{ matrix.lua }} module tests
- run: |
- (cd tests/module && cargo build --release --features "${{ matrix.lua }}")
- (cd tests/module/loader && cargo test --release --features "${{ matrix.lua }},vendored")
- shell: bash
+ - uses: actions/checkout@main
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ toolchain: ${{ matrix.rust }}
+ target: ${{ matrix.target }}
+ - uses: Swatinem/rust-cache@v2
+ - name: Run ${{ matrix.lua }} module tests
+ run: |
+ (cd tests/module && cargo build --release --features "${{ matrix.lua }}")
+ (cd tests/module/loader && cargo test --release --features "${{ matrix.lua }},vendored")
+ shell: bash
test_modules_windows:
name: Test modules on Windows
@@ -201,42 +208,96 @@ jobs:
run:
shell: msys2 {0}
steps:
- - uses: msys2/setup-msys2@v2
- - uses: actions/checkout@v2
- - name: Install Rust & Lua
- run: |
- pacman -S --noconfirm mingw-w64-x86_64-rust mingw-w64-x86_64-lua mingw-w64-x86_64-luajit mingw-w64-x86_64-pkg-config
- - name: Run ${{ matrix.lua }} module tests
- run: |
- (cd tests/module && cargo build --release --features "${{ matrix.lua }}")
- (cd tests/module/loader && cargo test --release --features "${{ matrix.lua }}")
+ - uses: msys2/setup-msys2@v2
+ - uses: actions/checkout@main
+ - name: Install Rust & Lua
+ run: |
+ pacman -S --noconfirm mingw-w64-x86_64-rust mingw-w64-x86_64-lua mingw-w64-x86_64-luajit mingw-w64-x86_64-pkg-config
+ - name: Run ${{ matrix.lua }} module tests
+ run: |
+ (cd tests/module && cargo build --release --features "${{ matrix.lua }}")
+ (cd tests/module/loader && cargo test --release --features "${{ matrix.lua }}")
+
+ test_wasm32_emscripten:
+ name: Test on wasm32-unknown-emscripten
+ runs-on: ubuntu-latest
+ needs: build
+ strategy:
+ matrix:
+ lua: [lua55, lua54, lua53, lua52, lua51, luau]
+ steps:
+ - uses: actions/checkout@main
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ toolchain: stable
+ target: wasm32-unknown-emscripten
+ - name: Install Emscripten
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y --no-install-recommends emscripten
+ - name: Run ${{ matrix.lua }} tests
+ run: |
+ 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-20.04
+ runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
- - uses: actions-rs/toolchain@v1
- with:
- toolchain: stable
- components: rustfmt
- override: true
- - run: cargo fmt -- --check
+ - uses: actions/checkout@main
+ - uses: dtolnay/rust-toolchain@nightly
+ with:
+ components: rustfmt
+ - run: cargo fmt -- --check
clippy:
- name: Clippy check
- runs-on: ubuntu-20.04
+ name: Clippy
+ runs-on: ubuntu-latest
strategy:
matrix:
- lua: [lua54, lua53, lua52, lua51, luajit, luau]
+ lua: [lua55, lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
steps:
- - uses: actions/checkout@v2
- - uses: actions-rs/toolchain@v1
+ - uses: actions/checkout@main
+ - uses: dtolnay/rust-toolchain@stable
with:
- toolchain: nightly
- components: clippy
- override: true
- - uses: actions-rs/clippy-check@v1
+ toolchain: nightly
+ components: clippy
+ - uses: giraffate/clippy-action@v1
with:
- token: ${{ secrets.GITHUB_TOKEN }}
- args: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
+ reporter: 'github-pr-review'
+ clippy_flags: --features "${{ matrix.lua }},vendored,async,send,serde,macros,anyhow,userdata-wrappers"
diff --git a/.github/workflows/typos.yml b/.github/workflows/typos.yml
new file mode 100644
index 00000000..1cd3c1c9
--- /dev/null
+++ b/.github/workflows/typos.yml
@@ -0,0 +1,22 @@
+name: Spelling Check
+on:
+ pull_request:
+ workflow_dispatch:
+
+permissions:
+ contents: read
+
+env:
+ CLICOLOR: 1
+
+jobs:
+ spelling:
+ name: Spell Check with Typos
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Actions Repository
+ uses: actions/checkout@main
+ - name: Check spelling
+ uses: crate-ci/typos@v1.42.1
+ with:
+ config: ./typos.toml
diff --git a/.gitignore b/.gitignore
index 5c21d7d9..fcfec84e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ Cargo.lock
.vscode/
.DS_Store
+.stignore
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f6a697ce..fb8e8562 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,458 @@
+## v0.12.0-rc.1 (Apr 21, 2026)
+
+- Rust 2024 edition
+- Removed `Error::ToLuaConversionError` variant as it was unused (and not practically useful)
+- New modules to group data types: `chunk`, `debug`, `error`, `function`, `table`, `string`, `state`, `thread`, `userdata`, `luau`
+- Support `__todebugstring` metamethod for pretty formatting userdata value (for debugging)
+- New `MaybeSync` trait that is required for userdata types
+- Removed lifetime from `BorrowedStr` and `BorrowedBytes`
+- New `Thread` methods: `is_resumable`, `is_running`, `is_finished`, `is_error`
+- Added `Thread::state` to get raw Lua state pointer
+- Luau `TextRequirer` is renamed to `FsRequirer`
+- GC interface refactor: `Lua::gc_inc/Lua::gc_gen` is replaced with `gc_set_mode`
+- Added `GcIncParams` and `GcGenParams` for GC tuning
+- New `UserDataMethods::add_method_once` and `UserDataMethods::add_async_method_once`
+- Initial Luau integer64 type support
+- Changed interface of `Function::wrap/wrap_mut/wrap_async` to support any Error type
+- Changed `AnyUserData::type_name` to return `LuaString` instead
+- Added `UserDataOwned` wrapper to take ownership of userdata `T` and implements `FromLua`
+
+## v0.11.6 (Jan 27, 2026)
+
+- Added Lua 5.5 support (`lua55` feature flag)
+- Luau updated to 0.705+
+- Added `AnyUserData::is_proxy` method to check if userdata is a proxy
+- Added `num_params`, `num_upvalues`, `is_vararg` to `FunctionInfo`
+
+## v0.11.5 (Nov 22, 2025)
+
+- Luau updated to 0.701
+- Added `Lua::set_memory_category` and `Lua::heap_dump` functions to profile (Luau) memory
+- Added `Lua::type_metatable` helper to get metatable of a primitive type
+- Added `Lua::traceback` function to generate stack traces at different levels
+- Added `add_method_once` /`add_async_method_once` UserData methods (experimental)
+- Make `AnyUserData::type_name` public
+- impl `IntoLuaMulti` for `&MultiValue`
+- Bugfixes and async perf improvements
+
+## v0.11.4 (Sep 29, 2025)
+
+- Make `Value::to_serializable` public
+- Add new serde option `detect_mixed_tables` (to encode mixed array+map tables)
+- Add `ObjectLike::get_path` helper (for tables and userdata)
+
+## v0.11.3 (Aug 30, 2025)
+
+- Add `Lua::yield_with` to use as `coroutine.yield` functional replacement in async functions for any Lua
+- Do not try to yield at non-yielable points in Luau interrupt (#632)
+- Add `Buffer::cursor` method (Luau)
+- Add `Lua::create_buffer_with_capacity` method (Luau)
+- Make Lua reference values cheap to clone (only increments ref count)
+- Fix panic on large (>67M entries) table creation
+
+## v0.11.2 (Aug 10, 2025)
+
+- Faster stack push for `Variadic`
+- Fix handling Windows paths with drive letter in Luau require (#623)
+- Make Luau registered aliases ascii case-insensitive (#620)
+- Fix deserializing negative zeros `-0.0` (#618)
+
+## v0.11.1 (Jul 15, 2025)
+
+- Fixed bug exhausting Lua auxiliary stack and leaving it without reserve (#615)
+- `Lua::push_c_function` now correctly handles OOM for Lua 5.1 and Luau
+
+## v0.11.0 (Jul 14, 2025)
+
+Changes since v0.11.0-beta.3
+
+- Allow linking external Lua libraries in a build script (e.g. pluto) using `external` mlua-sys feature flag
+- `Lua::inspect_stack` takes a callback with `&Debug` argument, instead of returning `Debug` directly
+- Added `Debug::function` method to get function running at a given level
+- `Debug::curr_line` is deprecated in favour of `Debug::current_line` that returns `Option`
+- Added `Lua::set_globals` method to replace global environment
+- `Table::set_metatable` now returns `Result<()>` (this operation can fail in sandboxed Luau mode)
+- `impl ToString` replaced with `Into` in `UserData` registration
+- `Value::as_str` and `Value::as_string_lossy` methods are deprecated (as they are non-idiomatic)
+- Bugfixes and improvements
+
+## v0.11.0-beta.3 (Jun 23, 2025)
+
+- Luau in sandboxed mode has reduced options in `collectgarbage` function (to follow the official doc)
+- `Function::deep_clone` now returns `Result` as this operation can trigger memory errors
+- Luau "Require" resolves included Lua files relative to the current directory (#605)
+- Fixed bug when finalizing `AsyncThread` on drop (`call_async` methods family)
+
+## v0.11.0-beta.2 (Jun 12, 2025)
+
+- Lua 5.4 updated to 5.4.8
+- Terminate Rust `Future` when `AsyncThread` is dropped (without relying on Lua GC)
+- Added `loadstring` function to Luau
+- Make `AsChunk` trait dyn-friendly
+- Luau `Require` trait synced with Luau 0.674
+- Luau `Require` trait methods now can return `Error` variant (in `NavigateError` enum)
+- Added `__type` to `Error`'s userdata metatable (for `typeof` function)
+- `parking_log/send_guard` is moved to `userdata-wrappers` feature flag
+- New `serde` feature flag to replace `serialize` (the old one is still available)
+
+## v0.11.0-beta.1 (May 7th, 2025)
+
+- New "require-by-string" for Luau (with `Require` trait and async support)
+- Added `Thread::resume_error` support for Luau
+- 52 bit integers support for Luau (this is a breaking change)
+- New features for Luau compiler (constants, disabled builtins, known members)
+- `AsyncThread` changed to `AsyncThread` (`A` pushed to stack immediately)
+- Lifetime `'a` moved from `AsChunk<'a>` to `AsChunk::source where Self: 'a`
+- `Lua::scope` pass `&Scope` instead of `&mut Scope` to closure
+- Added global hooks support (Lua 5.1+)
+- Added per-thread hooks support (Lua 5.1+)
+- `Lua::init_from_ptr` renamed to `Lua::get_or_init_from_ptr` and returns `&Lua`
+- `Lua:load_from_function` is deprecated (this is `register_module` now)
+- Added `Lua::register_module` and `Lua::preload_module`
+
+## v0.10.4 (May 5th, 2025)
+
+- Luau updated to 0.672
+- New serde option `encode_empty_tables_as_array` to serialize empty tables as arrays
+- Added `WeakLua` and `Lua::weak()` to create weak references to Lua state
+- Trigger abort when Luau userdata destructors are panic (Luau GC does not support it)
+- Added `AnyUserData::type_id()` method to get the type id of the userdata
+- Added `Chunk::name()`, `Chunk::environment()` and `Chunk::mode()` functions
+- Support borrowing underlying wrapped types for `UserDataRef` and `UserDataRefMut` (under `userdata-wrappers` feature)
+- Added large (52bit) integers support for Luau
+- Enable `serde` for `bstr` if `serialize` feature flag is enabled
+- Recursive warnings (Lua 5.4) are no longer allowed
+- Implemented `IntoLua`/`FromLua` for `BorrowedString` and `BorrowedBytes`
+- Implemented `IntoLua`/`FromLua` for `char`
+- Enable `Thread::reset()` for all Lua versions (limited support for 5.1-5.3)
+- Bugfixes and improvements
+
+## v0.10.3 (Jan 27th, 2025)
+
+- Set `Default` for `Value` to be `Nil`
+- Allow exhaustive match on `Value` (#502)
+- Add `Table::set_safeenv` method (Luau)
+
+## v0.10.2 (Dec 1st, 2024)
+
+- Switch proc-macro-error to proc-macro-error2 (#493)
+- Do not allow Lua to run GC finalizers on ref thread (#491)
+- Fix chunks loading in Luau when memory limit is enforced (#488)
+- Added `String::wrap` method to wrap arbitrary `AsRef<[u8]>` into `impl IntoLua`
+- Better FreeBSD/OpenBSD support (thanks to cos)
+- Delay "any" userdata metatable creation until first instance is created (#482)
+- Reduce amount of generated code for `UserData` (less generics)
+
+## v0.10.1 (Nov 9th, 2024)
+
+- Minimal Luau updated to 0.650
+- Added Luau native vector library support (this can change behavior if you use `vector` function!)
+- Added Lua `String::display` method
+- Improved pretty-printing for Lua tables (#478)
+- Added `Scope::create_any_userdata` to create Lua objects from any non-`'static` Rust types
+- Added `AnyUserData::destroy` method
+- New `userdata-wrappers` feature to `impl UserData` for `Rc`/`Arc`/`Rc>`/`Arc>` (similar to v0.9)
+- `UserDataRef` in `send` mode now uses shared lock if `T: Sync` (and exclusive lock otherwise)
+- Added `Scope::add_destructor` to attach custom destructors
+- Added `Lua::try_app_data_ref` and `Lua::try_app_data_mut` methods
+- Added `From` and `Into` support to `MultiValue` and `Variadic` types
+- Bug fixes and improvements (#477 #479)
+
+## v0.10.0 (Oct 25th, 2024)
+
+Changes since v0.10.0-rc.1
+
+- Added `error-send` feature flag (disabled by default) to require `Send + Sync` for `Error`
+- Some performance improvements
+
+## v0.10.0-rc.1
+
+- `Lua::scope` is back
+- Support yielding from hooks for Lua 5.3+
+- Support setting metatable for Lua builtin types (number/string/function/etc)
+- Added `LuaNativeFn`/`LuaNativeFnMut`/`LuaNativeAsyncFn` traits for using in `Function::wrap`
+- Added `Error::chain` method to return iterator over nested errors
+- Added `Lua::exec_raw` helper to execute low-level Lua C API code
+- Added `Either` enum to combine two types into a single one
+- Added a new `Buffer` type for Luau
+- Added `Value::is_error` and `Value::as_error` helpers
+- Added `Value::Other` variant to represent unknown Lua types (eg LuaJIT CDATA)
+- Added (optional) `anyhow` feature to implement `IntoLua` for `anyhow::Error`
+- Added `IntoLua`/`FromLua` for `OsString`/`OsStr` and `PathBuf`/`Path`
+
+## v0.10.0-beta.2
+
+- Updated `ThreadStatus` enum to include `Running` and `Finished` variants.
+- `Error::CoroutineInactive` renamed to `Error::CoroutineUnresumable`.
+- `IntoLua`/`IntoLuaMulti` now uses `impl trait` syntax for args (shorten from `a.get::<_, T>` to `a.get::`).
+- Removed undocumented `Lua::into_static`/`from_static` methods.
+- Futures now require `Send` bound if `send` feature is enabled.
+- Dropped lifetime from `UserDataMethods` and `UserDataFields` traits.
+- `Compiler::compile()` now returns `Result` (Luau).
+- Removed `Clone` requirement from `UserDataFields::add_field()`.
+- `TableExt` and `AnyUserDataExt` traits were combined into `ObjectLike` trait.
+- Disabled `send` feature in module mode (since we don't have exclusive access to Lua).
+- `Chunk::set_environment()` takes `Table` instead of `IntoLua` type.
+- Reduced the compile time contribution of `next_key_seed` and `next_value_seed`.
+- Reduced the compile time contribution of `serde_userdata`.
+- Performance improvements.
+
+## v0.10.0-beta.1
+
+- Dropped `'lua` lifetime (subtypes now store a weak reference to Lua)
+- Removed (experimental) owned types (they no longer needed)
+- Make Lua types truly `Send` and `Sync` (when enabling `send` feature flag)
+- Removed `UserData` impl for Rc/Arc types ("any" userdata functions can be used instead)
+- `Lua::replace_registry_value` takes `&mut RegistryKey`
+- `Lua::scope` temporary disabled (will be re-added in the next release)
+
+## v0.9.9
+
+- Minimal Luau updated to 0.629
+- Fixed bug when attempting to reset or resume already running coroutines (#416).
+- Added `RegistryKey::id()` method to get the underlying Lua registry key id.
+
+## v0.9.8
+
+- Fixed serializing same table multiple times (#408)
+- Use `mlua-sys` v0.6 (to support Luau 0.624+)
+- Fixed cross compilation of windows dlls from unix (#394)
+
+## v0.9.7
+
+- Implemented `IntoLua` for `RegistryKey`
+- Mark `__idiv` metamethod as available for luau
+- Added `Function::deep_clone()` method (Luau)
+- Added `SerializeOptions::detect_serde_json_arbitrary_precision` option
+- Added `Lua::create_buffer()` method (Luau)
+- Support serializing buffer type as a byte slice (Luau)
+- Perf: Implemented `push_into_stack`/`from_stack` for `Option`
+- Added `Lua::create_ser_any_userdata()` method
+
+## v0.9.6
+
+- Added `to_pointer` function to `Function`/`Table`/`Thread`
+- Implemented `IntoLua` for `&Value`
+- Implemented `FromLua` for `RegistryKey`
+- Faster (~5%) table array traversal during serialization
+- Some performance improvements for bool/int types
+
+## v0.9.5
+
+- Minimal Luau updated to 0.609
+- Luau max stack size increased to 1M (from 100K)
+- Implemented `IntoLua` for refs to `String`/`Table`/`Function`/`AnyUserData`/`Thread` + `RegistryKey`
+- Implemented `IntoLua` and `FromLua` for `OwnedThread`/`OwnedString`
+- Fixed `FromLua` derive proc macro to cover more cases
+
+## v0.9.4
+
+- Fixed loading all-in-one modules under mixed states (eg. main state and coroutines)
+
+## v0.9.3
+
+- WebAssembly support (`wasm32-unknown-emscripten` target)
+- Performance improvements (faster Lua function calls for lua51/jit/luau)
+
+## v0.9.2
+
+- Added binary modules support to Luau
+- Added Luau package module (uses `StdLib::PACKAGE`) with loaders (follows lua5.1 interface)
+- Added support of Luau 0.601+ buffer type (represented as userdata in Rust)
+- LuaJIT `cdata` type is also represented as userdata in Rust (instead of panic)
+- Vendored LuaJIT switched to rolling vanilla (from openresty)
+- Added `Table::for_each` method for fast table pairs traversal (faster than `pairs`)
+- Performance improvements around table traversal (and faster serialization)
+- Bug fixes and improvements
+
+## v0.9.1
+
+- impl Default for Lua
+- impl IntoLuaMulti for `std::result::Result<(), E>`
+- Fix using wrong userdata index after processing Variadic args (#311)
+
+## v0.9.0
+
+Changes since v0.9.0-rc.3
+
+- Improved non-static (scoped) userdata support
+- Added `Scope::create_any_userdata()` method
+- Added `Lua::set_vector_metatable()` method (`unstable` feature flag)
+- Added `OwnedThread` type (`unstable` feature flag)
+- Minimal Luau updated to 0.590
+- Added new option `sort_keys` to `DeserializeOptions` (`Lua::from_value()` method)
+- Changed `Table::raw_len()` output type to `usize`
+- Helper functions for `Value` (eg: `Value::as_number()`/`Value::as_string`/etc)
+- Performance improvements
+
+## v0.9.0-rc.3
+
+- Minimal Luau updated to 0.588
+
+## v0.9.0-rc.2
+
+- Added `#[derive(FromLua)]` macro to opt-in into `FromLua where T: 'static + Clone` (userdata type).
+- Support vendored module mode for windows (raw-dylib linking, Rust 1.71+)
+- `module` and `vendored` features are now mutually exclusive
+- Use `C-unwind` ABI (Rust 1.71+)
+- Changed `AsChunk` trait to support capturing wrapped Lua types
+
+## v0.9.0-rc.1
+
+- `UserDataMethods::add_async_method()` takes `&T` instead of cloning `T`
+- Implemented `PartialEq<[T]>` for tables
+- Added Luau 4-dimensional vectors support (`luau-vector4` feature)
+- `Table::sequence_values()` iterator no longer uses any metamethods (`Table::raw_sequence_values()` is deprecated)
+- Added `Table:is_empty()` function that checks both hash and array parts
+- Refactored Debug interface
+- Re-exported `ffi` (`mlua-sys`) crate for easier writing of unsafe code
+- Refactored Lua 5.4 warnings interface
+- Take `&str` as function name in `TableExt` and `AnyUserDataExt` traits
+- Added module attribule `skip_memory_check` to improve performance
+- Added `AnyUserData::wrap()` to provide more easy way of creating _any_ userdata in Lua
+
+## v0.9.0-beta.3
+
+- Added `OwnedAnyUserData::take()`
+- Switch to `DeserializeOwned`
+- Overwrite error context when called multiple times
+- New feature flag `luau-jit` to enable (experimental) Luau codegen backend
+- Set `__name` field in userdata metatable
+- Added `Value::to_string()` method similar to `luaL_tolstring`
+- Lua 5.4.6
+- Application data container now allows to mutably and immutably borrow different types at the same time
+- Performance optimizations
+- Support getting and setting environment for Lua functions.
+- Added `UserDataFields::add_field()` method to add static fields to UserData
+
+Breaking changes:
+- Require environment to be a `Table` instead of `Value` in Chunks.
+- `AsChunk::env()` renamed to `AsChunk::environment()`
+
+## v0.9.0-beta.2
+
+New features:
+- Added `Thread::set_hook()` function to set hook on threads
+- Added pretty print to the Debug formatting to Lua `Value` and `Table`
+- ffi layer moved to `mlua-sys` crate
+- Added OwnedString (unstable)
+
+Breaking changes:
+- Refactor `HookTriggers` (make it const)
+
+## v0.9.0-beta.1
+
+New features:
+- Owned Lua types (unstable feature flag)
+- New functions `Function::wrap`/`Function::wrap_mut`/`Function::wrap_async`
+- `Lua::register_userdata_type()` to register a custom userdata types (without requiring `UserData` trait)
+- `Lua::create_any_userdata()`
+- Added `create_userdata_ref`/`create_userdata_ref_mut` for scopes
+- Added `AnyUserDataExt` trait with auxiliary functions for `AnyUserData`
+- Added `UserDataRef` and `UserDataRefMut` type wrapped that implement `FromLua`
+- Improved error handling:
+ * Improved error reporting when calling Rust functions from Lua.
+ * Added `Error::BadArgument` to help identify bad argument position or name
+ * Added `ErrorContext` extension trait to attach additional context to `Error`
+
+Breaking changes:
+- Refactored `AsChunk` trait
+- `ToLua`/`ToLuaMulti` renamed to `IntoLua`/`IntoLuaMulti`
+- Renamed `to_lua_err` to `into_lua_err`
+- Removed `FromLua` impl for `T: UserData+Clone`
+- Removed `Lua::async_scope`
+- Added `&Lua` arg to Luau interrupt callback
+
+Other:
+- Better Debug for String
+- Allow deserializing values from serializable UserData using `Lua::from_value()` method
+- Added `Table::clear()` method
+- Added `Error::downcast_ref()` method
+- Support setting memory limit for Lua 5.1/JIT/Luau
+- Support setting module name in `#[lua_module(name = "...")]` macro
+- Minor fixes and improvements
+
+## v0.8.10
+
+- Update to Luau 0.590 (luau0-src to 0.7.x)
+- Fix loading luau code starting with \t
+- Pin lua-src and luajit-src versions
+
+## v0.8.9
+
+- Update minimal (vendored) Lua 5.4 to 5.4.6
+- Use `lua_closethread` instead of `lua_resetthread` in vendored mode (Lua 5.4.6)
+- Allow deserializing Lua null into unit (`()`) or unit struct.
+
+## v0.8.8
+
+- Fix potential deadlock when trying to reuse dropped registry keys.
+- Optimize userdata methods call when __index and fields_getters are nil
+
+## v0.8.7
+
+- Minimum Luau updated to 0.555 (`LUAI_MAXCSTACK` limit increased to 100000)
+- `_VERSION` in Luau now includes version number
+- Fixed lifetime of `DebugNames` in `Debug::names()` and `DebugSource` in `Debug::source()`
+- Fixed subtraction overflow when calculating index for `MultiValue::get()`
+
+## v0.8.6
+
+- Fixed bug when recycled Registry slot can be set to Nil
+
+## v0.8.5
+
+- Fixed potential unsoundness when using `Layout::from_size_align_unchecked` and Rust 1.65+
+- Performance optimizations around string and table creation in standalone mode
+- Added fast track path to Table `get`/`set`/`len` methods without metatable
+- Added new methods `push`/`pop`/`raw_push`/`raw_pop` to Table
+- Fix getting caller information from `Lua::load`
+- Better checks and tests when trying to modify a Luau readonly table
+
+## v0.8.4
+
+- Minimal Luau updated to 0.548
+
+## v0.8.3
+
+- Close to-be-closed variables for Lua 5.4 when using call_async functions (#192)
+- Fixed Lua assertion when inspecting another thread stack. (#195)
+- Use more reliable way to create LuaJIT VM (which can fail if use Rust allocator on non-x86 platforms)
+
+## v0.8.2
+
+- Performance optimizations in handling UserData
+- Minimal Luau updated to 0.536
+- Fixed bug in `Function::bind` when passing empty binds and no arguments (#189)
+
+## v0.8.1
+
+- Added `Lua::create_proxy` for accessing to UserData static fields and functions without instance
+- Added `Table::to_pointer()` and `String::to_pointer()` functions
+- Bugfixes and improvements (#176 #179)
+
+## v0.8.0
+Changes since 0.7.4
+- Luau support
+- Removed C glue
+- Added async support to `__index` and `__newindex` metamethods
+- Added `Function::info()` to get information about functions (#149).
+- Added `parking_lot` dependency under feature flag (for `UserData`)
+- `Hash` implementation for Lua String
+- Added `Value::to_pointer()` function
+- Performance improvements
+
+Breaking changes:
+- Refactored `AsChunk` trait (added implementation for `Path` and `PathBuf`).
+
+## v0.8.0-beta.5
+
+- Lua sources no longer needed to build modules
+- Added `__iter` metamethod for Luau
+- Added `Value::to_pointer()` function
+- Added `Function::coverage` for Luau to obtain coverage report
+- Bugfixes and improvements (#153 #161 #168)
+
## v0.8.0-beta.4
- Removed `&Lua` from `Lua::set_interrupt` as it's not safe (introduced in v0.8.0-beta.3)
@@ -28,7 +483,7 @@
## v0.8.0-beta.1
-- Roblox Luau support
+- Luau support
- Refactored ffi module. C glue is no longer required
- Added async support to `__index` and `__newindex` metamethods
@@ -141,7 +596,7 @@ Breaking changes:
- [**Breaking**] Removed `AnyUserData::has_metamethod()`
- Added `Thread::reset()` for luajit/lua54 to recycle threads.
It's possible to attach a new function to a thread (coroutine).
-- Added `chunk!` macro support to load chunks of Lua code using the Rust tokenizer and optinally capturing Rust variables.
+- Added `chunk!` macro support to load chunks of Lua code using the Rust tokenizer and optionally capturing Rust variables.
- Improved error reporting (`Error`'s `__tostring` method formats full stacktraces). This is useful in the module mode.
## v0.6.0-beta.1
@@ -197,7 +652,7 @@ Breaking changes:
- Lua 5.4 support with `MetaMethod::Close`.
- `lua53` feature is disabled by default. Now preferred Lua version have to be chosen explicitly.
-- Provide safety guaraness for Lua state, which means that potenially unsafe operations, like loading C modules (using `require` or `package.loadlib`) are disabled. Equalient for the previous `Lua::new()` function is `Lua::unsafe_new()`.
+- Provide safety guarantees for Lua state, which means that potentially unsafe operations, like loading C modules (using `require` or `package.loadlib`) are disabled. Equivalent to the previous `Lua::new()` function is `Lua::unsafe_new()`.
- New `send` feature to require `Send`.
- New `module` feature, that disables linking to Lua Core Libraries. Required for modules.
- Don't allow `'callback` outlive `'lua` in `Lua::create_function()` to fix [the unsoundness](tests/compile/static_callback_args.rs).
diff --git a/Cargo.toml b/Cargo.toml
index fb8a957b..1488205b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,107 +1,124 @@
[package]
name = "mlua"
-version = "0.8.0-beta.4" # remember to update html_root_url and mlua_derive
-authors = ["Aleksandr Orlenko ", "kyren "]
-edition = "2018"
-repository = "https://github.com/khvzak/mlua"
+version = "0.12.0-rc.1" # remember to update mlua_derive
+authors = ["Aleksandr Orlenko ", "kyren "]
+rust-version = "1.88"
+edition = "2024"
+repository = "https://github.com/mlua-rs/mlua"
documentation = "https://docs.rs/mlua"
readme = "README.md"
-keywords = ["lua", "luajit", "async", "futures", "scripting"]
+keywords = ["lua", "luajit", "luau", "async", "scripting"]
categories = ["api-bindings", "asynchronous"]
license = "MIT"
-links = "lua"
-build = "build/main.rs"
description = """
-High level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox Luau
+High level bindings to Lua 5.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", "serialize", "macros", "parking_lot"]
+features = ["lua55", "vendored", "async", "send", "serde", "macros"]
rustdoc-args = ["--cfg", "docsrs"]
[workspace]
members = [
"mlua_derive",
+ "mlua-sys",
]
[features]
-lua54 = []
-lua53 = []
-lua52 = []
-lua51 = []
-luajit = []
-luajit52 = ["luajit"]
-luau = ["luau0-src"]
-vendored = ["lua-src", "luajit-src"]
-module = ["mlua_derive"]
-async = ["futures-core", "futures-task", "futures-util"]
-send = []
-serialize = ["serde", "erased-serde"]
+lua55 = ["ffi/lua55"]
+lua54 = ["ffi/lua54"]
+lua53 = ["ffi/lua53"]
+lua52 = ["ffi/lua52"]
+lua51 = ["ffi/lua51"]
+luajit = ["ffi/luajit"]
+luajit52 = ["luajit", "ffi/luajit52"]
+luau = ["ffi/luau"]
+luau-jit = ["luau", "ffi/luau-codegen"]
+luau-vector4 = ["luau", "ffi/luau-vector4"]
+vendored = ["ffi/vendored"]
+module = ["mlua_derive", "ffi/module"]
+async = ["dep:futures-util"]
+send = ["error-send"]
+error-send = []
+serde = ["dep:serde", "dep:erased-serde", "dep:serde-value", "bstr/serde"]
macros = ["mlua_derive/macros"]
+anyhow = ["dep:anyhow", "error-send"]
+userdata-wrappers = ["parking_lot/send_guard"]
+
+# deprecated features
+serialize = ["serde"]
[dependencies]
-mlua_derive = { version = "=0.8.0-beta.1", optional = true, path = "mlua_derive" }
-bstr = { version = "0.2", features = ["std"], default_features = false }
-once_cell = { version = "1.0" }
+mlua_derive = { version = "=0.11.0", optional = true, path = "mlua_derive" }
+bstr = { version = "1.0", features = ["std"], default-features = false }
+either = "1.0"
num-traits = { version = "0.2.14" }
-rustc-hash = "1.0"
-futures-core = { version = "0.3.5", optional = true }
-futures-task = { version = "0.3.5", optional = true }
-futures-util = { version = "0.3.5", optional = true }
+rustc-hash = "2.0"
+futures-util = { version = "0.3", optional = true, default-features = false, features = ["std"] }
serde = { version = "1.0", optional = true }
-erased-serde = { version = "0.3", optional = true }
-parking_lot = { version = "0.12", optional = true }
+erased-serde = { version = "0.4", optional = true }
+serde-value = { version = "0.7", optional = true }
+parking_lot = { version = "0.12", features = ["arc_lock"] }
+anyhow = { version = "1.0", optional = true }
+libc = "0.2"
-[build-dependencies]
-cc = { version = "1.0" }
-pkg-config = { version = "0.3.17" }
-lua-src = { version = ">= 544.0.0, < 550.0.0", optional = true }
-luajit-src = { version = ">= 210.3.1, < 220.0.0", optional = true }
-luau0-src = { version = "0.3", optional = true }
+ffi = { package = "mlua-sys", version = "0.11.0-rc.1", path = "mlua-sys" }
[dev-dependencies]
-rustyline = "9.0"
-criterion = { version = "0.3.4", features = ["html_reports", "async_tokio"] }
trybuild = "1.0"
-futures = "0.3.5"
-hyper = { version = "0.14", features = ["client", "server"] }
-reqwest = { version = "0.11", features = ["json"] }
-tokio = { version = "1.0", features = ["full"] }
-futures-timer = "3.0"
+tokio = { version = "1.0", features = ["macros", "rt", "time"] }
serde = { version = "1.0", features = ["derive"] }
-serde_json = "1.0"
+serde_json = { version = "1.0", features = ["arbitrary_precision"] }
maplit = "1.0"
+static_assertions = "1.0"
+
+[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.13", features = ["json"] }
tempfile = "3"
+criterion = { version = "0.8", features = ["async_tokio"] }
+rustyline = "18.0"
+tokio = { version = "1.0", features = ["full"] }
+
+[lints.rust]
+unexpected_cfgs = { level = "allow", check-cfg = ['cfg(tarpaulin_include)'] }
[[bench]]
name = "benchmark"
harness = false
required-features = ["async"]
+[[bench]]
+name = "serde"
+harness = false
+required-features = ["serde"]
+
[[example]]
name = "async_http_client"
required-features = ["async", "macros"]
[[example]]
name = "async_http_reqwest"
-required-features = ["async", "serialize", "macros"]
+required-features = ["async", "serde", "macros"]
[[example]]
name = "async_http_server"
-required-features = ["async", "macros"]
+required-features = ["async", "macros", "send"]
[[example]]
name = "async_tcp_server"
-required-features = ["async", "macros"]
+required-features = ["async", "macros", "send"]
[[example]]
name = "guided_tour"
required-features = ["macros"]
[[example]]
-name = "serialize"
-required-features = ["serialize"]
+name = "serde"
+required-features = ["serde"]
[[example]]
name = "userdata"
diff --git a/FAQ.md b/FAQ.md
new file mode 100644
index 00000000..eb018184
--- /dev/null
+++ b/FAQ.md
@@ -0,0 +1,21 @@
+# mlua FAQ
+
+This file is for general questions that don't fit into the README or crate docs.
+
+## Loading a C module fails with error `undefined symbol: lua_xxx`. How to fix?
+
+Add the following rustflags to your [.cargo/config](http://doc.crates.io/config.html) in order to properly export Lua symbols:
+
+```toml
+[target.x86_64-unknown-linux-gnu]
+rustflags = ["-C", "link-args=-rdynamic"]
+
+[target.x86_64-apple-darwin]
+rustflags = ["-C", "link-args=-rdynamic"]
+```
+
+## I want to add support for a Lua VM fork to mlua. Do you accept pull requests?
+
+Adding new feature flag to support a Lua VM fork is a major step that requires huge effort to maintain it.
+Regular updates, testing, checking compatibility, etc.
+That's why I don't plan to support new Lua VM forks or other languages in mlua.
diff --git a/README.md b/README.md
index 35ad9926..b253b571 100644
--- a/README.md
+++ b/README.md
@@ -1,69 +1,82 @@
# mlua
[![Build Status]][github-actions] [![Latest Version]][crates.io] [![API Documentation]][docs.rs] [![Coverage Status]][codecov.io] ![MSRV]
-[Build Status]: https://github.com/khvzak/mlua/workflows/CI/badge.svg
-[github-actions]: https://github.com/khvzak/mlua/actions
+[Build Status]: https://github.com/mlua-rs/mlua/workflows/CI/badge.svg
+[github-actions]: https://github.com/mlua-rs/mlua/actions
[Latest Version]: https://img.shields.io/crates/v/mlua.svg
[crates.io]: https://crates.io/crates/mlua
[API Documentation]: https://docs.rs/mlua/badge.svg
[docs.rs]: https://docs.rs/mlua
-[Coverage Status]: https://codecov.io/gh/khvzak/mlua/branch/master/graph/badge.svg?token=99339FS1CG
-[codecov.io]: https://codecov.io/gh/khvzak/mlua
-[MSRV]: https://img.shields.io/badge/rust-1.53+-brightgreen.svg?&logo=rust
+[Coverage Status]: https://codecov.io/gh/mlua-rs/mlua/branch/main/graph/badge.svg?token=99339FS1CG
+[codecov.io]: https://codecov.io/gh/mlua-rs/mlua
+[MSRV]: https://img.shields.io/badge/rust-1.79+-brightgreen.svg?&logo=rust
-[Guided Tour](examples/guided_tour.rs)
+[Guided Tour] | [Benchmarks] | [FAQ]
-`mlua` is bindings to [Lua](https://www.lua.org) programming language for Rust with a goal to provide
-_safe_ (as far as it's possible), high level, easy to use, practical and flexible API.
+[Guided Tour]: examples/guided_tour.rs
+[Benchmarks]: https://github.com/khvzak/script-bench-rs
+[FAQ]: FAQ.md
-Started as `rlua` fork, `mlua` supports Lua 5.4, 5.3, 5.2, 5.1 (including LuaJIT) and [Roblox Luau] and allows to write native Lua modules in Rust as well as use Lua in a standalone mode.
+## The main branch is the development version of `mlua`. Please see the [v0.11](https://github.com/mlua-rs/mlua/tree/v0.11) branch for the stable versions of `mlua`.
-`mlua` tested on Windows/macOS/Linux including module mode in [GitHub Actions] on `x86_64` platform and cross-compilation to `aarch64` (other targets are also supported).
+`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.
-[GitHub Actions]: https://github.com/khvzak/mlua/actions
-[Roblox Luau]: https://luau-lang.org
+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).
+
+WebAssembly (WASM) is supported through the `wasm32-unknown-emscripten` target for all Lua/Luau versions excluding JIT.
+
+[GitHub Actions]: https://github.com/mlua-rs/mlua/actions
+[Luau]: https://luau.org
## Usage
### Feature flags
-`mlua` uses feature flags to reduce the amount of dependencies, compiled code and allow to choose only required set of features.
+`mlua` uses feature flags to reduce the 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.
-* `lua54`: activate Lua [5.4] support
-* `lua53`: activate Lua [5.3] support
-* `lua52`: activate Lua [5.2] support
-* `lua51`: activate Lua [5.1] support
-* `luajit`: activate [LuaJIT] support
-* `luajit52`: activate [LuaJIT] support with partial compatibility with Lua 5.2
-* `luau`: activate [Luau] support (auto vendored mode)
-* `vendored`: build static Lua(JIT) library from sources during `mlua` compilation using [lua-src] or [luajit-src] crates
+* `lua55`: enable Lua [5.5] support
+* `lua54`: enable Lua [5.4] support
+* `lua53`: enable Lua [5.3] support
+* `lua52`: enable Lua [5.2] support
+* `lua51`: enable Lua [5.1] support
+* `luajit`: enable [LuaJIT] support
+* `luajit52`: enable [LuaJIT] support with partial compatibility with Lua 5.2
+* `luau`: enable [Luau] support (auto vendored mode)
+* `luau-jit`: enable [Luau] support with JIT backend.
+* `luau-vector4`: enable [Luau] support with 4-dimensional vector.
+* `vendored`: build static Lua(JIT) libraries from sources during `mlua` compilation using [lua-src] or [luajit-src]
* `module`: enable module mode (building loadable `cdylib` library for Lua)
* `async`: enable async/await support (any executor can be used, eg. [tokio] or [async-std])
-* `send`: make `mlua::Lua` transferable across thread boundaries (adds [`Send`] requirement to `mlua::Function` and `mlua::UserData`)
-* `serialize`: add serialization and deserialization support to `mlua` types using [serde] framework
+* `send`: make `mlua::Lua: Send + Sync` (adds [`Send`] requirement to `mlua::Function` and `mlua::UserData`)
+* `error-send`: make `mlua:Error: Send + Sync`
+* `serde`: add serialization and deserialization support to `mlua` types using [serde]
* `macros`: enable procedural macros (such as `chunk!`)
-* `parking_lot`: support UserData types wrapped in [parking_lot]'s primitives (`Arc` and `Arc`)
+* `anyhow`: enable `anyhow::Error` conversion into Lua
+* `userdata-wrappers`: opt into `impl UserData` for `Rc`/`Arc`/`Rc>`/`Arc>` 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
[5.1]: https://www.lua.org/manual/5.1/manual.html
[LuaJIT]: https://luajit.org/
-[Luau]: https://github.com/Roblox/luau
-[lua-src]: https://github.com/khvzak/lua-src-rs
-[luajit-src]: https://github.com/khvzak/luajit-src-rs
+[Luau]: https://github.com/luau-lang/luau
+[lua-src]: https://github.com/mlua-rs/lua-src-rs
+[luajit-src]: https://github.com/mlua-rs/luajit-src-rs
[tokio]: https://github.com/tokio-rs/tokio
[async-std]: https://github.com/async-rs/async-std
[`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
[serde]: https://github.com/serde-rs/serde
-[parking_lot]: https://github.com/Amanieu/parking_lot
### Async/await support
`mlua` supports async/await for all Lua versions including Luau.
-This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6) and require running [Thread](https://docs.rs/mlua/latest/mlua/struct.Thread.html) along with enabling `feature = "async"` in `Cargo.toml`.
+This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6) and requires running [Thread](https://docs.rs/mlua/latest/mlua/struct.Thread.html) along with enabling `feature = "async"` in `Cargo.toml`.
**Examples**:
- [HTTP Client](examples/async_http_client.rs)
@@ -71,11 +84,25 @@ This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6
- [HTTP Server](examples/async_http_server.rs)
- [TCP Server](examples/async_tcp_server.rs)
-### Serialization (serde) support
-With `serialize` feature flag enabled, `mlua` allows you to serialize/deserialize any type that implements [`serde::Serialize`] and [`serde::Deserialize`] into/from [`mlua::Value`]. In addition `mlua` provides [`serde::Serialize`] trait implementation for it (including `UserData` support).
+**shell command examples**:
+```shell
+# async http client (hyper)
+cargo run --example async_http_client --features=lua54,async,macros
+
+# async http client (reqwest)
+cargo run --example async_http_reqwest --features=lua54,async,macros,serde
+
+# async http server
+cargo run --example async_http_server --features=lua54,async,macros,send
+curl -v http://localhost:3000
+```
+
+### Serde support
+
+With the `serde` feature flag enabled, `mlua` allows you to serialize/deserialize any type that implements [`serde::Serialize`] and [`serde::Deserialize`] into/from [`mlua::Value`]. In addition, `mlua` provides the [`serde::Serialize`] trait implementation for `mlua::Value` (including `UserData` support).
-[Example](examples/serialize.rs)
+[Example](examples/serde.rs)
[`serde::Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html
[`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
@@ -85,28 +112,28 @@ With `serialize` feature flag enabled, `mlua` allows you to serialize/deserializ
You have to enable one of the features: `lua54`, `lua53`, `lua52`, `lua51`, `luajit(52)` or `luau`, according to the chosen Lua version.
-By default `mlua` uses `pkg-config` tool to find lua includes and libraries for the chosen Lua version.
-In most cases it works as desired, although sometimes could be more preferable to use a custom lua library.
-To achieve this, mlua supports `LUA_INC`, `LUA_LIB`, `LUA_LIB_NAME` and `LUA_LINK` environment variables.
+By default `mlua` uses `pkg-config` to find Lua includes and libraries for the chosen Lua version.
+In most cases it works as desired, although sometimes it may be preferable to use a custom Lua library.
+To achieve this, mlua supports the `LUA_LIB`, `LUA_LIB_NAME` and `LUA_LINK` environment variables.
`LUA_LINK` is optional and may be `dylib` (a dynamic library) or `static` (a static library, `.a` archive).
-An example how to use them:
+An example of how to use them:
``` sh
-my_project $ LUA_INC=$HOME/tmp/lua-5.2.4/src LUA_LIB=$HOME/tmp/lua-5.2.4/src LUA_LIB_NAME=lua LUA_LINK=static cargo build
+my_project $ LUA_LIB=$HOME/tmp/lua-5.2.4/src LUA_LIB_NAME=lua LUA_LINK=static cargo build
```
-`mlua` also supports vendored lua/luajit using the auxiliary crates [lua-src](https://crates.io/crates/lua-src) and
+`mlua` also supports vendored Lua/LuaJIT using the auxiliary crates [lua-src](https://crates.io/crates/lua-src) and
[luajit-src](https://crates.io/crates/luajit-src).
-Just enable the `vendored` feature and cargo will automatically build and link specified lua/luajit version. This is the easiest way to get started with `mlua`.
+Just enable the `vendored` feature and cargo will automatically build and link the specified Lua/LuaJIT version. This is the easiest way to get started with `mlua`.
### Standalone mode
-In a standalone mode `mlua` allows to add to your application scripting support with a gently configured Lua runtime to ensure safety and soundness.
+In standalone mode, `mlua` allows adding scripting support to your application with a properly configured Lua runtime to ensure safety and soundness.
-Add to `Cargo.toml` :
+Add to `Cargo.toml`:
``` toml
[dependencies]
-mlua = { version = "0.8.0-beta.4", features = ["lua54", "vendored"] }
+mlua = { version = "0.11", features = ["lua54", "vendored"] }
```
`main.rs`
@@ -130,21 +157,21 @@ fn main() -> LuaResult<()> {
```
### Module mode
-In a module mode `mlua` allows to create a compiled Lua module that can be loaded from Lua code using [`require`](https://www.lua.org/manual/5.4/manual.html#pdf-require). In this case `mlua` uses an external Lua runtime which could lead to potential unsafety due to unpredictability of the Lua environment and usage of libraries such as [`debug`](https://www.lua.org/manual/5.4/manual.html#6.10).
+In module mode, `mlua` allows creating a compiled Lua module that can be loaded from Lua code using [`require`](https://www.lua.org/manual/5.4/manual.html#pdf-require). In this case `mlua` uses an external Lua runtime which could lead to potential unsafety due to the unpredictability of the Lua environment and usage of libraries such as [`debug`](https://www.lua.org/manual/5.4/manual.html#6.10).
[Example](examples/module)
-Add to `Cargo.toml` :
+Add to `Cargo.toml`:
``` toml
[lib]
crate-type = ["cdylib"]
[dependencies]
-mlua = { version = "0.8.0-beta.4", features = ["lua54", "vendored", "module"] }
+mlua = { version = "0.11", features = ["lua54", "module"] }
```
-`lib.rs` :
+`lib.rs`:
``` rust
use mlua::prelude::*;
@@ -171,7 +198,7 @@ $ lua5.4 -e 'require("my_module").hello("world")'
hello, world!
```
-On macOS, you need to set additional linker arguments. One option is to compile with `cargo rustc --release -- -C link-arg=-undefined -C link-arg=dynamic_lookup`, the other is to create a `.cargo/config` with the following content:
+On macOS, you need to set additional linker arguments. One option is to compile with `cargo rustc --release -- -C link-arg=-undefined -C link-arg=dynamic_lookup`, the other is to create a `.cargo/config.toml` with the following content:
``` toml
[target.x86_64-apple-darwin]
rustflags = [
@@ -186,29 +213,31 @@ rustflags = [
]
```
On Linux you can build modules normally with `cargo build --release`.
-Vendored and non-vendored builds are supported for these OS.
-On Windows `vendored` mode for modules is not supported since you need to link to a Lua dll.
-Easiest way is to use either MinGW64 (as part of [MSYS2](https://github.com/msys2/msys2) package) with `pkg-config` or
-MSVC with `LUA_INC` / `LUA_LIB` / `LUA_LIB_NAME` environment variables.
+On Windows the target module will be linked with the `lua5x.dll` library (depending on your feature flags).
+Your main application should provide this library.
-More details about compiling and linking Lua modules can be found on the [Building Modules](http://lua-users.org/wiki/BuildingModules) page.
+Module builds don't require Lua binaries or headers to be installed on the system.
### Publishing to luarocks.org
-There is a LuaRocks build backend for mlua modules [`luarocks-build-rust-mlua`].
+There is a LuaRocks build backend for mlua modules: [`luarocks-build-rust-mlua`].
Modules written in Rust and published to luarocks:
+- [`decasify`](https://github.com/alerque/decasify)
- [`lua-ryaml`](https://github.com/khvzak/lua-ryaml)
+- [`tiktoken_core`](https://github.com/gptlang/lua-tiktoken)
+- [`toml-edit`](https://github.com/vhyrro/toml-edit.lua)
+- [`typst-lua`](https://github.com/rousbound/typst-lua)
[`luarocks-build-rust-mlua`]: https://luarocks.org/modules/khvzak/luarocks-build-rust-mlua
## Safety
-One of the `mlua` goals is to provide *safe* API between Rust and Lua.
-Every place where the Lua C API may trigger an error longjmp in any way is protected by `lua_pcall`,
-and the user of the library is protected from directly interacting with unsafe things like the Lua stack,
-and there is overhead associated with this safety.
+One of `mlua`'s goals is to provide a *safe* API between Rust and Lua.
+Every place where the Lua C API may trigger an error longjmp is protected by `lua_pcall`,
+and the user of the library is protected from directly interacting with unsafe things like the Lua stack.
+There is overhead associated with this safety.
Unfortunately, `mlua` does not provide absolute safety even without using `unsafe` .
This library contains a huge amount of unsafe code. There are almost certainly bugs still lurking in this library!
@@ -216,8 +245,8 @@ It is surprisingly, fiendishly difficult to use the Lua C API without the potent
## Panic handling
-`mlua` wraps panics that are generated inside Rust callbacks in a regular Lua error. Panics could be
-resumed then by returning or propagating the Lua error to Rust code.
+`mlua` wraps panics that are generated inside Rust callbacks in a regular Lua error. Panics can then be
+resumed by returning or propagating the Lua error to Rust code.
For example:
``` rust
@@ -236,16 +265,16 @@ let _ = lua.load(r#"
unreachable!()
```
-Optionally `mlua` can disable Rust panics catching in Lua via `pcall`/`xpcall` and automatically resume
+Optionally, `mlua` can disable Rust panic catching in Lua via `pcall`/`xpcall` and automatically resume
them across the Lua API boundary. This is controlled via `LuaOptions` and done by wrapping the Lua `pcall`/`xpcall`
-functions on a way to prevent catching errors that are wrapped Rust panics.
+functions to prevent catching errors that are wrapped Rust panics.
`mlua` should also be panic safe in another way as well, which is that any `Lua` instances or handles
-remains usable after a user generated panic, and such panics should not break internal invariants or
+remain usable after a user generated panic, and such panics should not break internal invariants or
leak Lua stack space. This is mostly important to safely use `mlua` types in Drop impls, as you should not be
using panics for general error handling.
-Below is a list of `mlua` behaviors that should be considered a bug.
+Below is a list of `mlua` behaviors that should be considered bugs.
If you encounter them, a bug report would be very welcome:
+ If you can cause UB with `mlua` without typing the word "unsafe", this is a bug.
@@ -256,6 +285,14 @@ If you encounter them, a bug report would be very welcome:
+ If you detect that, after catching a panic or during a Drop triggered from a panic, a `Lua` or handle method is triggering other bugs or there is a Lua stack space leak, this is a bug. `mlua` instances are supposed to remain fully usable in the face of user generated panics. This guarantee does not extend to panics marked with "mlua internal error" simply because that is already indicative of a separate bug.
+## Sandboxing
+
+Please check the [Luau Sandboxing] page if you are interested in running untrusted Lua scripts in a controlled environment.
+
+`mlua` provides the `Lua::sandbox` method for enabling sandbox mode (Luau only).
+
+[Luau Sandboxing]: https://luau.org/sandbox
+
## License
-This project is licensed under the [MIT license](LICENSE)
+This project is licensed under the [MIT license](LICENSE).
diff --git a/benches/benchmark.rs b/benches/benchmark.rs
index 346ee7a8..3e3bcb96 100644
--- a/benches/benchmark.rs
+++ b/benches/benchmark.rs
@@ -1,5 +1,7 @@
-use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
+use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
+
+use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
use tokio::runtime::Runtime;
use tokio::task;
@@ -10,10 +12,10 @@ fn collect_gc_twice(lua: &Lua) {
lua.gc_collect().unwrap();
}
-fn create_table(c: &mut Criterion) {
+fn table_create_empty(c: &mut Criterion) {
let lua = Lua::new();
- c.bench_function("create [table empty]", |b| {
+ c.bench_function("table [create empty]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
@@ -24,34 +26,101 @@ fn create_table(c: &mut Criterion) {
});
}
-fn create_array(c: &mut Criterion) {
+fn table_create_array(c: &mut Criterion) {
let lua = Lua::new();
- c.bench_function("create [array] 10", |b| {
+ c.bench_function("table [create array]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
- let table = lua.create_table().unwrap();
- for i in 1..=10 {
- table.set(i, i).unwrap();
- }
+ lua.create_sequence_from(1..=10).unwrap();
},
BatchSize::SmallInput,
);
});
}
-fn create_string_table(c: &mut Criterion) {
+fn table_create_hash(c: &mut Criterion) {
let lua = Lua::new();
- c.bench_function("create [table string] 10", |b| {
+ c.bench_function("table [create hash]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
- let table = lua.create_table().unwrap();
- for &s in &["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"] {
- let s = lua.create_string(s).unwrap();
- table.set(s.clone(), s).unwrap();
+ lua.create_table_from(
+ ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
+ .into_iter()
+ .map(|s| (s, s)),
+ )
+ .unwrap();
+ },
+ BatchSize::SmallInput,
+ );
+ });
+}
+
+fn table_get_set(c: &mut Criterion) {
+ let lua = Lua::new();
+
+ c.bench_function("table [get and set]", |b| {
+ b.iter_batched(
+ || {
+ collect_gc_twice(&lua);
+ lua.create_table().unwrap()
+ },
+ |table| {
+ for (i, s) in ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
+ .into_iter()
+ .enumerate()
+ {
+ table.raw_set(s, i).unwrap();
+ assert_eq!(table.raw_get::(s).unwrap(), i);
+ }
+ },
+ BatchSize::SmallInput,
+ );
+ });
+}
+
+fn table_traversal_pairs(c: &mut Criterion) {
+ let lua = Lua::new();
+
+ c.bench_function("table [traversal pairs]", |b| {
+ b.iter_batched(
+ || lua.globals(),
+ |globals| {
+ for kv in globals.pairs::() {
+ let (_k, _v) = kv.unwrap();
+ }
+ },
+ BatchSize::SmallInput,
+ );
+ });
+}
+
+fn table_traversal_for_each(c: &mut Criterion) {
+ let lua = Lua::new();
+
+ c.bench_function("table [traversal for_each]", |b| {
+ b.iter_batched(
+ || lua.globals(),
+ |globals| globals.for_each::(|_k, _v| Ok(())),
+ BatchSize::SmallInput,
+ );
+ });
+}
+
+fn table_traversal_sequence(c: &mut Criterion) {
+ let lua = Lua::new();
+
+ let table = lua.create_sequence_from(1..1000).unwrap();
+
+ c.bench_function("table [traversal sequence]", |b| {
+ b.iter_batched(
+ || table.clone(),
+ |table| {
+ for v in table.sequence_values::() {
+ let _i = v.unwrap();
}
},
BatchSize::SmallInput,
@@ -59,234 +128,309 @@ fn create_string_table(c: &mut Criterion) {
});
}
-fn create_function(c: &mut Criterion) {
+fn table_ref_clone(c: &mut Criterion) {
let lua = Lua::new();
- c.bench_function("create [function] 10", |b| {
+ let t = lua.create_table().unwrap();
+
+ c.bench_function("table [ref clone]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
- for i in 0..10 {
- lua.create_function(move |_, ()| Ok(i)).unwrap();
- }
+ let _t2 = t.clone();
+ },
+ BatchSize::SmallInput,
+ );
+ });
+}
+
+fn function_create(c: &mut Criterion) {
+ let lua = Lua::new();
+
+ c.bench_function("function [create Rust]", |b| {
+ b.iter_batched(
+ || collect_gc_twice(&lua),
+ |_| {
+ lua.create_function(|_, ()| Ok(123)).unwrap();
+ },
+ BatchSize::SmallInput,
+ );
+ });
+}
+
+fn function_call_sum(c: &mut Criterion) {
+ let lua = Lua::new();
+
+ let sum = lua
+ .create_function(|_, (a, b, c): (i64, i64, i64)| Ok(a + b - c))
+ .unwrap();
+
+ c.bench_function("function [call Rust sum]", |b| {
+ b.iter_batched(
+ || collect_gc_twice(&lua),
+ |_| {
+ assert_eq!(sum.call::((10, 20, 30)).unwrap(), 0);
+ },
+ BatchSize::SmallInput,
+ );
+ });
+}
+
+fn function_call_lua_sum(c: &mut Criterion) {
+ let lua = Lua::new();
+
+ let sum = lua
+ .load("function(a, b, c) return a + b - c end")
+ .eval::()
+ .unwrap();
+
+ c.bench_function("function [call Lua sum]", |b| {
+ b.iter_batched(
+ || collect_gc_twice(&lua),
+ |_| {
+ assert_eq!(sum.call::((10, 20, 30)).unwrap(), 0);
},
BatchSize::SmallInput,
);
});
}
-fn call_lua_function(c: &mut Criterion) {
+fn function_call_concat(c: &mut Criterion) {
let lua = Lua::new();
- c.bench_function("call Lua function [sum] 3 10", |b| {
- b.iter_batched_ref(
+ let concat = lua
+ .create_function(|_, (a, b): (LuaString, LuaString)| Ok(format!("{}{}", a.to_str()?, b.to_str()?)))
+ .unwrap();
+ let i = AtomicUsize::new(0);
+
+ c.bench_function("function [call Rust concat string]", |b| {
+ b.iter_batched(
|| {
collect_gc_twice(&lua);
- lua.load("function(a, b, c) return a + b + c end")
- .eval::()
- .unwrap()
+ i.fetch_add(1, Ordering::Relaxed)
},
- |function| {
- for i in 0..10 {
- let _result: i64 = function.call((i, i + 1, i + 2)).unwrap();
- }
+ |i| {
+ assert_eq!(concat.call::(("num:", i)).unwrap(), format!("num:{i}"));
},
BatchSize::SmallInput,
);
});
}
-fn call_sum_callback(c: &mut Criterion) {
+fn function_call_lua_concat(c: &mut Criterion) {
let lua = Lua::new();
- let callback = lua
- .create_function(|_, (a, b, c): (i64, i64, i64)| Ok(a + b + c))
+
+ let concat = lua
+ .load("function(a, b) return a..b end")
+ .eval::()
.unwrap();
- lua.globals().set("callback", callback).unwrap();
+ let i = AtomicUsize::new(0);
- c.bench_function("call Rust callback [sum] 3 10", |b| {
- b.iter_batched_ref(
+ c.bench_function("function [call Lua concat string]", |b| {
+ b.iter_batched(
|| {
collect_gc_twice(&lua);
- lua.load("function() for i = 1,10 do callback(i, i+1, i+2) end end")
- .eval::()
- .unwrap()
+ i.fetch_add(1, Ordering::Relaxed)
},
- |function| {
- function.call::<_, ()>(()).unwrap();
+ |i| {
+ assert_eq!(concat.call::(("num:", i)).unwrap(), format!("num:{i}"));
},
BatchSize::SmallInput,
);
});
}
-fn call_async_sum_callback(c: &mut Criterion) {
- let options = LuaOptions::new().thread_cache_size(1024);
+fn function_async_call_sum(c: &mut Criterion) {
+ let options = LuaOptions::new().thread_pool_size(1024);
let lua = Lua::new_with(LuaStdLib::ALL_SAFE, options).unwrap();
- let callback = lua
+
+ let sum = lua
.create_async_function(|_, (a, b, c): (i64, i64, i64)| async move {
task::yield_now().await;
- Ok(a + b + c)
+ Ok(a + b - c)
})
.unwrap();
- lua.globals().set("callback", callback).unwrap();
- c.bench_function("call async Rust callback [sum] 3 10", |b| {
+ c.bench_function("function [async call Rust sum]", |b| {
let rt = Runtime::new().unwrap();
b.to_async(rt).iter_batched(
- || {
- collect_gc_twice(&lua);
- lua.load("function() for i = 1,10 do callback(i, i+1, i+2) end end")
- .eval::()
- .unwrap()
- },
- |function| async move {
- function.call_async::<_, ()>(()).await.unwrap();
+ || collect_gc_twice(&lua),
+ |_| async {
+ assert_eq!(sum.call_async::((10, 20, 30)).await.unwrap(), 0);
},
BatchSize::SmallInput,
);
});
}
-fn call_concat_callback(c: &mut Criterion) {
+fn registry_value_create(c: &mut Criterion) {
let lua = Lua::new();
- let callback = lua
- .create_function(|_, (a, b): (LuaString, LuaString)| {
- Ok(format!("{}{}", a.to_str()?, b.to_str()?))
- })
- .unwrap();
- lua.globals().set("callback", callback).unwrap();
+ lua.gc_stop();
- c.bench_function("call Rust callback [concat string] 10", |b| {
- b.iter_batched_ref(
- || {
- collect_gc_twice(&lua);
- lua.load("function() for i = 1,10 do callback('a', tostring(i)) end end")
- .eval::()
- .unwrap()
- },
- |function| {
- function.call::<_, ()>(()).unwrap();
- },
+ c.bench_function("registry value [create]", |b| {
+ b.iter_batched(
+ || collect_gc_twice(&lua),
+ |_| lua.create_registry_value("hello").unwrap(),
BatchSize::SmallInput,
);
});
}
-fn create_registry_values(c: &mut Criterion) {
+fn registry_value_get(c: &mut Criterion) {
let lua = Lua::new();
+ lua.gc_stop();
+
+ let value = lua.create_registry_value("hello").unwrap();
- c.bench_function("create [registry value] 10", |b| {
+ c.bench_function("registry value [get]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
- for _ in 0..10 {
- lua.create_registry_value(lua.pack(true).unwrap()).unwrap();
- }
- lua.expire_registry_values();
+ assert_eq!(lua.registry_value::(&value).unwrap(), "hello");
},
BatchSize::SmallInput,
);
});
}
-fn create_userdata(c: &mut Criterion) {
- struct UserData(i64);
+fn userdata_create(c: &mut Criterion) {
+ struct UserData(#[allow(unused)] i64);
impl LuaUserData for UserData {}
let lua = Lua::new();
- c.bench_function("create [table userdata] 10", |b| {
+ c.bench_function("userdata [create]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
- let table: LuaTable = lua.create_table().unwrap();
- for i in 1..11 {
- table.set(i, UserData(i)).unwrap();
- }
+ lua.create_userdata(UserData(123)).unwrap();
+ },
+ BatchSize::SmallInput,
+ );
+ });
+}
+
+fn userdata_call_index(c: &mut Criterion) {
+ struct UserData(#[allow(unused)] i64);
+ impl LuaUserData for UserData {
+ fn add_methods>(methods: &mut M) {
+ methods.add_meta_method(LuaMetaMethod::Index, move |_, _, key: LuaString| Ok(key));
+ }
+ }
+
+ let lua = Lua::new();
+ let ud = lua.create_userdata(UserData(123)).unwrap();
+ let index = lua
+ .load("function(ud) return ud.test end")
+ .eval::()
+ .unwrap();
+
+ c.bench_function("userdata [call index]", |b| {
+ b.iter_batched(
+ || collect_gc_twice(&lua),
+ |_| {
+ assert_eq!(index.call::(&ud).unwrap(), "test");
},
BatchSize::SmallInput,
);
});
}
-fn call_userdata_index(c: &mut Criterion) {
+fn userdata_call_method(c: &mut Criterion) {
struct UserData(i64);
impl LuaUserData for UserData {
- fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
- methods.add_meta_method(LuaMetaMethod::Index, move |_, _, index: String| Ok(index));
+ fn add_methods>(methods: &mut M) {
+ methods.add_method("add", |_, this, i: i64| Ok(this.0 + i));
}
}
let lua = Lua::new();
- lua.globals().set("userdata", UserData(10)).unwrap();
+ let ud = lua.create_userdata(UserData(123)).unwrap();
+ let method = lua
+ .load("function(ud, i) return ud:add(i) end")
+ .eval::()
+ .unwrap();
+ let i = AtomicUsize::new(0);
- c.bench_function("call [userdata index] 10", |b| {
- b.iter_batched_ref(
+ c.bench_function("userdata [call method]", |b| {
+ b.iter_batched(
|| {
collect_gc_twice(&lua);
- lua.load("function() for i = 1,10 do local v = userdata.test end end")
- .eval::()
- .unwrap()
+ i.fetch_add(1, Ordering::Relaxed)
},
- |function| {
- function.call::<_, ()>(()).unwrap();
+ |i| {
+ assert_eq!(method.call::((&ud, i)).unwrap(), 123 + i);
},
BatchSize::SmallInput,
);
});
}
-fn call_userdata_method(c: &mut Criterion) {
- struct UserData(i64);
+// A userdata method call that goes through an implicit `__index` function
+fn userdata_call_method_complex(c: &mut Criterion) {
+ struct UserData(u64);
impl LuaUserData for UserData {
- fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
- methods.add_method("method", |_, this, ()| Ok(this.0));
+ fn register(registry: &mut LuaUserDataRegistry) {
+ registry.add_field_method_get("val", |_, this| Ok(this.0));
+ registry.add_method_mut("inc_by", |_, this, by: u64| {
+ this.0 += by;
+ Ok(this.0)
+ });
+
+ #[cfg(feature = "luau")]
+ registry.enable_namecall();
}
}
let lua = Lua::new();
- lua.globals().set("userdata", UserData(10)).unwrap();
+ let ud = lua.create_userdata(UserData(0)).unwrap();
+ let inc_by = lua
+ .load("function(ud, s) return ud:inc_by(s) end")
+ .eval::()
+ .unwrap();
- c.bench_function("call [userdata method] 10", |b| {
- b.iter_batched_ref(
+ c.bench_function("userdata [call method complex]", |b| {
+ b.iter_batched(
|| {
collect_gc_twice(&lua);
- lua.load("function() for i = 1,10 do userdata:method() end end")
- .eval::()
- .unwrap()
},
- |function| {
- function.call::<_, ()>(()).unwrap();
+ |_| {
+ inc_by.call::<()>((&ud, 1)).unwrap();
},
BatchSize::SmallInput,
);
});
}
-fn call_async_userdata_method(c: &mut Criterion) {
- #[derive(Clone, Copy)]
+fn userdata_async_call_method(c: &mut Criterion) {
struct UserData(i64);
impl LuaUserData for UserData {
- fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
- methods.add_async_method("method", |_, this, ()| async move { Ok(this.0) });
+ fn add_methods>(methods: &mut M) {
+ methods.add_async_method("add", |_, this, i: i64| async move {
+ task::yield_now().await;
+ Ok(this.0 + i)
+ });
}
}
- let options = LuaOptions::new().thread_cache_size(1024);
+ let options = LuaOptions::new().thread_pool_size(1024);
let lua = Lua::new_with(LuaStdLib::ALL_SAFE, options).unwrap();
- lua.globals().set("userdata", UserData(10)).unwrap();
+ let ud = lua.create_userdata(UserData(123)).unwrap();
+ let method = lua
+ .load("function(ud, i) return ud:add(i) end")
+ .eval::()
+ .unwrap();
+ let i = AtomicUsize::new(0);
- c.bench_function("call async [userdata method] 10", |b| {
+ c.bench_function("userdata [async call method] 10", |b| {
let rt = Runtime::new().unwrap();
b.to_async(rt).iter_batched(
|| {
collect_gc_twice(&lua);
- lua.load("function() for i = 1,10 do userdata:method() end end")
- .eval::()
- .unwrap()
+ (method.clone(), ud.clone(), i.fetch_add(1, Ordering::Relaxed))
},
- |function| async move {
- function.call_async::<_, ()>(()).await.unwrap();
+ |(method, ud, i)| async move {
+ assert_eq!(method.call_async::((ud, i)).await.unwrap(), 123 + i);
},
BatchSize::SmallInput,
);
@@ -296,23 +440,34 @@ fn call_async_userdata_method(c: &mut Criterion) {
criterion_group! {
name = benches;
config = Criterion::default()
- .sample_size(300)
+ .sample_size(500)
.measurement_time(Duration::from_secs(10))
.noise_threshold(0.02);
targets =
- create_table,
- create_array,
- create_string_table,
- create_function,
- call_lua_function,
- call_sum_callback,
- call_async_sum_callback,
- call_concat_callback,
- create_registry_values,
- create_userdata,
- call_userdata_index,
- call_userdata_method,
- call_async_userdata_method,
+ table_create_empty,
+ table_create_array,
+ table_create_hash,
+ table_get_set,
+ table_traversal_pairs,
+ table_traversal_for_each,
+ table_traversal_sequence,
+ table_ref_clone,
+
+ function_create,
+ function_call_sum,
+ function_call_lua_sum,
+ function_call_concat,
+ function_call_lua_concat,
+ function_async_call_sum,
+
+ registry_value_create,
+ registry_value_get,
+
+ userdata_create,
+ userdata_call_index,
+ userdata_call_method,
+ userdata_call_method_complex,
+ userdata_async_call_method,
}
criterion_main!(benches);
diff --git a/benches/serde.rs b/benches/serde.rs
new file mode 100644
index 00000000..002061ac
--- /dev/null
+++ b/benches/serde.rs
@@ -0,0 +1,90 @@
+use std::time::Duration;
+
+use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
+
+use mlua::prelude::*;
+
+fn collect_gc_twice(lua: &Lua) {
+ lua.gc_collect().unwrap();
+ lua.gc_collect().unwrap();
+}
+
+fn encode_json(c: &mut Criterion) {
+ let lua = Lua::new();
+
+ let encode = lua
+ .create_function(|_, t: LuaValue| Ok(serde_json::to_string(&t).unwrap()))
+ .unwrap();
+ let table = lua
+ .load(
+ r#"{
+ name = "Clark Kent",
+ address = {
+ city = "Smallville",
+ state = "Kansas",
+ country = "USA",
+ },
+ age = 22,
+ parents = {"Jonathan Kent", "Martha Kent"},
+ superman = true,
+ interests = {"flying", "saving the world", "kryptonite"},
+ }"#,
+ )
+ .eval::()
+ .unwrap();
+
+ c.bench_function("serialize json", |b| {
+ b.iter_batched(
+ || collect_gc_twice(&lua),
+ |_| {
+ encode.call::(&table).unwrap();
+ },
+ BatchSize::SmallInput,
+ );
+ });
+}
+
+fn decode_json(c: &mut Criterion) {
+ let lua = Lua::new();
+
+ let decode = lua
+ .create_function(|lua, s: String| {
+ lua.to_value(&serde_json::from_str::(&s).unwrap())
+ })
+ .unwrap();
+ let json = r#"{
+ "name": "Clark Kent",
+ "address": {
+ "city": "Smallville",
+ "state": "Kansas",
+ "country": "USA"
+ },
+ "age": 22,
+ "parents": ["Jonathan Kent", "Martha Kent"],
+ "superman": true,
+ "interests": ["flying", "saving the world", "kryptonite"]
+ }"#;
+
+ c.bench_function("deserialize json", |b| {
+ b.iter_batched(
+ || collect_gc_twice(&lua),
+ |_| {
+ decode.call::(json).unwrap();
+ },
+ BatchSize::SmallInput,
+ );
+ });
+}
+
+criterion_group! {
+ name = benches;
+ config = Criterion::default()
+ .sample_size(500)
+ .measurement_time(Duration::from_secs(10))
+ .noise_threshold(0.02);
+ targets =
+ encode_json,
+ decode_json,
+}
+
+criterion_main!(benches);
diff --git a/build/find_dummy.rs b/build/find_dummy.rs
deleted file mode 100644
index d8ad44b5..00000000
--- a/build/find_dummy.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-use std::path::PathBuf;
-
-pub fn probe_lua() -> Option {
- None
-}
diff --git a/build/find_normal.rs b/build/find_normal.rs
deleted file mode 100644
index 3e72609d..00000000
--- a/build/find_normal.rs
+++ /dev/null
@@ -1,93 +0,0 @@
-#![allow(dead_code)]
-
-use std::env;
-use std::ops::Bound;
-use std::path::PathBuf;
-
-fn get_env_var(name: &str) -> String {
- match env::var(name) {
- Ok(val) => val,
- Err(env::VarError::NotPresent) => String::new(),
- Err(err) => panic!("cannot get {}: {}", name, err),
- }
-}
-
-pub fn probe_lua() -> Option {
- let include_dir = get_env_var("LUA_INC");
- let lib_dir = get_env_var("LUA_LIB");
- let lua_lib = get_env_var("LUA_LIB_NAME");
-
- println!("cargo:rerun-if-env-changed=LUA_INC");
- println!("cargo:rerun-if-env-changed=LUA_LIB");
- println!("cargo:rerun-if-env-changed=LUA_LIB_NAME");
- println!("cargo:rerun-if-env-changed=LUA_LINK");
-
- let need_lua_lib = cfg!(any(not(feature = "module"), target_os = "windows"));
-
- if !include_dir.is_empty() {
- if need_lua_lib {
- if lib_dir.is_empty() {
- panic!("LUA_LIB is not set");
- }
- if lua_lib.is_empty() {
- panic!("LUA_LIB_NAME is not set");
- }
-
- let mut link_lib = "";
- if get_env_var("LUA_LINK") == "static" {
- link_lib = "static=";
- };
- println!("cargo:rustc-link-search=native={}", lib_dir);
- println!("cargo:rustc-link-lib={}{}", link_lib, lua_lib);
- }
- return Some(PathBuf::from(include_dir));
- }
-
- // Find using `pkg-config`
-
- #[cfg(feature = "lua54")]
- let (incl_bound, excl_bound, alt_probe, ver) = ("5.4", "5.5", "lua5.4", "5.4");
- #[cfg(feature = "lua53")]
- let (incl_bound, excl_bound, alt_probe, ver) = ("5.3", "5.4", "lua5.3", "5.3");
- #[cfg(feature = "lua52")]
- let (incl_bound, excl_bound, alt_probe, ver) = ("5.2", "5.3", "lua5.2", "5.2");
- #[cfg(feature = "lua51")]
- let (incl_bound, excl_bound, alt_probe, ver) = ("5.1", "5.2", "lua5.1", "5.1");
-
- #[cfg(any(
- feature = "lua54",
- feature = "lua53",
- feature = "lua52",
- feature = "lua51"
- ))]
- {
- let mut lua = pkg_config::Config::new()
- .range_version((Bound::Included(incl_bound), Bound::Excluded(excl_bound)))
- .cargo_metadata(need_lua_lib)
- .probe("lua");
-
- if lua.is_err() {
- lua = pkg_config::Config::new()
- .cargo_metadata(need_lua_lib)
- .probe(alt_probe);
- }
-
- lua.unwrap_or_else(|_| panic!("cannot find Lua {} using `pkg-config`", ver))
- .include_paths
- .get(0)
- .cloned()
- }
-
- #[cfg(feature = "luajit")]
- {
- let lua = pkg_config::Config::new()
- .range_version((Bound::Included("2.0.4"), Bound::Unbounded))
- .cargo_metadata(need_lua_lib)
- .probe("luajit");
-
- lua.expect("cannot find LuaJIT using `pkg-config`")
- .include_paths
- .get(0)
- .cloned()
- }
-}
diff --git a/build/main.rs b/build/main.rs
deleted file mode 100644
index 5cb979a9..00000000
--- a/build/main.rs
+++ /dev/null
@@ -1,115 +0,0 @@
-#[cfg_attr(
- any(
- feature = "luau",
- all(
- feature = "vendored",
- any(
- feature = "lua54",
- feature = "lua53",
- feature = "lua52",
- feature = "lua51",
- feature = "luajit"
- )
- )
- ),
- path = "find_vendored.rs"
-)]
-#[cfg_attr(
- all(
- not(feature = "vendored"),
- any(
- feature = "lua54",
- feature = "lua53",
- feature = "lua52",
- feature = "lua51",
- feature = "luajit"
- )
- ),
- path = "find_normal.rs"
-)]
-#[cfg_attr(
- not(any(
- feature = "lua54",
- feature = "lua53",
- feature = "lua52",
- feature = "lua51",
- feature = "luajit",
- feature = "luau"
- )),
- path = "find_dummy.rs"
-)]
-mod find;
-
-fn main() {
- #[cfg(not(any(
- feature = "lua54",
- feature = "lua53",
- feature = "lua52",
- feature = "lua51",
- feature = "luajit",
- feature = "luau"
- )))]
- compile_error!(
- "You must enable one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
- );
-
- #[cfg(all(
- feature = "lua54",
- any(
- feature = "lua53",
- feature = "lua52",
- feature = "lua51",
- feature = "luajit",
- feature = "luau"
- )
- ))]
- compile_error!(
- "You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
- );
-
- #[cfg(all(
- feature = "lua53",
- any(
- feature = "lua52",
- feature = "lua51",
- feature = "luajit",
- feature = "luau"
- )
- ))]
- compile_error!(
- "You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
- );
-
- #[cfg(all(
- feature = "lua52",
- any(feature = "lua51", feature = "luajit", feature = "luau")
- ))]
- compile_error!(
- "You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
- );
-
- #[cfg(all(feature = "lua51", any(feature = "luajit", feature = "luau")))]
- compile_error!(
- "You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
- );
-
- #[cfg(all(feature = "luajit", feature = "luau"))]
- compile_error!(
- "You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
- );
-
- // We don't support "vendored module" mode on windows
- #[cfg(all(feature = "vendored", feature = "module", target_os = "windows"))]
- compile_error!(
- "Vendored (static) builds are not supported for modules on Windows.\n"
- + "Please, use `pkg-config` or custom mode to link to a Lua dll."
- );
-
- #[cfg(all(feature = "luau", feature = "module"))]
- compile_error!("Luau does not support module mode");
-
- #[cfg(any(not(feature = "module"), target_os = "windows"))]
- find::probe_lua();
-
- println!("cargo:rerun-if-changed=build");
-}
diff --git a/docs/release_notes/v0.10.md b/docs/release_notes/v0.10.md
new file mode 100644
index 00000000..2684a4f3
--- /dev/null
+++ b/docs/release_notes/v0.10.md
@@ -0,0 +1,195 @@
+## mlua v0.10 release notes
+
+The v0.10 version of mlua has a goal to improve the user experience while keeping the same performance and safety guarantees.
+This document highlights the most notable features. For a full list of changes, see the [CHANGELOG].
+
+[CHANGELOG]: https://github.com/mlua-rs/mlua/blob/main/CHANGELOG.md
+
+### New features
+
+#### `'static` Lua types
+
+In previous mlua versions, it was required to have a `'lua` lifetime attached to every Lua value. v0.9 introduced (experimental) owned types that are `'static` without a lifetime attached, but they kept strong references to the Lua instance.
+In v0.10 all Lua types are `'static` and have only weak reference to the Lua instance. It means they are more flexible and can be used in more places without worrying about memory leaks.
+
+#### Truly `send` feature
+
+In this version Lua is `Send + Sync` when the `send` feature flag is enabled (previously was only `Send`). It means Lua instance and their values can be safely shared between threads and used in multi threaded async contexts.
+
+```rust
+let lua = Lua::new();
+
+lua.globals().set("i", 0)?;
+let func = lua.load("i = i + ...").into_function()?;
+
+std::thread::scope(|s| {
+ s.spawn(|| {
+ for i in 0..5 {
+ func.call::<()>(i).unwrap();
+ }
+ });
+ s.spawn(|| {
+ for i in 0..5 {
+ func.call::<()>(i).unwrap();
+ }
+ });
+});
+
+assert_eq!(lua.globals().get::("i")?, 20);
+```
+
+Under the hood, to synchronize access to the Lua state, mlua uses [`ReentrantMutex`] which can be recursively locked by a single thread. Only one thread can execute Lua code at a time, but it's possible to share Lua values between threads.
+
+This has some performance penalties (about 10-20%) compared to the lock free mode. This flag is disabled by default and is not supported in module mode.
+
+[`ReentrantMutex`]: https://docs.rs/parking_lot/latest/parking_lot/type.ReentrantMutex.html
+
+#### Register Rust functions with variable number of arguments
+
+The new traits `LuaNativeFn`/`LuaNativeFnMut`/`LuaNativeAsyncFn` have been introduced to provide a way to register Rust functions with variable number of arguments in Lua, without needing to pass all arguments as a tuple.
+
+They are used by `Function::wrap`/`Function::wrap_mut`/`Function::wrap_async` methods:
+
+```rust
+let add = Function::wrap(|a: i64, b: i64| Ok(a + b));
+
+lua.globals().set("add", add).unwrap();
+
+// Prints 50
+lua.load(r#"print(add(5, 45))"#).exec().unwrap();
+```
+
+To wrap functions that return direct value (non-`Result`) you can use `Function::wrap_raw` method.
+
+#### Setting metatable for Lua builtin types
+
+For Lua builtin types (like `string`, `function`, `number`, etc.) that have a shared metatable for all instances, it's now possible to set a custom metatable for them.
+
+```rust
+let mt = lua.create_table()?;
+mt.set("__tostring", lua.create_function(|_, b: bool| Ok(if b { "2" } else { "0" }))?)?;
+lua.set_type_metatable::(Some(mt));
+lua.load("assert(tostring(true) == '2')").exec().unwrap();
+```
+
+### Improvements
+
+#### New `ObjectLike` trait
+
+The `ObjectLike` trait is a combination of the `AnyUserDataExt` and `TableExt` traits used in previous versions. It provides a unified interface for working with Lua tables and userdata.
+
+#### `Either` enum
+
+The `Either` enum is a simple enum that can hold either `L` or `R` value. It's useful when you need to return or receive one of two types in a function.
+This type implements `IntoLua` and `FromLua` traits and can generate a meaningful error message when conversion fails.
+
+```rust
+let func = Function::wrap(|x: Either| Ok(format!("received: {x}")));
+
+lua.globals().set("func", func).unwrap();
+
+// Prints: received: 123
+lua.load(r#"print(func(123))"#).exec().unwrap();
+
+// Prints: bad argument #1: error converting Lua table to Either
+lua.load(r#"print(pcall(func, {}))"#).exec().unwrap();
+```
+
+#### `Lua::exec_raw` helper to execute low-level Lua C API code
+
+For advanced users, it's now possible to execute low-level Lua C API code using the `Lua::exec_raw` method.
+
+```rust
+let t = lua.create_sequence_from([1, 2, 3, 4, 5])?;
+let sum: i64 = unsafe {
+ lua.exec_raw(&t, |state| {
+ // top of the stack: table `t`
+ let mut sum = 0;
+ // push nil as the first key
+ mlua::ffi::lua_pushnil(state);
+ while mlua::ffi::lua_next(state, -2) != 0 {
+ sum += mlua::ffi::lua_tointeger(state, -1);
+ // Remove the value, keep the key for the next iteration
+ mlua::ffi::lua_pop(state, 1);
+ }
+ mlua::ffi::lua_pop(state, 1);
+ mlua::ffi::lua_pushinteger(state, sum);
+ // top of the stack: sum
+ })
+}?;
+assert_eq!(sum, 15);
+```
+
+The `exec_raw` method is longjmp-safe. It's not recommended to move `Drop` types into the closure to avoid possible memory leaks.
+
+#### `anyhow` feature flag
+
+The new `anyhow` feature flag adds `IntoLua` and `Into` implementation for the `anyhow::Error` type.
+
+```rust
+let f = lua.create_function(|_, ()| {
+ Err(anyhow!("error message"))?;
+ Ok(())
+})?;
+```
+
+### Breaking changes
+
+#### Scope changes
+
+The following `Scope` methods were changed:
+- Removed `Scope::create_any_userdata`
+- `Scope::create_nonstatic_userdata` is renamed to `Scope::create_userdata`
+
+Instead, scope has comprehensive support for borrowed userdata: `create_any_userdata_ref`, `create_any_userdata_ref_mut`, `create_userdata_ref`, `create_userdata_ref_mut`.
+
+`UserDataRef` and `UserDataRefMut` are no longer acceptable for scoped userdata access as they require owned underlying data.
+In mlua v0.9 this could cause a read-after-free bug in some edge cases.
+
+To temporarily borrow underlying data, the `AnyUserData::borrow_scoped` and `AnyUserData::borrow_mut_scoped` methods were introduced:
+
+```rust
+let data = "hello".to_string();
+lua.scope(|scope| {
+ let ud = scope.create_any_userdata_ref(&data)?;
+
+ // We can only borrow scoped userdata using this method
+ ud.borrow_scoped::(|s| {
+ assert_eq!(s, "hello");
+ })?;
+
+ Ok(())
+})?;
+```
+
+Those methods work for scoped and regular userdata objects (but still require `T: 'static`).
+
+#### String changes
+
+Since `mlua::String` holds a weak reference to Lua without any guarantees about the lifetime of the underlying data, getting a `&str` or `&[u8]` from it is no longer safe.
+Lua instance can be destroyed while reference to the data is still alive:
+
+```rust
+let lua = Lua::new();
+let s: mlua::String = lua.create_string("hello, world")?; // only weak reference to Lua!
+let s_ref: &str = s.to_str()?; // this is not safe!
+drop(lua);
+println!("{s_ref}"); // use after free!
+```
+
+To solve this issue, return types of `mlua::String::to_str` and `mlua::String::as_bytes` methods changed to `BorrowedStr` and `BorrowedBytes` respectively.
+
+These new types hold a strong reference to the Lua instance and can be safely converted to `&str` or `&[u8]`:
+
+```rust
+let lua = Lua::new();
+let s: mlua::String = lua.create_string("hello, world")?;
+let s_ref: mlua::BorrowedStr = s.to_str()?; // The strong reference to Lua is held here
+drop(lua);
+println!("{s_ref}"); // ok
+```
+
+The good news is that `BorrowedStr` implements `Deref`/`AsRef` as well as `Display`, `Debug`, `Eq`, `PartialEq` and other traits for easy usage.
+The same applies to `BorrowedBytes`.
+
+Unfortunately, `mlua::String::to_string_lossy` cannot return `Cow<'a, str>` anymore, because it requires a strong reference to Lua. It now returns Rust `String` instead.
diff --git a/docs/release_notes/v0.9.md b/docs/release_notes/v0.9.md
new file mode 100644
index 00000000..9de0ce2d
--- /dev/null
+++ b/docs/release_notes/v0.9.md
@@ -0,0 +1,361 @@
+## mlua v0.9 release notes
+
+The v0.9 version of mlua is a major release that includes a number of API changes and improvements. This release is a stepping stone towards the v1.0.
+This document highlights the most important changes. For a full list of changes, see the [CHANGELOG].
+
+[CHANGELOG]: https://github.com/mlua-rs/mlua/blob/main/CHANGELOG.md
+
+### New features
+
+#### 1. New Any UserData API
+
+This is a long awaited feature that allows to register in Lua foreign types that cannot implement `UserData` trait because of the Rust orphan rules.
+
+Now you can register any type that implements [`Any`] trait as a userdata type.
+
+Consider the following example:
+
+```rust
+lua.register_userdata_type::(|reg| {
+ reg.add_method("len", |_, this, ()| Ok(this.len()));
+
+ reg.add_method_mut("push", |_, this, s: String| {
+ this.push_str(&s);
+ Ok(())
+ });
+
+ reg.add_meta_method(MetaMethod::ToString, |lua, this, ()| lua.create_string(this));
+})?;
+
+let s = lua.create_any_userdata("hello".to_string())?;
+lua.load(chunk! {
+ print("s:len() is " .. $s:len())
+ $s:push(" world")
+ // Prints: hello, world
+ print($s)
+})
+.exec()?;
+```
+
+In this example we registered [`std::string::String`] as a userdata type with a set of methods and then created an instance of this type in Lua.
+
+It's _not_ required to register a type before using the `Lua::create_any_userdata()` method, instead an empty metatable will be created for you.
+You can also register the same type multiple times with different methods. Any previously created instances will share the old metatable, while new instances will have the new one.
+
+The new set of API is called `any_userdata` because it allows to register types that implements [`Any`] trait.
+
+[`std::string::String`]: https://doc.rust-lang.org/stable/std/string/struct.String.html
+[`Any`]: https://doc.rust-lang.org/stable/std/any/trait.Any.html
+
+#### 2. Scope support for the new any userdata types
+
+When you need to create non-static userdata instances in Lua, the usual way is use `Lua::scope()` helper to make them scoped. When out of scope, any scoped objects will be automatically
+dropped. The only downside of this approach is that every new instance will have a new metatable. This is not very fast if you need to create a lot of instances.
+
+With the new Any UserData API, you can place non-static references `&T` where `T: 'static` into a scope and they will share a single static metatable.
+
+```rust
+lua.register_userdata_type::(|reg| {
+ reg.add_method_mut("replace", |_, this, (pat, to): (String, String)| {
+ *this = this.replace(&pat, &to);
+ Ok(())
+ });
+
+ reg.add_meta_method(MetaMethod::ToString, |lua, this, ()| lua.create_string(this));
+})?;
+
+let mut s = "hello, world".to_string();
+
+lua.scope(|scope| {
+ // This userdata instance holds only a mutable reference to our string
+ let ud = scope.create_any_userdata_ref_mut(&mut s)?;
+ lua.load(chunk! {
+ $ud:replace("world", "user")
+ })
+ .exec()
+})?;
+
+// Prints: hello, user!
+println!("{s}!");
+```
+
+#### 3. Owned types (`unstable`)
+
+One of the common questions was how to embed a Lua type into Rust struct to use it later. It was non-trivial to do because of the `'lua` lifetime attached to every Lua value.
+
+In v0.9 mlua introduces "owned" types `OwnedTable`/`OwnedFunction`/`OwnedString`/`OwnedAnyUserData`/ `OwnedThread`that are `'static` (no lifetime attached).
+
+```rust
+let lua = Lua::new();
+
+struct MyStruct {
+ table: OwnedTable,
+ func: OwnedFunction,
+}
+
+let my_struct = MyStruct {
+ table: lua.globals().into_owned(),
+ func: lua
+ .create_function(|_, t: Table| Ok(format!("{t:#?}")))?
+ .into_owned(),
+};
+
+// It's safe to drop Lua!
+drop(lua);
+
+let result = my_struct.func.call::<_, String>(my_struct.table)?;
+println!("{result}");
+```
+
+Prior to v0.9, it was possible to do by creating a reference to the Lua value in registry using `Lua::create_registry_value()`
+and retrieving value later using `Lua::registry_value()` method.
+
+All owned handles hold a *strong* reference to the current Lua instance.
+Be warned, if you place them into a Lua type (eg. `UserData` or a Rust callback), it is *very easy*
+to accidentally cause reference cycles that would prevent destroying Lua instance.
+
+Please note this functionality is available under the `unstable` feature flag and not available when the `send` feature is enabled.
+
+#### New ffi module
+
+In v0.9 release the internal `ffi` module has been moved into the new [`mlua-sys`] crate and became available for public use.
+This crate provides unified Lua FFI API (targeting Lua 5.4) using a (limited) compatibility layer for older versions.
+
+mlua re-exports the `ffi` module aliasing the `mlua-sys` crate and provides (unsafe) functionality to work with raw Lua state:
+
+```rust
+unsafe {
+ unsafe extern "C-unwind" fn lua_add(state: *mut mlua::lua_State) -> i32 {
+ let a = mlua::ffi::luaL_checkinteger(state, 1);
+ let b = mlua::ffi::luaL_checkinteger(state, 2);
+ mlua::ffi::lua_pushinteger(state, a + b);
+ 1
+ }
+
+ let add = lua.create_c_function(lua_add)?;
+ assert_eq!(add.call::<_, i32>((2, 3))?, 5);
+}
+```
+
+[`mlua-sys`]: https://crates.io/crates/mlua-sys
+
+#### Luau JIT support
+
+mlua brings support for the new [Luau] JIT backend under the `luau-jit` feature flag.
+
+It will automatically trigger JIT compilation for new Lua chunks. To disable it, just call `lua.enable_jit(false)` before loading Lua code
+(but any previously compiled chunks will remain JIT-compiled).
+
+[Luau]: https://luau-lang.org
+
+### Improvements
+
+#### 1. Better error reporting
+
+When calling a Rust function from Lua and passing wrong arguments, previous mlua versions reported an error message without any context or reference to the particular argument.
+
+In v0.9 it reports an error message with the argument index and expected type:
+
+```rust
+let func = lua.create_function(|_, _a: i32| Ok(()))?;
+lua.load(chunk! {
+ local ok, err = pcall($func, "not a number")
+ // Prints: bad argument #1: error converting Lua string to i32 (expected number or string coercible to number)
+ print(err)
+})
+.exec()?;
+```
+
+Similar changes have been made for userdata functions and methods:
+
+```rust
+lua.register_userdata_type::<&'static str>(|reg| {
+ reg.add_method("len", |_, this, ()| Ok(this.len()));
+})?;
+
+let s = lua.create_any_userdata("hello")?;
+lua.load(chunk! {
+ local ok, err = pcall($s.len, 123)
+ // Prints: bad argument `self` to `&str.len`: error converting Lua integer to userdata
+ print(err)
+})
+.exec()?;
+```
+
+#### 2. Error context
+
+Similar to the [`anyhow`] Error type, now it's possible to attach context to Lua errors:
+
+```rust
+let read = lua.create_function(|lua, path: String| {
+ let bytes = std::fs::read(&path)
+ .into_lua_err()
+ .context(format!("Failed to open `{path}`"))?;
+ Ok(lua.create_string(bytes))
+})?;
+
+lua.load(chunk! {
+ local ok, err = pcall($read, "/nonexistent")
+ /// Prints:
+ /// Failed to open /nonexistent
+ /// No such file or directory (os error 2)
+ /// stack traceback:
+ /// ...
+ print(err)
+})
+.exec()?;
+```
+
+[`anyhow`]: https://crates.io/crates/anyhow
+
+#### 4. New methods `Function::wrap`/`AnyUserData::wrap`
+
+Sometimes it's useful to have `IntoLua` trait implementation for a Rust function or type `T: Any` without needing to call `Lua::create_function()`/`Lua::create_any_userdata()` methods.
+Since v0.9 you can call the new methods `Function::wrap()`/`AnyUserData::wrap()` that allows to do this. They return an abstract type that `impl IntoLua`:
+
+```rust
+lua.globals().set("print_rust", Function::wrap(|_, s: String| Ok(println!("{}", s))))?;
+lua.globals().set("rust_ud", AnyUserData::wrap("hello"))?;
+```
+
+In addition there are also `Function::wrap_mut()`/`Function::wrap_async()` methods that allow to wrap mutable and async functions respectively.
+
+For a `T: 'UserData + 'static` the `IntoLua` trait is still always implemented.
+
+#### `UserDataRef` and `UserDataRefMut` type wrappers
+
+The new wrappers `UserDataRef` and `UserDataRefMut` are receivers for userdata type `T` and borrow underlying instance for the lifetime of the wrapper.
+
+```rust
+lua.globals()
+ .set("ud", AnyUserData::wrap("hello".to_string()))?;
+
+let mut ud_mut: UserDataRefMut = lua.globals().get("ud")?;
+ud_mut.push_str(", Rust");
+drop(ud_mut);
+
+let ud_ref: UserDataRef = lua.globals().get("ud")?;
+// Prints: hello, Rust
+println!("{}", *ud_ref);
+```
+
+In the previous mlua versions the same functionality can be achieved by receiving `AnyUserData` and calling `AnyUserData::borrow()`/`AnyUserData::borrow_mut()` methods.
+
+The new wrappers are identical to Rust [`Ref`]/[`RefMut`] types.
+
+[`Ref`]: https://doc.rust-lang.org/std/cell/struct.Ref.html
+[`RefMut`]: https://doc.rust-lang.org/std/cell/struct.RefMut.html
+
+#### New `AnyUserDataExt` trait
+
+Similar to the `TableExt` trait, the `AnyUserDataExt` provides a set of extra methods for the `AnyUserData` type.
+
+1) `AnyUserDataExt::get()/set()` to get/set a value by key from the userdata, assuming it has `__index` metamethod.
+
+2) `AnyUserDataExt::call()` to call the userdata as a function assuming it has `__call` metamethod.
+
+3) `AnyUserData::call_method(name, ...)` to call the userdata method, assuming it has `__index` metamethod and the associated function.
+
+#### Pretty formatting Lua values
+
+`mlua::Value` implements a new format `:#?` that allows to (recursively) pretty print Lua values:
+
+```rust
+println!("{:#?}", lua.globals());
+```
+
+Prints:
+```
+{
+ ["_G"] = table: 0x7fa2d0706260,
+ ["_VERSION"] = "Lua 5.4",
+ ["assert"] = function: 0x10451d11d,
+ ["collectgarbage"] = function: 0x10451d198,
+ ["coroutine"] = {
+ ["close"] = function: 0x10451e28f,
+ ...
+ },
+ ["dofile"] = function: 0x10451d37c,
+ ...
+}
+```
+
+In addition a new method `Value::to_string()` has been added to convert `Value` to a string (using `__tostring` metamethod if available).
+
+#### Environment for Lua functions
+
+Any Lua functions have an associated environment table that is used to resolve global variables. By default it sets to a Lua globals table.
+
+In the new release it's possible to get or update a function environment using `Function::environment()` or `Function::set_environment()` methods respectively.
+
+```rust
+let f = lua.load("return a").into_function()?;
+
+assert_eq!(f.environment(), Some(lua.globals()));
+
+lua.globals().set("a", 1)?;
+assert_eq!(f.call::<_, i32>(())?, 1);
+
+f.set_environment(lua.create_table_from([("a", "hello")])?)?;
+assert_eq!(f.call::<_, mlua::String>(())?, "hello");
+```
+
+#### Performance optimizations
+
+The new mlua version has a number of performance improvements. Please check the [benchmarks results] to see how mlua compares to rlua and rhai.
+
+[benchmarks results]: https://github.com/mlua-rs/script-bench-rs
+
+### Changes in `module` mode
+
+#### New attributes
+
+The `lua_module` macro now support the following attributes:
+
+- `name=...` - sets name of the module (defaults to the name of the function).
+
+Eg.:
+
+```rust
+#[mlua::lua_module(name = "alt_module")]
+fn my_module(lua: &Lua) -> LuaResult {
+ lua.create_table()
+}
+```
+
+Under the hood a new function `luaopen_alt_module` will be created for the Lua module loader.
+
+- `skip_memory_check` - skip memory allocation checks for some operations.
+
+In module mode, mlua runs in an unknown environment and cannot tell whether there are any memory limits or not. As a result, some operations that require memory allocation run in
+protected mode. Setting this attribute will improve performance of such operations with risk of having uncaught exceptions and memory leaks.
+
+#### Improved Windows target
+
+In previous mlua versions, building a Lua module for Windows requires having Lua development libraries installed on the system.
+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
+`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.
+
+### Breaking changes
+
+1) `ToLua`/`ToLuaMulti` traits have been renamed to `IntoLua`/`IntoLuaMulti` respectively (with the methods called `into_lua`/`into_lua_multi`).
+
+The main reason for this change is following the Rust self [convention](https://rust-lang.github.io/rust-clippy/master/index.html#/wrong_self_convention).
+
+2) Removed `FromLua` implementation for `T: UserData + Clone`.
+
+During the usage of mlua, it was found that this implementation is not very useful and prevents custom `FromLua` implementations for `T: UserData`.
+It should be a developer decision to opt-in `FromLua` for their `T` if needed rather than having enabled it unconditionally.
+
+To opt-in `FromLua` for `T: Clone` you can use a simple `#[derive(FromLua)]` macro (requires `feature = "macros"`):
+
+```rust
+#[derive(Clone, Copy, mlua::FromLua)]
+struct MyUserData(i32);
+```
+
+`T` is not required to implement `UserData` because of the new relaxed restrictions on userdata types.
diff --git a/examples/async_http_client.rs b/examples/async_http_client.rs
index e7918082..a2d9ed83 100644
--- a/examples/async_http_client.rs
+++ b/examples/async_http_client.rs
@@ -1,33 +1,36 @@
use std::collections::HashMap;
-use hyper::body::{Body as HyperBody, HttpBody as _};
-use hyper::Client as HyperClient;
+use http_body_util::BodyExt as _;
+use hyper::body::Incoming;
+use hyper_util::client::legacy::Client as HyperClient;
+use hyper_util::rt::TokioExecutor;
-use mlua::{chunk, AnyUserData, ExternalResult, Lua, Result, UserData, UserDataMethods};
+use mlua::{ExternalResult, Lua, Result, UserData, UserDataMethods, chunk};
-struct BodyReader(HyperBody);
+struct BodyReader(Incoming);
impl UserData for BodyReader {
- fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
- methods.add_async_function("read", |lua, reader: AnyUserData| async move {
- let mut reader = reader.borrow_mut::()?;
- if let Some(bytes) = reader.0.data().await {
- let bytes = bytes.to_lua_err()?;
- return Some(lua.create_string(&bytes)).transpose();
+ fn add_methods>(methods: &mut M) {
+ // Every call returns a next chunk
+ methods.add_async_method_mut("read", |lua, mut reader, ()| async move {
+ if let Some(bytes) = reader.0.frame().await {
+ if let Some(bytes) = bytes.into_lua_err()?.data_ref() {
+ return Some(lua.create_string(&bytes)).transpose();
+ }
}
Ok(None)
});
}
}
-#[tokio::main]
+#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> {
let lua = Lua::new();
let fetch_url = lua.create_async_function(|lua, uri: String| async move {
- let client = HyperClient::new();
- let uri = uri.parse().to_lua_err()?;
- let resp = client.get(uri).await.to_lua_err()?;
+ let client = HyperClient::builder(TokioExecutor::new()).build_http::();
+ let uri = uri.parse().into_lua_err()?;
+ let resp = client.get(uri).await.into_lua_err()?;
let lua_resp = lua.create_table()?;
lua_resp.set("status", resp.status().as_u16())?;
@@ -37,7 +40,7 @@ async fn main() -> Result<()> {
headers
.entry(key.as_str())
.or_insert(Vec::new())
- .push(value.to_str().to_lua_err()?);
+ .push(value.to_str().into_lua_err()?);
}
lua_resp.set("headers", headers)?;
@@ -56,11 +59,11 @@ async fn main() -> Result<()> {
end
end
repeat
- local body = res.body:read()
- if body then
- print(body)
+ local chunk = res.body:read()
+ if chunk then
+ print(chunk)
end
- until not body
+ until not chunk
})
.into_function()?;
diff --git a/examples/async_http_reqwest.rs b/examples/async_http_reqwest.rs
index 78c425bb..91206d4a 100644
--- a/examples/async_http_reqwest.rs
+++ b/examples/async_http_reqwest.rs
@@ -1,33 +1,27 @@
-use mlua::{chunk, ExternalResult, Lua, LuaSerdeExt, Result};
+use mlua::{ExternalResult, Lua, LuaSerdeExt, Result, Value, chunk};
-#[tokio::main]
+#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> {
let lua = Lua::new();
- let null = lua.null();
-
let fetch_json = lua.create_async_function(|lua, uri: String| async move {
let resp = reqwest::get(&uri)
.await
.and_then(|resp| resp.error_for_status())
- .to_lua_err()?;
- let json = resp.json::().await.to_lua_err()?;
+ .into_lua_err()?;
+ let json = resp.json::().await.into_lua_err()?;
lua.to_value(&json)
})?;
+ let dbg = lua.create_function(|_, value: Value| {
+ println!("{value:#?}");
+ Ok(())
+ })?;
+
let f = lua
.load(chunk! {
- function print_r(t, indent)
- local indent = indent or ""
- for k, v in pairs(t) do
- io.write(indent, tostring(k))
- if type(v) == "table" then io.write(":\n") print_r(v, indent.." ")
- else io.write(": ", v == $null and "null" or tostring(v), "\n") end
- end
- end
-
local res = $fetch_json(...)
- print_r(res)
+ $dbg(res)
})
.into_function()?;
diff --git a/examples/async_http_server.rs b/examples/async_http_server.rs
index 43ae7a95..f5057ed6 100644
--- a/examples/async_http_server.rs
+++ b/examples/async_http_server.rs
@@ -1,60 +1,70 @@
+use std::convert::Infallible;
use std::future::Future;
use std::net::SocketAddr;
use std::pin::Pin;
-use std::rc::Rc;
-use std::task::{Context, Poll};
-use hyper::server::conn::AddrStream;
-use hyper::service::Service;
-use hyper::{Body, Request, Response, Server};
+use http_body_util::combinators::BoxBody;
+use http_body_util::{BodyExt as _, Empty, Full};
+use hyper::body::{Bytes, Incoming};
+use hyper::server::conn::http1;
+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};
-struct LuaRequest(SocketAddr, Request);
+/// Wrapper around incoming request that implements UserData
+struct LuaRequest(SocketAddr, Request);
impl UserData for LuaRequest {
- fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
- methods.add_method("remote_addr", |_lua, req, ()| Ok((req.0).to_string()));
- methods.add_method("method", |_lua, req, ()| Ok((req.1).method().to_string()));
+ fn add_methods>(methods: &mut M) {
+ methods.add_method("remote_addr", |_, req, ()| Ok((req.0).to_string()));
+ methods.add_method("method", |_, req, ()| Ok((req.1).method().to_string()));
+ methods.add_method("path", |_, req, ()| Ok(req.1.uri().path().to_string()));
}
}
-pub struct Svc(Rc, SocketAddr);
-
-impl Service> for Svc {
- type Response = Response;
- type Error = LuaError;
- type Future = Pin>>>;
+/// Service that handles incoming requests
+#[derive(Clone)]
+pub struct Svc {
+ handler: Function,
+ peer_addr: SocketAddr,
+}
- fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> {
- Poll::Ready(Ok(()))
+impl Svc {
+ pub fn new(handler: Function, peer_addr: SocketAddr) -> Self {
+ Self { handler, peer_addr }
}
+}
+
+impl hyper::service::Service> for Svc {
+ type Response = Response>;
+ type Error = LuaError;
+ type Future = Pin> + Send>>;
- fn call(&mut self, req: Request) -> Self::Future {
+ fn call(&self, req: Request) -> Self::Future {
// If handler returns an error then generate 5xx response
- let lua = self.0.clone();
- let lua_req = LuaRequest(self.1, req);
+ let handler = self.handler.clone();
+ let lua_req = LuaRequest(self.peer_addr, req);
Box::pin(async move {
- let handler: Function = lua.named_registry_value("http_handler")?;
- match handler.call_async::<_, Table>(lua_req).await {
+ match handler.call_async::(lua_req).await {
Ok(lua_resp) => {
- let status = lua_resp.get::<_, Option>("status")?.unwrap_or(200);
+ let status = lua_resp.get::