Compare commits

...

68 Commits

Author SHA1 Message Date
Alex Orlenko 93d63cef35 v0.4.2 2020-08-17 12:17:08 +01:00
Alex Orlenko b743245aba Update CHANGELOG 2020-08-17 11:14:40 +01:00
Alex Orlenko a172c021c9 Update set_memory_limit doc 2020-07-30 12:16:25 +01:00
Alex Orlenko de2c5cd9a9 Fix compilation warnings on nightly rust 2020-07-28 21:10:55 +01:00
Alex Orlenko d201beadc9 Add ChunkMode enum to mark chunks as text or binary 2020-07-28 21:04:21 +01:00
Alex Orlenko dd58cdad52 Add Function::dump() to dump lua function to a binary chunk 2020-07-27 23:26:33 +01:00
Alex Orlenko 5c8a5e0a5a Merge pull request #9 from HybridEidolon/bytecode-chunks
Make Lua::load load binary chunks when unsafe
2020-07-27 14:19:33 +01:00
Alex Orlenko e07c53eafe Update compile tests (2) 2020-07-27 13:51:21 +01:00
Alex Orlenko ad619390e1 Run compile tests on macos (was ubuntu-18.04) 2020-07-27 11:34:48 +01:00
Alex Orlenko 350602ab6e Update lua-src dependency to 5.4.0 2020-07-27 10:52:28 +01:00
Alex Orlenko 4b1bc88273 Update compile tests 2020-07-27 10:49:01 +01:00
Eidolon 883bf082b9 Make Lua::load load binary chunks when unsafe 2020-07-27 03:37:38 -05:00
Alex Orlenko 4265250cfd 0.4.1 release
Fix docs.rs build features
Update Cargo.toml description
2020-06-08 14:08:07 +01:00
Alex Orlenko 1d9fed2207 0.4.0 release 2020-06-08 02:28:26 +01:00
Alex Orlenko a67cbafff1 Run LuaJIT 2.0.5 tests in 1 thread
Seems there is a race in a LuaJIT 2.0 that makes the tests unstable.
Works ok on LuaJIT 2.1 and all Lua.
2020-06-08 02:19:53 +01:00
Alex Orlenko 39bcb19ae7 Update dependencies 2020-06-08 00:22:53 +01:00
Alex Orlenko efcaef3db7 Enable hooks support for LuaJIT 2020-06-07 20:38:19 +01:00
Alex Orlenko 0a13a9631d Fix LuaJIT 2.1 libraries loading 2020-06-07 20:38:19 +01:00
Alex Orlenko 3d42bc4ca6 Refactor main_state handling
Don't allow to set hook if main_state is not available
Remove Lua 5.1 dirty hack
2020-06-07 20:38:19 +01:00
Alex Orlenko 2eb40deafd Add test to check loading module from lua coroutine (thread) 2020-06-07 20:38:19 +01:00
Alex Orlenko 2595fe9885 Update CHANGELOG 2020-06-07 20:38:19 +01:00
Alex Orlenko 4865089a78 Update README 2020-06-07 20:38:19 +01:00
Alex Orlenko a35b8dda68 Raise compile_error! instead of panic! in the build script 2020-06-07 20:38:19 +01:00
Alex Orlenko 95b9fe8dac Fix clippy warnings 2020-06-07 20:38:19 +01:00
Alex Orlenko 5952a1f709 New module feature
Don't link module with Lua core (see: http://lua-users.org/wiki/BuildingModules)
Example and tests for modules
2020-06-07 20:38:11 +01:00
Alex Orlenko 73c7c94d75 Remove default feature lua53 2020-06-06 16:07:16 +01:00
Alex Orlenko d5c22d989a Dirty hack to get lua 5.1 main state 2020-06-06 16:07:16 +01:00
Alex Orlenko a9a4cf13f1 Add has_metamethod to AnyUserData 2020-06-06 16:07:16 +01:00
Alex Orlenko c3822219e0 Add hooks support (based on rlua v0.17 implementation)
This feature works on lua54, lua53, lua52 and lua51 only.
LuaJIT is unstable.
2020-06-06 16:07:16 +01:00
Alex Orlenko f6da437d8b Update docs 2020-06-06 16:07:16 +01:00
Alex Orlenko 2fd6757f39 Add LuaJIT 2.0.5 stable support 2020-06-06 16:07:16 +01:00
Alex Orlenko 56407fac0d Update CHANGELOG 2020-06-06 16:07:13 +01:00
Alex Orlenko d0fa03e9c8 Bump version to 0.4.0-beta.1 2020-06-06 16:05:35 +01:00
Alex Orlenko cccec914ca Remove deprecated Table::call 2020-05-15 01:48:57 +01:00
Alex Orlenko 687ecc9247 Update documentation 2020-05-15 01:48:57 +01:00
Alex Orlenko 79bfb112aa Remove redundant lifetimes 2020-05-15 01:48:57 +01:00
Alex Orlenko 60c659ecff Add async http server example 2020-05-15 01:48:57 +01:00
Alex Orlenko 9f2d598517 Add clippy check and fix clippy warnings 2020-05-15 01:48:56 +01:00
Alex Orlenko 1492790ece Update compile tests to include async/send features 2020-05-12 03:16:29 +01:00
Alex Orlenko e88f086ed8 Update github workflow 2020-05-12 02:14:48 +01:00
Alex Orlenko 5a9a308790 Provide safe and unsafe Lua modes:
- In safe mode Lua would not have ability to load C code via `require` or `package.loadlib`
- Unsafe mode allows everything.
2020-05-12 02:14:48 +01:00
Alex Orlenko 1b2b94c808 Use Rust allocator for new Lua states that allows to set memory limit 2020-05-12 02:14:48 +01:00
Alex Orlenko 526e7418d8 Add MetaMethod::Close to support Lua 5.4 to-be-closed variables 2020-05-12 02:14:47 +01:00
Alex Orlenko 24d4f04c0d Add Lua 5.4 to github workflow 2020-05-11 02:43:34 +01:00
Alex Orlenko 539b569ff4 Add Lua 5.4 (rc2) support 2020-05-11 02:43:34 +01:00
Alex Orlenko 5c226b4915 Lua 5.4 FFI 2020-05-11 02:43:34 +01:00
Alex Orlenko d366ce0dd4 Scope support (including async) 2020-05-11 02:43:34 +01:00
Alex Orlenko 2bd5c2f6ca Hide Lua "Send" capability under the optional "send" feature flag 2020-05-11 02:43:34 +01:00
Alex Orlenko 7b0e4b4280 Add Send capability to Lua 2020-05-11 02:43:34 +01:00
Alex Orlenko 6e2bb73cff Bind Futures lifetimes to 'lua rather than 'static.
Fix async examples.
2020-05-11 02:43:34 +01:00
Alex Orlenko 7efcee853d Don't allow 'callback outlive 'lua 2020-05-11 02:43:33 +01:00
Alex Orlenko e9c2b8d306 0.3.2 release 2020-04-29 16:32:16 +01:00
Alex Orlenko d672e19365 Add ToLua implementation for slices and arrays 2020-04-29 16:12:58 +01:00
Alex Orlenko bda399a5b4 Get yield function in the moment of async callback creation.
This is usefull to temporary override `coroutine.yield` prior to create_async_callback()
and then restore to original value.
2020-04-28 14:29:36 +01:00
Alex Orlenko fe5e87b0f5 Fix bugs with metatable cache:
- Don't use thread_local as Lua can be created in one thread and executed in another (in module mode);
- Make per state storage without luaL_ref;
2020-04-28 14:29:14 +01:00
Alex Orlenko 0f32e9cb43 0.3.1 release 2020-04-20 01:55:57 +01:00
Alex Orlenko 0efa0fcb6a Update documentation 2020-04-20 01:52:01 +01:00
Alex Orlenko 4e19ae6ccf Update tests (async and table) 2020-04-20 01:14:34 +01:00
Alex Orlenko c826798a6d Minor refactor 2020-04-19 16:51:35 +01:00
Alex Orlenko ee08050c1f Add TableExt trait with call_method/function methods 2020-04-19 16:15:16 +01:00
Alex Orlenko d8897d867b Update examples 2020-04-19 01:23:42 +01:00
Alex Orlenko 222f4df668 Add family of call_async function
Update documentation
Move async tests to a separate file
2020-04-18 21:26:12 +01:00
Alex Orlenko afaa0eb639 0.3.0 release 2020-04-17 23:55:58 +01:00
Alex Orlenko db5ad6bc60 Include async build/test to github workflow 2020-04-17 23:55:58 +01:00
Alex Orlenko ef06c5eec9 Update Cargo.toml 2020-04-17 23:55:57 +01:00
Alex Orlenko f7dc9da107 Update README
Cargo fmt and minor changes
2020-04-17 22:52:34 +01:00
Alex Orlenko 47e8a80c1c v0.3.0-alpha.1 with async support
Squashed commit of the async branch.
2020-04-17 22:39:50 +01:00
Alex Orlenko 1a788c48f1 Cherry-pick changes from rlua:
- Make Value::type_name() public
- Update CallbackError and ExternalError Display impl
2020-04-15 21:23:00 +01:00
90 changed files with 4892 additions and 1119 deletions
+139 -109
View File
@@ -7,141 +7,171 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
thing: os: [ubuntu-18.04, macos-latest, windows-latest]
- stable rust: [stable]
- macos-x86_64 lua: [lua54, lua53, lua52, lua51, luajit]
- x86_64-msvc
include: include:
- thing: stable - os: ubuntu-18.04
target: x86_64-unknown-linux-gnu target: x86_64-unknown-linux-gnu
rust: stable - os: macos-latest
os: ubuntu-latest
- thing: macos-x86_64
target: x86_64-apple-darwin target: x86_64-apple-darwin
rust: stable - os: windows-latest
os: macos-latest
- thing: x86_64-msvc
target: x86_64-pc-windows-msvc target: x86_64-pc-windows-msvc
rust: stable-x86_64-msvc
os: windows-latest
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v2
- name: Install Rust (rustup) - uses: actions-rs/toolchain@v1
if: matrix.os != 'macos-latest' with:
run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- name: Build ${{ matrix.lua }} vendored
run: |
cargo build --release --features "${{ matrix.lua }} vendored"
cargo build --release --features "${{ matrix.lua }} vendored async send"
shell: bash shell: bash
- name: Install Lua (ubuntu) - name: Build ${{ matrix.lua }} pkg-config
if: matrix.os == 'ubuntu-latest' if: ${{ matrix.os == 'ubuntu-18.04' && matrix.lua != 'lua54' }}
run: | run: |
sudo apt-get update -y sudo apt-get update -y
sudo apt-get install -y --no-install-recommends liblua5.3-dev liblua5.2-dev liblua5.1-0-dev libluajit-5.1-dev sudo apt-get install -y --no-install-recommends liblua5.3-dev liblua5.2-dev liblua5.1-0-dev libluajit-5.1-dev
- name: Install Rust (macos) cargo build --release --features "${{ matrix.lua }}"
if: matrix.os == 'macos-latest'
run: |
curl https://sh.rustup.rs | sh -s -- -y
echo ::add-path::$HOME/.cargo/bin
shell: bash
- name: Install GCC (aarch64-linux)
run: |
sudo apt-get update -y
sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross
if: matrix.thing == 'aarch64-linux'
shell: bash
- run: rustup target add ${{ matrix.target }}
- name: Build (Lua 5.3/5.2/5.1 and LuaJIT vendored)
run: |
for FEATURE in lua53 lua52 lua51 luajit; do
echo "Building $FEATURE"
cargo build --target ${{ matrix.target }} --release --no-default-features --features "$FEATURE vendored"
done
shell: bash
- name: Build (Lua 5.3/5.2/5.1 and LuaJIT via pkg-config)
if: matrix.os == 'ubuntu-latest'
run: |
for FEATURE in lua53 lua52 lua51 luajit; do
echo "Building $FEATURE"
cargo build --target ${{ matrix.target }} --release --no-default-features --features $FEATURE
done
shell: bash
test_linux: test:
name: Test on Linux name: Test
runs-on: ubuntu-latest runs-on: ${{ matrix.os }}
needs: build needs: build
strategy:
matrix:
os: [ubuntu-18.04, macos-latest, windows-latest]
rust: [stable]
lua: [lua54, lua53, lua52, lua51, luajit]
include:
- os: ubuntu-18.04
target: x86_64-unknown-linux-gnu
- os: macos-latest
target: x86_64-apple-darwin
- os: windows-latest
target: x86_64-pc-windows-msvc
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v2
- name: Install Rust - uses: actions-rs/toolchain@v1
run: rustup update stable --no-self-update && rustup default stable with:
shell: bash toolchain: ${{ matrix.rust }}
- name: Run tests (Lua 5.3 vendored) target: ${{ matrix.target }}
run: cargo test --release --no-default-features --features "lua53 vendored" override: true
shell: bash - name: Run ${{ matrix.lua }} tests
- name: Run tests (Lua 5.2 vendored) if: ${{ matrix.os != 'macos-latest' || matrix.lua != 'luajit' }}
run: cargo test --release --no-default-features --features "lua52 vendored"
shell: bash
- name: Run tests (Lua 5.1 vendored)
run: cargo test --release --no-default-features --features "lua51 vendored"
shell: bash
- name: Run tests (LuaJIT vendored)
run: cargo test --release --no-default-features --features "luajit vendored"
shell: bash
- name: Run compile test (Lua 5.3)
run: | run: |
sudo apt-get update -y cargo test --release --features "${{ matrix.lua }} vendored"
sudo apt-get install -y --no-install-recommends liblua5.3-dev cargo test --release --features "${{ matrix.lua }} vendored async send"
cargo test --release --no-default-features --features "lua53 vendored" -- --ignored shell: bash
- name: Run compile tests (macos lua53)
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua53' }}
run: |
cargo test --release --features "${{ matrix.lua }} vendored" -- --ignored
cargo test --release --features "${{ matrix.lua }} vendored async send" -- --ignored
shell: bash
test_macos: test_luajit_macos:
name: Test on MacOS name: Test LuaJIT on macOS
runs-on: macos-latest runs-on: macos-latest
needs: build needs: build
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v2
- name: Install Rust - uses: actions-rs/toolchain@v1
with:
toolchain: nightly
target: x86_64-apple-darwin
override: true
- name: Run LuaJIT 2.0.5 tests
run: | run: |
curl https://sh.rustup.rs | sh -s -- -y brew install luajit pkg-config
echo ::add-path::$HOME/.cargo/bin cargo test --tests --release --features "luajit async send" -- --test-threads=1
shell: bash shell: bash
- name: Run tests (Lua 5.3 vendored) - name: Run LuaJIT vendored tests
run: cargo test --release --no-default-features --features "lua53 vendored" run: |
shell: bash cargo test --release --features "luajit vendored async send"
- name: Run tests (Lua 5.2 vendored)
run: cargo test --release --no-default-features --features "lua52 vendored"
shell: bash
- name: Run tests (Lua 5.1 vendored)
run: cargo test --release --no-default-features --features "lua51 vendored"
shell: bash
- name: Run tests (LuaJIT vendored)
run: cargo test --release --no-default-features --features "luajit vendored"
shell: bash shell: bash
test_windows: test_modules:
name: Test on Windows name: Test modules on Linux and macOS
runs-on: ${{ matrix.os }}
needs: build
strategy:
matrix:
os: [ubuntu-18.04, macos-latest]
rust: [stable]
lua: [lua54, lua53, lua52, lua51, luajit]
include:
- os: ubuntu-18.04
target: x86_64-unknown-linux-gnu
- os: macos-latest
target: x86_64-apple-darwin
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- name: Run ${{ matrix.lua }} module tests
run: |
(cd examples/module && cargo build --release --features "${{ matrix.lua }} vendored")
(cd tests/module && cargo test --release --features "${{ matrix.lua }} vendored")
shell: bash
test_modules_windows:
name: Test modules on Windows
runs-on: windows-latest runs-on: windows-latest
needs: build needs: build
strategy:
matrix:
lua: [lua53, lua51, luajit]
defaults:
run:
shell: bash.exe --login -eo pipefail "{0}"
env:
MSYSTEM: MINGW64
CHERE_INVOKING: 1
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v2
- name: Install Rust - name: Set up shell
run: rustup update stable --no-self-update && rustup default stable run: echo ::add-path::C:\msys64\usr\bin\
shell: bash shell: pwsh
- name: Run tests (Lua 5.3 vendored) - name: Install Rust & Lua
run: cargo test --release --no-default-features --features "lua53 vendored" run: |
shell: bash pacman -S --noconfirm mingw-w64-x86_64-rust mingw-w64-x86_64-lua mingw-w64-x86_64-lua51 mingw-w64-x86_64-luajit
- name: Run tests (Lua 5.2 vendored) - name: Run ${{ matrix.lua }} module tests
run: cargo test --release --no-default-features --features "lua52 vendored" run: |
shell: bash (cd examples/module && cargo build --release --features "${{ matrix.lua }}")
- name: Run tests (Lua 5.1 vendored) (cd tests/module && cargo test --release --features "${{ matrix.lua }}")
run: cargo test --release --no-default-features --features "lua51 vendored"
shell: bash
- name: Run tests (LuaJIT vendored)
run: cargo test --release --no-default-features --features "luajit vendored"
shell: bash
rustfmt: rustfmt:
name: Rustfmt name: Rustfmt
runs-on: ubuntu-latest runs-on: ubuntu-18.04
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v2
- name: Install Rust - uses: actions-rs/toolchain@v1
run: rustup update stable && rustup default stable && rustup component add rustfmt with:
toolchain: stable
components: rustfmt
override: true
- run: cargo fmt -- --check - run: cargo fmt -- --check
clippy:
name: Clippy check
runs-on: ubuntu-18.04
strategy:
matrix:
lua: [lua54, lua53, lua52, lua51, luajit]
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
components: clippy
override: true
- uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --features "${{ matrix.lua }},async,send,vendored
+22
View File
@@ -0,0 +1,22 @@
## v0.4.2
- Added `Function::dump()` to dump lua function to a binary chunk
- Added `ChunkMode` enum to mark chunks as text or binary
- Updated `set_memory_limit` doc
## v0.4.0
- 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()`.
- 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).
- Added `Lua::into_static()` to make `'static` Lua state. This is useful to spawn async Lua threads that requires `'static`.
- New function `Lua::set_memory_limit()` (similar to `rlua`) to enable memory restrictions in Lua VM (requires Lua >= 5.2).
- `Scope`, temporary removed in v0.3, is back with async support.
- Removed deprecated `Table::call()` function.
- Added hooks support (backported from rlua 0.17).
- New `AnyUserData::has_metamethod()` function.
- LuaJIT 2.0.5 (the latest stable) support.
- Various bug fixes and improvements.
+41 -12
View File
@@ -1,52 +1,81 @@
[package] [package]
name = "mlua" name = "mlua"
version = "0.2.2" version = "0.4.2"
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"] authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
edition = "2018" edition = "2018"
repository = "https://github.com/khvzak/mlua" repository = "https://github.com/khvzak/mlua"
documentation = "https://docs.rs/mlua" documentation = "https://docs.rs/mlua"
readme = "README.md" readme = "README.md"
keywords = ["lua", "luajit"] keywords = ["lua", "luajit", "async", "futures"]
categories = ["api-bindings"] categories = ["api-bindings", "asynchronous"]
license = "MIT" license = "MIT"
links = "lua" links = "lua"
build = "build/main.rs" build = "build/main.rs"
description = """ description = """
High level bindings to Lua 5.1/5.2/5.3 (including LuaJIT) High level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT)
with support of writing native lua modules in Rust. with async/await features and support of writing native lua modules in Rust.
""" """
[badges] [badges]
# github-actions = { repository = "khvzak/mlua", workflow = "CI" } # github-actions = { repository = "khvzak/mlua", workflow = "CI" }
maintenance = { status = "actively-developed" }
[package.metadata.docs.rs]
features = ["async", "send", "lua53"]
[workspace] [workspace]
members = [ members = [
"mlua_derive", "mlua_derive",
"examples/module",
"tests/module",
] ]
[features] [features]
default = ["lua53"] lua54 = []
lua53 = [] lua53 = []
lua52 = [] lua52 = []
lua51 = [] lua51 = []
luajit = [] luajit = []
vendored = ["lua-src", "luajit-src"] vendored = ["lua-src", "luajit-src"]
module = []
async = ["futures-core", "futures-task", "futures-util"]
send = []
[dependencies] [dependencies]
num-traits = { version = "0.2.6" }
bstr = { version = "0.2", features = ["std"], default_features = false } bstr = { version = "0.2", features = ["std"], default_features = false }
lazy_static = { version = "1.4" }
num-traits = { version = "0.2.11" }
futures-core = { version = "0.3.5", optional = true }
futures-task = { version = "0.3.5", optional = true }
futures-util = { version = "0.3.5", optional = true }
[build-dependencies] [build-dependencies]
cc = { version = "1.0" } cc = { version = "1.0" }
pkg-config = { version = "0.3.11" } pkg-config = { version = "0.3.17" }
lua-src = { version = "535.0.1", optional = true } lua-src = { version = "540.0.0", optional = true }
luajit-src = { version = "210.0.0", optional = true } luajit-src = { version = "210.1.0", optional = true }
[dev-dependencies] [dev-dependencies]
rustyline = "5.0" rustyline = "6.0"
criterion = "0.2.0" criterion = "0.3"
trybuild = "1.0" trybuild = "1.0"
futures = "0.3.5"
hyper = "0.13"
tokio = { version = "0.2", features = ["full"] }
futures-timer = "3.0"
[[bench]] [[bench]]
name = "benchmark" name = "benchmark"
harness = false harness = false
[[example]]
name = "async_http_client"
required-features = ["async"]
[[example]]
name = "async_http_server"
required-features = ["async", "send"]
[[example]]
name = "async_tcp_server"
required-features = ["async"]
+59 -25
View File
@@ -10,19 +10,37 @@
[Guided Tour](examples/guided_tour.rs) [Guided Tour](examples/guided_tour.rs)
A fork of [rlua 0.15](https://github.com/kyren/rlua/tree/0.15.3) which provides a high level `mlua` is bindings to [Lua](https://www.lua.org) programming language for Rust with a goal to provide
interface between Rust and Lua. Unlike `rlua` , `mlua` supports Lua 5.1 (including LuaJIT), 5.2 and 5.3. _safe_ (as far as it's possible), high level, easy to use, practical and flexible API.
The `mlua` goal is to be an easy to use, practical and flexible API between Rust and Lua but
*__not__* always 100% safe due to the Lua VM nature. Also, `mlua` provides a way to write native lua Started as [rlua v0.15](https://github.com/amethyst/rlua/tree/0.15.3) fork, `mlua` supports *__all__* major Lua versions (including LuaJIT) and allows to write native Lua modules in Rust as well as use Lua in a standalone mode.
modules in Rust.
`mlua` supports the following Lua versions (and tested on Windows/macOS/Linux):
- Lua 5.4 (`feature = "lua54"`)
- Lua 5.3 (`feature = "lua53"`)
- Lua 5.2 (`feature = "lua52"`)
- Lua 5.1 (`feature = "lua51"`)
- LuaJIT 2.1.0 beta (`feature = "luajit"`)
- LuaJIT 2.0.5 stable (`feature = "luajit"`)
Additional `feature = "vendored"` enables building static Lua from sources during `mlua` compilation.
## Usage ## Usage
### Choosing Lua version ### Async/await support
The following features could be used to choose Lua version: `lua53` (default), `lua52`, `lua51` and `luajit`. Starting from v0.3, `mlua` supports async/await for all Lua versions. 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`.
By default mlua uses `pkg-config` tool to find lua includes and lib. **Examples**:
- [HTTP Client](examples/async_http_client.rs)
- [HTTP Server](examples/async_http_server.rs)
- [TCP Server](examples/async_tcp_server.rs)
### Compiling
You have to enable one of the features `lua54`, `lua53`, `lua52`, `lua51` or `luajit`, according to the choosen 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. 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. To achieve this, mlua supports `LUA_INC`, `LUA_LIB`, `LUA_LIB_NAME` and `LUA_LINK` environment variables.
`LUA_LINK` is optional and may be `dylib` (a dynamic library) or `static` (a static library, `.a` archive). `LUA_LINK` is optional and may be `dylib` (a dynamic library) or `static` (a static library, `.a` archive).
@@ -34,14 +52,14 @@ my_project $ LUA_INC=$HOME/tmp/lua-5.2.4/src LUA_LIB=$HOME/tmp/lua-5.2.4/src LUA
`mlua` also supports vendored lua/luajit using the auxilary crates [lua-src](https://crates.io/crates/lua-src) and `mlua` also supports vendored lua/luajit using the auxilary crates [lua-src](https://crates.io/crates/lua-src) and
[luajit-src](https://crates.io/crates/luajit-src). [luajit-src](https://crates.io/crates/luajit-src).
Just enable the `vendored` feature and cargo will automatically build and link specified lua/luajit version. This is the easiest way to get started with mlua. Just enable the `vendored` feature and cargo will automatically build and link specified lua/luajit version. This is the easiest way to get started with `mlua`.
### Standalone mode ### Standalone mode
Add to `Cargo.toml` : Add to `Cargo.toml` :
``` toml ``` toml
[dependencies] [dependencies]
mlua = "0.2" mlua = { version = "0.4", features = ["lua53"] }
``` ```
`main.rs` `main.rs`
@@ -66,6 +84,8 @@ fn main() -> LuaResult<()> {
### Module mode ### Module mode
[Example](examples/module)
Add to `Cargo.toml` : Add to `Cargo.toml` :
``` toml ``` toml
@@ -73,8 +93,8 @@ Add to `Cargo.toml` :
crate-type = ["cdylib"] crate-type = ["cdylib"]
[dependencies] [dependencies]
mlua = "0.2" mlua = { version = "0.4", features = ["lua53", "module"] }
mlua_derive = "0.2" mlua_derive = "0.4"
``` ```
`lib.rs` : `lib.rs` :
@@ -97,27 +117,42 @@ fn my_module(lua: &Lua) -> LuaResult<LuaTable> {
} }
``` ```
And then (macos example): And then (**macOS** example):
``` sh ``` sh
$ cargo build $ cargo rustc -- -C link-arg=-undefined -C link-arg=dynamic_lookup
$ ln -s ./target/debug/libmy_module.dylib ./my_module.so $ ln -s ./target/debug/libmy_module.dylib ./my_module.so
$ lua5.3 -e 'require("my_module").hello("world")' $ lua5.3 -e 'require("my_module").hello("world")'
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:
``` toml
[target.x86_64-apple-darwin]
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
```
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 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.
More details about compiling and linking Lua modules can be found on the [Building Modules](http://lua-users.org/wiki/BuildingModules) page.
## Safety ## Safety
One of the `mlua` goals is to provide *safe* API between Rust and Lua. 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 Every place where the Lua C API may trigger an error longjmp in any way is protected by `lua_pcall`,
in any way is protected by `lua_pcall` , and the user of the library is protected and the user of the library is protected from directly interacting with unsafe things like the Lua stack,
from directly interacting with unsafe things like the Lua stack, and there is and there is overhead associated with this safety.
overhead associated with this safety.
Unfortunately, `mlua` does not provide absolute safety even without using `unsafe` . Unfortunately, `mlua` does not provide absolute safety even without using `unsafe` .
This library contains a huge amount of unsafe code. There are almost This library contains a huge amount of unsafe code. There are almost certainly bugs still lurking in this library!
certainly bugs still lurking in this library! It is surprisingly, fiendishly It is surprisingly, fiendishly difficult to use the Lua C API without the potential for unsafety.
difficult to use the Lua C API without the potential for unsafety.
## Panic handling ## Panic handling
@@ -141,10 +176,9 @@ let _ = lua.load(r#"
unreachable!() unreachable!()
``` ```
`mlua` should also be panic safe in another way as well, which is that any `Lua` `mlua` should also be panic safe in another way as well, which is that any `Lua` instances or handles
instances or handles remains usable after a user generated panic, and such remains usable after a user generated panic, and such panics should not break internal invariants or
panics should not break internal invariants or leak Lua stack space. This is leak Lua stack space. This is mostly important to safely use `mlua` types in Drop impls, as you should not be
mostly important to safely use `mlua` types in Drop impls, as you should not be
using panics for general error handling. using panics for general error handling.
Below is a list of `mlua` behaviors that should be considered a bug. Below is a list of `mlua` behaviors that should be considered a bug.
+5
View File
@@ -0,0 +1,5 @@
use std::path::PathBuf;
pub fn probe_lua() -> PathBuf {
unreachable!()
}
+59 -27
View File
@@ -1,73 +1,103 @@
use std::env; use std::env;
use std::ffi::OsString;
use std::fs::File; use std::fs::File;
use std::io::{BufRead, BufReader, Result}; use std::io::{BufRead, BufReader, Result};
use std::ops::Bound; use std::ops::Bound;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
pub fn probe_lua() -> PathBuf { pub fn probe_lua() -> PathBuf {
let include_dir = env::var_os("LUA_INC").unwrap_or(OsString::new()); let include_dir = env::var_os("LUA_INC").unwrap_or_default();
let lib_dir = env::var_os("LUA_LIB").unwrap_or(OsString::new()); let lib_dir = env::var_os("LUA_LIB").unwrap_or_default();
let lua_lib = env::var_os("LUA_LIB_NAME").unwrap_or(OsString::new()); let lua_lib = env::var_os("LUA_LIB_NAME").unwrap_or_default();
println!("cargo:rerun-if-env-changed=LUA_INC"); println!("cargo:rerun-if-env-changed=LUA_INC");
println!("cargo:rerun-if-env-changed=LUA_LIB"); println!("cargo:rerun-if-env-changed=LUA_LIB");
println!("cargo:rerun-if-env-changed=LUA_LIB_NAME"); println!("cargo:rerun-if-env-changed=LUA_LIB_NAME");
println!("cargo:rerun-if-env-changed=LUA_LINK"); println!("cargo:rerun-if-env-changed=LUA_LINK");
if include_dir != "" && lib_dir != "" && lua_lib != "" { let need_lua_lib = cfg!(any(not(feature = "module"), target_os = "windows"));
if include_dir != "" && (!need_lua_lib || lib_dir != "") {
if lua_lib == "" {
panic!("LUA_LIB_NAME is not set");
}
let _version = use_custom_lua(&include_dir, &lib_dir, &lua_lib).unwrap(); let _version = use_custom_lua(&include_dir, &lib_dir, &lua_lib).unwrap();
return PathBuf::from(include_dir); return PathBuf::from(include_dir);
} }
// Find using via pkg-config // Find using `pkg-config`
#[cfg(feature = "lua54")]
{
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included("5.4"), Bound::Excluded("5.5")))
.cargo_metadata(need_lua_lib)
.probe("lua");
if lua.is_err() {
lua = pkg_config::Config::new()
.cargo_metadata(need_lua_lib)
.probe("lua5.4");
}
lua.unwrap().include_paths[0].clone()
}
#[cfg(feature = "lua53")] #[cfg(feature = "lua53")]
{ {
let mut lua = pkg_config::Config::new() let mut lua = pkg_config::Config::new()
.range_version((Bound::Included("5.3"), Bound::Excluded("5.4"))) .range_version((Bound::Included("5.3"), Bound::Excluded("5.4")))
.cargo_metadata(need_lua_lib)
.probe("lua"); .probe("lua");
if lua.is_err() { if lua.is_err() {
lua = pkg_config::Config::new().probe("lua5.3"); lua = pkg_config::Config::new()
.cargo_metadata(need_lua_lib)
.probe("lua5.3");
} }
return lua.unwrap().include_paths[0].clone(); lua.unwrap().include_paths[0].clone()
} }
#[cfg(feature = "lua52")] #[cfg(feature = "lua52")]
{ {
let mut lua = pkg_config::Config::new() let mut lua = pkg_config::Config::new()
.range_version((Bound::Included("5.2"), Bound::Excluded("5.3"))) .range_version((Bound::Included("5.2"), Bound::Excluded("5.3")))
.cargo_metadata(need_lua_lib)
.probe("lua"); .probe("lua");
if lua.is_err() { if lua.is_err() {
lua = pkg_config::Config::new().probe("lua5.2"); lua = pkg_config::Config::new()
.cargo_metadata(need_lua_lib)
.probe("lua5.2");
} }
return lua.unwrap().include_paths[0].clone(); lua.unwrap().include_paths[0].clone()
} }
#[cfg(feature = "lua51")] #[cfg(feature = "lua51")]
{ {
let mut lua = pkg_config::Config::new() let mut lua = pkg_config::Config::new()
.range_version((Bound::Included("5.1"), Bound::Excluded("5.2"))) .range_version((Bound::Included("5.1"), Bound::Excluded("5.2")))
.cargo_metadata(need_lua_lib)
.probe("lua"); .probe("lua");
if lua.is_err() { if lua.is_err() {
lua = pkg_config::Config::new().probe("lua5.1"); lua = pkg_config::Config::new()
.cargo_metadata(need_lua_lib)
.probe("lua5.1");
} }
return lua.unwrap().include_paths[0].clone(); lua.unwrap().include_paths[0].clone()
} }
#[cfg(feature = "luajit")] #[cfg(feature = "luajit")]
{ {
let lua = pkg_config::Config::new() let lua = pkg_config::Config::new()
.range_version((Bound::Included("2.1.0"), Bound::Unbounded)) .range_version((Bound::Included("2.0.5"), Bound::Unbounded))
.cargo_metadata(need_lua_lib)
.probe("luajit"); .probe("luajit");
return lua.unwrap().include_paths[0].clone(); lua.unwrap().include_paths[0].clone()
} }
} }
@@ -87,20 +117,22 @@ fn use_custom_lua<S: AsRef<Path>>(include_dir: &S, lib_dir: &S, lua_lib: &S) ->
} }
} }
let mut link_lib = String::new(); let link_lib = match env::var("LUA_LINK") {
if env::var("LUA_LINK").unwrap_or(String::new()) == "static" { Ok(s) if s == "static" => "static=",
link_lib = "static=".to_string(); _ => "",
} };
println!( if cfg!(any(not(feature = "module"), target_os = "windows")) {
"cargo:rustc-link-search=native={}", println!(
lib_dir.as_ref().display() "cargo:rustc-link-search=native={}",
); lib_dir.as_ref().display()
println!( );
"cargo:rustc-link-lib={}{}", println!(
link_lib, "cargo:rustc-link-lib={}{}",
lua_lib.as_ref().display() link_lib,
); lua_lib.as_ref().display()
);
}
Ok(version_found) Ok(version_found)
} }
+4 -5
View File
@@ -1,11 +1,8 @@
use std::path::PathBuf; use std::path::PathBuf;
#[cfg(any(feature = "lua53", feature = "lua52", feature = "lua51"))]
use lua_src;
#[cfg(feature = "luajit")]
use luajit_src;
pub fn probe_lua() -> PathBuf { pub fn probe_lua() -> PathBuf {
#[cfg(feature = "lua54")]
let artifacts = lua_src::Build::new().build(lua_src::Lua54);
#[cfg(feature = "lua53")] #[cfg(feature = "lua53")]
let artifacts = lua_src::Build::new().build(lua_src::Lua53); let artifacts = lua_src::Build::new().build(lua_src::Lua53);
#[cfg(feature = "lua52")] #[cfg(feature = "lua52")]
@@ -15,6 +12,8 @@ pub fn probe_lua() -> PathBuf {
#[cfg(feature = "luajit")] #[cfg(feature = "luajit")]
let artifacts = luajit_src::Build::new().build(); let artifacts = luajit_src::Build::new().build();
#[cfg(not(feature = "module"))]
artifacts.print_cargo_metadata(); artifacts.print_cargo_metadata();
artifacts.include_dir().to_owned() artifacts.include_dir().to_owned()
} }
+58 -8
View File
@@ -5,8 +5,42 @@ use std::io::{Error, ErrorKind, Result};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::Command; use std::process::Command;
#[cfg_attr(feature = "vendored", path = "find_vendored.rs")] #[cfg_attr(
#[cfg_attr(not(feature = "vendored"), path = "find_normal.rs")] 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"
)),
path = "find_dummy.rs"
)]
mod find; mod find;
trait CommandExt { trait CommandExt {
@@ -59,27 +93,43 @@ fn build_glue<P: AsRef<Path> + std::fmt::Debug>(include_path: &P) {
fn main() { fn main() {
#[cfg(not(any( #[cfg(not(any(
feature = "lua54",
feature = "lua53", feature = "lua53",
feature = "lua52", feature = "lua52",
feature = "lua51", feature = "lua51",
feature = "luajit" feature = "luajit"
)))] )))]
panic!("You must enable one of the features: lua53, lua52, lua51, luajit"); compile_error!("You must enable one of the features: lua54, lua53, lua52, lua51, luajit");
#[cfg(all(
feature = "lua54",
any(
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit"
)
))]
compile_error!("You can enable only one of the features: lua54, lua53, lua52, lua51, luajit");
#[cfg(all( #[cfg(all(
feature = "lua53", feature = "lua53",
any(feature = "lua52", feature = "lua51", feature = "luajit") any(feature = "lua52", feature = "lua51", feature = "luajit")
))] ))]
panic!("You can enable only one of the features: lua53, lua52, lua51, luajit"); compile_error!("You can enable only one of the features: lua54, lua53, lua52, lua51, luajit");
#[cfg(all(feature = "lua52", any(feature = "lua51", feature = "luajit")))] #[cfg(all(feature = "lua52", any(feature = "lua51", feature = "luajit")))]
panic!("You can enable only one of the features: lua53, lua52, lua51, luajit"); compile_error!("You can enable only one of the features: lua54, lua53, lua52, lua51, luajit");
#[cfg(all(feature = "lua51", feature = "luajit"))] #[cfg(all(feature = "lua51", feature = "luajit"))]
panic!("You can enable only one of the features: lua53, lua52, lua51, luajit"); compile_error!("You can enable only one of the features: lua54, lua53, lua52, lua51, luajit");
#[cfg(all(feature = "lua51", feature = "luajit"))] // We don't support "vendored module" mode on windows
panic!("You can enable only one of the features: lua53, lua52, lua51, luajit"); #[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."
);
let include_dir = find::probe_lua(); let include_dir = find::probe_lua();
build_glue(&include_dir); build_glue(&include_dir);
+82
View File
@@ -0,0 +1,82 @@
use std::collections::HashMap;
use std::sync::Arc;
use bstr::BString;
use hyper::{body::Body as HyperBody, Client as HyperClient};
use tokio::{stream::StreamExt, sync::Mutex};
use mlua::{Error, Lua, Result, UserData, UserDataMethods};
#[derive(Clone)]
struct BodyReader(Arc<Mutex<HyperBody>>);
impl BodyReader {
fn new(body: HyperBody) -> Self {
BodyReader(Arc::new(Mutex::new(body)))
}
}
impl UserData for BodyReader {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("read", |_, reader, ()| async move {
let mut reader = reader.0.lock().await;
let bytes = reader.try_next().await.map_err(Error::external)?;
if let Some(bytes) = bytes {
return Ok(Some(BString::from(bytes.as_ref())));
}
Ok(None)
});
}
}
#[tokio::main]
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().map_err(Error::external)?;
let resp = client.get(uri).await.map_err(Error::external)?;
let lua_resp = lua.create_table()?;
lua_resp.set("status", resp.status().as_u16())?;
let mut headers = HashMap::new();
for (key, value) in resp.headers().iter() {
headers
.entry(key.as_str())
.or_insert(Vec::new())
.push(value.to_str().unwrap());
}
lua_resp.set("headers", headers)?;
lua_resp.set("body", BodyReader::new(resp.into_body()))?;
Ok(lua_resp)
})?;
let globals = lua.globals();
globals.set("fetch_url", fetch_url)?;
let f = lua
.load(
r#"
local res = fetch_url(...);
print(res.status)
for key, vals in pairs(res.headers) do
for _, val in ipairs(vals) do
print(key..": "..val)
end
end
repeat
local body = res.body:read()
if body then
print(body)
end
until not body
"#,
)
.into_function()?;
f.call_async("http://httpbin.org/ip").await
}
+101
View File
@@ -0,0 +1,101 @@
use std::net::SocketAddr;
use std::sync::Arc;
use hyper::server::conn::AddrStream;
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Request, Response, Server};
use mlua::{Error, Function, Lua, Result, Table, UserData, UserDataMethods};
#[derive(Clone)]
struct LuaRequest(Arc<(SocketAddr, Request<Body>)>);
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).0.to_string()));
methods.add_method("method", |_lua, req, ()| Ok((req.0).1.method().to_string()));
}
}
async fn run_server(handler: Function<'static>) -> Result<()> {
let make_svc = make_service_fn(|socket: &AddrStream| {
let remote_addr = socket.remote_addr();
let handler = handler.clone();
async move {
Ok::<_, Error>(service_fn(move |req: Request<Body>| {
let handler = handler.clone();
async move {
let lua_req = LuaRequest(Arc::new((remote_addr, req)));
let lua_resp: Table = handler.call_async(lua_req).await?;
let body = lua_resp
.get::<_, Option<String>>("body")?
.unwrap_or_default();
let mut resp = Response::builder()
.status(lua_resp.get::<_, Option<u16>>("status")?.unwrap_or(200));
if let Some(headers) = lua_resp.get::<_, Option<Table>>("headers")? {
for pair in headers.pairs::<String, String>() {
let (h, v) = pair?;
resp = resp.header(&h, v);
}
}
Ok::<_, Error>(resp.body(Body::from(body)).unwrap())
}
}))
}
});
let addr = ([127, 0, 0, 1], 3000).into();
let server = Server::bind(&addr).executor(LocalExec).serve(make_svc);
println!("Listening on http://{}", addr);
tokio::task::LocalSet::new()
.run_until(server)
.await
.map_err(Error::external)
}
#[tokio::main]
async fn main() -> Result<()> {
let lua = Lua::new().into_static();
let handler: Function = lua
.load(
r#"
function(req)
return {
status = 200,
headers = {
["X-Req-Method"] = req:method(),
["X-Remote-Addr"] = req:remote_addr(),
},
body = "Hello, World!"
}
end
"#,
)
.eval()?;
run_server(handler).await?;
// Consume the static reference and drop it.
// This is safe as long as we don't hold any other references to Lua
// or alive resources.
unsafe { Lua::from_static(lua) };
Ok(())
}
#[derive(Clone, Copy, Debug)]
struct LocalExec;
impl<F> hyper::rt::Executor<F> for LocalExec
where
F: std::future::Future + 'static, // not requiring `Send`
{
fn execute(&self, fut: F) {
tokio::task::spawn_local(fut);
}
}
+129
View File
@@ -0,0 +1,129 @@
use std::net::Shutdown;
use std::sync::Arc;
use bstr::BString;
use tokio::net::{TcpListener, TcpStream};
use tokio::prelude::*;
use tokio::sync::Mutex;
use tokio::task;
use mlua::{Function, Lua, Result, UserData, UserDataMethods};
#[derive(Clone)]
struct LuaTcp;
#[derive(Clone)]
struct LuaTcpListener(Arc<Mutex<TcpListener>>);
#[derive(Clone)]
struct LuaTcpStream(Arc<Mutex<TcpStream>>);
impl UserData for LuaTcp {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_function("bind", |_, addr: String| async move {
let listener = TcpListener::bind(addr).await?;
Ok(LuaTcpListener(Arc::new(Mutex::new(listener))))
});
}
}
impl UserData for LuaTcpListener {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("accept", |_, listener, ()| async move {
let (stream, _) = listener.0.lock().await.accept().await?;
Ok(LuaTcpStream(Arc::new(Mutex::new(stream))))
});
}
}
impl UserData for LuaTcpStream {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("peer_addr", |_, stream, ()| async move {
Ok(stream.0.lock().await.peer_addr()?.to_string())
});
methods.add_async_method("read", |_, stream, size: usize| async move {
let mut buf = vec![0; size];
let n = stream.0.lock().await.read(&mut buf).await?;
buf.truncate(n);
Ok(BString::from(buf))
});
methods.add_async_method("write", |_, stream, data: BString| async move {
let n = stream.0.lock().await.write(&data).await?;
Ok(n)
});
methods.add_async_method("close", |_, stream, ()| async move {
stream.0.lock().await.shutdown(Shutdown::Both)?;
Ok(())
});
}
}
async fn run_server(lua: &'static Lua) -> Result<()> {
let spawn = lua.create_function(move |_, func: Function| {
task::spawn_local(async move { func.call_async::<_, ()>(()).await.unwrap() });
Ok(())
})?;
let globals = lua.globals();
globals.set("tcp", LuaTcp)?;
globals.set("spawn", spawn)?;
let server = lua
.load(
r#"
local addr = ...
local listener = tcp.bind(addr)
print("listening on "..addr)
local accept_new = true
while true do
local stream = listener:accept()
local peer_addr = stream:peer_addr()
print("connected from "..peer_addr)
if not accept_new then
return
end
spawn(function()
while true do
local data = stream:read(100)
data = data:match("^%s*(.-)%s*$") -- trim
print("["..peer_addr.."] "..data)
if data == "bye" then
stream:write("bye bye\n")
stream:close()
return
end
if data == "exit" then
stream:close()
accept_new = false
return
end
stream:write("echo: "..data.."\n")
end
end)
end
"#,
)
.into_function()?;
task::LocalSet::new()
.run_until(server.call_async::<_, ()>("0.0.0.0:1234"))
.await
}
#[tokio::main]
async fn main() {
let lua = Lua::new().into_static();
run_server(lua).await.unwrap();
// Consume the static reference and drop it.
// This is safe as long as we don't hold any other references to Lua
// or alive resources.
unsafe { Lua::from_static(lua) };
}
-28
View File
@@ -164,34 +164,6 @@ fn main() -> Result<()> {
< f32::EPSILON < f32::EPSILON
); );
// Normally, Rust types passed to `Lua` must be `Send`, because `Lua` itself is `Send`, and
// must be `'static`, because there is no way to be sure of their lifetime inside the Lua
// state. There is, however, a limited way to lift both of these requirements. You can
// call `Lua::scope` to create userdata and callbacks types that only live for as long
// as the call to scope, but do not have to be `Send` OR `'static`.
{
let mut rust_val = 0;
lua.scope(|scope| {
// We create a 'sketchy' Lua callback that holds a mutable reference to the variable
// `rust_val`. Outside of a `Lua::scope` call, this would not be allowed
// because it could be unsafe.
lua.globals().set(
"sketchy",
scope.create_function_mut(|_, ()| {
rust_val = 42;
Ok(())
})?,
)?;
lua.load("sketchy()").exec()
})?;
assert_eq!(rust_val, 42);
}
// We were able to run our 'sketchy' function inside the scope just fine. However, if we // We were able to run our 'sketchy' function inside the scope just fine. However, if we
// try to run our 'sketchy' function outside of the scope, the function we created will have // try to run our 'sketchy' function outside of the scope, the function we created will have
// been invalidated and we will generate an error. If our function wasn't invalidated, we // been invalidated and we will generate an error. If our function wasn't invalidated, we
+5
View File
@@ -0,0 +1,5 @@
[target.x86_64-apple-darwin]
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
+20
View File
@@ -0,0 +1,20 @@
[package]
name = "rust_module"
version = "0.0.0"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2018"
[lib]
crate-type = ["cdylib"]
[features]
lua54 = ["mlua/lua54"]
lua53 = ["mlua/lua53"]
lua52 = ["mlua/lua52"]
lua51 = ["mlua/lua51"]
luajit = ["mlua/luajit"]
vendored = ["mlua/vendored"]
[dependencies]
mlua = { path = "../..", features = ["module"] }
mlua_derive = { path = "../../mlua_derive" }
+17
View File
@@ -0,0 +1,17 @@
use mlua::prelude::*;
fn sum(_: &Lua, (a, b): (i64, i64)) -> LuaResult<i64> {
Ok(a + b)
}
fn used_memory(lua: &Lua, _: ()) -> LuaResult<usize> {
Ok(lua.used_memory())
}
#[mlua_derive::lua_module]
fn rust_module(lua: &Lua) -> LuaResult<LuaTable> {
let exports = lua.create_table()?;
exports.set("sum", lua.create_function(sum)?)?;
exports.set("used_memory", lua.create_function(used_memory)?)?;
Ok(exports)
}
+2 -2
View File
@@ -1,7 +1,7 @@
[package] [package]
name = "mlua_derive" name = "mlua_derive"
version = "0.2.0" version = "0.4.0"
authors = ["Aleksandr Orlenko <zxteam@protonmail.com>"] authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2018" edition = "2018"
description = "Procedural macros for the mlua crate." description = "Procedural macros for the mlua crate."
repository = "https://github.com/khvzak/mlua" repository = "https://github.com/khvzak/mlua"
+2
View File
@@ -21,6 +21,8 @@ pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
let ext_entrypoint_name = Ident::new(&format!("luaopen_{}", item.sig.ident), Span::call_site()); let ext_entrypoint_name = Ident::new(&format!("luaopen_{}", item.sig.ident), Span::call_site());
let wrapped = quote_spanned! { span => let wrapped = quote_spanned! { span =>
mlua::require_module_feature!();
#[no_mangle] #[no_mangle]
unsafe extern "C" fn #ext_entrypoint_name(state: *mut mlua::lua_State) -> std::os::raw::c_int { unsafe extern "C" fn #ext_entrypoint_name(state: *mut mlua::lua_State) -> std::os::raw::c_int {
#item #item
+52 -8
View File
@@ -12,7 +12,7 @@ use crate::lua::Lua;
use crate::string::String; use crate::string::String;
use crate::table::Table; use crate::table::Table;
use crate::thread::Thread; use crate::thread::Thread;
use crate::types::{LightUserData, Number}; use crate::types::{LightUserData, MaybeSend, Number};
use crate::userdata::{AnyUserData, UserData}; use crate::userdata::{AnyUserData, UserData};
use crate::value::{FromLua, Nil, ToLua, Value}; use crate::value::{FromLua, Nil, ToLua, Value};
@@ -122,7 +122,7 @@ impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
} }
} }
impl<'lua, T: 'static + Send + UserData> ToLua<'lua> for T { impl<'lua, T: 'static + MaybeSend + UserData> ToLua<'lua> for T {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> { fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::UserData(lua.create_userdata(self)?)) Ok(Value::UserData(lua.create_userdata(self)?))
} }
@@ -167,7 +167,7 @@ impl<'lua> ToLua<'lua> for bool {
} }
impl<'lua> FromLua<'lua> for bool { impl<'lua> FromLua<'lua> for bool {
fn from_lua(v: Value, _: &'lua Lua) -> Result<Self> { fn from_lua(v: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match v { match v {
Value::Nil => Ok(false), Value::Nil => Ok(false),
Value::Boolean(b) => Ok(b), Value::Boolean(b) => Ok(b),
@@ -183,7 +183,7 @@ impl<'lua> ToLua<'lua> for LightUserData {
} }
impl<'lua> FromLua<'lua> for LightUserData { impl<'lua> FromLua<'lua> for LightUserData {
fn from_lua(value: Value, _: &'lua Lua) -> Result<Self> { fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match value { match value {
Value::LightUserData(ud) => Ok(ud), Value::LightUserData(ud) => Ok(ud),
_ => Err(Error::FromLuaConversionError { _ => Err(Error::FromLuaConversionError {
@@ -216,7 +216,7 @@ impl<'lua> FromLua<'lua> for StdString {
} }
} }
impl<'lua, 'a> ToLua<'lua> for &'a str { impl<'lua> ToLua<'lua> for &str {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> { fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self)?)) Ok(Value::String(lua.create_string(self)?))
} }
@@ -250,13 +250,13 @@ impl<'lua> FromLua<'lua> for CString {
} }
} }
impl<'lua, 'a> ToLua<'lua> for &'a CStr { impl<'lua> ToLua<'lua> for &CStr {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> { fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self.to_bytes())?)) Ok(Value::String(lua.create_string(self.to_bytes())?))
} }
} }
impl<'lua, 'a> ToLua<'lua> for BString { impl<'lua> ToLua<'lua> for BString {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> { fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(&self)?)) Ok(Value::String(lua.create_string(&self)?))
} }
@@ -278,7 +278,7 @@ impl<'lua> FromLua<'lua> for BString {
} }
} }
impl<'lua, 'a> ToLua<'lua> for &BStr { impl<'lua> ToLua<'lua> for &BStr {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> { fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(&self)?)) Ok(Value::String(lua.create_string(&self)?))
} }
@@ -373,6 +373,50 @@ macro_rules! lua_convert_float {
lua_convert_float!(f32); lua_convert_float!(f32);
lua_convert_float!(f64); lua_convert_float!(f64);
impl<'lua, T> ToLua<'lua> for &[T]
where
T: Clone + ToLua<'lua>,
{
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(
lua.create_sequence_from(self.iter().cloned())?,
))
}
}
macro_rules! lua_convert_array {
($($N:literal)+) => {
$(
impl<'lua, T> ToLua<'lua> for [T; $N]
where
T: Clone + ToLua<'lua>,
{
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
(&self).to_lua(lua)
}
}
impl<'lua, T> ToLua<'lua> for &[T; $N]
where
T: Clone + ToLua<'lua>,
{
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(
lua.create_sequence_from(self.iter().cloned())?,
))
}
}
)+
}
}
lua_convert_array! {
0 1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
30 31 32
}
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Vec<T> { impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Vec<T> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> { fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_sequence_from(self)?)) Ok(Value::Table(lua.create_sequence_from(self)?))
+64 -8
View File
@@ -1,6 +1,9 @@
use std::error::Error as StdError; use std::error::Error as StdError;
use std::fmt; use std::fmt;
use std::io::Error as IoError;
use std::net::AddrParseError;
use std::result::Result as StdResult; use std::result::Result as StdResult;
use std::str::Utf8Error;
use std::string::String as StdString; use std::string::String as StdString;
use std::sync::Arc; use std::sync::Arc;
@@ -33,6 +36,18 @@ pub enum Error {
/// The Lua VM returns this error when there is an error running a `__gc` metamethod. /// The Lua VM returns this error when there is an error running a `__gc` metamethod.
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua53", feature = "lua52"))]
GarbageCollectorError(StdString), GarbageCollectorError(StdString),
/// Potentially unsafe action in safe mode.
SafetyError(StdString),
/// Setting memory limit is not available.
///
/// This error can only happen when Lua state was not created by us and does not have the
/// custom allocator attached.
MemoryLimitNotAvailable,
/// Main thread is not available.
///
/// This error can only happen in Lua5.1/LuaJIT module mode, when module loaded within a coroutine.
/// These Lua versions does not have `LUA_RIDX_MAINTHREAD` registry key.
MainThreadNotAvailable,
/// A mutable callback has triggered Lua code that has called the same mutable callback again. /// A mutable callback has triggered Lua code that has called the same mutable callback again.
/// ///
/// This is an error because a mutable callback can only be borrowed mutably once. /// This is an error because a mutable callback can only be borrowed mutably once.
@@ -124,7 +139,10 @@ pub enum Error {
/// Returning `Err(ExternalError(...))` from a Rust callback will raise the error as a Lua /// Returning `Err(ExternalError(...))` from a Rust callback will raise the error as a Lua
/// error. The Rust code that originally invoked the Lua code then receives a `CallbackError`, /// error. The Rust code that originally invoked the Lua code then receives a `CallbackError`,
/// from which the original error (and a stack traceback) can be recovered. /// from which the original error (and a stack traceback) can be recovered.
#[cfg(feature = "send")]
ExternalError(Arc<dyn StdError + Send + Sync>), ExternalError(Arc<dyn StdError + Send + Sync>),
#[cfg(not(feature = "send"))]
ExternalError(Arc<dyn StdError>),
} }
/// A specialized `Result` type used by `mlua`'s API. /// A specialized `Result` type used by `mlua`'s API.
@@ -142,6 +160,15 @@ impl fmt::Display for Error {
Error::GarbageCollectorError(ref msg) => { Error::GarbageCollectorError(ref msg) => {
write!(fmt, "garbage collector error: {}", msg) write!(fmt, "garbage collector error: {}", msg)
} }
Error::SafetyError(ref msg) => {
write!(fmt, "safety error: {}", msg)
},
Error::MemoryLimitNotAvailable => {
write!(fmt, "setting memory limit is not available")
}
Error::MainThreadNotAvailable => {
write!(fmt, "main thread is not available in Lua 5.1")
}
Error::RecursiveMutCallback => write!(fmt, "mutable callback called recursively"), Error::RecursiveMutCallback => write!(fmt, "mutable callback called recursively"),
Error::CallbackDestructed => write!( Error::CallbackDestructed => write!(
fmt, fmt,
@@ -184,10 +211,10 @@ impl fmt::Display for Error {
Error::MismatchedRegistryKey => { Error::MismatchedRegistryKey => {
write!(fmt, "RegistryKey used from different Lua state") write!(fmt, "RegistryKey used from different Lua state")
} }
Error::CallbackError { ref traceback, ref cause } => { Error::CallbackError { ref traceback, .. } => {
write!(fmt, "callback error: {}: {}", cause, traceback) write!(fmt, "callback error: {}", traceback)
} }
Error::ExternalError(ref err) => write!(fmt, "external error: {}", err), Error::ExternalError(ref err) => write!(fmt, "{}", err),
} }
} }
} }
@@ -196,26 +223,37 @@ impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> { fn source(&self) -> Option<&(dyn StdError + 'static)> {
match *self { match *self {
Error::CallbackError { ref cause, .. } => Some(cause.as_ref()), Error::CallbackError { ref cause, .. } => Some(cause.as_ref()),
Error::ExternalError(ref err) => Some(err.as_ref()), Error::ExternalError(ref err) => err.source(),
_ => None, _ => None,
} }
} }
} }
impl Error { impl Error {
#[cfg(feature = "send")]
pub fn external<T: Into<Box<dyn StdError + Send + Sync>>>(err: T) -> Error { pub fn external<T: Into<Box<dyn StdError + Send + Sync>>>(err: T) -> Error {
Error::ExternalError(err.into().into()) Error::ExternalError(err.into().into())
} }
#[cfg(not(feature = "send"))]
pub fn external<T: Into<Box<dyn StdError>>>(err: T) -> Error {
Error::ExternalError(err.into().into())
}
} }
pub trait ExternalError { pub trait ExternalError {
fn to_lua_err(self) -> Error; fn to_lua_err(self) -> Error;
} }
impl<E> ExternalError for E #[cfg(feature = "send")]
where impl<E: Into<Box<dyn StdError + Send + Sync>>> ExternalError for E {
E: Into<Box<dyn StdError + Send + Sync>>, fn to_lua_err(self) -> Error {
{ Error::external(self)
}
}
#[cfg(not(feature = "send"))]
impl<E: Into<Box<dyn StdError>>> ExternalError for E {
fn to_lua_err(self) -> Error { fn to_lua_err(self) -> Error {
Error::external(self) Error::external(self)
} }
@@ -233,3 +271,21 @@ where
self.map_err(|e| e.to_lua_err()) self.map_err(|e| e.to_lua_err())
} }
} }
impl std::convert::From<AddrParseError> for Error {
fn from(err: AddrParseError) -> Self {
Error::external(err)
}
}
impl std::convert::From<IoError> for Error {
fn from(err: IoError) -> Self {
Error::external(err)
}
}
impl std::convert::From<Utf8Error> for Error {
fn from(err: Utf8Error) -> Self {
Error::external(err)
}
}
+42 -31
View File
@@ -22,6 +22,8 @@
// Based on github.com/keplerproject/lua-compat-5.3 // Based on github.com/keplerproject/lua-compat-5.3
#![allow(clippy::needless_return)]
use std::ffi::CStr; use std::ffi::CStr;
use std::mem; use std::mem;
use std::os::raw::{c_char, c_int, c_void}; use std::os::raw::{c_char, c_int, c_void};
@@ -42,18 +44,18 @@ use super::lua::{
lua_createtable, lua_dump_old, lua_error, lua_getfield_old, lua_getstack, lua_gettable_old, lua_createtable, lua_dump_old, lua_error, lua_getfield_old, lua_getstack, lua_gettable_old,
lua_gettop, lua_insert, lua_isstring, lua_istable, lua_newuserdata, lua_pop, lua_pushboolean, lua_gettop, lua_insert, lua_isstring, lua_istable, lua_newuserdata, lua_pop, lua_pushboolean,
lua_pushcfunction, lua_pushfstring, lua_pushinteger, lua_pushliteral, lua_pushlstring_old, lua_pushcfunction, lua_pushfstring, lua_pushinteger, lua_pushliteral, lua_pushlstring_old,
lua_pushnumber, lua_pushthread, lua_pushvalue, lua_rawget_old, lua_rawgeti_old, lua_rawset, lua_pushnil, lua_pushnumber, lua_pushthread, lua_pushvalue, lua_rawget_old, lua_rawgeti_old,
lua_replace, lua_setfield, lua_setglobal, lua_setmetatable, lua_settable, lua_toboolean, lua_rawset, lua_replace, lua_setfield, lua_setglobal, lua_setmetatable, lua_settable,
lua_tointeger, lua_tolstring, lua_tonumber, lua_topointer, lua_tostring, lua_touserdata, lua_toboolean, lua_tointeger, lua_tolstring, lua_tonumber, lua_topointer, lua_tostring,
lua_type, lua_typename, lua_touserdata, lua_type, lua_typename,
}; };
#[cfg(any(feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua51", feature = "luajit"))]
use super::lua::{ use super::lua::{
lua_checkstack, lua_concat, lua_equal, lua_getfenv, lua_getinfo, lua_getmetatable, lua_checkstack, lua_concat, lua_equal, lua_getfenv, lua_getinfo, lua_getmetatable,
lua_isnumber, lua_lessthan, lua_newtable, lua_next, lua_objlen, lua_pushcclosure, lua_isnumber, lua_lessthan, lua_newtable, lua_next, lua_objlen, lua_pushcclosure,
lua_pushlightuserdata, lua_pushnil, lua_pushstring_old, lua_rawequal, lua_remove, lua_pushlightuserdata, lua_pushstring_old, lua_rawequal, lua_remove, lua_resume_old,
lua_resume_old, lua_setfenv, lua_settop, LUA_OPADD, LUA_OPUNM, lua_setfenv, lua_settop, LUA_OPADD, LUA_OPUNM,
}; };
#[cfg(feature = "lua52")] #[cfg(feature = "lua52")]
@@ -114,7 +116,7 @@ unsafe fn compat53_checkmode(
} }
} }
if mode != ptr::null() && strchr(mode, *modename) == ptr::null() { if !mode.is_null() && strchr(mode, *modename).is_null() {
lua_pushfstring( lua_pushfstring(
L, L,
cstr!("attempt to load a %s chunk (mode is '%s')"), cstr!("attempt to load a %s chunk (mode is '%s')"),
@@ -234,7 +236,7 @@ pub unsafe fn lua_absindex(L: *mut lua_State, mut idx: c_int) -> c_int {
} }
#[cfg(any(feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua51", feature = "luajit"))]
static COMPAT53_ARITH_CODE: &'static str = r#" static COMPAT53_ARITH_CODE: &str = r#"
local op,a,b=... local op,a,b=...
if op == 0 then return a+b if op == 0 then return a+b
elseif op == 1 then return a-b elseif op == 1 then return a-b
@@ -287,7 +289,7 @@ pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
if lua_type(L, idx) == lua::LUA_TNUMBER { if lua_type(L, idx) == lua::LUA_TNUMBER {
let n = lua_tonumber(L, idx); let n = lua_tonumber(L, idx);
let i = lua_tointeger(L, idx); let i = lua_tointeger(L, idx);
if i as f64 == n { if (n - i as lua_Number).abs() < lua_Number::EPSILON {
return 1; return 1;
} }
} }
@@ -297,7 +299,7 @@ pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
#[cfg(any(feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua51", feature = "luajit"))]
pub unsafe fn lua_tonumberx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Number { pub unsafe fn lua_tonumberx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Number {
let n = lua_tonumber(L, i); let n = lua_tonumber(L, i);
if isnum != ptr::null_mut() { if !isnum.is_null() {
*isnum = if n != 0.0 || lua_isnumber(L, i) != 0 { *isnum = if n != 0.0 || lua_isnumber(L, i) != 0 {
1 1
} else { } else {
@@ -312,15 +314,14 @@ pub unsafe fn lua_tonumberx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> l
pub unsafe fn lua_tointegerx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Integer { pub unsafe fn lua_tointegerx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Integer {
let mut ok = 0; let mut ok = 0;
let n = lua_tonumberx(L, i, &mut ok); let n = lua_tonumberx(L, i, &mut ok);
if ok != 0 { let n_int = n as lua_Integer;
if n == n as lua_Integer as lua_Number { if ok != 0 && (n - n_int as lua_Number).abs() < lua_Number::EPSILON {
if isnum != ptr::null_mut() { if !isnum.is_null() {
*isnum = 1; *isnum = 1;
}
return n as lua_Integer;
} }
return n_int;
} }
if isnum != ptr::null_mut() { if !isnum.is_null() {
*isnum = 0; *isnum = 0;
} }
return 0; return 0;
@@ -531,7 +532,7 @@ pub unsafe fn lua_getextraspace(L: *mut lua_State) -> *mut c_void {
let is_main = lua_pushthread(L); let is_main = lua_pushthread(L);
lua_rawget(L, -2); lua_rawget(L, -2);
let mut _ptr = lua_touserdata(L, -1); let mut _ptr = lua_touserdata(L, -1);
if _ptr == ptr::null_mut() { if _ptr.is_null() {
lua_pop(L, 1); lua_pop(L, 1);
_ptr = lua_newuserdata(L, LUA_EXTRASPACE as usize); _ptr = lua_newuserdata(L, LUA_EXTRASPACE as usize);
if is_main != 0 { if is_main != 0 {
@@ -547,7 +548,7 @@ pub unsafe fn lua_getextraspace(L: *mut lua_State) -> *mut c_void {
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
lua_rawget(L, -3); lua_rawget(L, -3);
let mptr = lua_touserdata(L, -1); let mptr = lua_touserdata(L, -1);
if mptr != ptr::null_mut() { if !mptr.is_null() {
ptr::copy_nonoverlapping(mptr, _ptr, LUA_EXTRASPACE as usize) ptr::copy_nonoverlapping(mptr, _ptr, LUA_EXTRASPACE as usize)
} else { } else {
ptr::write_bytes(_ptr, 0, LUA_EXTRASPACE as usize); ptr::write_bytes(_ptr, 0, LUA_EXTRASPACE as usize);
@@ -575,7 +576,7 @@ pub unsafe fn lua_pushglobaltable(L: *mut lua_State) {
#[cfg(any(feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua51", feature = "luajit"))]
pub unsafe fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char) { pub unsafe fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char) {
if lua_checkstack(L, sz + lua::LUA_MINSTACK) == 0 { if lua_checkstack(L, sz + lua::LUA_MINSTACK) == 0 {
if msg != ptr::null() { if !msg.is_null() {
luaL_error(L, cstr!("stack overflow (%s)"), msg); luaL_error(L, cstr!("stack overflow (%s)"), msg);
} else { } else {
lua_pushliteral(L, "stack overflow"); lua_pushliteral(L, "stack overflow");
@@ -654,7 +655,7 @@ pub unsafe fn luaL_traceback(
0 0
}; };
if msg != ptr::null() { if !msg.is_null() {
lua_pushfstring(L, cstr!("%s\n"), msg); lua_pushfstring(L, cstr!("%s\n"), msg);
} }
lua_pushliteral(L, "stack traceback:"); lua_pushliteral(L, "stack traceback:");
@@ -708,10 +709,8 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
} }
} }
}; };
} else { } else if lua_isstring(L, -1) == 0 {
if lua_isstring(L, -1) == 0 { luaL_error(L, cstr!("'__tostring' must return a string"));
luaL_error(L, cstr!("'__tostring' must return a string"));
}
} }
lua_tolstring(L, -1, len) lua_tolstring(L, -1, len)
} }
@@ -727,7 +726,7 @@ pub unsafe fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char) {
pub unsafe fn luaL_testudata(L: *mut lua_State, i: c_int, tname: *const c_char) -> *mut c_void { pub unsafe fn luaL_testudata(L: *mut lua_State, i: c_int, tname: *const c_char) -> *mut c_void {
let mut p = lua_touserdata(L, i); let mut p = lua_touserdata(L, i);
luaL_checkstack(L, 2, cstr!("not enough stack slots")); luaL_checkstack(L, 2, cstr!("not enough stack slots"));
if p == ptr::null_mut() || lua_getmetatable(L, i) == 0 { if p.is_null() || lua_getmetatable(L, i) == 0 {
return ptr::null_mut(); return ptr::null_mut();
} else { } else {
luaL_getmetatable(L, tname); luaL_getmetatable(L, tname);
@@ -743,7 +742,7 @@ pub unsafe fn luaL_testudata(L: *mut lua_State, i: c_int, tname: *const c_char)
#[cfg(any(feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua51", feature = "luajit"))]
pub unsafe fn luaL_setfuncs(L: *mut lua_State, mut l: *const luaL_Reg, nup: c_int) { pub unsafe fn luaL_setfuncs(L: *mut lua_State, mut l: *const luaL_Reg, nup: c_int) {
luaL_checkstack(L, nup + 1, cstr!("too many upvalues")); luaL_checkstack(L, nup + 1, cstr!("too many upvalues"));
while (*l).name != ptr::null() { while !(*l).name.is_null() {
// fill the table with given functions // fill the table with given functions
l = l.offset(1); l = l.offset(1);
lua_pushstring(L, (*l).name); lua_pushstring(L, (*l).name);
@@ -786,13 +785,25 @@ pub unsafe fn luaL_requiref(
lua_pop(L, 1); lua_pop(L, 1);
lua_pushcfunction(L, openf); lua_pushcfunction(L, openf);
lua_pushstring(L, modname); lua_pushstring(L, modname);
lua_call(L, 1, 1); #[cfg(any(feature = "lua52", feature = "lua51"))]
lua_pushvalue(L, -1); {
lua_setfield(L, -3, modname); lua_call(L, 1, 1);
lua_pushvalue(L, -1);
lua_setfield(L, -3, modname);
}
#[cfg(feature = "luajit")]
{
lua_call(L, 1, 0);
lua_getfield(L, -1, modname);
}
} }
if glb != 0 { if cfg!(any(feature = "lua52", feature = "lua51")) && glb != 0 {
lua_pushvalue(L, -1); lua_pushvalue(L, -1);
lua_setglobal(L, modname); lua_setglobal(L, modname);
} }
if cfg!(feature = "luajit") && glb == 0 {
lua_pushnil(L);
lua_setglobal(L, modname);
}
lua_replace(L, -2); lua_replace(L, -2);
} }
+10 -6
View File
@@ -1,6 +1,6 @@
// The MIT License (MIT) // The MIT License (MIT)
// //
// Copyright (c) 2019 A. Orlenko // Copyright (c) 2019-2020 A. Orlenko
// Copyright (c) 2014 J.C. Moyer // Copyright (c) 2014 J.C. Moyer
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -53,7 +53,7 @@ typedef struct rs_item {
#define RS_INT(name, val) \ #define RS_INT(name, val) \
{ TY_INT, name, .int_val = val } { TY_INT, name, .int_val = val }
#if LUA_VERSION_NUM == 503 #if LUA_VERSION_NUM >= 503
#define TY_LUAINT 1 #define TY_LUAINT 1
#define RS_LUAINT(name, val) \ #define RS_LUAINT(name, val) \
{ TY_LUAINT, name, .lua_int_val = val } { TY_LUAINT, name, .lua_int_val = val }
@@ -84,6 +84,8 @@ const char *rs_int_type(int width) {
return "i32"; return "i32";
case 8: case 8:
return "i64"; return "i64";
case 16:
return "i128";
} }
} }
@@ -96,6 +98,8 @@ const char *rs_uint_type(int width) {
return "u32"; return "u32";
case 8: case 8:
return "u64"; return "u64";
case 16:
return "u128";
} }
} }
@@ -139,7 +143,7 @@ int write_int_item(FILE *f, const char *name, int value) {
return fprintf(f, "pub const %s: c_int = %d;\n", name, value); return fprintf(f, "pub const %s: c_int = %d;\n", name, value);
} }
#if LUA_VERSION_NUM == 503 #if LUA_VERSION_NUM >= 503
int write_lua_int_item(FILE *f, const char *name, LUA_INTEGER value) { int write_lua_int_item(FILE *f, const char *name, LUA_INTEGER value) {
return fprintf(f, "pub const %s: LUA_INTEGER = " LUA_INTEGER_FMT ";\n", name, return fprintf(f, "pub const %s: LUA_INTEGER = " LUA_INTEGER_FMT ";\n", name,
value); value);
@@ -152,7 +156,7 @@ int write_str_item(FILE *f, const char *name, const char *value) {
char *buf = malloc(bufsz); char *buf = malloc(bufsz);
int ret; int ret;
escape(value, buf, bufsz); escape(value, buf, bufsz);
ret = fprintf(f, "pub const %s: &'static str = \"%s\";\n", name, buf); ret = fprintf(f, "pub const %s: &str = \"%s\";\n", name, buf);
free(buf); free(buf);
return ret; return ret;
} }
@@ -171,7 +175,7 @@ int write_item(FILE *f, const rs_item *c) {
switch (c->type) { switch (c->type) {
case TY_INT: case TY_INT:
return write_int_item(f, c->name, c->int_val); return write_int_item(f, c->name, c->int_val);
#if LUA_VERSION_NUM == 503 #if LUA_VERSION_NUM >= 503
case TY_LUAINT: case TY_LUAINT:
return write_lua_int_item(f, c->name, c->lua_int_val); return write_lua_int_item(f, c->name, c->lua_int_val);
#endif #endif
@@ -251,7 +255,7 @@ int main(int argc, const char **argv) {
// == lauxlib.h ========================================================== // == lauxlib.h ==========================================================
RS_COMMENT("lauxlib.h"), RS_COMMENT("lauxlib.h"),
#if LUA_VERSION_NUM == 503 #if LUA_VERSION_NUM >= 503
RS_INT("LUAL_NUMSIZES", LUAL_NUMSIZES), RS_INT("LUAL_NUMSIZES", LUAL_NUMSIZES),
#endif #endif
+25 -23
View File
@@ -1,6 +1,6 @@
// The MIT License (MIT) // The MIT License (MIT)
// //
// Copyright (c) 2019 A. Orlenko // Copyright (c) 2019-2020 A. Orlenko
// Copyright (c) 2014 J.C. Moyer // Copyright (c) 2014 J.C. Moyer
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -28,7 +28,7 @@ use std::ptr;
use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State}; use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State};
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub use super::glue::LUAL_NUMSIZES; pub use super::glue::LUAL_NUMSIZES;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
@@ -49,10 +49,10 @@ pub struct luaL_Reg {
pub func: lua_CFunction, pub func: lua_CFunction,
} }
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)] #[inline(always)]
pub unsafe fn luaL_checkversion(L: *mut lua_State) { pub unsafe fn luaL_checkversion(L: *mut lua_State) {
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
luaL_checkversion_( luaL_checkversion_(
L, L,
lua::LUA_VERSION_NUM as lua_Number, lua::LUA_VERSION_NUM as lua_Number,
@@ -63,19 +63,19 @@ pub unsafe fn luaL_checkversion(L: *mut lua_State) {
} }
extern "C" { extern "C" {
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number, sz: usize); pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number, sz: usize);
#[cfg(feature = "lua52")] #[cfg(feature = "lua52")]
pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number); pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number);
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int; pub fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
#[link_name = "luaL_getmetafield"] #[link_name = "luaL_getmetafield"]
pub fn luaL_getmetafield_old(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int; pub fn luaL_getmetafield_old(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
pub fn luaL_callmeta(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int; pub fn luaL_callmeta(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char; pub fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
pub fn luaL_argerror(L: *mut lua_State, arg: c_int, l: *const c_char) -> c_int; pub fn luaL_argerror(L: *mut lua_State, arg: c_int, l: *const c_char) -> c_int;
pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char; pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char;
@@ -90,20 +90,20 @@ extern "C" {
pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer; pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer;
pub fn luaL_optinteger(L: *mut lua_State, arg: c_int, def: lua_Integer) -> lua_Integer; pub fn luaL_optinteger(L: *mut lua_State, arg: c_int, def: lua_Integer) -> lua_Integer;
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char); pub fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char);
pub fn luaL_checktype(L: *mut lua_State, arg: c_int, t: c_int); pub fn luaL_checktype(L: *mut lua_State, arg: c_int, t: c_int);
pub fn luaL_checkany(L: *mut lua_State, arg: c_int); pub fn luaL_checkany(L: *mut lua_State, arg: c_int);
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_int; pub fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_int;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
#[link_name = "luaL_newmetatable"] #[link_name = "luaL_newmetatable"]
pub fn luaL_newmetatable_old(L: *mut lua_State, tname: *const c_char) -> c_int; pub fn luaL_newmetatable_old(L: *mut lua_State, tname: *const c_char) -> c_int;
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char); pub fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char);
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaL_testudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void; pub fn luaL_testudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void; pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
@@ -118,9 +118,9 @@ extern "C" {
lst: *const *const c_char, lst: *const *const c_char,
) -> c_int; ) -> c_int;
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaL_fileresult(L: *mut lua_State, stat: c_int, fname: *const c_char) -> c_int; pub fn luaL_fileresult(L: *mut lua_State, stat: c_int, fname: *const c_char) -> c_int;
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaL_execresult(L: *mut lua_State, stat: c_int) -> c_int; pub fn luaL_execresult(L: *mut lua_State, stat: c_int) -> c_int;
} }
@@ -132,21 +132,21 @@ extern "C" {
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int; pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
pub fn luaL_unref(L: *mut lua_State, t: c_int, r: c_int); pub fn luaL_unref(L: *mut lua_State, t: c_int, r: c_int);
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char) pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char)
-> c_int; -> c_int;
#[cfg(any(feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua51", feature = "luajit"))]
pub fn luaL_loadfile(L: *mut lua_State, filename: *const c_char) -> c_int; pub fn luaL_loadfile(L: *mut lua_State, filename: *const c_char) -> c_int;
} }
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)] #[inline(always)]
pub unsafe fn luaL_loadfile(L: *mut lua_State, f: *const c_char) -> c_int { pub unsafe fn luaL_loadfile(L: *mut lua_State, f: *const c_char) -> c_int {
luaL_loadfilex(L, f, ptr::null()) luaL_loadfilex(L, f, ptr::null())
} }
extern "C" { extern "C" {
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaL_loadbufferx( pub fn luaL_loadbufferx(
L: *mut lua_State, L: *mut lua_State,
buff: *const c_char, buff: *const c_char,
@@ -165,9 +165,11 @@ extern "C" {
pub fn luaL_newstate() -> *mut lua_State; pub fn luaL_newstate() -> *mut lua_State;
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer; pub fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer;
// TODO (lua54): luaL_addgsub
pub fn luaL_gsub( pub fn luaL_gsub(
L: *mut lua_State, L: *mut lua_State,
s: *const c_char, s: *const c_char,
@@ -175,17 +177,17 @@ extern "C" {
r: *const c_char, r: *const c_char,
) -> *const c_char; ) -> *const c_char;
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaL_setfuncs(L: *mut lua_State, l: *const luaL_Reg, nup: c_int); pub fn luaL_setfuncs(L: *mut lua_State, l: *const luaL_Reg, nup: c_int);
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int; pub fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int;
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, level: c_int); pub fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, level: c_int);
// Skip Lua 5.2 implementation in favor of the compat53 one // Skip Lua 5.2 implementation in favor of the compat53 one
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn luaL_requiref( pub fn luaL_requiref(
L: *mut lua_State, L: *mut lua_State,
modname: *const c_char, modname: *const c_char,
@@ -288,7 +290,7 @@ pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) {
// luaL_opt would be implemented here but it is undocumented, so it's omitted // luaL_opt would be implemented here but it is undocumented, so it's omitted
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)] #[inline(always)]
pub unsafe fn luaL_loadbuffer( pub unsafe fn luaL_loadbuffer(
L: *mut lua_State, L: *mut lua_State,
@@ -301,4 +303,4 @@ pub unsafe fn luaL_loadbuffer(
// TODO: Add buffer API // TODO: Add buffer API
// omitted: old module system compatibility // omitted: old module system compatibility (removed in 5.4)
+172 -68
View File
@@ -1,6 +1,6 @@
// The MIT License (MIT) // The MIT License (MIT)
// //
// Copyright (c) 2019 A. Orlenko // Copyright (c) 2019-2020 A. Orlenko
// Copyright (c) 2014 J.C. Moyer // Copyright (c) 2014 J.C. Moyer
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -23,9 +23,11 @@
//! Contains definitions from `lua.h`. //! Contains definitions from `lua.h`.
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
use std::os::raw::c_uchar; use std::os::raw::c_uchar;
use std::os::raw::{c_char, c_int, c_void}; use std::os::raw::{c_char, c_int, c_void};
#[cfg(feature = "lua54")]
use std::os::raw::{c_uint, c_ushort};
use std::ptr; use std::ptr;
use super::luaconf; use super::luaconf;
@@ -36,7 +38,10 @@ pub use super::glue::LUA_REGISTRYINDEX;
#[cfg(any(feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua51", feature = "luajit"))]
pub use super::glue::{LUA_ENVIRONINDEX, LUA_GLOBALSINDEX}; pub use super::glue::{LUA_ENVIRONINDEX, LUA_GLOBALSINDEX};
pub const LUA_SIGNATURE: &'static [u8] = b"\x1bLua"; #[cfg(not(feature = "luajit"))]
pub const LUA_SIGNATURE: &[u8] = b"\x1bLua";
#[cfg(feature = "luajit")]
pub const LUA_SIGNATURE: &[u8] = b"\x1bLJ";
// option for multiple returns in 'lua_pcall' and 'lua_call' // option for multiple returns in 'lua_pcall' and 'lua_call'
pub const LUA_MULTRET: c_int = -1; pub const LUA_MULTRET: c_int = -1;
@@ -51,13 +56,14 @@ pub use super::compat53::{
#[cfg(any(feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua51", feature = "luajit"))]
pub use super::compat53::{ pub use super::compat53::{
lua_absindex, lua_arith, lua_compare, lua_copy, lua_len, lua_pushglobaltable, lua_pushstring, lua_absindex, lua_arith, lua_compare, lua_copy, lua_len, lua_pushglobaltable, lua_pushstring,
lua_rawlen, lua_rawsetp, lua_resume, lua_setuservalue, lua_tonumberx, lua_upvalueindex, lua_rawlen, lua_rawsetp, lua_resume as lua_resume_53, lua_setuservalue, lua_tonumberx,
lua_upvalueindex,
}; };
#[cfg(feature = "lua52")] #[cfg(feature = "lua52")]
pub use super::compat53::lua_getglobal; pub use super::compat53::lua_getglobal;
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)] #[inline(always)]
pub fn lua_upvalueindex(i: c_int) -> c_int { pub fn lua_upvalueindex(i: c_int) -> c_int {
LUA_REGISTRYINDEX - i LUA_REGISTRYINDEX - i
@@ -71,7 +77,7 @@ pub const LUA_ERRSYNTAX: c_int = 3;
pub const LUA_ERRMEM: c_int = 4; pub const LUA_ERRMEM: c_int = 4;
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua53", feature = "lua52"))]
pub const LUA_ERRGCMM: c_int = 5; pub const LUA_ERRGCMM: c_int = 5;
#[cfg(any(feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua54", feature = "lua51", feature = "luajit"))]
pub const LUA_ERRERR: c_int = 5; pub const LUA_ERRERR: c_int = 5;
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua53", feature = "lua52"))]
pub const LUA_ERRERR: c_int = 6; pub const LUA_ERRERR: c_int = 6;
@@ -91,6 +97,8 @@ pub const LUA_TFUNCTION: c_int = 6;
pub const LUA_TUSERDATA: c_int = 7; pub const LUA_TUSERDATA: c_int = 7;
pub const LUA_TTHREAD: c_int = 8; pub const LUA_TTHREAD: c_int = 8;
#[cfg(feature = "lua54")]
pub const LUA_NUMTYPES: c_int = 9;
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua53", feature = "lua52"))]
pub const LUA_NUMTAGS: c_int = 9; pub const LUA_NUMTAGS: c_int = 9;
@@ -98,11 +106,11 @@ pub const LUA_NUMTAGS: c_int = 9;
pub const LUA_MINSTACK: c_int = 20; pub const LUA_MINSTACK: c_int = 20;
// predefined values in the registry // predefined values in the registry
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub const LUA_RIDX_MAINTHREAD: lua_Integer = 1; pub const LUA_RIDX_MAINTHREAD: lua_Integer = 1;
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub const LUA_RIDX_GLOBALS: lua_Integer = 2; pub const LUA_RIDX_GLOBALS: lua_Integer = 2;
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub const LUA_RIDX_LAST: lua_Integer = LUA_RIDX_GLOBALS; pub const LUA_RIDX_LAST: lua_Integer = LUA_RIDX_GLOBALS;
/// A Lua number, usually equivalent to `f64`. /// A Lua number, usually equivalent to `f64`.
@@ -115,14 +123,14 @@ pub type lua_Integer = luaconf::LUA_INTEGER;
pub type lua_Unsigned = luaconf::LUA_UNSIGNED; pub type lua_Unsigned = luaconf::LUA_UNSIGNED;
// type for continuation-function contexts // type for continuation-function contexts
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub type lua_KContext = luaconf::LUA_KCONTEXT; pub type lua_KContext = luaconf::LUA_KCONTEXT;
/// Type for native functions that can be passed to Lua. /// Type for native functions that can be passed to Lua.
pub type lua_CFunction = unsafe extern "C" fn(L: *mut lua_State) -> c_int; pub type lua_CFunction = unsafe extern "C" fn(L: *mut lua_State) -> c_int;
// Type for continuation functions // Type for continuation functions
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub type lua_KFunction = pub type lua_KFunction =
unsafe extern "C" fn(L: *mut lua_State, status: c_int, ctx: lua_KContext) -> c_int; unsafe extern "C" fn(L: *mut lua_State, status: c_int, ctx: lua_KContext) -> c_int;
@@ -132,7 +140,7 @@ pub type lua_Reader =
pub type lua_Writer = pub type lua_Writer =
unsafe extern "C" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int; unsafe extern "C" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
/// Type for memory-allocation functions. // Type for memory-allocation functions.
pub type lua_Alloc = unsafe extern "C" fn( pub type lua_Alloc = unsafe extern "C" fn(
ud: *mut c_void, ud: *mut c_void,
ptr: *mut c_void, ptr: *mut c_void,
@@ -140,19 +148,28 @@ pub type lua_Alloc = unsafe extern "C" fn(
nsize: usize, nsize: usize,
) -> *mut c_void; ) -> *mut c_void;
// Type for warning functions
#[cfg(feature = "lua54")]
pub type lua_WarnFunction =
unsafe extern "C" fn(ud: *mut c_void, msg: *const c_char, tocont: c_int);
extern "C" { extern "C" {
// state manipulation // state manipulation
pub fn lua_newstate(f: lua_Alloc, ud: *mut c_void) -> *mut lua_State; pub fn lua_newstate(f: lua_Alloc, ud: *mut c_void) -> *mut lua_State;
pub fn lua_close(L: *mut lua_State); pub fn lua_close(L: *mut lua_State);
pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State; pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State;
#[cfg(feature = "lua54")]
pub fn lua_resetthread(L: *mut lua_State) -> c_int;
pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction; pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction;
#[cfg(feature = "lua54")]
pub fn lua_version(L: *mut lua_State) -> lua_Number;
#[cfg(feature = "lua53")] #[cfg(feature = "lua53")]
pub fn lua_version(L: *mut lua_State) -> *const lua_Number; pub fn lua_version(L: *mut lua_State) -> *const lua_Number;
// basic stack manipulation // basic stack manipulation
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn lua_absindex(L: *mut lua_State, idx: c_int) -> c_int; pub fn lua_absindex(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_gettop(L: *mut lua_State) -> c_int; pub fn lua_gettop(L: *mut lua_State) -> c_int;
pub fn lua_settop(L: *mut lua_State, idx: c_int); pub fn lua_settop(L: *mut lua_State, idx: c_int);
@@ -163,9 +180,9 @@ extern "C" {
pub fn lua_insert(L: *mut lua_State, idx: c_int); pub fn lua_insert(L: *mut lua_State, idx: c_int);
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
pub fn lua_replace(L: *mut lua_State, idx: c_int); pub fn lua_replace(L: *mut lua_State, idx: c_int);
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_rotate(L: *mut lua_State, idx: c_int, n: c_int); pub fn lua_rotate(L: *mut lua_State, idx: c_int, n: c_int);
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int); pub fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int);
pub fn lua_checkstack(L: *mut lua_State, sz: c_int) -> c_int; pub fn lua_checkstack(L: *mut lua_State, sz: c_int) -> c_int;
@@ -175,7 +192,7 @@ extern "C" {
pub fn lua_isnumber(L: *mut lua_State, idx: c_int) -> c_int; pub fn lua_isnumber(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_isstring(L: *mut lua_State, idx: c_int) -> c_int; pub fn lua_isstring(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_iscfunction(L: *mut lua_State, idx: c_int) -> c_int; pub fn lua_iscfunction(L: *mut lua_State, idx: c_int) -> c_int;
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int; pub fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_isuserdata(L: *mut lua_State, idx: c_int) -> c_int; pub fn lua_isuserdata(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_type(L: *mut lua_State, idx: c_int) -> c_int; pub fn lua_type(L: *mut lua_State, idx: c_int) -> c_int;
@@ -183,17 +200,17 @@ extern "C" {
#[cfg(any(feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua51", feature = "luajit"))]
pub fn lua_tonumber(L: *mut lua_State, idx: c_int) -> lua_Number; pub fn lua_tonumber(L: *mut lua_State, idx: c_int) -> lua_Number;
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn lua_tonumberx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Number; pub fn lua_tonumberx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Number;
#[cfg(any(feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua51", feature = "luajit"))]
pub fn lua_tointeger(L: *mut lua_State, idx: c_int) -> lua_Integer; pub fn lua_tointeger(L: *mut lua_State, idx: c_int) -> lua_Integer;
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_tointegerx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Integer; pub fn lua_tointegerx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Integer;
pub fn lua_toboolean(L: *mut lua_State, idx: c_int) -> c_int; pub fn lua_toboolean(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char; pub fn lua_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
#[cfg(any(feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua51", feature = "luajit"))]
pub fn lua_objlen(L: *mut lua_State, idx: c_int) -> usize; pub fn lua_objlen(L: *mut lua_State, idx: c_int) -> usize;
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize; pub fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize;
pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> lua_CFunction; pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> lua_CFunction;
pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void; pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
@@ -215,31 +232,31 @@ pub const LUA_OPPOW: c_int = 5;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
pub const LUA_OPUNM: c_int = 6; pub const LUA_OPUNM: c_int = 6;
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPMOD: c_int = 3; pub const LUA_OPMOD: c_int = 3;
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPPOW: c_int = 4; pub const LUA_OPPOW: c_int = 4;
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPDIV: c_int = 5; pub const LUA_OPDIV: c_int = 5;
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPIDIV: c_int = 6; pub const LUA_OPIDIV: c_int = 6;
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPBAND: c_int = 7; pub const LUA_OPBAND: c_int = 7;
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPBOR: c_int = 8; pub const LUA_OPBOR: c_int = 8;
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPBXOR: c_int = 9; pub const LUA_OPBXOR: c_int = 9;
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPSHL: c_int = 10; pub const LUA_OPSHL: c_int = 10;
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPSHR: c_int = 11; pub const LUA_OPSHR: c_int = 11;
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPUNM: c_int = 12; pub const LUA_OPUNM: c_int = 12;
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPBNOT: c_int = 13; pub const LUA_OPBNOT: c_int = 13;
extern "C" { extern "C" {
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn lua_arith(L: *mut lua_State, op: c_int); pub fn lua_arith(L: *mut lua_State, op: c_int);
} }
@@ -253,7 +270,7 @@ extern "C" {
pub fn lua_rawequal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int; pub fn lua_rawequal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
#[cfg(any(feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua51", feature = "luajit"))]
pub fn lua_lessthan(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int; pub fn lua_lessthan(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn lua_compare(L: *mut lua_State, idx1: c_int, idx2: c_int, op: c_int) -> c_int; pub fn lua_compare(L: *mut lua_State, idx1: c_int, idx2: c_int, op: c_int) -> c_int;
} }
@@ -263,13 +280,13 @@ extern "C" {
pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number); pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number);
pub fn lua_pushinteger(L: *mut lua_State, n: lua_Integer); pub fn lua_pushinteger(L: *mut lua_State, n: lua_Integer);
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char; pub fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
#[link_name = "lua_pushlstring"] #[link_name = "lua_pushlstring"]
pub fn lua_pushlstring_old(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char; pub fn lua_pushlstring_old(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char;
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn lua_pushstring(L: *mut lua_State, s: *const c_char) -> *const c_char; pub fn lua_pushstring(L: *mut lua_State, s: *const c_char) -> *const c_char;
#[cfg(any(feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua51", feature = "luajit"))]
#[link_name = "lua_pushstring"] #[link_name = "lua_pushstring"]
@@ -286,49 +303,59 @@ extern "C" {
// get functions (Lua -> stack) // get functions (Lua -> stack)
extern "C" { extern "C" {
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_getglobal(L: *mut lua_State, var: *const c_char) -> c_int; pub fn lua_getglobal(L: *mut lua_State, var: *const c_char) -> c_int;
#[cfg(feature = "lua52")] #[cfg(feature = "lua52")]
#[link_name = "lua_getglobal"] #[link_name = "lua_getglobal"]
pub fn lua_getglobal_old(L: *mut lua_State, var: *const c_char); pub fn lua_getglobal_old(L: *mut lua_State, var: *const c_char);
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_gettable(L: *mut lua_State, idx: c_int) -> c_int; pub fn lua_gettable(L: *mut lua_State, idx: c_int) -> c_int;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
#[link_name = "lua_gettable"] #[link_name = "lua_gettable"]
pub fn lua_gettable_old(L: *mut lua_State, idx: c_int); pub fn lua_gettable_old(L: *mut lua_State, idx: c_int);
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_getfield(L: *mut lua_State, idx: c_int, k: *const c_char) -> c_int; pub fn lua_getfield(L: *mut lua_State, idx: c_int, k: *const c_char) -> c_int;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
#[link_name = "lua_getfield"] #[link_name = "lua_getfield"]
pub fn lua_getfield_old(L: *mut lua_State, idx: c_int, k: *const c_char); pub fn lua_getfield_old(L: *mut lua_State, idx: c_int, k: *const c_char);
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_geti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int; pub fn lua_geti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int;
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int; pub fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
#[link_name = "lua_rawget"] #[link_name = "lua_rawget"]
pub fn lua_rawget_old(L: *mut lua_State, idx: c_int); pub fn lua_rawget_old(L: *mut lua_State, idx: c_int);
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int; pub fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
#[link_name = "lua_rawgeti"] #[link_name = "lua_rawgeti"]
pub fn lua_rawgeti_old(L: *mut lua_State, idx: c_int, n: lua_Integer); pub fn lua_rawgeti_old(L: *mut lua_State, idx: c_int, n: lua_Integer);
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int; pub fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int;
#[cfg(feature = "lua52")] #[cfg(feature = "lua52")]
#[link_name = "lua_rawgetp"] #[link_name = "lua_rawgetp"]
pub fn lua_rawgetp_old(L: *mut lua_State, idx: c_int, p: *const c_void); pub fn lua_rawgetp_old(L: *mut lua_State, idx: c_int, p: *const c_void);
pub fn lua_createtable(L: *mut lua_State, narr: c_int, nrec: c_int); pub fn lua_createtable(L: *mut lua_State, narr: c_int, nrec: c_int);
#[cfg(feature = "lua54")]
pub fn lua_newuserdatauv(L: *mut lua_State, sz: usize, nuvalue: c_int) -> *mut c_void;
#[cfg(any(
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit"
))]
pub fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void; pub fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void;
pub fn lua_getmetatable(L: *mut lua_State, objindex: c_int) -> c_int; pub fn lua_getmetatable(L: *mut lua_State, objindex: c_int) -> c_int;
#[cfg(feature = "lua54")]
pub fn lua_getiuservalue(L: *mut lua_State, idx: c_int, n: c_int) -> c_int;
#[cfg(feature = "lua53")] #[cfg(feature = "lua53")]
pub fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int; pub fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int;
#[cfg(feature = "lua52")] #[cfg(feature = "lua52")]
@@ -338,28 +365,48 @@ extern "C" {
pub fn lua_getfenv(L: *mut lua_State, idx: c_int); pub fn lua_getfenv(L: *mut lua_State, idx: c_int);
} }
#[cfg(feature = "lua54")]
#[inline(always)]
pub unsafe fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void {
lua_newuserdatauv(L, sz, 1)
}
#[cfg(feature = "lua54")]
#[inline(always)]
pub unsafe fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int {
lua_getiuservalue(L, idx, 1)
}
// set functions (stack -> Lua) // set functions (stack -> Lua)
extern "C" { extern "C" {
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn lua_setglobal(L: *mut lua_State, var: *const c_char); pub fn lua_setglobal(L: *mut lua_State, var: *const c_char);
pub fn lua_settable(L: *mut lua_State, idx: c_int); pub fn lua_settable(L: *mut lua_State, idx: c_int);
pub fn lua_setfield(L: *mut lua_State, idx: c_int, k: *const c_char); pub fn lua_setfield(L: *mut lua_State, idx: c_int, k: *const c_char);
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_seti(L: *mut lua_State, idx: c_int, n: lua_Integer); pub fn lua_seti(L: *mut lua_State, idx: c_int, n: lua_Integer);
pub fn lua_rawset(L: *mut lua_State, idx: c_int); pub fn lua_rawset(L: *mut lua_State, idx: c_int);
pub fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer); pub fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer);
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void); pub fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void);
pub fn lua_setmetatable(L: *mut lua_State, objindex: c_int) -> c_int; pub fn lua_setmetatable(L: *mut lua_State, objindex: c_int) -> c_int;
#[cfg(feature = "lua54")]
pub fn lua_setiuservalue(L: *mut lua_State, idx: c_int, n: c_int) -> c_int;
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua53", feature = "lua52"))]
pub fn lua_setuservalue(L: *mut lua_State, idx: c_int); pub fn lua_setuservalue(L: *mut lua_State, idx: c_int);
#[cfg(any(feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua51", feature = "luajit"))]
pub fn lua_setfenv(L: *mut lua_State, idx: c_int) -> c_int; pub fn lua_setfenv(L: *mut lua_State, idx: c_int) -> c_int;
} }
#[cfg(feature = "lua54")]
#[inline(always)]
pub unsafe fn lua_setuservalue(L: *mut lua_State, idx: c_int) {
lua_setiuservalue(L, idx, 1);
}
// 'load' and 'call' functions (load and run Lua code) // 'load' and 'call' functions (load and run Lua code)
extern "C" { extern "C" {
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_callk( pub fn lua_callk(
L: *mut lua_State, L: *mut lua_State,
nargs: c_int, nargs: c_int,
@@ -376,7 +423,7 @@ extern "C" {
k: Option<lua_CFunction>, k: Option<lua_CFunction>,
); );
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_pcallk( pub fn lua_pcallk(
L: *mut lua_State, L: *mut lua_State,
nargs: c_int, nargs: c_int,
@@ -412,7 +459,7 @@ extern "C" {
mode: *const c_char, mode: *const c_char,
) -> c_int; ) -> c_int;
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_dump( pub fn lua_dump(
L: *mut lua_State, L: *mut lua_State,
writer: lua_Writer, writer: lua_Writer,
@@ -424,13 +471,13 @@ extern "C" {
pub fn lua_dump_old(L: *mut lua_State, writer: lua_Writer, data: *mut c_void) -> c_int; pub fn lua_dump_old(L: *mut lua_State, writer: lua_Writer, data: *mut c_void) -> c_int;
} }
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)] #[inline(always)]
pub unsafe fn lua_call(L: *mut lua_State, n: c_int, r: c_int) { pub unsafe fn lua_call(L: *mut lua_State, n: c_int, r: c_int) {
lua_callk(L, n, r, 0, None) lua_callk(L, n, r, 0, None)
} }
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)] #[inline(always)]
pub unsafe fn lua_pcall(L: *mut lua_State, n: c_int, r: c_int, f: c_int) -> c_int { pub unsafe fn lua_pcall(L: *mut lua_State, n: c_int, r: c_int, f: c_int) -> c_int {
lua_pcallk(L, n, r, f, 0, None) lua_pcallk(L, n, r, f, 0, None)
@@ -438,7 +485,7 @@ pub unsafe fn lua_pcall(L: *mut lua_State, n: c_int, r: c_int, f: c_int) -> c_in
// coroutine functions // coroutine functions
extern "C" { extern "C" {
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_yieldk( pub fn lua_yieldk(
L: *mut lua_State, L: *mut lua_State,
nresults: c_int, nresults: c_int,
@@ -455,23 +502,57 @@ extern "C" {
#[cfg(any(feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua51", feature = "luajit"))]
pub fn lua_yield(L: *mut lua_State, nresults: c_int) -> c_int; pub fn lua_yield(L: *mut lua_State, nresults: c_int) -> c_int;
#[cfg(feature = "lua54")]
pub fn lua_resume(
L: *mut lua_State,
from: *mut lua_State,
narg: c_int,
nres: *mut c_int,
) -> c_int;
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua53", feature = "lua52"))]
pub fn lua_resume(L: *mut lua_State, from: *mut lua_State, narg: c_int) -> c_int; #[link_name = "lua_resume"]
pub fn lua_resume_53(L: *mut lua_State, from: *mut lua_State, narg: c_int) -> c_int;
#[cfg(any(feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua51", feature = "luajit"))]
#[link_name = "lua_resume"] #[link_name = "lua_resume"]
pub fn lua_resume_old(L: *mut lua_State, narg: c_int) -> c_int; pub fn lua_resume_old(L: *mut lua_State, narg: c_int) -> c_int;
pub fn lua_status(L: *mut lua_State) -> c_int; pub fn lua_status(L: *mut lua_State) -> c_int;
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_isyieldable(L: *mut lua_State) -> c_int; pub fn lua_isyieldable(L: *mut lua_State) -> c_int;
} }
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)] #[inline(always)]
pub unsafe fn lua_yield(L: *mut lua_State, n: c_int) -> c_int { pub unsafe fn lua_yield(L: *mut lua_State, n: c_int) -> c_int {
lua_yieldk(L, n, 0, None) lua_yieldk(L, n, 0, None)
} }
#[cfg(any(
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit"
))]
pub unsafe fn lua_resume(
L: *mut lua_State,
from: *mut lua_State,
narg: c_int,
nres: *mut c_int,
) -> c_int {
let ret = lua_resume_53(L, from, narg);
if ret == LUA_OK || ret == LUA_YIELD {
*nres = lua_gettop(L);
}
ret
}
// warning-related functions
#[cfg(feature = "lua54")]
extern "C" {
pub fn lua_setwarnf(L: *mut lua_State, f: lua_WarnFunction, ud: *mut c_void);
pub fn lua_warning(L: *mut lua_State, msg: *const c_char, tocont: c_int);
}
// garbage-collection function and options // garbage-collection function and options
pub const LUA_GCSTOP: c_int = 0; pub const LUA_GCSTOP: c_int = 0;
pub const LUA_GCRESTART: c_int = 1; pub const LUA_GCRESTART: c_int = 1;
@@ -481,10 +562,22 @@ pub const LUA_GCCOUNTB: c_int = 4;
pub const LUA_GCSTEP: c_int = 5; pub const LUA_GCSTEP: c_int = 5;
pub const LUA_GCSETPAUSE: c_int = 6; pub const LUA_GCSETPAUSE: c_int = 6;
pub const LUA_GCSETSTEPMUL: c_int = 7; pub const LUA_GCSETSTEPMUL: c_int = 7;
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub const LUA_GCISRUNNING: c_int = 9; pub const LUA_GCISRUNNING: c_int = 9;
#[cfg(feature = "lua54")]
pub const LUA_GCGEN: c_int = 10;
#[cfg(feature = "lua54")]
pub const LUA_GCINC: c_int = 11;
extern "C" { extern "C" {
#[cfg(feature = "lua54")]
pub fn lua_gc(L: *mut lua_State, what: c_int, ...) -> c_int;
#[cfg(any(
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit"
))]
pub fn lua_gc(L: *mut lua_State, what: c_int, data: c_int) -> c_int; pub fn lua_gc(L: *mut lua_State, what: c_int, data: c_int) -> c_int;
} }
@@ -493,29 +586,31 @@ extern "C" {
pub fn lua_error(L: *mut lua_State) -> !; pub fn lua_error(L: *mut lua_State) -> !;
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int; pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_concat(L: *mut lua_State, n: c_int); pub fn lua_concat(L: *mut lua_State, n: c_int);
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn lua_len(L: *mut lua_State, idx: c_int); pub fn lua_len(L: *mut lua_State, idx: c_int);
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_stringtonumber(L: *mut lua_State, s: *const c_char) -> usize; pub fn lua_stringtonumber(L: *mut lua_State, s: *const c_char) -> usize;
pub fn lua_getallocf(L: *mut lua_State, ud: *mut *mut c_void) -> lua_Alloc; pub fn lua_getallocf(L: *mut lua_State, ud: *mut *mut c_void) -> lua_Alloc;
pub fn lua_setallocf(L: *mut lua_State, f: lua_Alloc, ud: *mut c_void); pub fn lua_setallocf(L: *mut lua_State, f: lua_Alloc, ud: *mut c_void);
#[cfg(feature = "lua54")]
pub fn lua_toclose(L: *mut lua_State, idx: c_int);
} }
// some useful macros // some useful macros
// here, implemented as Rust functions // here, implemented as Rust functions
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
#[inline(always)] #[inline(always)]
pub unsafe fn lua_getextraspace(L: *mut lua_State) -> *mut c_void { pub unsafe fn lua_getextraspace(L: *mut lua_State) -> *mut c_void {
L.offset(-super::glue::LUA_EXTRASPACE as isize) as *mut c_void L.offset(-super::glue::LUA_EXTRASPACE as isize) as *mut c_void
} }
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)] #[inline(always)]
pub unsafe fn lua_tonumber(L: *mut lua_State, i: c_int) -> lua_Number { pub unsafe fn lua_tonumber(L: *mut lua_State, i: c_int) -> lua_Number {
lua_tonumberx(L, i, ptr::null_mut()) lua_tonumberx(L, i, ptr::null_mut())
} }
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)] #[inline(always)]
pub unsafe fn lua_tointeger(L: *mut lua_State, i: c_int) -> lua_Integer { pub unsafe fn lua_tointeger(L: *mut lua_State, i: c_int) -> lua_Integer {
lua_tointegerx(L, i, ptr::null_mut()) lua_tointegerx(L, i, ptr::null_mut())
@@ -601,7 +696,7 @@ pub unsafe fn lua_getglobal(L: *mut lua_State, var: *const c_char) -> c_int {
lua_getfield(L, LUA_GLOBALSINDEX, var) lua_getfield(L, LUA_GLOBALSINDEX, var)
} }
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)] #[inline(always)]
pub unsafe fn lua_pushglobaltable(L: *mut lua_State) -> c_int { pub unsafe fn lua_pushglobaltable(L: *mut lua_State) -> c_int {
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS) lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)
@@ -612,20 +707,20 @@ pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
lua_tolstring(L, i, ptr::null_mut()) lua_tolstring(L, i, ptr::null_mut())
} }
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
#[inline(always)] #[inline(always)]
pub unsafe fn lua_insert(L: *mut lua_State, idx: c_int) { pub unsafe fn lua_insert(L: *mut lua_State, idx: c_int) {
lua_rotate(L, idx, 1) lua_rotate(L, idx, 1)
} }
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
#[inline(always)] #[inline(always)]
pub unsafe fn lua_remove(L: *mut lua_State, idx: c_int) { pub unsafe fn lua_remove(L: *mut lua_State, idx: c_int) {
lua_rotate(L, idx, -1); lua_rotate(L, idx, -1);
lua_pop(L, 1) lua_pop(L, 1)
} }
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
#[inline(always)] #[inline(always)]
pub unsafe fn lua_replace(L: *mut lua_State, idx: c_int) { pub unsafe fn lua_replace(L: *mut lua_State, idx: c_int) {
lua_copy(L, -1, idx); lua_copy(L, -1, idx);
@@ -647,7 +742,7 @@ pub const LUA_MASKLINE: c_int = 1 << (LUA_HOOKLINE as usize);
pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize); pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize);
/// Type for functions to be called on debug events. /// Type for functions to be called on debug events.
pub type lua_Hook = extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug); pub type lua_Hook = unsafe extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug);
extern "C" { extern "C" {
pub fn lua_getstack(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int; pub fn lua_getstack(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int;
@@ -657,18 +752,21 @@ extern "C" {
pub fn lua_getupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char; pub fn lua_getupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
pub fn lua_setupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char; pub fn lua_setupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn lua_upvalueid(L: *mut lua_State, fidx: c_int, n: c_int) -> *mut c_void; pub fn lua_upvalueid(L: *mut lua_State, fidx: c_int, n: c_int) -> *mut c_void;
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn lua_upvaluejoin(L: *mut lua_State, fidx1: c_int, n1: c_int, fidx2: c_int, n2: c_int); pub fn lua_upvaluejoin(L: *mut lua_State, fidx1: c_int, n1: c_int, fidx2: c_int, n2: c_int);
pub fn lua_sethook(L: *mut lua_State, func: lua_Hook, mask: c_int, count: c_int); pub fn lua_sethook(L: *mut lua_State, func: Option<lua_Hook>, mask: c_int, count: c_int);
pub fn lua_gethook(L: *mut lua_State) -> Option<lua_Hook>; pub fn lua_gethook(L: *mut lua_State) -> Option<lua_Hook>;
pub fn lua_gethookmask(L: *mut lua_State) -> c_int; pub fn lua_gethookmask(L: *mut lua_State) -> c_int;
pub fn lua_gethookcount(L: *mut lua_State) -> c_int; pub fn lua_gethookcount(L: *mut lua_State) -> c_int;
#[cfg(feature = "lua54")]
pub fn lua_setcstacklimit(L: *mut lua_State, limit: c_uint) -> c_int;
} }
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[repr(C)] #[repr(C)]
pub struct lua_Debug { pub struct lua_Debug {
pub event: c_int, pub event: c_int,
@@ -676,6 +774,8 @@ pub struct lua_Debug {
pub namewhat: *const c_char, pub namewhat: *const c_char,
pub what: *const c_char, pub what: *const c_char,
pub source: *const c_char, pub source: *const c_char,
#[cfg(feature = "lua54")]
pub srclen: usize,
pub currentline: c_int, pub currentline: c_int,
pub linedefined: c_int, pub linedefined: c_int,
pub lastlinedefined: c_int, pub lastlinedefined: c_int,
@@ -683,6 +783,10 @@ pub struct lua_Debug {
pub nparams: c_uchar, pub nparams: c_uchar,
pub isvararg: c_char, pub isvararg: c_char,
pub istailcall: c_char, pub istailcall: c_char,
#[cfg(feature = "lua54")]
pub ftransfer: c_ushort,
#[cfg(feature = "lua54")]
pub ntransfer: c_ushort,
pub short_src: [c_char; luaconf::LUA_IDSIZE as usize], pub short_src: [c_char; luaconf::LUA_IDSIZE as usize],
// lua.h mentions this is for private use // lua.h mentions this is for private use
i_ci: *mut c_void, i_ci: *mut c_void,
+3 -3
View File
@@ -1,6 +1,6 @@
// The MIT License (MIT) // The MIT License (MIT)
// //
// Copyright (c) 2019 A. Orlenko // Copyright (c) 2019-2020 A. Orlenko
// Copyright (c) 2014 J.C. Moyer // Copyright (c) 2014 J.C. Moyer
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -30,8 +30,8 @@ pub use super::glue::LUA_UNSIGNED;
pub use super::glue::LUA_IDSIZE; pub use super::glue::LUA_IDSIZE;
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub use super::glue::LUAL_NUMSIZES; pub use super::glue::LUAL_NUMSIZES;
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub type LUA_KCONTEXT = isize; // intptr_t pub type LUA_KCONTEXT = isize; // intptr_t
+4 -4
View File
@@ -1,6 +1,6 @@
// The MIT License (MIT) // The MIT License (MIT)
// //
// Copyright (c) 2019 A. Orlenko // Copyright (c) 2019-2020 A. Orlenko
// Copyright (c) 2014 J.C. Moyer // Copyright (c) 2014 J.C. Moyer
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -32,7 +32,7 @@ pub use super::glue::{
LUA_STRLIBNAME, LUA_TABLIBNAME, LUA_STRLIBNAME, LUA_TABLIBNAME,
}; };
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub use super::glue::LUA_UTF8LIBNAME; pub use super::glue::LUA_UTF8LIBNAME;
#[cfg(any(feature = "lua52", feature = "luajit"))] #[cfg(any(feature = "lua52", feature = "luajit"))]
@@ -43,13 +43,13 @@ pub use super::glue::{LUA_FFILIBNAME, LUA_JITLIBNAME};
extern "C" { extern "C" {
pub fn luaopen_base(L: *mut lua_State) -> c_int; pub fn luaopen_base(L: *mut lua_State) -> c_int;
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaopen_coroutine(L: *mut lua_State) -> c_int; pub fn luaopen_coroutine(L: *mut lua_State) -> c_int;
pub fn luaopen_table(L: *mut lua_State) -> c_int; pub fn luaopen_table(L: *mut lua_State) -> c_int;
pub fn luaopen_io(L: *mut lua_State) -> c_int; pub fn luaopen_io(L: *mut lua_State) -> c_int;
pub fn luaopen_os(L: *mut lua_State) -> c_int; pub fn luaopen_os(L: *mut lua_State) -> c_int;
pub fn luaopen_string(L: *mut lua_State) -> c_int; pub fn luaopen_string(L: *mut lua_State) -> c_int;
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn luaopen_utf8(L: *mut lua_State) -> c_int; pub fn luaopen_utf8(L: *mut lua_State) -> c_int;
#[cfg(feature = "lua52")] #[cfg(feature = "lua52")]
pub fn luaopen_bit32(L: *mut lua_State) -> c_int; pub fn luaopen_bit32(L: *mut lua_State) -> c_int;
+50 -12
View File
@@ -1,6 +1,6 @@
// The MIT License (MIT) // The MIT License (MIT)
// //
// Copyright (c) 2019 A. Orlenko // Copyright (c) 2019-2020 A. Orlenko
// Copyright (c) 2014 J.C. Moyer // Copyright (c) 2014 J.C. Moyer
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -36,11 +36,14 @@ pub use self::lua::{
lua_Unsigned, lua_Writer, lua_Unsigned, lua_Writer,
}; };
#[cfg(feature = "lua53")] #[cfg(feature = "lua54")]
pub use self::lua::lua_WarnFunction;
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub use self::lua::{lua_KContext, lua_KFunction}; pub use self::lua::{lua_KContext, lua_KFunction};
#[cfg(any(feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua51", feature = "luajit"))]
pub use self::lua::lua_setfenv; pub use self::lua::{lua_getfenv, lua_setfenv};
// C API functions // C API functions
pub use self::lua::{ pub use self::lua::{
@@ -155,10 +158,16 @@ pub use self::lua::{
lua_yield, lua_yield,
}; };
#[cfg(feature = "lua53")] #[cfg(feature = "lua54")]
pub use self::lua::{
lua_getiuservalue, lua_newuserdatauv, lua_resetthread, lua_setcstacklimit, lua_setiuservalue,
lua_setwarnf, lua_toclose, lua_warning,
};
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub use self::lua::{lua_isyieldable, lua_version}; pub use self::lua::{lua_isyieldable, lua_version};
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub use self::lua::{lua_callk, lua_pcallk, lua_upvalueid, lua_upvaluejoin, lua_yieldk}; pub use self::lua::{lua_callk, lua_pcallk, lua_upvalueid, lua_upvaluejoin, lua_yieldk};
// auxiliary library types // auxiliary library types
@@ -177,7 +186,7 @@ pub use self::lauxlib::{
luaL_where, luaL_where,
}; };
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub use self::lauxlib::{luaL_execresult, luaL_fileresult, luaL_loadfilex}; pub use self::lauxlib::{luaL_execresult, luaL_fileresult, luaL_loadfilex};
// lualib.h functions // lualib.h functions
@@ -186,7 +195,7 @@ pub use self::lualib::{
luaopen_package, luaopen_string, luaopen_table, luaopen_package, luaopen_string, luaopen_table,
}; };
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub use self::lualib::{luaopen_coroutine, luaopen_utf8}; pub use self::lualib::{luaopen_coroutine, luaopen_utf8};
#[cfg(feature = "lua52")] #[cfg(feature = "lua52")]
@@ -202,17 +211,23 @@ pub use self::lua::{
LUA_HOOKCOUNT, LUA_HOOKLINE, LUA_HOOKRET, LUA_HOOKTAILCALL, LUA_MASKCALL, LUA_MASKCOUNT, LUA_HOOKCOUNT, LUA_HOOKLINE, LUA_HOOKRET, LUA_HOOKTAILCALL, LUA_MASKCALL, LUA_MASKCOUNT,
LUA_MASKLINE, LUA_MASKRET, LUA_MINSTACK, LUA_MULTRET, LUA_OK, LUA_OPADD, LUA_OPDIV, LUA_OPEQ, LUA_MASKLINE, LUA_MASKRET, LUA_MINSTACK, LUA_MULTRET, LUA_OK, LUA_OPADD, LUA_OPDIV, LUA_OPEQ,
LUA_OPLE, LUA_OPLT, LUA_OPMOD, LUA_OPMUL, LUA_OPPOW, LUA_OPSUB, LUA_OPUNM, LUA_REGISTRYINDEX, LUA_OPLE, LUA_OPLT, LUA_OPMOD, LUA_OPMUL, LUA_OPPOW, LUA_OPSUB, LUA_OPUNM, LUA_REGISTRYINDEX,
LUA_TBOOLEAN, LUA_TFUNCTION, LUA_TLIGHTUSERDATA, LUA_TNIL, LUA_TNONE, LUA_TNUMBER, LUA_TSTRING, LUA_SIGNATURE, LUA_TBOOLEAN, LUA_TFUNCTION, LUA_TLIGHTUSERDATA, LUA_TNIL, LUA_TNONE,
LUA_TTABLE, LUA_TTHREAD, LUA_TUSERDATA, LUA_YIELD, LUA_TNUMBER, LUA_TSTRING, LUA_TTABLE, LUA_TTHREAD, LUA_TUSERDATA, LUA_YIELD,
}; };
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub use self::lua::{ pub use self::lua::{
LUA_OPBAND, LUA_OPBNOT, LUA_OPBOR, LUA_OPBXOR, LUA_OPIDIV, LUA_OPSHL, LUA_OPSHR, LUA_OPBAND, LUA_OPBNOT, LUA_OPBOR, LUA_OPBXOR, LUA_OPIDIV, LUA_OPSHL, LUA_OPSHR,
}; };
#[cfg(feature = "lua54")]
pub use self::lua::{LUA_GCGEN, LUA_GCINC};
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub use self::lua::{LUA_GCISRUNNING, LUA_RIDX_GLOBALS, LUA_RIDX_MAINTHREAD};
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua53", feature = "lua52"))]
pub use self::lua::{LUA_ERRGCMM, LUA_GCISRUNNING, LUA_RIDX_GLOBALS, LUA_RIDX_MAINTHREAD}; pub use self::lua::LUA_ERRGCMM;
#[cfg(any(feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua51", feature = "luajit"))]
pub use self::lua::{LUA_ENVIRONINDEX, LUA_GLOBALSINDEX}; pub use self::lua::{LUA_ENVIRONINDEX, LUA_GLOBALSINDEX};
@@ -226,7 +241,7 @@ pub use self::lualib::{
LUA_STRLIBNAME, LUA_TABLIBNAME, LUA_STRLIBNAME, LUA_TABLIBNAME,
}; };
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
pub use self::lualib::LUA_UTF8LIBNAME; pub use self::lualib::LUA_UTF8LIBNAME;
#[cfg(any(feature = "lua52", feature = "luajit"))] #[cfg(any(feature = "lua52", feature = "luajit"))]
@@ -238,7 +253,30 @@ pub use self::lualib::{LUA_FFILIBNAME, LUA_JITLIBNAME};
// Not actually defined in lua.h / luaconf.h // Not actually defined in lua.h / luaconf.h
pub const LUA_MAX_UPVALUES: c_int = 255; pub const LUA_MAX_UPVALUES: c_int = 255;
// Copied from https://github.com/rust-lang/rust/blob/master/src/libstd/sys_common/alloc.rs
#[cfg(all(any(
target_arch = "x86",
target_arch = "arm",
target_arch = "mips",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "asmjs",
target_arch = "wasm32",
target_arch = "hexagon"
)))]
pub const SYS_MIN_ALIGN: usize = 8;
#[cfg(all(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "mips64",
target_arch = "s390x",
target_arch = "sparc64",
target_arch = "riscv64"
)))]
pub const SYS_MIN_ALIGN: usize = 16;
#[allow(unused_imports, dead_code, non_camel_case_types)] #[allow(unused_imports, dead_code, non_camel_case_types)]
#[allow(clippy::unreadable_literal)]
mod glue { mod glue {
include!(concat!(env!("OUT_DIR"), "/glue.rs")); include!(concat!(env!("OUT_DIR"), "/glue.rs"));
} }
+84 -2
View File
@@ -1,5 +1,5 @@
use std::os::raw::c_int; use std::os::raw::{c_int, c_void};
use std::ptr; use std::{ptr, slice};
use crate::error::{Error, Result}; use crate::error::{Error, Result};
use crate::ffi; use crate::ffi;
@@ -9,6 +9,9 @@ use crate::util::{
}; };
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti}; use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
#[cfg(feature = "async")]
use {futures_core::future::LocalBoxFuture, futures_util::future};
/// Handle to an internal Lua function. /// Handle to an internal Lua function.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Function<'lua>(pub(crate) LuaRef<'lua>); pub struct Function<'lua>(pub(crate) LuaRef<'lua>);
@@ -86,6 +89,49 @@ impl<'lua> Function<'lua> {
R::from_lua_multi(results, lua) R::from_lua_multi(results, lua)
} }
/// Returns a Feature that, when polled, calls `self`, passing `args` as function arguments,
/// and drives the execution.
///
/// Internaly it wraps the function to an [`AsyncThread`].
///
/// Requires `feature = "async"`
///
/// # Examples
///
/// ```
/// use std::time::Duration;
/// use futures_timer::Delay;
/// # use mlua::{Lua, Result};
/// # #[tokio::main]
/// # async fn main() -> Result<()> {
/// # let lua = Lua::new();
///
/// let sleep = lua.create_async_function(move |_lua, n: u64| async move {
/// Delay::new(Duration::from_millis(n)).await;
/// Ok(())
/// })?;
///
/// sleep.call_async(10).await?;
///
/// # Ok(())
/// # }
/// ```
///
/// [`AsyncThread`]: struct.AsyncThread.html
#[cfg(feature = "async")]
pub fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
{
let lua = self.0.lua;
match lua.create_thread(self.clone()) {
Ok(t) => Box::pin(t.into_async(args)),
Err(e) => Box::pin(future::err(e)),
}
}
/// Returns a function that, when called, calls `self`, passing `args` as the first set of /// Returns a function that, when called, calls `self`, passing `args` as the first set of
/// arguments. /// arguments.
/// ///
@@ -159,6 +205,42 @@ impl<'lua> Function<'lua> {
Ok(Function(lua.pop_ref())) Ok(Function(lua.pop_ref()))
} }
} }
/// Dumps the function as a binary chunk.
///
/// If `strip` is true, the binary representation may not include all debug information
/// about the function, to save space.
pub fn dump(&self, strip: bool) -> Result<Vec<u8>> {
unsafe extern "C" fn writer(
_state: *mut ffi::lua_State,
buf: *const c_void,
buf_len: usize,
data: *mut c_void,
) -> c_int {
let data = &mut *(data as *mut Vec<u8>);
let buf = slice::from_raw_parts(buf as *const u8, buf_len);
data.extend_from_slice(buf);
0
}
let lua = self.0.lua;
let mut data: Vec<u8> = Vec::new();
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 1);
lua.push_ref(&self.0);
let strip = if strip { 1 } else { 0 };
ffi::lua_dump(
lua.state,
writer,
&mut data as *mut Vec<u8> as *mut c_void,
strip,
);
ffi::lua_pop(lua.state, 1);
}
Ok(data)
}
} }
impl<'lua> PartialEq for Function<'lua> { impl<'lua> PartialEq for Function<'lua> {
+195
View File
@@ -0,0 +1,195 @@
use std::ffi::CStr;
use std::marker::PhantomData;
use std::os::raw::{c_char, c_int};
use crate::ffi::{self, lua_Debug, lua_State};
use crate::lua::Lua;
use crate::util::callback_error;
/// Contains information about currently executing Lua code.
///
/// The `Debug` structure is provided as a parameter to the hook function set with
/// [`Lua::set_hook`]. You may call the methods on this structure to retrieve information about the
/// Lua code executing at the time that the hook function was called. Further information can be
/// found in the [Lua 5.3 documentaton][lua_doc].
///
/// [lua_doc]: https://www.lua.org/manual/5.3/manual.html#lua_Debug
/// [`Lua::set_hook`]: struct.Lua.html#method.set_hook
#[derive(Clone)]
pub struct Debug<'a> {
ar: *mut lua_Debug,
state: *mut lua_State,
_phantom: PhantomData<&'a ()>,
}
impl<'a> Debug<'a> {
/// Corresponds to the `n` what mask.
pub fn names(&self) -> DebugNames<'a> {
unsafe {
mlua_assert!(
ffi::lua_getinfo(self.state, cstr!("n"), self.ar) != 0,
"lua_getinfo failed with `n`"
);
DebugNames {
name: ptr_to_str((*self.ar).name),
name_what: ptr_to_str((*self.ar).namewhat),
}
}
}
/// Corresponds to the `n` what mask.
pub fn source(&self) -> DebugSource<'a> {
unsafe {
mlua_assert!(
ffi::lua_getinfo(self.state, cstr!("S"), self.ar) != 0,
"lua_getinfo failed with `S`"
);
DebugSource {
source: ptr_to_str((*self.ar).source),
short_src: ptr_to_str((*self.ar).short_src.as_ptr()),
line_defined: (*self.ar).linedefined as i32,
last_line_defined: (*self.ar).lastlinedefined as i32,
what: ptr_to_str((*self.ar).what),
}
}
}
/// Corresponds to the `l` what mask. Returns the current line.
pub fn curr_line(&self) -> i32 {
unsafe {
mlua_assert!(
ffi::lua_getinfo(self.state, cstr!("l"), self.ar) != 0,
"lua_getinfo failed with `l`"
);
(*self.ar).currentline as i32
}
}
/// Corresponds to the `t` what mask. Returns true if the hook is in a function tail call, false
/// otherwise.
pub fn is_tail_call(&self) -> bool {
unsafe {
mlua_assert!(
ffi::lua_getinfo(self.state, cstr!("t"), self.ar) != 0,
"lua_getinfo failed with `t`"
);
(*self.ar).currentline != 0
}
}
/// Corresponds to the `u` what mask.
pub fn stack(&self) -> DebugStack {
unsafe {
mlua_assert!(
ffi::lua_getinfo(self.state, cstr!("u"), self.ar) != 0,
"lua_getinfo failed with `u`"
);
DebugStack {
num_ups: (*self.ar).nups as i32,
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
num_params: (*self.ar).nparams as i32,
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
is_vararg: (*self.ar).isvararg != 0,
}
}
}
}
#[derive(Clone, Debug)]
pub struct DebugNames<'a> {
pub name: Option<&'a [u8]>,
pub name_what: Option<&'a [u8]>,
}
#[derive(Clone, Debug)]
pub struct DebugSource<'a> {
pub source: Option<&'a [u8]>,
pub short_src: Option<&'a [u8]>,
pub line_defined: i32,
pub last_line_defined: i32,
pub what: Option<&'a [u8]>,
}
#[derive(Copy, Clone, Debug)]
pub struct DebugStack {
pub num_ups: i32,
/// Requires `feature = "lua54/lua53/lua52"`
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", doc))]
pub num_params: i32,
/// Requires `feature = "lua54/lua53/lua52"`
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", doc))]
pub is_vararg: bool,
}
/// Determines when a hook function will be called by Lua.
#[derive(Clone, Copy, Debug, Default)]
pub struct HookTriggers {
/// Before a function call.
pub on_calls: bool,
/// When Lua returns from a function.
pub on_returns: bool,
/// Before executing a new line, or returning from a function call.
pub every_line: bool,
/// After a certain number of VM instructions have been executed. When set to `Some(count)`,
/// `count` is the number of VM instructions to execute before calling the hook.
///
/// # Performance
///
/// Setting this option to a low value can incur a very high overhead.
pub every_nth_instruction: Option<u32>,
}
impl HookTriggers {
// Compute the mask to pass to `lua_sethook`.
pub(crate) fn mask(&self) -> c_int {
let mut mask: c_int = 0;
if self.on_calls {
mask |= ffi::LUA_MASKCALL
}
if self.on_returns {
mask |= ffi::LUA_MASKRET
}
if self.every_line {
mask |= ffi::LUA_MASKLINE
}
if self.every_nth_instruction.is_some() {
mask |= ffi::LUA_MASKCOUNT
}
mask
}
// Returns the `count` parameter to pass to `lua_sethook`, if applicable. Otherwise, zero is
// returned.
pub(crate) fn count(&self) -> c_int {
self.every_nth_instruction.unwrap_or(0) as c_int
}
}
pub(crate) unsafe extern "C" fn hook_proc(state: *mut lua_State, ar: *mut lua_Debug) {
callback_error(state, |_| {
let debug = Debug {
ar,
state,
_phantom: PhantomData,
};
let lua = Lua::make_from_ptr(state);
let hook_cb = mlua_expect!(lua.hook_callback(), "no hook callback set in hook_proc");
#[allow(clippy::match_wild_err_arm)]
match hook_cb.try_borrow_mut() {
Ok(mut b) => (&mut *b)(&lua, debug),
Err(_) => mlua_panic!("Lua should not allow hooks to be called within another hook"),
}?;
Ok(())
});
}
unsafe fn ptr_to_str<'a>(input: *const c_char) -> Option<&'a [u8]> {
if input.is_null() {
None
} else {
Some(CStr::from_ptr(input).to_bytes())
}
}
+23 -2
View File
@@ -24,6 +24,18 @@
//! The [`UserData`] trait can be implemented by user-defined types to make them available to Lua. //! The [`UserData`] trait can be implemented by user-defined types to make them available to Lua.
//! Methods and operators to be used from Lua can be added using the [`UserDataMethods`] API. //! Methods and operators to be used from Lua can be added using the [`UserDataMethods`] API.
//! //!
//! # Async/await support
//!
//! The [`create_async_function`] allows creating non-blocking functions that returns [`Future`].
//! Lua code with async capabilities can be executed by [`call_async`] family of functions or polling
//! [`AsyncThread`] using any runtime (eg. Tokio).
//!
//! Requires `feature = "async"`.
//!
//! # `Send` requirement
//! By default `mlua` is `!Send`. This can be changed by enabling `feature = "send"` that adds `Send` requirement
//! to `Function`s and [`UserData`].
//!
//! [Lua programming language]: https://www.lua.org/ //! [Lua programming language]: https://www.lua.org/
//! [`Lua`]: struct.Lua.html //! [`Lua`]: struct.Lua.html
//! [executing]: struct.Lua.html#method.exec //! [executing]: struct.Lua.html#method.exec
@@ -35,6 +47,10 @@
//! [`FromLuaMulti`]: trait.FromLuaMulti.html //! [`FromLuaMulti`]: trait.FromLuaMulti.html
//! [`UserData`]: trait.UserData.html //! [`UserData`]: trait.UserData.html
//! [`UserDataMethods`]: trait.UserDataMethods.html //! [`UserDataMethods`]: trait.UserDataMethods.html
//! [`create_async_function`]: struct.Lua.html#method.create_async_function
//! [`call_async`]: struct.Function.html#method.call_async
//! [`AsyncThread`]: struct.AsyncThread.html
//! [`Future`]: ../futures_core/future/trait.Future.html
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any* // Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
// warnings at all. // warnings at all.
@@ -47,6 +63,7 @@ mod conversion;
mod error; mod error;
mod ffi; mod ffi;
mod function; mod function;
mod hook;
mod lua; mod lua;
mod multi; mod multi;
mod scope; mod scope;
@@ -63,15 +80,19 @@ pub use crate::ffi::lua_State;
pub use crate::error::{Error, ExternalError, ExternalResult, Result}; pub use crate::error::{Error, ExternalError, ExternalResult, Result};
pub use crate::function::Function; pub use crate::function::Function;
pub use crate::lua::{Chunk, Lua}; pub use crate::hook::{Debug, DebugNames, DebugSource, DebugStack, HookTriggers};
pub use crate::lua::{Chunk, ChunkMode, GCMode, Lua};
pub use crate::multi::Variadic; pub use crate::multi::Variadic;
pub use crate::scope::Scope; pub use crate::scope::Scope;
pub use crate::stdlib::StdLib; pub use crate::stdlib::StdLib;
pub use crate::string::String; pub use crate::string::String;
pub use crate::table::{Table, TablePairs, TableSequence}; pub use crate::table::{Table, TableExt, TablePairs, TableSequence};
pub use crate::thread::{Thread, ThreadStatus}; pub use crate::thread::{Thread, ThreadStatus};
pub use crate::types::{Integer, LightUserData, Number, RegistryKey}; pub use crate::types::{Integer, LightUserData, Number, RegistryKey};
pub use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods}; pub use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
pub use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value}; pub use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
#[cfg(feature = "async")]
pub use crate::thread::AsyncThread;
pub mod prelude; pub mod prelude;
+1048 -214
View File
File diff suppressed because it is too large Load Diff
+16
View File
@@ -78,3 +78,19 @@ macro_rules! mlua_expect {
mlua_expect!($res, $msg) mlua_expect!($res, $msg)
}; };
} }
#[cfg(feature = "module")]
#[doc(hidden)]
#[macro_export]
macro_rules! require_module_feature {
() => {};
}
#[cfg(not(feature = "module"))]
#[doc(hidden)]
#[macro_export]
macro_rules! require_module_feature {
() => {
compile_error!("Feature `module` must be enabled in the `mlua` crate");
};
}
+1 -1
View File
@@ -146,7 +146,7 @@ macro_rules! impl_tuple {
} }
impl<'lua> FromLuaMulti<'lua> for () { impl<'lua> FromLuaMulti<'lua> for () {
fn from_lua_multi(_: MultiValue, _: &'lua Lua) -> Result<Self> { fn from_lua_multi(_: MultiValue<'lua>, _: &'lua Lua) -> Result<Self> {
Ok(()) Ok(())
} }
} }
+10 -6
View File
@@ -3,10 +3,14 @@
pub use crate::{ pub use crate::{
AnyUserData as LuaAnyUserData, Chunk as LuaChunk, Error as LuaError, AnyUserData as LuaAnyUserData, Chunk as LuaChunk, Error as LuaError,
ExternalError as LuaExternalError, ExternalResult as LuaExternalResult, FromLua, FromLuaMulti, ExternalError as LuaExternalError, ExternalResult as LuaExternalResult, FromLua, FromLuaMulti,
Function as LuaFunction, Integer as LuaInteger, LightUserData as LuaLightUserData, Lua, Function as LuaFunction, GCMode as LuaGCMode, Integer as LuaInteger,
MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber, LightUserData as LuaLightUserData, Lua, MetaMethod as LuaMetaMethod,
RegistryKey as LuaRegistryKey, Result as LuaResult, Scope as LuaScope, String as LuaString, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber, RegistryKey as LuaRegistryKey,
Table as LuaTable, TablePairs as LuaTablePairs, TableSequence as LuaTableSequence, Result as LuaResult, String as LuaString, Table as LuaTable, TableExt as LuaTableExt,
Thread as LuaThread, ThreadStatus as LuaThreadStatus, ToLua, ToLuaMulti, TablePairs as LuaTablePairs, TableSequence as LuaTableSequence, Thread as LuaThread,
UserData as LuaUserData, UserDataMethods as LuaUserDataMethods, Value as LuaValue, ThreadStatus as LuaThreadStatus, ToLua, ToLuaMulti, UserData as LuaUserData,
UserDataMethods as LuaUserDataMethods, Value as LuaValue,
}; };
#[cfg(feature = "async")]
pub use crate::AsyncThread as LuaAsyncThread;
+153 -37
View File
@@ -1,6 +1,5 @@
use std::any::Any; use std::any::Any;
use std::cell::Cell; use std::cell::{Cell, RefCell};
use std::cell::RefCell;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem; use std::mem;
use std::os::raw::c_void; use std::os::raw::c_void;
@@ -10,7 +9,7 @@ use crate::error::{Error, Result};
use crate::ffi; use crate::ffi;
use crate::function::Function; use crate::function::Function;
use crate::lua::Lua; use crate::lua::Lua;
use crate::types::{Callback, LuaRef}; use crate::types::{Callback, LuaRef, MaybeSend};
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods}; use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
use crate::util::{ use crate::util::{
assert_stack, init_userdata_metatable, protect_lua_closure, push_string, push_userdata, assert_stack, init_userdata_metatable, protect_lua_closure, push_string, push_userdata,
@@ -18,15 +17,24 @@ use crate::util::{
}; };
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti, Value}; use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti, Value};
#[cfg(feature = "async")]
use {
crate::types::AsyncCallback,
futures_core::future::Future,
futures_util::future::{self, TryFutureExt},
std::os::raw::c_char,
};
/// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and /// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and
/// callbacks that are not required to be Send or 'static. /// callbacks that are not required to be Send or 'static.
/// ///
/// See [`Lua::scope`] for more details. /// See [`Lua::scope`] for more details.
/// ///
/// [`Lua::scope`]: struct.Lua.html#method.scope /// [`Lua::scope`]: struct.Lua.html#method.scope
#[allow(clippy::type_complexity)]
pub struct Scope<'lua, 'scope> { pub struct Scope<'lua, 'scope> {
lua: &'lua Lua, lua: &'lua Lua,
destructors: RefCell<Vec<(LuaRef<'lua>, fn(LuaRef<'lua>) -> Box<dyn Any>)>>, destructors: RefCell<Vec<(LuaRef<'lua>, fn(LuaRef<'lua>) -> Vec<Box<dyn Any>>)>>,
_scope_invariant: PhantomData<Cell<&'scope ()>>, _scope_invariant: PhantomData<Cell<&'scope ()>>,
} }
@@ -93,15 +101,48 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
}) })
} }
/// Wraps a Rust async function or closure, creating a callable Lua function handle to it.
///
/// This is a version of [`Lua::create_async_function`] that creates a callback which expires on
/// scope drop. See [`Lua::scope`] and [`Lua::async_scope`] for more details.
///
/// Requires `feature = "async"`
///
/// [`Lua::create_async_function`]: struct.Lua.html#method.create_async_function
/// [`Lua::scope`]: struct.Lua.html#method.scope
/// [`Lua::async_scope`]: struct.Lua.html#method.async_scope
#[cfg(feature = "async")]
pub fn create_async_function<'callback, A, R, F, FR>(
&'callback self,
func: F,
) -> Result<Function<'lua>>
where
A: FromLuaMulti<'callback>,
R: ToLuaMulti<'callback>,
F: 'scope + Fn(&'callback Lua, A) -> FR,
FR: 'callback + Future<Output = Result<R>>,
{
unsafe {
self.create_async_callback(Box::new(move |lua, args| {
let args = match A::from_lua_multi(args, lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
Box::pin(func(lua, args).and_then(move |ret| future::ready(ret.to_lua_multi(lua))))
}))
}
}
/// Create a Lua userdata object from a custom userdata type. /// Create a Lua userdata object from a custom userdata type.
/// ///
/// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on /// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
/// scope drop, and does not require that the userdata type be Send (but still requires that the /// scope drop, and does not require that the userdata type be Send (but still requires that the
/// UserData be 'static). See [`Lua::scope`] for more details. /// UserData be 'static).
/// See [`Lua::scope`] for more details.
/// ///
/// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata /// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata
/// [`Lua::scope`]: struct.Lua.html#method.scope /// [`Lua::scope`]: struct.Lua.html#method.scope
pub fn create_static_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>> pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
where where
T: 'static + UserData, T: 'static + UserData,
{ {
@@ -115,7 +156,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
u.lua.push_ref(&u); u.lua.push_ref(&u);
// We know the destructor has not run yet because we hold a reference to the // We know the destructor has not run yet because we hold a reference to the
// userdata. // userdata.
Box::new(take_userdata::<RefCell<T>>(state)) vec![Box::new(take_userdata::<RefCell<T>>(state))]
})); }));
Ok(u) Ok(u)
} }
@@ -129,7 +170,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
/// ///
/// Lifting the requirement that the UserData type be 'static comes with some important /// Lifting the requirement that the UserData type be 'static comes with some important
/// limitations, so if you only need to eliminate the Send requirement, it is probably better to /// limitations, so if you only need to eliminate the Send requirement, it is probably better to
/// use [`Scope::create_static_userdata`] instead. /// use [`Scope::create_userdata`] instead.
/// ///
/// The main limitation that comes from using non-'static userdata is that the produced userdata /// The main limitation that comes from using non-'static userdata is that the produced userdata
/// will no longer have a `TypeId` associated with it, becuase `TypeId` can only work for /// will no longer have a `TypeId` associated with it, becuase `TypeId` can only work for
@@ -140,7 +181,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
/// a single metatable for multiple non-'static types, so there is a higher cost associated with /// a single metatable for multiple non-'static types, so there is a higher cost associated with
/// creating the userdata metatable each time a new userdata is created. /// creating the userdata metatable each time a new userdata is created.
/// ///
/// [`create_static_userdata`]: #method.create_static_userdata /// [`Scope::create_userdata`]: #method.create_userdata
/// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata /// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata
/// [`Lua::scope`]: struct.Lua.html#method.scope /// [`Lua::scope`]: struct.Lua.html#method.scope
/// [`UserDataMethods`]: trait.UserDataMethods.html /// [`UserDataMethods`]: trait.UserDataMethods.html
@@ -177,8 +218,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
ffi::lua_getuservalue(lua.state, -1); ffi::lua_getuservalue(lua.state, -1);
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
{ {
ffi::lua_pushinteger(lua.state, 1); ffi::lua_rawgeti(lua.state, -1, 1);
ffi::lua_gettable(lua.state, -2);
ffi::lua_remove(lua.state, -2); ffi::lua_remove(lua.state, -2);
} }
return ffi::lua_touserdata(lua.state, -1) return ffi::lua_touserdata(lua.state, -1)
@@ -192,21 +232,17 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
match method { match method {
NonStaticMethod::Method(method) => { NonStaticMethod::Method(method) => {
let method_data = data.clone();
let f = Box::new(move |lua, mut args: MultiValue<'callback>| { let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
if !check_ud_type(lua, args.pop_front()) { if !check_ud_type(lua, args.pop_front()) {
return Err(Error::UserDataTypeMismatch); return Err(Error::UserDataTypeMismatch);
} }
let data = method_data let data = data.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
.try_borrow()
.map_err(|_| Error::UserDataBorrowError)?;
method(lua, &*data, args) method(lua, &*data, args)
}); });
unsafe { scope.create_callback(f) } unsafe { scope.create_callback(f) }
} }
NonStaticMethod::MethodMut(method) => { NonStaticMethod::MethodMut(method) => {
let method = RefCell::new(method); let method = RefCell::new(method);
let method_data = data.clone();
let f = Box::new(move |lua, mut args: MultiValue<'callback>| { let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
if !check_ud_type(lua, args.pop_front()) { if !check_ud_type(lua, args.pop_front()) {
return Err(Error::UserDataTypeMismatch); return Err(Error::UserDataTypeMismatch);
@@ -214,7 +250,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let mut method = method let mut method = method
.try_borrow_mut() .try_borrow_mut()
.map_err(|_| Error::RecursiveMutCallback)?; .map_err(|_| Error::RecursiveMutCallback)?;
let mut data = method_data let mut data = data
.try_borrow_mut() .try_borrow_mut()
.map_err(|_| Error::UserDataBorrowMutError)?; .map_err(|_| Error::UserDataBorrowMutError)?;
(&mut *method)(lua, &mut *data, args) (&mut *method)(lua, &mut *data, args)
@@ -245,15 +281,14 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
assert_stack(lua.state, 6); assert_stack(lua.state, 6);
push_userdata(lua.state, ())?; push_userdata(lua.state, ())?;
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
ffi::lua_pushlightuserdata(lua.state, data.as_ptr() as *mut c_void); ffi::lua_pushlightuserdata(lua.state, data.as_ptr() as *mut c_void);
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
protect_lua_closure(lua.state, 0, 1, |state| { protect_lua_closure(lua.state, 0, 1, |state| {
// Lua 5.2/5.1 allows to store only table. Then we will wrap the value. // Lua 5.2/5.1 allows to store only table. Then we will wrap the value.
ffi::lua_createtable(state, 1, 0); ffi::lua_createtable(state, 1, 0);
ffi::lua_pushinteger(state, 1);
ffi::lua_pushlightuserdata(state, data.as_ptr() as *mut c_void); ffi::lua_pushlightuserdata(state, data.as_ptr() as *mut c_void);
ffi::lua_settable(state, -3); ffi::lua_rawseti(state, -2, 1);
})?; })?;
ffi::lua_setuservalue(lua.state, -2); ffi::lua_setuservalue(lua.state, -2);
@@ -312,18 +347,69 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
assert_stack(state, 3); assert_stack(state, 3);
f.lua.push_ref(&f); f.lua.push_ref(&f);
ffi::lua_getupvalue(state, -1, 1);
// We know the destructor has not run yet because we hold a reference to the callback. // We know the destructor has not run yet because we hold a reference to the callback.
let ud = take_userdata::<Callback>(state);
ffi::lua_getupvalue(state, -1, 1);
let ud1 = take_userdata::<Callback>(state);
ffi::lua_pushnil(state); ffi::lua_pushnil(state);
ffi::lua_setupvalue(state, -2, 1); ffi::lua_setupvalue(state, -2, 1);
ffi::lua_getupvalue(state, -1, 2);
let ud2 = take_userdata::<Lua>(state);
ffi::lua_pushnil(state);
ffi::lua_setupvalue(state, -2, 2);
ffi::lua_pop(state, 1); ffi::lua_pop(state, 1);
Box::new(ud) vec![Box::new(ud1), Box::new(ud2)]
})); }));
Ok(f) Ok(f)
} }
#[cfg(feature = "async")]
unsafe fn create_async_callback<'callback>(
&self,
f: AsyncCallback<'callback, 'scope>,
) -> Result<Function<'lua>> {
let f = mem::transmute::<AsyncCallback<'callback, 'scope>, AsyncCallback<'lua, 'static>>(f);
let f = self.lua.create_async_callback(f)?;
let mut destructors = self.destructors.borrow_mut();
destructors.push((f.0.clone(), |f| {
let state = f.lua.state;
assert_stack(state, 4);
f.lua.push_ref(&f);
// We know the destructor has not run yet because we hold a reference to the callback.
// First, get the environment table
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
ffi::lua_getupvalue(state, -1, 1);
#[cfg(any(feature = "lua51", feature = "luajit"))]
ffi::lua_getfenv(state, -1);
// Then, get the get_poll() closure using the corresponding key
let key = "get_poll";
ffi::lua_pushlstring(state, key.as_ptr() as *const c_char, key.len());
ffi::lua_rawget(state, -2);
// Finally, destroy all upvalues
ffi::lua_getupvalue(state, -1, 1);
let ud1 = take_userdata::<AsyncCallback>(state);
ffi::lua_pushnil(state);
ffi::lua_setupvalue(state, -2, 1);
ffi::lua_getupvalue(state, -1, 2);
let ud2 = take_userdata::<Lua>(state);
ffi::lua_pushnil(state);
ffi::lua_setupvalue(state, -2, 2);
ffi::lua_pop(state, 1);
vec![Box::new(ud1), Box::new(ud2)]
}));
Ok(f)
}
} }
impl<'lua, 'scope> Drop for Scope<'lua, 'scope> { impl<'lua, 'scope> Drop for Scope<'lua, 'scope> {
@@ -337,7 +423,7 @@ impl<'lua, 'scope> Drop for Scope<'lua, 'scope> {
.destructors .destructors
.get_mut() .get_mut()
.drain(..) .drain(..)
.map(|(r, dest)| dest(r)) .flat_map(|(r, dest)| dest(r))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
drop(to_drop); drop(to_drop);
@@ -368,10 +454,10 @@ impl<'lua, T: UserData> Default for NonStaticUserDataMethods<'lua, T> {
impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'lua, T> { impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'lua, T> {
fn add_method<S, A, R, M>(&mut self, name: &S, method: M) fn add_method<S, A, R, M>(&mut self, name: &S, method: M)
where where
S: ?Sized + AsRef<[u8]>, S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>, M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
{ {
self.methods.push(( self.methods.push((
name.as_ref().to_vec(), name.as_ref().to_vec(),
@@ -383,10 +469,10 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
fn add_method_mut<S, A, R, M>(&mut self, name: &S, mut method: M) fn add_method_mut<S, A, R, M>(&mut self, name: &S, mut method: M)
where where
S: ?Sized + AsRef<[u8]>, S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>, M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
{ {
self.methods.push(( self.methods.push((
name.as_ref().to_vec(), name.as_ref().to_vec(),
@@ -396,12 +482,27 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
)); ));
} }
fn add_function<S, A, R, F>(&mut self, name: &S, function: F) #[cfg(feature = "async")]
fn add_async_method<S, A, R, M, MR>(&mut self, _name: &S, _method: M)
where where
S: ?Sized + AsRef<[u8]>, T: Clone,
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>, M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
MR: 'lua + Future<Output = Result<R>>,
{
// The panic should never happen as async non-static code wouldn't compile
// Non-static lifetime must be bounded to 'lua lifetime
mlua_panic!("asynchronous methods are not supported for non-static userdata")
}
fn add_function<S, A, R, F>(&mut self, name: &S, function: F)
where
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
{ {
self.methods.push(( self.methods.push((
name.as_ref().to_vec(), name.as_ref().to_vec(),
@@ -413,10 +514,10 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
fn add_function_mut<S, A, R, F>(&mut self, name: &S, mut function: F) fn add_function_mut<S, A, R, F>(&mut self, name: &S, mut function: F)
where where
S: ?Sized + AsRef<[u8]>, S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>, F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
{ {
self.methods.push(( self.methods.push((
name.as_ref().to_vec(), name.as_ref().to_vec(),
@@ -426,11 +527,26 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
)); ));
} }
#[cfg(feature = "async")]
fn add_async_function<S, A, R, F, FR>(&mut self, _name: &S, _function: F)
where
T: Clone,
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
FR: 'lua + Future<Output = Result<R>>,
{
// The panic should never happen as async non-static code wouldn't compile
// Non-static lifetime must be bounded to 'lua lifetime
mlua_panic!("asynchronous functions are not supported for non-static userdata")
}
fn add_meta_method<A, R, M>(&mut self, meta: MetaMethod, method: M) fn add_meta_method<A, R, M>(&mut self, meta: MetaMethod, method: M)
where where
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>, M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
{ {
self.meta_methods.push(( self.meta_methods.push((
meta, meta,
@@ -444,7 +560,7 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
where where
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>, M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
{ {
self.meta_methods.push(( self.meta_methods.push((
meta, meta,
@@ -458,7 +574,7 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
where where
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>, F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
{ {
self.meta_methods.push(( self.meta_methods.push((
meta, meta,
@@ -472,7 +588,7 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
where where
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>, F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
{ {
self.meta_methods.push(( self.meta_methods.push((
meta, meta,
+35 -10
View File
@@ -1,31 +1,56 @@
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign}; use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign};
use std::u32; use std::u32;
/// Flags describing the set of lua modules to load. /// Flags describing the set of lua standard libraries to load.
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct StdLib(u32); pub struct StdLib(u32);
impl StdLib { impl StdLib {
#[cfg(any(feature = "lua53", feature = "lua52"))] /// [`coroutine`](https://www.lua.org/manual/5.3/manual.html#6.2) library
pub const COROUTINE: StdLib = StdLib(1 << 0); ///
/// Requires `feature = "lua54/lua53/lua52"`
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", doc))]
pub const COROUTINE: StdLib = StdLib(1);
/// [`table`](https://www.lua.org/manual/5.3/manual.html#6.6) library
pub const TABLE: StdLib = StdLib(1 << 1); pub const TABLE: StdLib = StdLib(1 << 1);
/// [`io`](https://www.lua.org/manual/5.3/manual.html#6.8) library
pub const IO: StdLib = StdLib(1 << 2); pub const IO: StdLib = StdLib(1 << 2);
/// [`os`](https://www.lua.org/manual/5.3/manual.html#6.9) library
pub const OS: StdLib = StdLib(1 << 3); pub const OS: StdLib = StdLib(1 << 3);
/// [`string`](https://www.lua.org/manual/5.3/manual.html#6.4) library
pub const STRING: StdLib = StdLib(1 << 4); pub const STRING: StdLib = StdLib(1 << 4);
#[cfg(feature = "lua53")] /// [`utf8`](https://www.lua.org/manual/5.3/manual.html#6.5) library
///
/// Requires `feature = "lua54/lua53"`
#[cfg(any(feature = "lua54", feature = "lua53", doc))]
pub const UTF8: StdLib = StdLib(1 << 5); pub const UTF8: StdLib = StdLib(1 << 5);
#[cfg(any(feature = "lua52", feature = "luajit"))] /// [`bit`](https://www.lua.org/manual/5.2/manual.html#6.7) library
///
/// Requires `feature = "lua52/luajit"`
#[cfg(any(feature = "lua52", feature = "luajit", doc))]
pub const BIT: StdLib = StdLib(1 << 6); pub const BIT: StdLib = StdLib(1 << 6);
/// [`math`](https://www.lua.org/manual/5.3/manual.html#6.7) library
pub const MATH: StdLib = StdLib(1 << 7); pub const MATH: StdLib = StdLib(1 << 7);
/// [`package`](https://www.lua.org/manual/5.3/manual.html#6.3) library
pub const PACKAGE: StdLib = StdLib(1 << 8); pub const PACKAGE: StdLib = StdLib(1 << 8);
#[cfg(feature = "luajit")] /// [`jit`](http://luajit.org/ext_jit.html) library
///
/// Requires `feature = "luajit"`
#[cfg(any(feature = "luajit", doc))]
pub const JIT: StdLib = StdLib(1 << 9); pub const JIT: StdLib = StdLib(1 << 9);
#[cfg(feature = "luajit")]
pub const FFI: StdLib = StdLib(1 << 10);
pub const DEBUG: StdLib = StdLib(1 << 31); // always highest bit
/// (unsafe) [`ffi`](http://luajit.org/ext_ffi.html) library
///
/// Requires `feature = "luajit"`
#[cfg(any(feature = "luajit", doc))]
pub const FFI: StdLib = StdLib(1 << 30);
/// (unsafe) [`debug`](https://www.lua.org/manual/5.3/manual.html#6.10) library
pub const DEBUG: StdLib = StdLib(1 << 31);
/// (unsafe) All standard libraries
pub const ALL: StdLib = StdLib(u32::MAX); pub const ALL: StdLib = StdLib(u32::MAX);
pub const ALL_NO_DEBUG: StdLib = StdLib((1 << 31) - 1); /// The safe subset of the standard libraries
pub const ALL_SAFE: StdLib = StdLib((1 << 30) - 1);
pub fn contains(self, lib: Self) -> bool { pub fn contains(self, lib: Self) -> bool {
(self & lib).0 != 0 (self & lib).0 != 0
+126 -39
View File
@@ -8,10 +8,14 @@ use crate::types::{Integer, LuaRef};
use crate::util::{assert_stack, protect_lua, protect_lua_closure, StackGuard}; use crate::util::{assert_stack, protect_lua, protect_lua_closure, StackGuard};
use crate::value::{FromLua, FromLuaMulti, Nil, ToLua, ToLuaMulti, Value}; use crate::value::{FromLua, FromLuaMulti, Nil, ToLua, ToLuaMulti, Value};
#[cfg(feature = "async")]
use {futures_core::future::LocalBoxFuture, futures_util::future};
/// Handle to an internal Lua table. /// Handle to an internal Lua table.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Table<'lua>(pub(crate) LuaRef<'lua>); pub struct Table<'lua>(pub(crate) LuaRef<'lua>);
#[allow(clippy::len_without_is_empty)]
impl<'lua> Table<'lua> { impl<'lua> Table<'lua> {
/// Sets a key-value pair in the table. /// Sets a key-value pair in the table.
/// ///
@@ -133,39 +137,6 @@ impl<'lua> Table<'lua> {
} }
} }
/// Gets the function associated to `key` from the table and executes it,
/// passing the table itself as the first argument.
///
/// # Examples
///
/// Execute the table method with name "concat":
///
/// ```
/// # use mlua::{Lua, Result, Table};
/// # fn main() -> Result<()> {
/// # let lua = Lua::new();
/// # let object = lua.create_table()?;
/// # let concat = lua.create_function(|_, (_, a, b): (Table, String, String)| Ok(a + &b))?;
/// # object.set("concat", concat)?;
/// // simiar to: object:concat("param1", "param2")
/// object.call("concat", ("param1", "param2"))?;
/// # Ok(())
/// # }
/// ```
///
/// This might invoke the `__index` metamethod.
pub fn call<K, A, R>(&self, key: K, args: A) -> Result<R>
where
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
let lua = self.0.lua;
let mut args = args.to_lua_multi(lua)?;
args.push_front(Value::Table(self.clone()));
self.get::<_, Function>(key)?.call(args)
}
/// Compares two tables for equality. /// Compares two tables for equality.
/// ///
/// Tables are compared by reference first. /// Tables are compared by reference first.
@@ -262,7 +233,7 @@ impl<'lua> Table<'lua> {
V::from_lua(value, lua) V::from_lua(value, lua)
} }
/// Inserts element value at position idx to the table, shifting up the elements from table[idx]. /// Inserts element value at position `idx` to the table, shifting up the elements from `table[idx]`.
/// The worst case complexity is O(n), where n is the table length. /// The worst case complexity is O(n), where n is the table length.
pub fn raw_insert<V: ToLua<'lua>>(&self, idx: Integer, value: V) -> Result<()> { pub fn raw_insert<V: ToLua<'lua>>(&self, idx: Integer, value: V) -> Result<()> {
let lua = self.0.lua; let lua = self.0.lua;
@@ -292,11 +263,11 @@ impl<'lua> Table<'lua> {
/// Removes a key from the table. /// Removes a key from the table.
/// ///
/// If `key` is an integer, mlua shifts down the elements from table[key+1], /// If `key` is an integer, mlua shifts down the elements from `table[key+1]`,
/// and erases element table[key]. The complexity is O(n) in worst case, /// and erases element `table[key]`. The complexity is O(n) in worst case,
/// where n is the table length. /// where n is the table length.
/// ///
/// For othey key types this is equivalent to setting table[key] = nil. /// For othey key types this is equivalent to setting `table[key] = nil`.
pub fn raw_remove<K: ToLua<'lua>>(&self, key: K) -> Result<()> { pub fn raw_remove<K: ToLua<'lua>>(&self, key: K) -> Result<()> {
let lua = self.0.lua; let lua = self.0.lua;
let key = key.to_lua(lua)?; let key = key.to_lua(lua)?;
@@ -494,6 +465,121 @@ impl<'lua> AsRef<Table<'lua>> for Table<'lua> {
} }
} }
/// An extension trait for `Table`s that provides a variety of convenient functionality.
pub trait TableExt<'lua> {
/// Gets the function associated to `key` from the table and executes it,
/// passing the table itself along with `args` as function arguments.
///
/// This is a shortcut for
/// `table.get::<_, Function>(key)?.call((table.clone(), arg1, ..., argN))`
///
/// This might invoke the `__index` metamethod.
fn call_method<K, A, R>(&self, key: K, args: A) -> Result<R>
where
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>;
/// Gets the function associated to `key` from the table and executes it,
/// passing `args` as function arguments.
///
/// This is a shortcut for
/// `table.get::<_, Function>(key)?.call(args)`
///
/// This might invoke the `__index` metamethod.
fn call_function<K, A, R>(&self, key: K, args: A) -> Result<R>
where
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>;
/// Gets the function associated to `key` from the table and asynchronously executes it,
/// passing the table itself along with `args` as function arguments and returning Future.
///
/// Requires `feature = "async"`
///
/// This might invoke the `__index` metamethod.
#[cfg(feature = "async")]
fn call_async_method<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut;
/// Gets the function associated to `key` from the table and asynchronously executes it,
/// passing `args` as function arguments and returning Future.
///
/// Requires `feature = "async"`
///
/// This might invoke the `__index` metamethod.
#[cfg(feature = "async")]
fn call_async_function<'fut, K, A, R>(
&self,
key: K,
args: A,
) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut;
}
impl<'lua> TableExt<'lua> for Table<'lua> {
fn call_method<K, A, R>(&self, key: K, args: A) -> Result<R>
where
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
let lua = self.0.lua;
let mut args = args.to_lua_multi(lua)?;
args.push_front(Value::Table(self.clone()));
self.get::<_, Function>(key)?.call(args)
}
fn call_function<K, A, R>(&self, key: K, args: A) -> Result<R>
where
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
self.get::<_, Function>(key)?.call(args)
}
#[cfg(feature = "async")]
fn call_async_method<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
{
let lua = self.0.lua;
let mut args = match args.to_lua_multi(lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
args.push_front(Value::Table(self.clone()));
self.call_async_function(key, args)
}
#[cfg(feature = "async")]
fn call_async_function<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
{
match self.get::<_, Function>(key) {
Ok(func) => func.call_async(args),
Err(e) => Box::pin(future::err(e)),
}
}
}
/// An iterator over the pairs of a Lua table. /// An iterator over the pairs of a Lua table.
/// ///
/// This struct is created by the [`Table::pairs`] method. /// This struct is created by the [`Table::pairs`] method.
@@ -524,9 +610,10 @@ where
lua.push_ref(&self.table); lua.push_ref(&self.table);
lua.push_value(next_key)?; lua.push_value(next_key)?;
if protect_lua_closure(lua.state, 2, ffi::LUA_MULTRET, |state| { let next = protect_lua_closure(lua.state, 2, ffi::LUA_MULTRET, |state| {
ffi::lua_next(state, -2) != 0 ffi::lua_next(state, -2) != 0
})? { })?;
if next {
ffi::lua_pushvalue(lua.state, -2); ffi::lua_pushvalue(lua.state, -2);
let key = lua.pop_value(); let key = lua.pop_value();
let value = lua.pop_value(); let value = lua.pop_value();
+215 -2
View File
@@ -8,6 +8,24 @@ use crate::util::{
}; };
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti}; use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
#[cfg(feature = "async")]
use {
crate::{
error::ExternalError,
lua::{AsyncPollPending, Lua, WAKER_REGISTRY_KEY},
util::{get_gc_userdata, push_gc_userdata},
value::Value,
},
futures_core::{future::Future, stream::Stream},
std::{
cell::RefCell,
marker::PhantomData,
os::raw::c_void,
pin::Pin,
task::{Context, Poll, Waker},
},
};
/// Status of a Lua thread (or coroutine). /// Status of a Lua thread (or coroutine).
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ThreadStatus { pub enum ThreadStatus {
@@ -27,6 +45,20 @@ pub enum ThreadStatus {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Thread<'lua>(pub(crate) LuaRef<'lua>); pub struct Thread<'lua>(pub(crate) LuaRef<'lua>);
/// Thread (coroutine) representation as an async [`Future`] or [`Stream`].
///
/// Requires `feature = "async"`
///
/// [`Future`]: ../futures_core/future/trait.Future.html
/// [`Stream`]: ../futures_core/stream/trait.Stream.html
#[cfg(feature = "async")]
#[derive(Debug)]
pub struct AsyncThread<'lua, R> {
thread: Thread<'lua>,
args0: RefCell<Option<Result<MultiValue<'lua>>>>,
ret: PhantomData<R>,
}
impl<'lua> Thread<'lua> { impl<'lua> Thread<'lua> {
/// Resumes execution of this thread. /// Resumes execution of this thread.
/// ///
@@ -99,7 +131,9 @@ impl<'lua> Thread<'lua> {
} }
ffi::lua_xmove(lua.state, thread_state, nargs); ffi::lua_xmove(lua.state, thread_state, nargs);
let ret = ffi::lua_resume(thread_state, lua.state, nargs); let mut nresults = 0;
let ret = ffi::lua_resume(thread_state, lua.state, nargs, &mut nresults as *mut c_int);
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD { if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
protect_lua_closure(lua.state, 0, 0, |_| { protect_lua_closure(lua.state, 0, 0, |_| {
error_traceback(thread_state); error_traceback(thread_state);
@@ -108,7 +142,6 @@ impl<'lua> Thread<'lua> {
return Err(pop_error(thread_state, ret)); return Err(pop_error(thread_state, ret));
} }
let nresults = ffi::lua_gettop(thread_state);
let mut results = MultiValue::new(); let mut results = MultiValue::new();
ffi::lua_xmove(thread_state, lua.state, nresults); ffi::lua_xmove(thread_state, lua.state, nresults);
@@ -142,6 +175,61 @@ impl<'lua> Thread<'lua> {
} }
} }
} }
/// Converts Thread to an AsyncThread which implements Future and Stream traits.
///
/// `args` are passed as arguments to the thread function for first call.
/// The object call `resume()` while polling and also allows to run rust futures
/// to completion using an executor.
///
/// Using AsyncThread as a Stream allows to iterate through `coroutine.yield()`
/// values whereas Future version discards that values and poll until the final
/// one (returned from the thread function).
///
/// Requires `feature = "async"`
///
/// # Examples
///
/// ```
/// # use mlua::{Lua, Result, Thread};
/// use futures::stream::TryStreamExt;
/// # #[tokio::main]
/// # async fn main() -> Result<()> {
/// # let lua = Lua::new();
/// let thread: Thread = lua.load(r#"
/// coroutine.create(function (sum)
/// for i = 1,10 do
/// sum = sum + i
/// coroutine.yield(sum)
/// end
/// return sum
/// end)
/// "#).eval()?;
///
/// let mut stream = thread.into_async::<_, i64>(1);
/// let mut sum = 0;
/// while let Some(n) = stream.try_next().await? {
/// sum += n;
/// }
///
/// assert_eq!(sum, 286);
///
/// # Ok(())
/// # }
/// ```
#[cfg(feature = "async")]
pub fn into_async<A, R>(self, args: A) -> AsyncThread<'lua, R>
where
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
let args = args.to_lua_multi(&self.0.lua);
AsyncThread {
thread: self,
args0: RefCell::new(Some(args)),
ret: PhantomData,
}
}
} }
impl<'lua> PartialEq for Thread<'lua> { impl<'lua> PartialEq for Thread<'lua> {
@@ -149,3 +237,128 @@ impl<'lua> PartialEq for Thread<'lua> {
self.0 == other.0 self.0 == other.0
} }
} }
#[cfg(feature = "async")]
impl<'lua, R> Stream for AsyncThread<'lua, R>
where
R: FromLuaMulti<'lua>,
{
type Item = Result<R>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let lua = self.thread.0.lua;
match self.thread.status() {
ThreadStatus::Resumable => {}
_ => return Poll::Ready(None),
};
let _wg = WakerGuard::new(lua.state, cx.waker().clone());
let ret: MultiValue = if let Some(args) = self.args0.borrow_mut().take() {
self.thread.resume(args?)?
} else {
self.thread.resume(())?
};
if is_poll_pending(lua, &ret) {
return Poll::Pending;
}
cx.waker().wake_by_ref();
Poll::Ready(Some(R::from_lua_multi(ret, lua)))
}
}
#[cfg(feature = "async")]
impl<'lua, R> Future for AsyncThread<'lua, R>
where
R: FromLuaMulti<'lua>,
{
type Output = Result<R>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let lua = self.thread.0.lua;
match self.thread.status() {
ThreadStatus::Resumable => {}
_ => return Poll::Ready(Err("Thread already finished".to_lua_err())),
};
let _wg = WakerGuard::new(lua.state, cx.waker().clone());
let ret: MultiValue = if let Some(args) = self.args0.borrow_mut().take() {
self.thread.resume(args?)?
} else {
self.thread.resume(())?
};
if is_poll_pending(lua, &ret) {
return Poll::Pending;
}
if let ThreadStatus::Resumable = self.thread.status() {
// Ignore value returned via yield()
cx.waker().wake_by_ref();
return Poll::Pending;
}
Poll::Ready(R::from_lua_multi(ret, lua))
}
}
#[cfg(feature = "async")]
fn is_poll_pending(lua: &Lua, val: &MultiValue) -> bool {
if val.len() != 1 {
return false;
}
if let Some(Value::UserData(ud)) = val.iter().next() {
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 3);
lua.push_ref(&ud.0);
let is_pending = get_gc_userdata::<AsyncPollPending>(lua.state, -1)
.as_ref()
.is_some();
ffi::lua_pop(lua.state, 1);
return is_pending;
}
}
false
}
#[cfg(feature = "async")]
struct WakerGuard(*mut ffi::lua_State);
#[cfg(feature = "async")]
impl WakerGuard {
pub fn new(state: *mut ffi::lua_State, waker: Waker) -> Result<WakerGuard> {
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 6);
ffi::lua_pushlightuserdata(state, &WAKER_REGISTRY_KEY as *const u8 as *mut c_void);
push_gc_userdata(state, waker)?;
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
Ok(WakerGuard(state))
}
}
}
#[cfg(feature = "async")]
impl Drop for WakerGuard {
fn drop(&mut self) {
unsafe {
let state = self.0;
let _sg = StackGuard::new(state);
assert_stack(state, 2);
ffi::lua_pushlightuserdata(state, &WAKER_REGISTRY_KEY as *const u8 as *mut c_void);
ffi::lua_pushnil(state);
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
}
}
}
+27 -10
View File
@@ -1,9 +1,14 @@
use std::cell::RefCell;
use std::os::raw::{c_int, c_void}; use std::os::raw::{c_int, c_void};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::{fmt, mem, ptr}; use std::{fmt, mem, ptr};
#[cfg(feature = "async")]
use futures_core::future::LocalBoxFuture;
use crate::error::Result; use crate::error::Result;
use crate::ffi; use crate::ffi;
use crate::hook::Debug;
use crate::lua::Lua; use crate::lua::Lua;
use crate::util::{assert_stack, StackGuard}; use crate::util::{assert_stack, StackGuard};
use crate::value::MultiValue; use crate::value::MultiValue;
@@ -20,24 +25,35 @@ pub struct LightUserData(pub *mut c_void);
pub(crate) type Callback<'lua, 'a> = pub(crate) type Callback<'lua, 'a> =
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>> + 'a>; Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>> + 'a>;
#[cfg(feature = "async")]
pub(crate) type AsyncCallback<'lua, 'a> =
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> LocalBoxFuture<'lua, Result<MultiValue<'lua>>> + 'a>;
pub(crate) type HookCallback = Arc<RefCell<dyn FnMut(&Lua, Debug) -> Result<()>>>;
#[cfg(feature = "send")]
pub trait MaybeSend: Send {}
#[cfg(feature = "send")]
impl<T: Send> MaybeSend for T {}
#[cfg(not(feature = "send"))]
pub trait MaybeSend {}
#[cfg(not(feature = "send"))]
impl<T> MaybeSend for T {}
/// An auto generated key into the Lua registry. /// An auto generated key into the Lua registry.
/// ///
/// This is a handle to a value stored inside the Lua registry. It is not directly usable like the /// This is a handle to a value stored inside the Lua registry. It is not automatically
/// `Table` or `Function` handle types, but since it doesn't hold a reference to a parent Lua and is /// garbage collected on Drop, but it can be removed with [`Lua::remove_registry_value`],
/// Send + Sync + 'static, it is much more flexible and can be used in many situations where it is /// and instances not manually removed can be garbage collected with [`Lua::expire_registry_values`].
/// impossible to directly store a normal handle type. It is not automatically garbage collected on
/// Drop, but it can be removed with [`Lua::remove_registry_value`], and instances not manually
/// removed can be garbage collected with [`Lua::expire_registry_values`].
/// ///
/// Be warned, If you place this into Lua via a `UserData` type or a rust callback, it is *very /// Be warned, If you place this into Lua via a `UserData` type or a rust callback, it is *very
/// easy* to accidentally cause reference cycles that the Lua garbage collector cannot resolve. /// easy* to accidentally cause reference cycles that the Lua garbage collector cannot resolve.
/// Instead of placing a `RegistryKey` into a `UserData` type, prefer instead to use /// Instead of placing a `RegistryKey` into a `UserData` type, prefer instead to use
/// [`UserData::set_user_value`] / [`UserData::get_user_value`], and instead of moving a RegistryKey /// [`UserData::set_user_value`] / [`UserData::get_user_value`].
/// into a callback, prefer [`Lua::scope`].
/// ///
/// [`Lua::remove_registry_value`]: struct.Lua.html#method.remove_registry_value /// [`Lua::remove_registry_value`]: struct.Lua.html#method.remove_registry_value
/// [`Lua::expire_registry_values`]: struct.Lua.html#method.expire_registry_values /// [`Lua::expire_registry_values`]: struct.Lua.html#method.expire_registry_values
/// [`Lua::scope`]: struct.Lua.html#method.scope
/// [`UserData::set_user_value`]: struct.UserData.html#method.set_user_value /// [`UserData::set_user_value`]: struct.UserData.html#method.set_user_value
/// [`UserData::get_user_value`]: struct.UserData.html#method.get_user_value /// [`UserData::get_user_value`]: struct.UserData.html#method.get_user_value
pub struct RegistryKey { pub struct RegistryKey {
@@ -53,7 +69,8 @@ impl fmt::Debug for RegistryKey {
impl Drop for RegistryKey { impl Drop for RegistryKey {
fn drop(&mut self) { fn drop(&mut self) {
if let Some(list) = mlua_expect!(self.unref_list.lock(), "unref_list poisoned").as_mut() { let mut unref_list = mlua_expect!(self.unref_list.lock(), "unref list poisoned");
if let Some(list) = unref_list.as_mut() {
list.push(self.registry_id); list.push(self.registry_id);
} }
} }
+113 -30
View File
@@ -1,13 +1,16 @@
use std::cell::{Ref, RefCell, RefMut}; use std::cell::{Ref, RefCell, RefMut};
#[cfg(feature = "async")]
use std::future::Future;
use crate::error::{Error, Result}; use crate::error::{Error, Result};
use crate::ffi; use crate::ffi;
use crate::function::Function; use crate::function::Function;
use crate::lua::Lua; use crate::lua::Lua;
use crate::table::Table; use crate::table::Table;
use crate::types::LuaRef; use crate::types::{LuaRef, MaybeSend};
use crate::util::{assert_stack, get_userdata, StackGuard}; use crate::util::{assert_stack, get_userdata, StackGuard};
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti}; use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti, Value};
/// Kinds of metamethods that can be overridden. /// Kinds of metamethods that can be overridden.
/// ///
@@ -31,26 +34,31 @@ pub enum MetaMethod {
Pow, Pow,
/// The unary minus (`-`) operator. /// The unary minus (`-`) operator.
Unm, Unm,
#[cfg(feature = "lua53")]
/// The floor division (//) operator. /// The floor division (//) operator.
/// Requires `feature = "lua54/lua53"`
#[cfg(any(feature = "lua54", feature = "lua53", doc))]
IDiv, IDiv,
#[cfg(feature = "lua53")]
/// The bitwise AND (&) operator. /// The bitwise AND (&) operator.
/// Requires `feature = "lua54/lua53"`
#[cfg(any(feature = "lua54", feature = "lua53", doc))]
BAnd, BAnd,
#[cfg(feature = "lua53")]
/// The bitwise OR (|) operator. /// The bitwise OR (|) operator.
/// Requires `feature = "lua54/lua53"`
#[cfg(any(feature = "lua54", feature = "lua53", doc))]
BOr, BOr,
#[cfg(feature = "lua53")]
/// The bitwise XOR (binary ~) operator. /// The bitwise XOR (binary ~) operator.
/// Requires `feature = "lua54/lua53"`
#[cfg(any(feature = "lua54", feature = "lua53", doc))]
BXor, BXor,
#[cfg(feature = "lua53")]
/// The bitwise NOT (unary ~) operator. /// The bitwise NOT (unary ~) operator.
/// Requires `feature = "lua54/lua53"`
#[cfg(any(feature = "lua54", feature = "lua53", doc))]
BNot, BNot,
#[cfg(feature = "lua53")]
/// The bitwise left shift (<<) operator. /// The bitwise left shift (<<) operator.
#[cfg(any(feature = "lua54", feature = "lua53", doc))]
Shl, Shl,
#[cfg(feature = "lua53")]
/// The bitwise right shift (>>) operator. /// The bitwise right shift (>>) operator.
#[cfg(any(feature = "lua54", feature = "lua53", doc))]
Shr, Shr,
/// The string concatenation operator `..`. /// The string concatenation operator `..`.
Concat, Concat,
@@ -72,11 +80,25 @@ pub enum MetaMethod {
/// ///
/// This is not an operator, but will be called by methods such as `tostring` and `print`. /// This is not an operator, but will be called by methods such as `tostring` and `print`.
ToString, ToString,
#[cfg(any(feature = "lua53", feature = "lua52"))]
/// The `__pairs` metamethod. /// The `__pairs` metamethod.
/// ///
/// This is not an operator, but it will be called by the built-in `pairs` function. /// This is not an operator, but it will be called by the built-in `pairs` function.
///
/// Requires `feature = "lua54/lua53/lua52"`
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", doc))]
Pairs, Pairs,
/// The `__close` metamethod.
///
/// Executed when a variable, that marked as to-be-closed, goes out of scope.
///
/// More information about to-be-closed variabled can be found in the Lua 5.4
/// [documentation][lua_doc].
///
/// Requires `feature = "lua54"`
///
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#3.3.8
#[cfg(any(feature = "lua54", doc))]
Close,
} }
impl MetaMethod { impl MetaMethod {
@@ -89,20 +111,22 @@ impl MetaMethod {
MetaMethod::Mod => b"__mod", MetaMethod::Mod => b"__mod",
MetaMethod::Pow => b"__pow", MetaMethod::Pow => b"__pow",
MetaMethod::Unm => b"__unm", MetaMethod::Unm => b"__unm",
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
MetaMethod::IDiv => b"__idiv", MetaMethod::IDiv => b"__idiv",
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
MetaMethod::BAnd => b"__band", MetaMethod::BAnd => b"__band",
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
MetaMethod::BOr => b"__bor", MetaMethod::BOr => b"__bor",
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
MetaMethod::BXor => b"__bxor", MetaMethod::BXor => b"__bxor",
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
MetaMethod::BNot => b"__bnot", MetaMethod::BNot => b"__bnot",
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
MetaMethod::Shl => b"__shl", MetaMethod::Shl => b"__shl",
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
MetaMethod::Shr => b"__shr", MetaMethod::Shr => b"__shr",
MetaMethod::Concat => b"__concat", MetaMethod::Concat => b"__concat",
MetaMethod::Len => b"__len", MetaMethod::Len => b"__len",
MetaMethod::Eq => b"__eq", MetaMethod::Eq => b"__eq",
@@ -112,8 +136,12 @@ impl MetaMethod {
MetaMethod::NewIndex => b"__newindex", MetaMethod::NewIndex => b"__newindex",
MetaMethod::Call => b"__call", MetaMethod::Call => b"__call",
MetaMethod::ToString => b"__tostring", MetaMethod::ToString => b"__tostring",
#[cfg(any(feature = "lua53", feature = "lua52"))]
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
MetaMethod::Pairs => b"__pairs", MetaMethod::Pairs => b"__pairs",
#[cfg(feature = "lua54")]
MetaMethod::Close => b"__close",
} }
} }
} }
@@ -134,7 +162,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
S: ?Sized + AsRef<[u8]>, S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>; M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>;
/// Add a regular method which accepts a `&mut T` as the first parameter. /// Add a regular method which accepts a `&mut T` as the first parameter.
/// ///
@@ -146,7 +174,25 @@ pub trait UserDataMethods<'lua, T: UserData> {
S: ?Sized + AsRef<[u8]>, S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>; M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>;
/// Add an async method which accepts a `T` as the first parameter and returns Future.
/// The passed `T` is cloned from the original value.
///
/// Refer to [`add_method`] for more information about the implementation.
///
/// Requires `feature = "async"`
///
/// [`add_method`]: #method.add_method
#[cfg(feature = "async")]
fn add_async_method<S, A, R, M, MR>(&mut self, name: &S, method: M)
where
T: Clone,
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
MR: 'lua + Future<Output = Result<R>>;
/// Add a regular method as a function which accepts generic arguments, the first argument will /// Add a regular method as a function which accepts generic arguments, the first argument will
/// be a `UserData` of type T if the method is called with Lua method syntax: /// be a `UserData` of type T if the method is called with Lua method syntax:
@@ -162,7 +208,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
S: ?Sized + AsRef<[u8]>, S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>; F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>;
/// Add a regular method as a mutable function which accepts generic arguments. /// Add a regular method as a mutable function which accepts generic arguments.
/// ///
@@ -174,7 +220,25 @@ pub trait UserDataMethods<'lua, T: UserData> {
S: ?Sized + AsRef<[u8]>, S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>; F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>;
/// Add a regular method as an async function which accepts generic arguments
/// and returns Future.
///
/// This is an async version of [`add_function`].
///
/// Requires `feature = "async"`
///
/// [`add_function`]: #method.add_function
#[cfg(feature = "async")]
fn add_async_function<S, A, R, F, FR>(&mut self, name: &S, function: F)
where
T: Clone,
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
FR: 'lua + Future<Output = Result<R>>;
/// Add a metamethod which accepts a `&T` as the first parameter. /// Add a metamethod which accepts a `&T` as the first parameter.
/// ///
@@ -188,7 +252,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
where where
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>; M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>;
/// Add a metamethod as a function which accepts a `&mut T` as the first parameter. /// Add a metamethod as a function which accepts a `&mut T` as the first parameter.
/// ///
@@ -202,7 +266,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
where where
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>; M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>;
/// Add a metamethod which accepts generic arguments. /// Add a metamethod which accepts generic arguments.
/// ///
@@ -213,7 +277,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
where where
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>; F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>;
/// Add a metamethod as a mutable function which accepts generic arguments. /// Add a metamethod as a mutable function which accepts generic arguments.
/// ///
@@ -224,7 +288,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
where where
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>; F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>;
} }
/// Trait for custom userdata types. /// Trait for custom userdata types.
@@ -293,7 +357,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
/// [`UserDataMethods`]: trait.UserDataMethods.html /// [`UserDataMethods`]: trait.UserDataMethods.html
pub trait UserData: Sized { pub trait UserData: Sized {
/// Adds custom methods and operators specific to this userdata. /// Adds custom methods and operators specific to this userdata.
fn add_methods<'lua, T: UserDataMethods<'lua, Self>>(_methods: &mut T) {} fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {}
} }
/// Handle to an internal Lua userdata for any type that implements [`UserData`]. /// Handle to an internal Lua userdata for any type that implements [`UserData`].
@@ -352,18 +416,19 @@ impl<'lua> AnyUserData<'lua> {
/// Sets an associated value to this `AnyUserData`. /// Sets an associated value to this `AnyUserData`.
/// ///
/// The value may be any Lua value whatsoever, and can be retrieved with [`get_user_value`]. /// The value may be any Lua value whatsoever, and can be retrieved with [`get_user_value`].
/// As Lua < 5.3 allows to store only tables, the value will be stored in a table at index 1.
/// ///
/// [`get_user_value`]: #method.get_user_value /// [`get_user_value`]: #method.get_user_value
pub fn set_user_value<V: ToLua<'lua>>(&self, v: V) -> Result<()> { pub fn set_user_value<V: ToLua<'lua>>(&self, v: V) -> Result<()> {
let lua = self.0.lua; let lua = self.0.lua;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
let v = { let v = {
// Lua 5.2/5.1 allows to store only table. Then we will wrap the value. // Lua 5.2/5.1 allows to store only a table. Then we will wrap the value.
let t = lua.create_table()?; let t = lua.create_table()?;
t.raw_set(1, v)?; t.raw_set(1, v)?;
crate::Value::Table(t) crate::Value::Table(t)
}; };
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
let v = v.to_lua(lua)?; let v = v.to_lua(lua)?;
unsafe { unsafe {
let _sg = StackGuard::new(lua.state); let _sg = StackGuard::new(lua.state);
@@ -377,6 +442,8 @@ impl<'lua> AnyUserData<'lua> {
/// Returns an associated value set by [`set_user_value`]. /// Returns an associated value set by [`set_user_value`].
/// ///
/// For Lua < 5.3 the value will be automatically extracted from the table wrapper from index 1.
///
/// [`set_user_value`]: #method.set_user_value /// [`set_user_value`]: #method.set_user_value
pub fn get_user_value<V: FromLua<'lua>>(&self) -> Result<V> { pub fn get_user_value<V: FromLua<'lua>>(&self) -> Result<V> {
let lua = self.0.lua; let lua = self.0.lua;
@@ -389,10 +456,26 @@ impl<'lua> AnyUserData<'lua> {
}; };
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
return crate::Table::from_lua(res, lua)?.get(1); return crate::Table::from_lua(res, lua)?.get(1);
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
V::from_lua(res, lua) V::from_lua(res, lua)
} }
/// Checks for a metamethod in this `AnyUserData`
pub fn has_metamethod(&self, method: MetaMethod) -> Result<bool> {
match self.get_metatable() {
Ok(mt) => {
let name = self.0.lua.create_string(method.name())?;
if let Value::Nil = mt.raw_get(name)? {
Ok(false)
} else {
Ok(true)
}
}
Err(Error::UserDataTypeMismatch) => Ok(false),
Err(e) => Err(e),
}
}
fn get_metatable(&self) -> Result<Table<'lua>> { fn get_metatable(&self) -> Result<Table<'lua>> {
unsafe { unsafe {
let lua = self.0.lua; let lua = self.0.lua;
+141 -129
View File
@@ -1,14 +1,20 @@
use std::any::Any; use std::any::{Any, TypeId};
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt::Write; use std::fmt::Write;
use std::os::raw::{c_char, c_int, c_void}; use std::os::raw::{c_char, c_int, c_void};
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe}; use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
use std::sync::Arc; use std::sync::{Arc, Mutex};
use std::{mem, ptr, slice}; use std::{mem, ptr, slice};
use crate::error::{Error, Result}; use crate::error::{Error, Result};
use crate::ffi; use crate::ffi;
lazy_static::lazy_static! {
// The capacity must(!) be greater than number of stored keys
static ref METATABLE_CACHE: Mutex<HashMap<TypeId, u8>> = Mutex::new(HashMap::with_capacity(32));
}
// Checks that Lua has enough free stack space for future stack operations. On failure, this will // Checks that Lua has enough free stack space for future stack operations. On failure, this will
// panic with an internal error message. // panic with an internal error message.
pub unsafe fn assert_stack(state: *mut ffi::lua_State, amount: c_int) { pub unsafe fn assert_stack(state: *mut ffi::lua_State, amount: c_int) {
@@ -51,10 +57,11 @@ impl Drop for StackGuard {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
let top = ffi::lua_gettop(self.state); let top = ffi::lua_gettop(self.state);
if top < self.top {
mlua_panic!("{} too many stack values popped", self.top - top)
}
if top > self.top { if top > self.top {
ffi::lua_settop(self.state, self.top); ffi::lua_settop(self.state, self.top);
} else if top < self.top {
mlua_panic!("{} too many stack values popped", self.top - top);
} }
} }
} }
@@ -175,8 +182,7 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
if let Some(err) = get_wrapped_error(state, -1).as_ref() { if let Some(err) = get_wrapped_error(state, -1).as_ref() {
ffi::lua_pop(state, 1); ffi::lua_pop(state, 1);
err.clone() err.clone()
} else if is_wrapped_panic(state, -1) { } else if let Some(panic) = get_gc_userdata::<WrappedPanic>(state, -1).as_mut() {
let panic = get_userdata::<WrappedPanic>(state, -1);
if let Some(p) = (*panic).0.take() { if let Some(p) = (*panic).0.take() {
resume_unwind(p); resume_unwind(p);
} else { } else {
@@ -255,6 +261,41 @@ pub unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
ptr::read(ud) ptr::read(ud)
} }
// Pushes the userdata and attaches a metatable with __gc method
// Internally uses 5 stack spaces, does not call checkstack
pub unsafe fn push_gc_userdata<T: Any>(state: *mut ffi::lua_State, t: T) -> Result<()> {
push_meta_gc_userdata::<T, T>(state, t)
}
pub unsafe fn push_meta_gc_userdata<MT: Any, T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
let ud = protect_lua_closure(state, 0, 1, move |state| {
ffi::lua_newuserdata(state, mem::size_of::<T>()) as *mut T
})?;
ptr::write(ud, t);
get_gc_metatable_for::<MT>(state);
ffi::lua_setmetatable(state, -2);
Ok(())
}
// Uses 2 stack spaces, does not call checkstack
pub unsafe fn get_gc_userdata<T: Any>(state: *mut ffi::lua_State, index: c_int) -> *mut T {
get_meta_gc_userdata::<T, T>(state, index)
}
pub unsafe fn get_meta_gc_userdata<MT: Any, T>(state: *mut ffi::lua_State, index: c_int) -> *mut T {
let ud = ffi::lua_touserdata(state, index) as *mut T;
if ud.is_null() || ffi::lua_getmetatable(state, index) == 0 {
return ptr::null_mut();
}
get_gc_metatable_for::<MT>(state);
let res = ffi::lua_rawequal(state, -1, -2) != 0;
ffi::lua_pop(state, 2);
if !res {
return ptr::null_mut();
}
ud
}
// Populates the given table with the appropriate members to be a userdata metatable for the given // Populates the given table with the appropriate members to be a userdata metatable for the given
// type. This function takes the given table at the `metatable` index, and adds an appropriate __gc // type. This function takes the given table at the `metatable` index, and adds an appropriate __gc
// member to it for the given type and a __metatable entry to protect the table from script access. // member to it for the given type and a __metatable entry to protect the table from script access.
@@ -380,14 +421,14 @@ where
Ok(Err(err)) => { Ok(Err(err)) => {
ffi::lua_settop(state, 1); ffi::lua_settop(state, 1);
ptr::write(ud as *mut WrappedError, WrappedError(err)); ptr::write(ud as *mut WrappedError, WrappedError(err));
get_error_metatable(state); get_gc_metatable_for::<WrappedError>(state);
ffi::lua_setmetatable(state, -2); ffi::lua_setmetatable(state, -2);
ffi::lua_error(state) ffi::lua_error(state)
} }
Err(p) => { Err(p) => {
ffi::lua_settop(state, 1); ffi::lua_settop(state, 1);
ptr::write(ud as *mut WrappedPanic, WrappedPanic(Some(p))); ptr::write(ud as *mut WrappedPanic, WrappedPanic(Some(p)));
get_panic_metatable(state); get_gc_metatable_for::<WrappedPanic>(state);
ffi::lua_setmetatable(state, -2); ffi::lua_setmetatable(state, -2);
ffi::lua_error(state) ffi::lua_error(state)
} }
@@ -431,80 +472,104 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
cause: Arc::new(error), cause: Arc::new(error),
}), }),
); );
get_error_metatable(state); get_gc_metatable_for::<WrappedError>(state);
ffi::lua_setmetatable(state, -2); ffi::lua_setmetatable(state, -2);
} else if !is_wrapped_panic(state, -1) { } else if get_gc_userdata::<WrappedPanic>(state, -1).is_null()
if ffi::lua_checkstack(state, LUA_TRACEBACK_STACK) != 0 { && ffi::lua_checkstack(state, LUA_TRACEBACK_STACK) != 0
let s = ffi::luaL_tolstring(state, -1, ptr::null_mut()); {
ffi::luaL_traceback(state, state, s, 0); let s = ffi::luaL_tolstring(state, -1, ptr::null_mut());
ffi::lua_remove(state, -2); ffi::luaL_traceback(state, state, s, 0);
} ffi::lua_remove(state, -2);
} }
1 1
} }
// Does not call lua_checkstack, uses 2 stack spaces.
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub unsafe fn set_main_state(state: *mut ffi::lua_State) {
ffi::lua_pushlightuserdata(state, &MAIN_THREAD_REGISTRY_KEY as *const u8 as *mut c_void);
ffi::lua_pushthread(state);
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
}
// Does not call lua_checkstack, uses 1 stack space. // Does not call lua_checkstack, uses 1 stack space.
pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> *mut ffi::lua_State { pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua_State> {
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_MAINTHREAD); {
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_MAINTHREAD);
let main_state = ffi::lua_tothread(state, -1);
ffi::lua_pop(state, 1);
Some(main_state)
}
#[cfg(any(feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua51", feature = "luajit"))]
{ {
ffi::lua_pushlightuserdata(state, &MAIN_THREAD_REGISTRY_KEY as *const u8 as *mut c_void); // Check the current state first
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX); let is_main_state = ffi::lua_pushthread(state) == 1;
ffi::lua_pop(state, 1);
if is_main_state {
Some(state)
} else {
None
}
} }
let main_state = ffi::lua_tothread(state, -1);
ffi::lua_pop(state, 1);
main_state
} }
// Pushes a WrappedError to the top of the stack. Uses two stack spaces and does not call // Pushes a WrappedError to the top of the stack. Uses two stack spaces and does not call
// lua_checkstack. // lua_checkstack.
pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) -> Result<()> { pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) -> Result<()> {
let ud = protect_lua_closure(state, 0, 1, move |state| { push_gc_userdata::<WrappedError>(state, WrappedError(err))
ffi::lua_newuserdata(state, mem::size_of::<WrappedError>()) as *mut WrappedError
})?;
ptr::write(ud, WrappedError(err));
get_error_metatable(state);
ffi::lua_setmetatable(state, -2);
Ok(())
} }
// Checks if the value at the given index is a WrappedError, and if it is returns a pointer to it, // Checks if the value at the given index is a WrappedError, and if it is returns a pointer to it,
// otherwise returns null. Uses 2 stack spaces and does not call lua_checkstack. // otherwise returns null. Uses 2 stack spaces and does not call lua_checkstack.
pub unsafe fn get_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> *const Error { pub unsafe fn get_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> *const Error {
let userdata = ffi::lua_touserdata(state, index); let ud = get_gc_userdata::<WrappedError>(state, index);
if userdata.is_null() { if ud.is_null() {
return ptr::null(); return ptr::null();
} }
&(*ud).0
}
if ffi::lua_getmetatable(state, index) == 0 { // Initialize the internal (with __gc) metatable for a type T
return ptr::null(); pub unsafe fn init_gc_metatable_for<T: Any>(
state: *mut ffi::lua_State,
customize_fn: Option<fn(*mut ffi::lua_State)>,
) {
let type_id = TypeId::of::<T>();
let ref_addr = {
let mut mt_cache = mlua_expect!(METATABLE_CACHE.lock(), "cannot lock metatable cache");
mlua_assert!(
mt_cache.capacity() - mt_cache.len() > 0,
"out of metatable cache capacity"
);
mt_cache.insert(type_id, 0);
&mt_cache[&type_id] as *const u8
};
ffi::lua_newtable(state);
ffi::lua_pushstring(state, cstr!("__gc"));
ffi::lua_pushcfunction(state, userdata_destructor::<T>);
ffi::lua_rawset(state, -3);
ffi::lua_pushstring(state, cstr!("__metatable"));
ffi::lua_pushboolean(state, 0);
ffi::lua_rawset(state, -3);
if let Some(f) = customize_fn {
f(state)
} }
get_error_metatable(state); ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *mut c_void);
let res = ffi::lua_rawequal(state, -1, -2) != 0; }
ffi::lua_pop(state, 2);
if res { pub unsafe fn get_gc_metatable_for<T: Any>(state: *mut ffi::lua_State) {
&(*get_userdata::<WrappedError>(state, -1)).0 let type_id = TypeId::of::<T>();
} else { let ref_addr = {
ptr::null() let mt_cache = mlua_expect!(METATABLE_CACHE.lock(), "cannot lock metatable cache");
} mlua_expect!(mt_cache.get(&type_id), "gc metatable does not exist") as *const u8
};
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *mut c_void);
} }
// Initialize the error, panic, and destructed userdata metatables. // Initialize the error, panic, and destructed userdata metatables.
pub unsafe fn init_error_registry(state: *mut ffi::lua_State) { pub unsafe fn init_error_registry(state: *mut ffi::lua_State) {
assert_stack(state, 8); assert_stack(state, 8);
// Create error metatable // Create error and panic metatables
unsafe extern "C" fn error_tostring(state: *mut ffi::lua_State) -> c_int { unsafe extern "C" fn error_tostring(state: *mut ffi::lua_State) -> c_int {
let err_buf = callback_error(state, |_| { let err_buf = callback_error(state, |_| {
@@ -524,8 +589,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) {
// kind of recursive error structure?) // kind of recursive error structure?)
let _ = write!(&mut (*err_buf), "{}", error); let _ = write!(&mut (*err_buf), "{}", error);
Ok(err_buf) Ok(err_buf)
} else if is_wrapped_panic(state, -1) { } else if let Some(panic) = get_gc_userdata::<WrappedPanic>(state, -1).as_ref() {
let panic = get_userdata::<WrappedPanic>(state, -1);
if let Some(ref p) = (*panic).0 { if let Some(ref p) = (*panic).0 {
ffi::lua_pushlightuserdata( ffi::lua_pushlightuserdata(
state, state,
@@ -564,56 +628,31 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) {
1 1
} }
ffi::lua_pushlightuserdata( init_gc_metatable_for::<WrappedError>(
state, state,
&ERROR_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void, Some(|state| {
ffi::lua_pushstring(state, cstr!("__tostring"));
ffi::lua_pushcfunction(state, error_tostring);
ffi::lua_rawset(state, -3);
}),
); );
ffi::lua_newtable(state);
ffi::lua_pushstring(state, cstr!("__gc")); init_gc_metatable_for::<WrappedPanic>(
ffi::lua_pushcfunction(state, userdata_destructor::<WrappedError>);
ffi::lua_rawset(state, -3);
ffi::lua_pushstring(state, cstr!("__tostring"));
ffi::lua_pushcfunction(state, error_tostring);
ffi::lua_rawset(state, -3);
ffi::lua_pushstring(state, cstr!("__metatable"));
ffi::lua_pushboolean(state, 0);
ffi::lua_rawset(state, -3);
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
// Create panic metatable
ffi::lua_pushlightuserdata(
state, state,
&PANIC_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void, Some(|state| {
ffi::lua_pushstring(state, cstr!("__tostring"));
ffi::lua_pushcfunction(state, error_tostring);
ffi::lua_rawset(state, -3);
}),
); );
ffi::lua_newtable(state);
ffi::lua_pushstring(state, cstr!("__gc"));
ffi::lua_pushcfunction(state, userdata_destructor::<WrappedPanic>);
ffi::lua_rawset(state, -3);
ffi::lua_pushstring(state, cstr!("__tostring"));
ffi::lua_pushcfunction(state, error_tostring);
ffi::lua_rawset(state, -3);
ffi::lua_pushstring(state, cstr!("__metatable"));
ffi::lua_pushboolean(state, 0);
ffi::lua_rawset(state, -3);
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
// Create destructed userdata metatable // Create destructed userdata metatable
unsafe extern "C" fn destructed_error(state: *mut ffi::lua_State) -> c_int { unsafe extern "C" fn destructed_error(state: *mut ffi::lua_State) -> c_int {
ffi::luaL_checkstack(state, 2, ptr::null()); ffi::luaL_checkstack(state, 2, ptr::null());
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedError>()) as *mut WrappedError; let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedError>()) as *mut WrappedError;
ptr::write(ud, WrappedError(Error::CallbackDestructed)); ptr::write(ud, WrappedError(Error::CallbackDestructed));
get_error_metatable(state); get_gc_metatable_for::<WrappedError>(state);
ffi::lua_setmetatable(state, -2); ffi::lua_setmetatable(state, -2);
ffi::lua_error(state) ffi::lua_error(state)
} }
@@ -632,12 +671,19 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) {
cstr!("__mod"), cstr!("__mod"),
cstr!("__pow"), cstr!("__pow"),
cstr!("__unm"), cstr!("__unm"),
#[cfg(any(feature = "lua54", feature = "lua53"))]
cstr!("__idiv"), cstr!("__idiv"),
#[cfg(any(feature = "lua54", feature = "lua53"))]
cstr!("__band"), cstr!("__band"),
#[cfg(any(feature = "lua54", feature = "lua53"))]
cstr!("__bor"), cstr!("__bor"),
#[cfg(any(feature = "lua54", feature = "lua53"))]
cstr!("__bxor"), cstr!("__bxor"),
#[cfg(any(feature = "lua54", feature = "lua53"))]
cstr!("__bnot"), cstr!("__bnot"),
#[cfg(any(feature = "lua54", feature = "lua53"))]
cstr!("__shl"), cstr!("__shl"),
#[cfg(any(feature = "lua54", feature = "lua53"))]
cstr!("__shr"), cstr!("__shr"),
cstr!("__concat"), cstr!("__concat"),
cstr!("__len"), cstr!("__len"),
@@ -648,8 +694,12 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) {
cstr!("__newindex"), cstr!("__newindex"),
cstr!("__call"), cstr!("__call"),
cstr!("__tostring"), cstr!("__tostring"),
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
cstr!("__pairs"), cstr!("__pairs"),
#[cfg(any(feature = "lua53", feature = "lua52"))]
cstr!("__ipairs"), cstr!("__ipairs"),
#[cfg(feature = "lua54")]
cstr!("__close"),
] { ] {
ffi::lua_pushstring(state, method); ffi::lua_pushstring(state, method);
ffi::lua_pushcfunction(state, destructed_error); ffi::lua_pushcfunction(state, destructed_error);
@@ -709,40 +759,6 @@ unsafe fn to_string<'a>(state: *mut ffi::lua_State, index: c_int) -> Cow<'a, str
} }
} }
// Checks if the value at the given index is a WrappedPanic. Uses 2 stack spaces and does not call
// lua_checkstack.
unsafe fn is_wrapped_panic(state: *mut ffi::lua_State, index: c_int) -> bool {
let userdata = ffi::lua_touserdata(state, index);
if userdata.is_null() {
return false;
}
if ffi::lua_getmetatable(state, index) == 0 {
return false;
}
get_panic_metatable(state);
let res = ffi::lua_rawequal(state, -1, -2) != 0;
ffi::lua_pop(state, 2);
res
}
unsafe fn get_error_metatable(state: *mut ffi::lua_State) {
ffi::lua_pushlightuserdata(
state,
&ERROR_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
);
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
}
unsafe fn get_panic_metatable(state: *mut ffi::lua_State) {
ffi::lua_pushlightuserdata(
state,
&PANIC_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
);
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
}
unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) { unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) {
ffi::lua_pushlightuserdata( ffi::lua_pushlightuserdata(
state, state,
@@ -751,9 +767,5 @@ unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) {
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX); ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
} }
#[cfg(any(feature = "lua51", feature = "luajit"))]
static MAIN_THREAD_REGISTRY_KEY: u8 = 0;
static ERROR_METATABLE_REGISTRY_KEY: u8 = 0;
static PANIC_METATABLE_REGISTRY_KEY: u8 = 0;
static DESTRUCTED_USERDATA_METATABLE: u8 = 0; static DESTRUCTED_USERDATA_METATABLE: u8 = 0;
static ERROR_PRINT_BUFFER_KEY: u8 = 0; static ERROR_PRINT_BUFFER_KEY: u8 = 0;
+5 -4
View File
@@ -48,18 +48,19 @@ pub enum Value<'lua> {
pub use self::Value::Nil; pub use self::Value::Nil;
impl<'lua> Value<'lua> { impl<'lua> Value<'lua> {
pub(crate) fn type_name(&self) -> &'static str { pub fn type_name(&self) -> &'static str {
match *self { match *self {
Value::Nil => "nil", Value::Nil => "nil",
Value::Boolean(_) => "boolean", Value::Boolean(_) => "boolean",
Value::LightUserData(_) => "light userdata", Value::LightUserData(_) => "lightuserdata",
Value::Integer(_) => "integer", Value::Integer(_) => "integer",
Value::Number(_) => "number", Value::Number(_) => "number",
Value::String(_) => "string", Value::String(_) => "string",
Value::Table(_) => "table", Value::Table(_) => "table",
Value::Function(_) => "function", Value::Function(_) => "function",
Value::Thread(_) => "thread", Value::Thread(_) => "thread",
Value::UserData(_) | Value::Error(_) => "userdata", Value::UserData(_) => "userdata",
Value::Error(_) => "error",
} }
} }
@@ -158,7 +159,7 @@ impl<'a, 'lua> IntoIterator for &'a MultiValue<'lua> {
type IntoIter = iter::Rev<slice::Iter<'a, Value<'lua>>>; type IntoIter = iter::Rev<slice::Iter<'a, Value<'lua>>>;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
(&self.0).into_iter().rev() (&self.0).iter().rev()
} }
} }
+413
View File
@@ -0,0 +1,413 @@
#![cfg(feature = "async")]
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000",
allow(unused_attributes)
)]
extern "system" {}
use std::cell::Cell;
use std::rc::Rc;
use std::sync::{
atomic::{AtomicI64, Ordering},
Arc,
};
use std::time::Duration;
use futures_timer::Delay;
use futures_util::stream::TryStreamExt;
use mlua::{Error, Function, Lua, Result, Table, TableExt, UserData, UserDataMethods};
#[tokio::test]
async fn test_async_function() -> Result<()> {
let lua = Lua::new();
let f = lua
.create_async_function(|_lua, (a, b, c): (i64, i64, i64)| async move { Ok((a + b) * c) })?;
lua.globals().set("f", f)?;
let res: i64 = lua.load("f(1, 2, 3)").eval_async().await?;
assert_eq!(res, 9);
Ok(())
}
#[tokio::test]
async fn test_async_sleep() -> Result<()> {
let lua = Lua::new();
let sleep = lua.create_async_function(move |_lua, n: u64| async move {
Delay::new(Duration::from_millis(n)).await;
Ok(format!("elapsed:{}ms", n))
})?;
lua.globals().set("sleep", sleep)?;
let res: String = lua.load(r"return sleep(...)").call_async(100).await?;
assert_eq!(res, "elapsed:100ms");
Ok(())
}
#[tokio::test]
async fn test_async_call() -> Result<()> {
let lua = Lua::new();
let hello = lua.create_async_function(|_lua, name: String| async move {
Delay::new(Duration::from_millis(10)).await;
Ok(format!("hello, {}!", name))
})?;
match hello.call::<_, ()>("alex") {
Err(Error::RuntimeError(_)) => {}
_ => panic!(
"non-async executing async function must fail on the yield stage with RuntimeError"
),
};
assert_eq!(hello.call_async::<_, String>("alex").await?, "hello, alex!");
// Executing non-async functions using async call is allowed
let sum = lua.create_function(|_lua, (a, b): (i64, i64)| return Ok(a + b))?;
assert_eq!(sum.call_async::<_, i64>((5, 1)).await?, 6);
Ok(())
}
#[tokio::test]
async fn test_async_bind_call() -> Result<()> {
let lua = Lua::new();
let sum = lua.create_async_function(|_lua, (a, b): (i64, i64)| async move { Ok(a + b) })?;
let plus_10 = sum.bind(10)?;
lua.globals().set("plus_10", plus_10)?;
assert_eq!(lua.load("plus_10(-1)").eval_async::<i64>().await?, 9);
assert_eq!(lua.load("plus_10(1)").eval_async::<i64>().await?, 11);
Ok(())
}
#[tokio::test]
async fn test_async_handle_yield() -> Result<()> {
let lua = Lua::new();
let sum = lua.create_async_function(|_lua, (a, b): (i64, i64)| async move {
Delay::new(Duration::from_millis(10)).await;
Ok(a + b)
})?;
lua.globals().set("sleep_sum", sum)?;
let res: String = lua
.load(
r#"
sum = sleep_sum(6, 7)
assert(sum == 13)
coroutine.yield("in progress")
return "done"
"#,
)
.call_async(())
.await?;
assert_eq!(res, "done");
let min = lua
.load(
r#"
function (a, b)
coroutine.yield("ignore me")
if a < b then return a else return b end
end
"#,
)
.eval::<Function>()?;
assert_eq!(min.call_async::<_, i64>((-1, 1)).await?, -1);
Ok(())
}
#[tokio::test]
async fn test_async_return_async_closure() -> Result<()> {
let lua = Lua::new();
let f = lua.create_async_function(|lua, a: i64| async move {
Delay::new(Duration::from_millis(10)).await;
let g = lua.create_async_function(move |_, b: i64| async move {
Delay::new(Duration::from_millis(10)).await;
return Ok(a + b);
})?;
Ok(g)
})?;
lua.globals().set("f", f)?;
let res: i64 = lua
.load("local g = f(1); return g(2) + g(3)")
.call_async(())
.await?;
assert_eq!(res, 7);
Ok(())
}
#[tokio::test]
async fn test_async_thread_stream() -> Result<()> {
let lua = Lua::new();
let thread = lua.create_thread(
lua.load(
r#"
function (sum)
for i = 1,10 do
sum = sum + i
coroutine.yield(sum)
end
return sum
end
"#,
)
.eval()?,
)?;
let mut stream = thread.into_async::<_, i64>(1);
let mut sum = 0;
while let Some(n) = stream.try_next().await? {
sum += n;
}
assert_eq!(sum, 286);
Ok(())
}
#[tokio::test]
async fn test_async_thread() -> Result<()> {
let lua = Lua::new();
let cnt = Arc::new(10); // sleep 10ms
let cnt2 = cnt.clone();
let f = lua.create_async_function(move |_lua, ()| {
let cnt3 = cnt2.clone();
async move {
Delay::new(Duration::from_millis(*cnt3.as_ref())).await;
Ok("done")
}
})?;
let res: String = lua.create_thread(f)?.into_async(()).await?;
assert_eq!(res, "done");
assert_eq!(Arc::strong_count(&cnt), 2);
lua.gc_collect()?; // thread_s is non-resumable and subject to garbage collection
assert_eq!(Arc::strong_count(&cnt), 1);
Ok(())
}
#[tokio::test]
async fn test_async_table() -> Result<()> {
let lua = Lua::new();
let table = lua.create_table()?;
table.set("val", 10)?;
let get_value = lua.create_async_function(|_, table: Table| async move {
Delay::new(Duration::from_millis(10)).await;
table.get::<_, i64>("val")
})?;
table.set("get_value", get_value)?;
let set_value = lua.create_async_function(|_, (table, n): (Table, i64)| async move {
Delay::new(Duration::from_millis(10)).await;
table.set("val", n)
})?;
table.set("set_value", set_value)?;
let sleep = lua.create_async_function(|_, n| async move {
Delay::new(Duration::from_millis(n)).await;
Ok(format!("elapsed:{}ms", n))
})?;
table.set("sleep", sleep)?;
assert_eq!(
table
.call_async_method::<_, _, i64>("get_value", ())
.await?,
10
);
table.call_async_method("set_value", 15).await?;
assert_eq!(
table
.call_async_method::<_, _, i64>("get_value", ())
.await?,
15
);
assert_eq!(
table
.call_async_function::<_, _, String>("sleep", 7)
.await?,
"elapsed:7ms"
);
Ok(())
}
#[tokio::test]
async fn test_async_userdata() -> Result<()> {
#[derive(Clone)]
struct MyUserData(Arc<AtomicI64>);
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("get_value", |_, data, ()| async move {
Delay::new(Duration::from_millis(10)).await;
Ok(data.0.load(Ordering::Relaxed))
});
methods.add_async_method("set_value", |_, data, n| async move {
Delay::new(Duration::from_millis(10)).await;
data.0.store(n, Ordering::Relaxed);
Ok(())
});
methods.add_async_function("sleep", |_, n| async move {
Delay::new(Duration::from_millis(n)).await;
Ok(format!("elapsed:{}ms", n))
});
}
}
let lua = Lua::new();
let globals = lua.globals();
let userdata = lua.create_userdata(MyUserData(Arc::new(AtomicI64::new(11))))?;
globals.set("userdata", userdata.clone())?;
lua.load(
r#"
assert(userdata:get_value() == 11)
userdata:set_value(12)
assert(userdata.sleep(5) == "elapsed:5ms")
assert(userdata:get_value() == 12)
"#,
)
.exec_async()
.await?;
Ok(())
}
#[tokio::test]
async fn test_async_scope() -> Result<()> {
let ref lua = Lua::new();
let ref rc = Rc::new(Cell::new(0));
let fut = lua.async_scope(|scope| async move {
let f = scope.create_async_function(move |_, n: u64| {
let rc2 = rc.clone();
async move {
rc2.set(42);
Delay::new(Duration::from_millis(n)).await;
assert_eq!(Rc::strong_count(&rc2), 2);
Ok(())
}
})?;
lua.globals().set("f", f.clone())?;
assert_eq!(Rc::strong_count(rc), 1);
let _ = f.call_async::<u64, ()>(10).await?;
assert_eq!(Rc::strong_count(rc), 1);
Ok(())
});
assert_eq!(Rc::strong_count(rc), 1);
let _ = fut.await?;
match lua
.globals()
.get::<_, Function>("f")?
.call_async::<_, ()>(10)
.await
{
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
Error::CallbackDestructed => {}
e => panic!("expected `CallbackDestructed` error cause, got {:?}", e),
},
r => panic!("improper return for destructed function: {:?}", r),
};
Ok(())
}
#[tokio::test]
async fn test_async_scope_userdata() -> Result<()> {
#[derive(Clone)]
struct MyUserData(Arc<AtomicI64>);
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("get_value", |_, data, ()| async move {
Delay::new(Duration::from_millis(10)).await;
Ok(data.0.load(Ordering::Relaxed))
});
methods.add_async_method("set_value", |_, data, n| async move {
Delay::new(Duration::from_millis(10)).await;
data.0.store(n, Ordering::Relaxed);
Ok(())
});
methods.add_async_function("sleep", |_, n| async move {
Delay::new(Duration::from_millis(n)).await;
Ok(format!("elapsed:{}ms", n))
});
}
}
let ref lua = Lua::new();
let ref arc = Arc::new(AtomicI64::new(11));
lua.async_scope(|scope| async move {
let ud = scope.create_userdata(MyUserData(arc.clone()))?;
lua.globals().set("userdata", ud)?;
lua.load(
r#"
assert(userdata:get_value() == 11)
userdata:set_value(12)
assert(userdata.sleep(5) == "elapsed:5ms")
assert(userdata:get_value() == 12)
"#,
)
.exec_async()
.await
})
.await?;
assert_eq!(Arc::strong_count(arc), 1);
match lua.load("userdata:get_value()").exec_async().await {
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
Error::CallbackDestructed => {}
e => panic!("expected `CallbackDestructed` error cause, got {:?}", e),
},
r => panic!("improper return for destructed userdata: {:?}", r),
};
Ok(())
}
+12
View File
@@ -1,3 +1,15 @@
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000",
allow(unused_attributes)
)]
extern "system" {}
use bstr::{BStr, BString}; use bstr::{BStr, BString};
use mlua::{Lua, Result}; use mlua::{Lua, Result};
+24
View File
@@ -0,0 +1,24 @@
#[test]
#[ignore]
fn test_compilation() {
let t = trybuild::TestCases::new();
t.compile_fail("tests/compile/function_borrow.rs");
t.compile_fail("tests/compile/lua_norefunwindsafe.rs");
t.compile_fail("tests/compile/ref_nounwindsafe.rs");
t.compile_fail("tests/compile/scope_callback_capture.rs");
t.compile_fail("tests/compile/scope_callback_inner.rs");
t.compile_fail("tests/compile/scope_callback_outer.rs");
t.compile_fail("tests/compile/scope_invariance.rs");
t.compile_fail("tests/compile/scope_mutable_aliasing.rs");
t.compile_fail("tests/compile/scope_userdata_borrow.rs");
t.compile_fail("tests/compile/static_callback_args.rs");
#[cfg(feature = "async")]
t.compile_fail("tests/compile/async_nonstatic_userdata.rs");
#[cfg(feature = "send")]
t.compile_fail("tests/compile/non_send.rs");
#[cfg(not(feature = "send"))]
t.pass("tests/compile/non_send.rs");
}
+17
View File
@@ -0,0 +1,17 @@
use mlua::{Lua, UserData, UserDataMethods};
fn main() {
let ref lua = Lua::new();
#[derive(Clone)]
struct MyUserData<'a>(&'a i64);
impl<'a> UserData for MyUserData<'a> {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("print", |_, data, ()| async move {
println!("{}", data.0);
Ok(())
});
}
}
}
@@ -0,0 +1,36 @@
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> $DIR/async_nonstatic_userdata.rs:11:72
|
11 | methods.add_async_method("print", |_, data, ()| async move {
| ________________________________________________________________________^
12 | | println!("{}", data.0);
13 | | Ok(())
14 | | });
| |_____________^
|
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the impl at 9:10...
--> $DIR/async_nonstatic_userdata.rs:9:10
|
9 | impl<'a> UserData for MyUserData<'a> {
| ^^
note: ...so that the types are compatible
--> $DIR/async_nonstatic_userdata.rs:11:72
|
11 | methods.add_async_method("print", |_, data, ()| async move {
| ________________________________________________________________________^
12 | | println!("{}", data.0);
13 | | Ok(())
14 | | });
| |_____________^
= note: expected `main::MyUserData<'_>`
found `main::MyUserData<'a>`
note: but, the lifetime must be valid for the lifetime `'lua` as defined on the method body at 10:24...
--> $DIR/async_nonstatic_userdata.rs:10:24
|
10 | fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
| ^^^^
note: ...so that the type `impl std::future::Future` will meet its required lifetime bounds
--> $DIR/async_nonstatic_userdata.rs:11:21
|
11 | methods.add_async_method("print", |_, data, ()| async move {
| ^^^^^^^^^^^^^^^^
+11
View File
@@ -0,0 +1,11 @@
error[E0277]: the type `std::cell::UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> $DIR/lua_norefunwindsafe.rs:7:5
|
7 | catch_unwind(|| lua.create_table().unwrap());
| ^^^^^^^^^^^^ `std::cell::UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<()>`
= note: required because it appears within the type `std::marker::PhantomData<std::cell::UnsafeCell<()>>`
= note: required because it appears within the type `mlua::lua::Lua`
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
= note: required because it appears within the type `[closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:48 lua:&mlua::lua::Lua]`
+17
View File
@@ -0,0 +1,17 @@
use std::cell::Cell;
use std::rc::Rc;
use mlua::{Lua, Result};
fn main() -> Result<()> {
let lua = Lua::new();
let data = Rc::new(Cell::new(0));
lua.create_function(move |_, ()| {
Ok(data.get())
})?
.call::<_, i32>(())?;
Ok(())
}
+14
View File
@@ -0,0 +1,14 @@
error[E0277]: `std::rc::Rc<std::cell::Cell<i32>>` cannot be sent between threads safely
--> $DIR/non_send.rs:11:9
|
11 | lua.create_function(move |_, ()| {
| _________^^^^^^^^^^^^^^^_-
| | |
| | `std::rc::Rc<std::cell::Cell<i32>>` cannot be sent between threads safely
12 | | Ok(data.get())
13 | | })?
| |_____- within this `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6 data:std::rc::Rc<std::cell::Cell<i32>>]`
|
= help: within `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6 data:std::rc::Rc<std::cell::Cell<i32>>]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<std::cell::Cell<i32>>`
= note: required because it appears within the type `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6 data:std::rc::Rc<std::cell::Cell<i32>>]`
= note: required because of the requirements on the impl of `mlua::types::MaybeSend` for `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6 data:std::rc::Rc<std::cell::Cell<i32>>]`
+13
View File
@@ -0,0 +1,13 @@
error[E0277]: the type `std::cell::UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> $DIR/ref_nounwindsafe.rs:8:5
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ^^^^^^^^^^^^ `std::cell::UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<()>`
= note: required because it appears within the type `std::marker::PhantomData<std::cell::UnsafeCell<()>>`
= note: required because it appears within the type `mlua::lua::Lua`
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
= note: required because it appears within the type `mlua::table::Table<'_>`
= note: required because it appears within the type `[closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:54 table:mlua::table::Table<'_>]`
@@ -1,15 +1,11 @@
use mlua::{Lua, Table, Result}; use mlua::{Lua, Table};
struct Test {
field: i32,
}
fn main() { fn main() {
let lua = Lua::new(); let lua = Lua::new();
lua.scope(|scope| -> Result<()> { lua.scope(|scope| {
let mut inner: Option<Table> = None; let mut inner: Option<Table> = None;
let f = scope let f = scope
.create_function_mut(move |lua, t: Table| { .create_function_mut(move |_, t: Table| {
if let Some(old) = inner.take() { if let Some(old) = inner.take() {
// Access old callback `Lua`. // Access old callback `Lua`.
} }
@@ -0,0 +1,26 @@
warning: unused variable: `old`
--> $DIR/scope_callback_capture.rs:9:29
|
9 | if let Some(old) = inner.take() {
| ^^^ help: if this is intentional, prefix it with an underscore: `_old`
|
= note: `#[warn(unused_variables)]` on by default
error[E0521]: borrowed data escapes outside of closure
--> $DIR/scope_callback_capture.rs:7:17
|
5 | lua.scope(|scope| {
| -----
| |
| `scope` declared here, outside of the closure body
| `scope` is a reference that is only valid in the closure body
6 | let mut inner: Option<Table> = None;
7 | let f = scope
| _________________^
8 | | .create_function_mut(move |_, t: Table| {
9 | | if let Some(old) = inner.take() {
10 | | // Access old callback `Lua`.
... |
13 | | Ok(())
14 | | })?;
| |______________^ `scope` escapes the closure body here
@@ -1,12 +1,8 @@
use mlua::{Lua, Table, Result}; use mlua::{Lua, Table};
struct Test {
field: i32,
}
fn main() { fn main() {
let lua = Lua::new(); let lua = Lua::new();
lua.scope(|scope| -> Result<()> { lua.scope(|scope| {
let mut inner: Option<Table> = None; let mut inner: Option<Table> = None;
let f = scope let f = scope
.create_function_mut(|_, t: Table| { .create_function_mut(|_, t: Table| {
+42
View File
@@ -0,0 +1,42 @@
error[E0521]: borrowed data escapes outside of closure
--> $DIR/scope_callback_inner.rs:7:17
|
5 | lua.scope(|scope| {
| -----
| |
| `scope` declared here, outside of the closure body
| `scope` is a reference that is only valid in the closure body
6 | let mut inner: Option<Table> = None;
7 | let f = scope
| _________________^
8 | | .create_function_mut(|_, t: Table| {
9 | | inner = Some(t);
10 | | Ok(())
11 | | })?;
| |______________^ `scope` escapes the closure body here
error[E0373]: closure may outlive the current function, but it borrows `inner`, which is owned by the current function
--> $DIR/scope_callback_inner.rs:8:34
|
5 | lua.scope(|scope| {
| ----- has type `&mlua::scope::Scope<'_, '2>`
...
8 | .create_function_mut(|_, t: Table| {
| ^^^^^^^^^^^^^ may outlive borrowed value `inner`
9 | inner = Some(t);
| ----- `inner` is borrowed here
|
note: function requires argument type to outlive `'2`
--> $DIR/scope_callback_inner.rs:7:17
|
7 | let f = scope
| _________________^
8 | | .create_function_mut(|_, t: Table| {
9 | | inner = Some(t);
10 | | Ok(())
11 | | })?;
| |______________^
help: to force the closure to take ownership of `inner` (and any other referenced variables), use the `move` keyword
|
8 | .create_function_mut(move |_, t: Table| {
| ^^^^^^^^^^^^^^^^^^
@@ -1,13 +1,9 @@
use mlua::{Lua, Table, Result}; use mlua::{Lua, Table};
struct Test {
field: i32,
}
fn main() { fn main() {
let lua = Lua::new(); let lua = Lua::new();
let mut outer: Option<Table> = None; let mut outer: Option<Table> = None;
lua.scope(|scope| -> Result<()> { lua.scope(|scope| {
let f = scope let f = scope
.create_function_mut(|_, t: Table| { .create_function_mut(|_, t: Table| {
outer = Some(t); outer = Some(t);
+30
View File
@@ -0,0 +1,30 @@
error[E0521]: borrowed data escapes outside of closure
--> $DIR/scope_callback_outer.rs:7:17
|
6 | lua.scope(|scope| {
| -----
| |
| `scope` declared here, outside of the closure body
| `scope` is a reference that is only valid in the closure body
7 | let f = scope
| _________________^
8 | | .create_function_mut(|_, t: Table| {
9 | | outer = Some(t);
10 | | Ok(())
11 | | })?;
| |______________^ `scope` escapes the closure body here
error[E0597]: `outer` does not live long enough
--> $DIR/scope_callback_outer.rs:9:17
|
6 | lua.scope(|scope| {
| ------- value captured here
...
9 | outer = Some(t);
| ^^^^^ borrowed value does not live long enough
...
15 | }
| -
| |
| `outer` dropped here while still borrowed
| borrow might be used here, when `outer` is dropped and runs the destructor for type `std::option::Option<mlua::table::Table<'_>>`
@@ -1,4 +1,4 @@
use mlua::{Lua, Result}; use mlua::Lua;
struct Test { struct Test {
field: i32, field: i32,
@@ -6,7 +6,7 @@ struct Test {
fn main() { fn main() {
let lua = Lua::new(); let lua = Lua::new();
lua.scope(|scope| -> Result<()> { lua.scope(|scope| {
let f = { let f = {
let mut test = Test { field: 0 }; let mut test = Test { field: 0 };
@@ -1,7 +1,7 @@
error[E0373]: closure may outlive the current function, but it borrows `test`, which is owned by the current function error[E0373]: closure may outlive the current function, but it borrows `test`, which is owned by the current function
--> $DIR/scope_invariance.rs:14:38 --> $DIR/scope_invariance.rs:14:38
| |
9 | lua.scope(|scope| -> Result<()> { 9 | lua.scope(|scope| {
| ----- has type `&mlua::scope::Scope<'_, '1>` | ----- has type `&mlua::scope::Scope<'_, '1>`
... ...
14 | .create_function_mut(|_, ()| { 14 | .create_function_mut(|_, ()| {
@@ -1,15 +1,15 @@
use mlua::{Lua, UserData, Result}; use mlua::{Lua, UserData};
struct MyUserData<'a>(&'a mut i32);
impl<'a> UserData for MyUserData<'a> {}
fn main() { fn main() {
struct MyUserData<'a>(&'a mut i32);
impl<'a> UserData for MyUserData<'a> {};
let mut i = 1; let mut i = 1;
let lua = Lua::new(); let lua = Lua::new();
lua.scope(|scope| -> Result<()> { lua.scope(|scope| {
let _a = scope.create_nonstatic_userdata(MyUserData(&mut i))?; let _a = scope.create_nonstatic_userdata(MyUserData(&mut i)).unwrap();
let _b = scope.create_nonstatic_userdata(MyUserData(&mut i))?; let _b = scope.create_nonstatic_userdata(MyUserData(&mut i)).unwrap();
Ok(()) Ok(())
}); });
} }
@@ -1,9 +1,9 @@
error[E0499]: cannot borrow `i` as mutable more than once at a time error[E0499]: cannot borrow `i` as mutable more than once at a time
--> $DIR/scope_mutable_aliasing.rs:12:61 --> $DIR/scope_mutable_aliasing.rs:12:61
| |
11 | let _a = scope.create_nonstatic_userdata(MyUserData(&mut i))?; 11 | let _a = scope.create_nonstatic_userdata(MyUserData(&mut i)).unwrap();
| ------ first mutable borrow occurs here | ------ first mutable borrow occurs here
12 | let _b = scope.create_nonstatic_userdata(MyUserData(&mut i))?; 12 | let _b = scope.create_nonstatic_userdata(MyUserData(&mut i)).unwrap();
| ------------------------- ^^^^^^ second mutable borrow occurs here | ------------------------- ^^^^^^ second mutable borrow occurs here
| | | |
| first borrow later used by call | first borrow later used by call
@@ -1,19 +1,18 @@
use mlua::{Lua, UserData, Result}; use mlua::{Lua, UserData};
struct MyUserData<'a>(&'a i32);
impl<'a> UserData for MyUserData<'a> {}
fn main() { fn main() {
// Should not allow userdata borrow to outlive lifetime of AnyUserData handle // Should not allow userdata borrow to outlive lifetime of AnyUserData handle
struct MyUserData<'a>(&'a i32);
impl<'a> UserData for MyUserData<'a> {};
let igood = 1; let igood = 1;
let lua = Lua::new(); let lua = Lua::new();
lua.scope(|scope| -> Result<()> { lua.scope(|scope| {
let _ugood = scope.create_nonstatic_userdata(MyUserData(&igood))?; let _ugood = scope.create_nonstatic_userdata(MyUserData(&igood)).unwrap();
let _ubad = { let _ubad = {
let ibad = 42; let ibad = 42;
scope.create_nonstatic_userdata(MyUserData(&ibad))?; scope.create_nonstatic_userdata(MyUserData(&ibad)).unwrap();
}; };
Ok(()) Ok(())
}); });
@@ -1,13 +1,13 @@
error[E0597]: `ibad` does not live long enough error[E0597]: `ibad` does not live long enough
--> $DIR/scope_userdata_borrow.rs:16:56 --> $DIR/scope_userdata_borrow.rs:15:56
| |
12 | lua.scope(|scope| -> Result<()> { 11 | lua.scope(|scope| {
| ----- has type `&mlua::scope::Scope<'_, '1>` | ----- has type `&mlua::scope::Scope<'_, '1>`
... ...
16 | scope.create_nonstatic_userdata(MyUserData(&ibad))?; 15 | scope.create_nonstatic_userdata(MyUserData(&ibad)).unwrap();
| -------------------------------------------^^^^^-- | -------------------------------------------^^^^^--
| | | | | |
| | borrowed value does not live long enough | | borrowed value does not live long enough
| argument requires that `ibad` is borrowed for `'1` | argument requires that `ibad` is borrowed for `'1`
17 | }; 16 | };
| - `ibad` dropped here while still borrowed | - `ibad` dropped here while still borrowed
+32
View File
@@ -0,0 +1,32 @@
use std::cell::RefCell;
use mlua::{Lua, Result, Table};
fn main() -> Result<()> {
thread_local! {
static BAD_TIME: RefCell<Option<Table<'static>>> = RefCell::new(None);
}
let lua = Lua::new();
lua.create_function(|_, table: Table| {
BAD_TIME.with(|bt| {
*bt.borrow_mut() = Some(table);
});
Ok(())
})?
.call::<_, ()>(lua.create_table()?)?;
// In debug, this will panic with a reference leak before getting to the next part but
// it segfaults anyway.
drop(lua);
BAD_TIME.with(|bt| {
println!(
"you're gonna have a bad time: {}",
bt.borrow().as_ref().unwrap().len().unwrap()
);
});
Ok(())
}
+35
View File
@@ -0,0 +1,35 @@
error[E0597]: `lua` does not live long enough
--> $DIR/static_callback_args.rs:12:5
|
12 | lua.create_function(|_, table: Table| {
| -^^
| |
| _____borrowed value does not live long enough
| |
13 | | BAD_TIME.with(|bt| {
14 | | *bt.borrow_mut() = Some(table);
15 | | });
16 | | Ok(())
17 | | })?
| |______- argument requires that `lua` is borrowed for `'static`
...
32 | }
| - `lua` dropped here while still borrowed
error[E0505]: cannot move out of `lua` because it is borrowed
--> $DIR/static_callback_args.rs:22:10
|
12 | lua.create_function(|_, table: Table| {
| ---
| |
| _____borrow of `lua` occurs here
| |
13 | | BAD_TIME.with(|bt| {
14 | | *bt.borrow_mut() = Some(table);
15 | | });
16 | | Ok(())
17 | | })?
| |______- argument requires that `lua` is borrowed for `'static`
...
22 | drop(lua);
| ^^^ move out of `lua` occurs here
-6
View File
@@ -1,6 +0,0 @@
#[test]
#[ignore]
fn test_compile_fail() {
let t = trybuild::TestCases::new();
t.compile_fail("tests/compile_fail/*.rs");
}
@@ -1,42 +0,0 @@
error[E0277]: the type `std::cell::UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> $DIR/lua_norefunwindsafe.rs:7:5
|
7 | catch_unwind(|| lua.create_table().unwrap());
| ^^^^^^^^^^^^ `std::cell::UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<()>`
= note: required because it appears within the type `std::marker::PhantomData<std::cell::UnsafeCell<()>>`
= note: required because it appears within the type `mlua::lua::Lua`
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
= note: required because it appears within the type `[closure@$DIR/tests/compile_fail/lua_norefunwindsafe.rs:7:18: 7:48 lua:&mlua::lua::Lua]`
error[E0277]: the type `std::cell::UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> $DIR/lua_norefunwindsafe.rs:7:5
|
7 | catch_unwind(|| lua.create_table().unwrap());
| ^^^^^^^^^^^^ `std::cell::UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<mlua::lua::ExtraData>`
= note: required because it appears within the type `std::cell::RefCell<mlua::lua::ExtraData>`
= note: required because it appears within the type `alloc::sync::ArcInner<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `std::marker::PhantomData<alloc::sync::ArcInner<std::cell::RefCell<mlua::lua::ExtraData>>>`
= note: required because it appears within the type `std::sync::Arc<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `mlua::lua::Lua`
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
= note: required because it appears within the type `[closure@$DIR/tests/compile_fail/lua_norefunwindsafe.rs:7:18: 7:48 lua:&mlua::lua::Lua]`
error[E0277]: the type `std::cell::UnsafeCell<isize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> $DIR/lua_norefunwindsafe.rs:7:5
|
7 | catch_unwind(|| lua.create_table().unwrap());
| ^^^^^^^^^^^^ `std::cell::UnsafeCell<isize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<isize>`
= note: required because it appears within the type `std::cell::Cell<isize>`
= note: required because it appears within the type `std::cell::RefCell<mlua::lua::ExtraData>`
= note: required because it appears within the type `alloc::sync::ArcInner<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `std::marker::PhantomData<alloc::sync::ArcInner<std::cell::RefCell<mlua::lua::ExtraData>>>`
= note: required because it appears within the type `std::sync::Arc<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `mlua::lua::Lua`
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
= note: required because it appears within the type `[closure@$DIR/tests/compile_fail/lua_norefunwindsafe.rs:7:18: 7:48 lua:&mlua::lua::Lua]`
@@ -1,48 +0,0 @@
error[E0277]: the type `std::cell::UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> $DIR/ref_nounwindsafe.rs:8:5
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ^^^^^^^^^^^^ `std::cell::UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<()>`
= note: required because it appears within the type `std::marker::PhantomData<std::cell::UnsafeCell<()>>`
= note: required because it appears within the type `mlua::lua::Lua`
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
= note: required because it appears within the type `mlua::table::Table<'_>`
= note: required because it appears within the type `[closure@$DIR/tests/compile_fail/ref_nounwindsafe.rs:8:18: 8:54 table:mlua::table::Table<'_>]`
error[E0277]: the type `std::cell::UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> $DIR/ref_nounwindsafe.rs:8:5
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ^^^^^^^^^^^^ `std::cell::UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<mlua::lua::ExtraData>`
= note: required because it appears within the type `std::cell::RefCell<mlua::lua::ExtraData>`
= note: required because it appears within the type `alloc::sync::ArcInner<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `std::marker::PhantomData<alloc::sync::ArcInner<std::cell::RefCell<mlua::lua::ExtraData>>>`
= note: required because it appears within the type `std::sync::Arc<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `mlua::lua::Lua`
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
= note: required because it appears within the type `mlua::table::Table<'_>`
= note: required because it appears within the type `[closure@$DIR/tests/compile_fail/ref_nounwindsafe.rs:8:18: 8:54 table:mlua::table::Table<'_>]`
error[E0277]: the type `std::cell::UnsafeCell<isize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> $DIR/ref_nounwindsafe.rs:8:5
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ^^^^^^^^^^^^ `std::cell::UnsafeCell<isize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<isize>`
= note: required because it appears within the type `std::cell::Cell<isize>`
= note: required because it appears within the type `std::cell::RefCell<mlua::lua::ExtraData>`
= note: required because it appears within the type `alloc::sync::ArcInner<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `std::marker::PhantomData<alloc::sync::ArcInner<std::cell::RefCell<mlua::lua::ExtraData>>>`
= note: required because it appears within the type `std::sync::Arc<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `mlua::lua::Lua`
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
= note: required because it appears within the type `mlua::table::Table<'_>`
= note: required because it appears within the type `[closure@$DIR/tests/compile_fail/ref_nounwindsafe.rs:8:18: 8:54 table:mlua::table::Table<'_>]`
@@ -1,45 +0,0 @@
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> $DIR/scope_callback_capture.rs:12:14
|
12 | .create_function_mut(move |lua, t: Table| {
| ^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 9:15...
--> $DIR/scope_callback_capture.rs:9:15
|
9 | lua.scope(|scope| -> Result<()> {
| _______________^
10 | | let mut inner: Option<Table> = None;
11 | | let f = scope
12 | | .create_function_mut(move |lua, t: Table| {
... |
20 | | Ok(())
21 | | });
| |_____^
note: ...so that reference does not outlive borrowed content
--> $DIR/scope_callback_capture.rs:11:17
|
11 | let f = scope
| ^^^^^
note: but, the lifetime must be valid for the method call at 9:5...
--> $DIR/scope_callback_capture.rs:9:5
|
9 | / lua.scope(|scope| -> Result<()> {
10 | | let mut inner: Option<Table> = None;
11 | | let f = scope
12 | | .create_function_mut(move |lua, t: Table| {
... |
20 | | Ok(())
21 | | });
| |______^
note: ...so that a type/lifetime parameter is in scope here
--> $DIR/scope_callback_capture.rs:9:5
|
9 | / lua.scope(|scope| -> Result<()> {
10 | | let mut inner: Option<Table> = None;
11 | | let f = scope
12 | | .create_function_mut(move |lua, t: Table| {
... |
20 | | Ok(())
21 | | });
| |______^
@@ -1,45 +0,0 @@
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> $DIR/scope_callback_inner.rs:12:14
|
12 | .create_function_mut(|_, t: Table| {
| ^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 9:15...
--> $DIR/scope_callback_inner.rs:9:15
|
9 | lua.scope(|scope| -> Result<()> {
| _______________^
10 | | let mut inner: Option<Table> = None;
11 | | let f = scope
12 | | .create_function_mut(|_, t: Table| {
... |
17 | | Ok(())
18 | | });
| |_____^
note: ...so that reference does not outlive borrowed content
--> $DIR/scope_callback_inner.rs:11:17
|
11 | let f = scope
| ^^^^^
note: but, the lifetime must be valid for the method call at 9:5...
--> $DIR/scope_callback_inner.rs:9:5
|
9 | / lua.scope(|scope| -> Result<()> {
10 | | let mut inner: Option<Table> = None;
11 | | let f = scope
12 | | .create_function_mut(|_, t: Table| {
... |
17 | | Ok(())
18 | | });
| |______^
note: ...so that a type/lifetime parameter is in scope here
--> $DIR/scope_callback_inner.rs:9:5
|
9 | / lua.scope(|scope| -> Result<()> {
10 | | let mut inner: Option<Table> = None;
11 | | let f = scope
12 | | .create_function_mut(|_, t: Table| {
... |
17 | | Ok(())
18 | | });
| |______^
@@ -1,11 +0,0 @@
error: borrowed data cannot be stored outside of its closure
--> $DIR/scope_callback_outer.rs:11:17
|
9 | let mut outer: Option<Table> = None;
| --------- ...so that variable is valid at time of its declaration
10 | lua.scope(|scope| -> Result<()> {
| --------------------- borrowed data cannot outlive this closure
11 | let f = scope
| ^^^^^ cannot be stored outside of its closure
12 | .create_function_mut(|_, t: Table| {
| ------------------- cannot infer an appropriate lifetime...
+26
View File
@@ -1,3 +1,15 @@
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000",
allow(unused_attributes)
)]
extern "system" {}
use mlua::{Function, Lua, Result, String}; use mlua::{Function, Lua, Result, String};
#[test] #[test]
@@ -75,3 +87,17 @@ fn test_rust_function() -> Result<()> {
Ok(()) Ok(())
} }
#[test]
fn test_dump() -> Result<()> {
let lua = unsafe { Lua::unsafe_new() };
let concat_lua = lua
.load(r#"function(arg1, arg2) return arg1 .. arg2 end"#)
.eval::<Function>()?;
let concat = lua.load(&concat_lua.dump(false)?).into_function()?;
assert_eq!(concat.call::<_, String>(("foo", "bar"))?, "foobar");
Ok(())
}
+261
View File
@@ -0,0 +1,261 @@
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000",
allow(unused_attributes)
)]
extern "system" {}
use std::cell::RefCell;
use std::ops::Deref;
use std::str;
use std::sync::{Arc, Mutex};
use mlua::{Error, HookTriggers, Lua, Result, Value};
#[test]
fn line_counts() -> Result<()> {
let output = Arc::new(Mutex::new(Vec::new()));
let hook_output = output.clone();
let lua = Lua::new();
lua.set_hook(
HookTriggers {
every_line: true,
..Default::default()
},
move |_lua, debug| {
hook_output.lock().unwrap().push(debug.curr_line());
Ok(())
},
)?;
lua.load(
r#"
local x = 2 + 3
local y = x * 63
local z = string.len(x..", "..y)
"#,
)
.exec()?;
lua.remove_hook();
let output = output.lock().unwrap();
if cfg!(feature = "luajit") && lua.load("jit.version_num").eval::<i64>()? >= 20100 {
assert_eq!(*output, vec![2, 3, 4, 0, 4]);
} else {
assert_eq!(*output, vec![2, 3, 4]);
}
Ok(())
}
#[test]
fn function_calls() -> Result<()> {
let output = Arc::new(Mutex::new(Vec::new()));
let hook_output = output.clone();
let lua = Lua::new();
lua.set_hook(
HookTriggers {
on_calls: true,
..Default::default()
},
move |_lua, debug| {
let names = debug.names();
let source = debug.source();
let name = names.name.map(|s| str::from_utf8(s).unwrap().to_owned());
let what = source.what.map(|s| str::from_utf8(s).unwrap().to_owned());
hook_output.lock().unwrap().push((name, what));
Ok(())
},
)?;
lua.load(
r#"
local v = string.len("Hello World")
"#,
)
.exec()?;
lua.remove_hook();
let output = output.lock().unwrap();
if cfg!(feature = "luajit") && lua.load("jit.version_num").eval::<i64>()? >= 20100 {
assert_eq!(
*output,
vec![
(None, Some("main".to_string())),
(Some("len".to_string()), Some("Lua".to_string()))
]
);
} else {
assert_eq!(
*output,
vec![
(None, Some("main".to_string())),
(Some("len".to_string()), Some("C".to_string()))
]
);
}
Ok(())
}
#[test]
fn error_within_hook() -> Result<()> {
let lua = Lua::new();
lua.set_hook(
HookTriggers {
every_line: true,
..Default::default()
},
|_lua, _debug| {
Err(Error::RuntimeError(
"Something happened in there!".to_string(),
))
},
)?;
let err = lua
.load("x = 1")
.exec()
.expect_err("panic didn't propagate");
match err {
Error::CallbackError { cause, .. } => match cause.deref() {
Error::RuntimeError(s) => assert_eq!(s, "Something happened in there!"),
_ => panic!("wrong callback error kind caught"),
},
_ => panic!("wrong error kind caught"),
};
Ok(())
}
#[test]
fn limit_execution_instructions() -> Result<()> {
let lua = Lua::new();
let mut max_instructions = 10000;
#[cfg(feature = "luajit")]
// For LuaJIT disable JIT, as compiled code does not trigger hooks
lua.load("jit.off()").exec()?;
lua.set_hook(
HookTriggers {
every_nth_instruction: Some(30),
..Default::default()
},
move |_lua, _debug| {
max_instructions -= 30;
if max_instructions < 0 {
Err(Error::RuntimeError("time's up".to_string()))
} else {
Ok(())
}
},
)?;
lua.globals().set("x", Value::Integer(0))?;
let _ = lua
.load(
r#"
for i = 1, 10000 do
x = x + 1
end
"#,
)
.exec()
.expect_err("instruction limit didn't occur");
Ok(())
}
#[test]
fn hook_removal() -> Result<()> {
let lua = Lua::new();
lua.set_hook(
HookTriggers {
every_nth_instruction: Some(1),
..Default::default()
},
|_lua, _debug| {
Err(Error::RuntimeError(
"this hook should've been removed by this time".to_string(),
))
},
)?;
assert!(lua.load("local x = 1").exec().is_err());
lua.remove_hook();
assert!(lua.load("local x = 1").exec().is_ok());
Ok(())
}
#[test]
fn hook_swap_within_hook() -> Result<()> {
thread_local! {
static TL_LUA: RefCell<Option<Lua>> = RefCell::new(None);
}
TL_LUA.with(|tl| {
*tl.borrow_mut() = Some(Lua::new());
});
TL_LUA.with(|tl| {
tl.borrow().as_ref().unwrap().set_hook(
HookTriggers {
every_line: true,
..Default::default()
},
move |lua, _debug| {
lua.globals().set("ok", 1i64)?;
TL_LUA.with(|tl| {
tl.borrow().as_ref().unwrap().set_hook(
HookTriggers {
every_line: true,
..Default::default()
},
move |lua, _debug| {
lua.load(
r#"
if ok ~= nil then
ok = ok + 1
end
"#,
)
.exec()
.expect("exec failure within hook");
TL_LUA.with(|tl| {
tl.borrow().as_ref().unwrap().remove_hook();
});
Ok(())
},
)
})
},
)
})?;
TL_LUA.with(|tl| {
let tl = tl.borrow();
let lua = tl.as_ref().unwrap();
lua.load(
r#"
local x = 1
x = 2
local y = 3
"#,
)
.exec()?;
assert_eq!(lua.globals().get::<_, i64>("ok")?, 2);
Ok(())
})
}
+44 -1
View File
@@ -1,13 +1,56 @@
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000",
allow(unused_attributes)
)]
extern "system" {}
use std::sync::Arc; use std::sync::Arc;
use mlua::{Lua, Result, UserData}; use mlua::{Lua, Result, UserData};
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
use mlua::Error;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[test]
fn test_memory_limit() -> Result<()> {
let lua = Lua::new();
let initial_memory = lua.used_memory();
assert!(
initial_memory > 0,
"used_memory reporting is wrong, lua uses memory for stdlib"
);
let f = lua
.load("local t = {}; for i = 1,10000 do t[i] = i end")
.into_function()?;
f.call::<_, ()>(()).expect("should trigger no memory limit");
lua.set_memory_limit(initial_memory + 10000)?;
match f.call::<_, ()>(()) {
Err(Error::MemoryError(_)) => {}
something_else => panic!("did not trigger memory error: {:?}", something_else),
};
lua.set_memory_limit(0)?;
f.call::<_, ()>(()).expect("should trigger no memory limit");
Ok(())
}
#[test] #[test]
fn test_gc_control() -> Result<()> { fn test_gc_control() -> Result<()> {
let lua = Lua::new(); let lua = Lua::new();
let globals = lua.globals(); let globals = lua.globals();
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
{ {
assert!(lua.gc_is_running()); assert!(lua.gc_is_running());
lua.gc_stop(); lua.gc_stop();
+5
View File
@@ -0,0 +1,5 @@
[target.x86_64-apple-darwin]
rustflags = ["-C", "link-args=-rdynamic"]
[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "link-args=-rdynamic"]
+16
View File
@@ -0,0 +1,16 @@
[package]
name = "module_runner"
version = "0.0.0"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2018"
[features]
lua54 = ["mlua/lua54"]
lua53 = ["mlua/lua53"]
lua52 = ["mlua/lua52"]
lua51 = ["mlua/lua51"]
luajit = ["mlua/luajit"]
vendored = ["mlua/vendored"]
[dependencies]
mlua = { path = "../.." }
+82
View File
@@ -0,0 +1,82 @@
use std::env;
use std::path::PathBuf;
use mlua::{Lua, Result};
#[test]
fn test_module() -> Result<()> {
let lua = make_lua()?;
lua.load(
r#"
local mod = require("rust_module")
assert(mod.sum(2,2) == 4)
"#,
)
.exec()
}
#[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "lua51"
))]
#[test]
fn test_module_from_thread() -> Result<()> {
let lua = make_lua()?;
lua.load(
r#"
local mod
local co = coroutine.create(function(a, b)
mod = require("rust_module")
assert(mod.sum(a, b) == a + b)
end)
coroutine.resume(co, 3, 5)
collectgarbage()
assert(mod.used_memory() > 0)
"#,
)
.exec()
}
fn make_lua() -> Result<Lua> {
let (dylib_path, dylib_ext, separator);
if cfg!(target_os = "macos") {
dylib_path = env::var("DYLD_FALLBACK_LIBRARY_PATH").unwrap();
dylib_ext = "dylib";
separator = ":";
} else if cfg!(target_os = "linux") {
dylib_path = env::var("LD_LIBRARY_PATH").unwrap();
dylib_ext = "so";
separator = ":";
} else if cfg!(target_os = "windows") {
dylib_path = env::var("PATH").unwrap();
dylib_ext = "dll";
separator = ";";
} else {
panic!("unknown target os");
};
let mut cpath = dylib_path
.split(separator)
.take(3)
.map(|p| {
let mut path = PathBuf::from(p);
path.push(format!("lib?.{}", dylib_ext));
path.to_str().unwrap().to_owned()
})
.collect::<Vec<_>>()
.join(";");
if cfg!(target_os = "windows") {
cpath = cpath.replace("\\", "\\\\");
cpath = cpath.replace("lib?.", "?.");
}
let lua = unsafe { Lua::unsafe_new() }; // To be able to load C modules
lua.load(&format!("package.cpath = \"{}\"", cpath)).exec()?;
Ok(lua)
}
+14 -5
View File
@@ -1,3 +1,15 @@
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000",
allow(unused_attributes)
)]
extern "system" {}
use std::cell::Cell; use std::cell::Cell;
use std::rc::Rc; use std::rc::Rc;
@@ -44,10 +56,8 @@ fn scope_drop() -> Result<()> {
let rc = Rc::new(()); let rc = Rc::new(());
lua.scope(|scope| { lua.scope(|scope| {
lua.globals().set( lua.globals()
"test", .set("test", scope.create_userdata(MyUserdata(rc.clone()))?)?;
scope.create_static_userdata(MyUserdata(rc.clone()))?,
)?;
assert_eq!(Rc::strong_count(&rc), 2); assert_eq!(Rc::strong_count(&rc), 2);
Ok(()) Ok(())
})?; })?;
@@ -198,7 +208,6 @@ fn scope_userdata_mismatch() -> Result<()> {
a.inc(a) a.inc(a)
b.inc(b) b.inc(b)
end end
function bad(a, b) function bad(a, b)
a.inc(b) a.inc(b)
end end
+12
View File
@@ -1,3 +1,15 @@
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000",
allow(unused_attributes)
)]
extern "system" {}
use std::borrow::Cow; use std::borrow::Cow;
use mlua::{Lua, Result, String}; use mlua::{Lua, Result, String};
+84 -1
View File
@@ -1,4 +1,16 @@
use mlua::{Lua, Nil, Result, Table, Value}; #![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000",
allow(unused_attributes)
)]
extern "system" {}
use mlua::{Lua, Nil, Result, Table, TableExt, Value};
#[test] #[test]
fn test_set_get() -> Result<()> { fn test_set_get() -> Result<()> {
@@ -109,6 +121,47 @@ fn test_table() -> Result<()> {
Ok(()) Ok(())
} }
#[test]
fn test_table_sequence_from() -> Result<()> {
let lua = Lua::new();
let get_table = lua.create_function(|_, t: Table| Ok(t))?;
assert_eq!(
get_table
.call::<_, Table>(vec![1, 2, 3])?
.sequence_values()
.collect::<Result<Vec<i64>>>()?,
vec![1, 2, 3]
);
assert_eq!(
get_table
.call::<_, Table>([1, 2, 3].as_ref())?
.sequence_values()
.collect::<Result<Vec<i64>>>()?,
vec![1, 2, 3]
);
assert_eq!(
get_table
.call::<_, Table>([1, 2, 3])?
.sequence_values()
.collect::<Result<Vec<i64>>>()?,
vec![1, 2, 3]
);
assert_eq!(
get_table
.call::<_, Table>(&[1, 2, 3])?
.sequence_values()
.collect::<Result<Vec<i64>>>()?,
vec![1, 2, 3]
);
Ok(())
}
#[test] #[test]
fn test_table_scope() -> Result<()> { fn test_table_scope() -> Result<()> {
let lua = Lua::new(); let lua = Lua::new();
@@ -226,3 +279,33 @@ fn test_table_error() -> Result<()> {
Ok(()) Ok(())
} }
#[test]
fn test_table_call() -> Result<()> {
let lua = Lua::new();
lua.load(
r#"
table = {a = 1}
function table.func(key)
return "func_"..key
end
function table:method(key)
return "method_"..self[key]
end
"#,
)
.exec()?;
let table: Table = lua.globals().get("table")?;
assert_eq!(table.call_function::<_, _, String>("func", "a")?, "func_a");
assert_eq!(
table.call_method::<_, _, String>("method", "a")?,
"method_1"
);
Ok(())
}
+125 -4
View File
@@ -1,12 +1,81 @@
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000",
allow(unused_attributes)
)]
extern "system" {}
use std::iter::FromIterator; use std::iter::FromIterator;
use std::panic::catch_unwind; use std::panic::catch_unwind;
use std::sync::Arc; use std::sync::Arc;
use std::{error, f32, f64, fmt}; use std::{error, f32, f64, fmt};
use mlua::{ use mlua::{
Error, ExternalError, Function, Lua, Nil, Result, String, Table, UserData, Value, Variadic, ChunkMode, Error, ExternalError, Function, Lua, Nil, Result, StdLib, String, Table, UserData,
Value, Variadic,
}; };
#[test]
fn test_safety() -> Result<()> {
let lua = Lua::new();
assert!(lua.load(r#"require "debug""#).exec().is_err());
match lua.load_from_std_lib(StdLib::DEBUG) {
Err(Error::SafetyError(_)) => {}
Err(e) => panic!("expected SafetyError, got {:?}", e),
Ok(_) => panic!("expected SafetyError, got no error"),
}
drop(lua);
let lua = unsafe { Lua::unsafe_new() };
assert!(lua.load(r#"require "debug""#).exec().is_ok());
drop(lua);
match Lua::new_with(StdLib::DEBUG) {
Err(Error::SafetyError(_)) => {}
Err(e) => panic!("expected SafetyError, got {:?}", e),
Ok(_) => panic!("expected SafetyError, got new Lua state"),
}
let lua = Lua::new();
match lua.load(r#"package.loadlib()"#).exec() {
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
Error::SafetyError(_) => {}
e => panic!("expected SafetyError cause, got {:?}", e),
},
Err(e) => panic!("expected CallbackError, got {:?}", e),
Ok(_) => panic!("expected CallbackError, got no error"),
};
match lua.load(r#"require "fake_ffi""#).exec() {
Err(Error::RuntimeError(msg)) => assert!(msg.contains("can't load C modules in safe mode")),
Err(e) => panic!("expected RuntimeError, got {:?}", e),
Ok(_) => panic!("expected RuntimeError, got no error"),
}
match lua.load("1 + 1").set_mode(ChunkMode::Binary).exec() {
Err(Error::SafetyError(msg)) => {
assert!(msg.contains("binary chunks are disabled in safe mode"))
}
Err(e) => panic!("expected SafetyError, got {:?}", e),
Ok(_) => panic!("expected SafetyError, got no error"),
}
let bytecode = lua.load("return 1 + 1").into_function()?.dump(true)?;
match lua.load(&bytecode).exec() {
Err(Error::SafetyError(msg)) => {
assert!(msg.contains("binary chunks are disabled in safe mode"))
}
Err(e) => panic!("expected SafetyError, got {:?}", e),
Ok(_) => panic!("expected SafetyError, got no error"),
}
Ok(())
}
#[test] #[test]
fn test_load() -> Result<()> { fn test_load() -> Result<()> {
let lua = Lua::new(); let lua = Lua::new();
@@ -75,6 +144,41 @@ fn test_eval() -> Result<()> {
Ok(()) Ok(())
} }
#[test]
fn test_load_mode() -> Result<()> {
let lua = unsafe { Lua::unsafe_new() };
assert_eq!(
lua.load("1 + 1").set_mode(ChunkMode::Text).eval::<i32>()?,
2
);
match lua.load("1 + 1").set_mode(ChunkMode::Binary).exec() {
Ok(_) => panic!("expected SyntaxError, got no error"),
Err(Error::SyntaxError { message: msg, .. }) => {
assert!(msg.contains("attempt to load a text chunk"))
}
Err(e) => panic!("expected SyntaxError, got {:?}", e),
};
let bytecode = lua.load("return 1 + 1").into_function()?.dump(true)?;
assert_eq!(lua.load(&bytecode).eval::<i32>()?, 2);
assert_eq!(
lua.load(&bytecode)
.set_mode(ChunkMode::Binary)
.eval::<i32>()?,
2
);
match lua.load(&bytecode).set_mode(ChunkMode::Text).exec() {
Ok(_) => panic!("expected SyntaxError, got no error"),
Err(Error::SyntaxError { message: msg, .. }) => {
assert!(msg.contains("attempt to load a binary chunk"))
}
Err(e) => panic!("expected SyntaxError, got {:?}", e),
};
Ok(())
}
#[test] #[test]
fn test_lua_multi() -> Result<()> { fn test_lua_multi() -> Result<()> {
let lua = Lua::new(); let lua = Lua::new();
@@ -389,7 +493,7 @@ fn test_num_conversion() -> Result<()> {
assert_eq!(lua.load("1.0").eval::<i64>()?, 1); assert_eq!(lua.load("1.0").eval::<i64>()?, 1);
assert_eq!(lua.load("1.0").eval::<f64>()?, 1.0); assert_eq!(lua.load("1.0").eval::<f64>()?, 1.0);
#[cfg(feature = "lua53")] #[cfg(any(feature = "lua54", feature = "lua53"))]
assert_eq!(lua.load("1.0").eval::<String>()?, "1.0"); assert_eq!(lua.load("1.0").eval::<String>()?, "1.0");
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
assert_eq!(lua.load("1.0").eval::<String>()?, "1"); assert_eq!(lua.load("1.0").eval::<String>()?, "1");
@@ -466,7 +570,12 @@ fn test_pcall_xpcall() -> Result<()> {
assert_eq!(globals.get::<_, String>("pcall_error")?, "testerror"); assert_eq!(globals.get::<_, String>("pcall_error")?, "testerror");
assert_eq!(globals.get::<_, bool>("xpcall_statusr")?, false); assert_eq!(globals.get::<_, bool>("xpcall_statusr")?, false);
#[cfg(any(feature = "lua53", feature = "lua52", feature = "luajit"))] #[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "luajit"
))]
assert_eq!( assert_eq!(
globals.get::<_, std::string::String>("xpcall_error")?, globals.get::<_, std::string::String>("xpcall_error")?,
"testerror" "testerror"
@@ -799,7 +908,7 @@ fn context_thread() -> Result<()> {
) )
.into_function()?; .into_function()?;
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
f.call::<_, ()>(lua.current_thread())?; f.call::<_, ()>(lua.current_thread())?;
#[cfg(any(feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua51", feature = "luajit"))]
@@ -828,3 +937,15 @@ fn context_thread_51() -> Result<()> {
Ok(()) Ok(())
} }
#[test]
#[cfg(feature = "luajit")]
fn test_jit_version() -> Result<()> {
let lua = Lua::new();
let jit: Table = lua.globals().get("jit")?;
assert!(jit
.get::<_, String>("version")?
.to_str()?
.contains("LuaJIT"));
Ok(())
}
+18 -1
View File
@@ -1,3 +1,15 @@
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000",
allow(unused_attributes)
)]
extern "system" {}
use std::panic::catch_unwind; use std::panic::catch_unwind;
use mlua::{Error, Function, Lua, Result, Thread, ThreadStatus}; use mlua::{Error, Function, Lua, Result, Thread, ThreadStatus};
@@ -100,7 +112,12 @@ fn coroutine_from_closure() -> Result<()> {
let thrd_main = lua.create_function(|_, ()| Ok(()))?; let thrd_main = lua.create_function(|_, ()| Ok(()))?;
lua.globals().set("main", thrd_main)?; lua.globals().set("main", thrd_main)?;
#[cfg(any(feature = "lua53", feature = "lua52", feature = "luajit"))] #[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "luajit"
))]
let thrd: Thread = lua.load("coroutine.create(main)").eval()?; let thrd: Thread = lua.load("coroutine.create(main)").eval()?;
#[cfg(feature = "lua51")] #[cfg(feature = "lua51")]
let thrd: Thread = lua let thrd: Thread = lua
+12
View File
@@ -1,3 +1,15 @@
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000",
allow(unused_attributes)
)]
extern "system" {}
use std::os::raw::c_void; use std::os::raw::c_void;
use mlua::{Function, LightUserData, Lua, Result}; use mlua::{Function, LightUserData, Lua, Result};
+71 -3
View File
@@ -1,5 +1,20 @@
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000",
allow(unused_attributes)
)]
extern "system" {}
use std::sync::Arc; use std::sync::Arc;
#[cfg(feature = "lua54")]
use std::sync::atomic::{AtomicI64, Ordering};
use mlua::{ use mlua::{
AnyUserData, ExternalError, Function, Lua, MetaMethod, Result, String, UserData, AnyUserData, ExternalError, Function, Lua, MetaMethod, Result, String, UserData,
UserDataMethods, Value, UserDataMethods, Value,
@@ -95,7 +110,7 @@ fn test_metamethods() -> Result<()> {
Err("no such custom index".to_lua_err()) Err("no such custom index".to_lua_err())
} }
}); });
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
methods.add_meta_method(MetaMethod::Pairs, |lua, data, ()| { methods.add_meta_method(MetaMethod::Pairs, |lua, data, ()| {
use std::iter::FromIterator; use std::iter::FromIterator;
let stateless_iter = lua.create_function(|_, (data, i): (MyUserData, i64)| { let stateless_iter = lua.create_function(|_, (data, i): (MyUserData, i64)| {
@@ -120,7 +135,7 @@ fn test_metamethods() -> Result<()> {
10 10
); );
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
let pairs_it = { let pairs_it = {
lua.load( lua.load(
r#" r#"
@@ -140,7 +155,7 @@ fn test_metamethods() -> Result<()> {
assert_eq!(lua.load("userdata1 - userdata2").eval::<MyUserData>()?.0, 4); assert_eq!(lua.load("userdata1 - userdata2").eval::<MyUserData>()?.0, 4);
assert_eq!(lua.load("userdata1:get()").eval::<i64>()?, 7); assert_eq!(lua.load("userdata1:get()").eval::<i64>()?, 7);
assert_eq!(lua.load("userdata2.inner").eval::<i64>()?, 3); assert_eq!(lua.load("userdata2.inner").eval::<i64>()?, 3);
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
assert_eq!(pairs_it.call::<_, i64>(())?, 28); assert_eq!(pairs_it.call::<_, i64>(())?, 28);
assert!(lua.load("userdata2.nonexist_field").eval::<()>().is_err()); assert!(lua.load("userdata2.nonexist_field").eval::<()>().is_err());
@@ -151,6 +166,58 @@ fn test_metamethods() -> Result<()> {
assert!(userdata2 != userdata3); // because references are differ assert!(userdata2 != userdata3); // because references are differ
assert!(userdata2.equals(userdata3)?); assert!(userdata2.equals(userdata3)?);
let userdata1: AnyUserData = globals.get("userdata1")?;
assert!(userdata1.has_metamethod(MetaMethod::Add)?);
assert!(userdata1.has_metamethod(MetaMethod::Sub)?);
assert!(userdata1.has_metamethod(MetaMethod::Index)?);
assert!(!userdata1.has_metamethod(MetaMethod::Pow)?);
Ok(())
}
#[test]
#[cfg(feature = "lua54")]
fn test_metamethod_close() -> Result<()> {
#[derive(Clone)]
struct MyUserData(Arc<AtomicI64>);
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("get", |_, data, ()| Ok(data.0.load(Ordering::Relaxed)));
methods.add_meta_method(MetaMethod::Close, |_, data, _err: Value| {
data.0.store(0, Ordering::Relaxed);
Ok(())
});
}
}
let lua = Lua::new();
let globals = lua.globals();
let ud = MyUserData(Arc::new(AtomicI64::new(-1)));
let ud2 = ud.clone();
globals.set(
"new_userdata",
lua.create_function(move |_lua, val: i64| {
let ud = ud2.clone();
ud.0.store(val, Ordering::Relaxed);
Ok(ud)
})?,
)?;
lua.load(
r#"
do
local ud <close> = new_userdata(7)
assert(ud:get() == 7)
end
"#,
)
.exec()?;
assert_eq!(ud.0.load(Ordering::Relaxed), 0);
Ok(()) Ok(())
} }
@@ -219,6 +286,7 @@ fn detroys_userdata() -> Result<()> {
#[test] #[test]
fn user_value() -> Result<()> { fn user_value() -> Result<()> {
struct MyUserData; struct MyUserData;
impl UserData for MyUserData {} impl UserData for MyUserData {}
let lua = Lua::new(); let lua = Lua::new();
+12
View File
@@ -1,3 +1,15 @@
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000",
allow(unused_attributes)
)]
extern "system" {}
use mlua::{Lua, Result, Value}; use mlua::{Lua, Result, Value};
#[test] #[test]