Compare commits

...

49 Commits

Author SHA1 Message Date
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
85 changed files with 4796 additions and 978 deletions
+139 -118
View File
@@ -7,150 +7,171 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
thing:
- stable
- macos-x86_64
- x86_64-msvc
os: [ubuntu-18.04, macos-latest, windows-latest]
rust: [stable]
lua: [lua54, lua53, lua52, lua51, luajit]
include:
- thing: stable
- os: ubuntu-18.04
target: x86_64-unknown-linux-gnu
rust: stable
os: ubuntu-latest
- thing: macos-x86_64
- os: macos-latest
target: x86_64-apple-darwin
rust: stable
os: macos-latest
- thing: x86_64-msvc
- os: windows-latest
target: x86_64-pc-windows-msvc
rust: stable-x86_64-msvc
os: windows-latest
steps:
- uses: actions/checkout@v1
- name: Install Rust (rustup)
if: matrix.os != 'macos-latest'
run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }}
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
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
- name: Install Lua (ubuntu)
if: matrix.os == 'ubuntu-latest'
- name: Build ${{ matrix.lua }} pkg-config
if: ${{ matrix.os == 'ubuntu-18.04' && matrix.lua != 'lua54' }}
run: |
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
- name: Install Rust (macos)
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+async)
run: |
for FEATURE in lua53 lua52 lua51 luajit; do
echo "Building $FEATURE"
cargo build --target ${{ matrix.target }} --release --no-default-features --features "$FEATURE vendored async"
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
cargo build --release --features "${{ matrix.lua }}"
test_linux:
name: Test on Linux
runs-on: ubuntu-latest
test:
name: Test
runs-on: ${{ matrix.os }}
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:
- uses: actions/checkout@v1
- name: Install Rust
run: rustup update stable --no-self-update && rustup default stable
shell: bash
- name: Run tests (Lua 5.3 vendored)
run: cargo test --release --no-default-features --features "lua53 vendored"
shell: bash
- name: Run tests (Lua 5.3 vendored+async)
run: cargo test --release --no-default-features --features "lua53 vendored async"
shell: bash
- 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 (Lua 5.1 vendored+async)
run: cargo test --release --no-default-features --features "lua51 vendored async"
shell: bash
- name: Run tests (LuaJIT vendored)
run: cargo test --release --no-default-features --features "luajit vendored"
shell: bash
- name: Run tests (LuaJIT vendored+async)
run: cargo test --release --no-default-features --features "luajit vendored async"
shell: bash
- name: Run compile test (Lua 5.3)
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- name: Run ${{ matrix.lua }} tests
if: ${{ matrix.os != 'macos-latest' || matrix.lua != 'luajit' }}
run: |
sudo apt-get update -y
sudo apt-get install -y --no-install-recommends liblua5.3-dev
cargo test --release --no-default-features --features "lua53 vendored" -- --ignored
cargo test --release --features "${{ matrix.lua }} vendored"
cargo test --release --features "${{ matrix.lua }} vendored async send"
shell: bash
- name: Run compile tests
if: ${{ matrix.os == 'ubuntu-18.04' && 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:
name: Test on MacOS
test_luajit_macos:
name: Test LuaJIT on macOS
runs-on: macos-latest
needs: build
steps:
- uses: actions/checkout@v1
- name: Install Rust
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
target: x86_64-apple-darwin
override: true
- name: Run LuaJIT 2.0.5 tests
run: |
curl https://sh.rustup.rs | sh -s -- -y
echo ::add-path::$HOME/.cargo/bin
brew install luajit pkg-config
cargo test --tests --release --features "luajit async send" -- --test-threads=1
shell: bash
- name: Run tests (Lua 5.3 vendored)
run: cargo test --release --no-default-features --features "lua53 vendored"
shell: bash
- 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"
- name: Run LuaJIT vendored tests
run: |
cargo test --release --features "luajit vendored async send"
shell: bash
test_windows:
name: Test on Windows
test_modules:
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
needs: build
strategy:
matrix:
lua: [lua53, lua51, luajit]
defaults:
run:
shell: bash.exe --login -eo pipefail "{0}"
env:
MSYSTEM: MINGW64
CHERE_INVOKING: 1
steps:
- uses: actions/checkout@v1
- name: Install Rust
run: rustup update stable --no-self-update && rustup default stable
shell: bash
- name: Run tests (Lua 5.3 vendored)
run: cargo test --release --no-default-features --features "lua53 vendored"
shell: bash
- 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
- uses: actions/checkout@v2
- name: Set up shell
run: echo ::add-path::C:\msys64\usr\bin\
shell: pwsh
- name: Install Rust & Lua
run: |
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 ${{ matrix.lua }} module tests
run: |
(cd examples/module && cargo build --release --features "${{ matrix.lua }}")
(cd tests/module && cargo test --release --features "${{ matrix.lua }}")
rustfmt:
name: Rustfmt
runs-on: ubuntu-latest
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v1
- name: Install Rust
run: rustup update stable && rustup default stable && rustup component add rustfmt
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
components: rustfmt
override: true
- 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
+16
View File
@@ -0,0 +1,16 @@
## 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.
+22 -13
View File
@@ -1,6 +1,6 @@
[package]
name = "mlua"
version = "0.3.0"
version = "0.4.0"
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
edition = "2018"
repository = "https://github.com/khvzak/mlua"
@@ -12,12 +12,13 @@ license = "MIT"
links = "lua"
build = "build/main.rs"
description = """
High level bindings to Lua 5.1/5.2/5.3 (including LuaJIT)
High level bindings to Lua 5.1/5.2/5.3/5.4 (including LuaJIT)
with async/await features and support of writing native lua modules in Rust.
"""
[badges]
# github-actions = { repository = "khvzak/mlua", workflow = "CI" }
maintenance = { status = "actively-developed" }
[package.metadata.docs.rs]
features = ["async"]
@@ -25,38 +26,42 @@ features = ["async"]
[workspace]
members = [
"mlua_derive",
"examples/module",
"tests/module",
]
[features]
default = ["lua53"]
lua54 = []
lua53 = []
lua52 = []
lua51 = []
luajit = []
vendored = ["lua-src", "luajit-src"]
module = []
async = ["futures-core", "futures-task", "futures-util"]
send = []
[dependencies]
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.4", optional = true }
futures-task = { version = "0.3.4", optional = true }
futures-util = { version = "0.3.4", optional = true }
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]
cc = { version = "1.0" }
pkg-config = { version = "0.3.17" }
lua-src = { version = "535.0.1", optional = true }
luajit-src = { version = "210.0.0", optional = true }
lua-src = { version = "535.0.4", optional = true }
luajit-src = { version = "210.1.0", optional = true }
[dev-dependencies]
rustyline = "6.0"
criterion = "0.3"
trybuild = "1.0"
futures = "0.3.5"
hyper = "0.13"
tokio = { version = "0.2.18", features = ["full"] }
futures-executor = "0.3.4"
futures-util = "0.3.4"
tokio = { version = "0.2", features = ["full"] }
futures-timer = "3.0"
[[bench]]
@@ -64,9 +69,13 @@ name = "benchmark"
harness = false
[[example]]
name = "async_tcp_server"
name = "async_http_client"
required-features = ["async"]
[[example]]
name = "async_http_client"
name = "async_http_server"
required-features = ["async", "send"]
[[example]]
name = "async_tcp_server"
required-features = ["async"]
+53 -27
View File
@@ -10,27 +10,37 @@
[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
interface between Rust and Lua. Unlike `rlua` , `mlua` supports Lua 5.1 (including LuaJIT), 5.2 and 5.3.
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
modules in Rust.
`mlua` is bindings to [Lua](https://www.lua.org) programming language for Rust with a goal to provide
_safe_ (as far as it's possible), high level, easy to use, practical and flexible API.
Started as [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.
`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
### Async
### Async/await support
Starting from 0.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 `async` feature in `Cargo.toml`.
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`.
**Examples**:
- [HTTP Client](examples/async_http_client.rs)
- [HTTP Server](examples/async_http_server.rs)
- [TCP Server](examples/async_tcp_server.rs)
### Choosing Lua version
### Compiling
The following features could be used to choose Lua version: `lua53` (default), `lua52`, `lua51` and `luajit`.
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 lib.
By default `mlua` uses `pkg-config` tool to find lua includes and libraries for the chosen Lua version.
In most cases it works as desired, although sometimes could be more preferable to use a custom lua library.
To achieve this, mlua supports `LUA_INC`, `LUA_LIB`, `LUA_LIB_NAME` and `LUA_LINK` environment variables.
`LUA_LINK` is optional and may be `dylib` (a dynamic library) or `static` (a static library, `.a` archive).
@@ -42,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
[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
Add to `Cargo.toml` :
``` toml
[dependencies]
mlua = "0.3"
mlua = { version = "0.4", features = ["lua53"] }
```
`main.rs`
@@ -74,6 +84,8 @@ fn main() -> LuaResult<()> {
### Module mode
[Example](examples/module)
Add to `Cargo.toml` :
``` toml
@@ -81,8 +93,8 @@ Add to `Cargo.toml` :
crate-type = ["cdylib"]
[dependencies]
mlua = "0.3"
mlua_derive = "0.3"
mlua = { version = "0.4", features = ["lua53", "module"] }
mlua_derive = "0.4"
```
`lib.rs` :
@@ -105,27 +117,42 @@ fn my_module(lua: &Lua) -> LuaResult<LuaTable> {
}
```
And then (macos example):
And then (**macOS** example):
``` sh
$ cargo build
$ cargo rustc -- -C link-arg=-undefined -C link-arg=dynamic_lookup
$ ln -s ./target/debug/libmy_module.dylib ./my_module.so
$ lua5.3 -e 'require("my_module").hello("world")'
hello, world!
```
On macOS, you need to set additional linker arguments. One option is to compile with `cargo rustc --release -- -C link-arg=-undefined -C link-arg=dynamic_lookup`, the other is to create a `.cargo/config` with the following content:
``` 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
One of the `mlua` goals is to provide *safe* API between Rust and Lua.
Every place where the Lua C API may trigger an error longjmp
in any way is protected by `lua_pcall` , and the user of the library is protected
from directly interacting with unsafe things like the Lua stack, and there is
overhead associated with this safety.
Every place where the Lua C API may trigger an error longjmp in any way is protected by `lua_pcall`,
and the user of the library is protected from directly interacting with unsafe things like the Lua stack,
and there is overhead associated with this safety.
Unfortunately, `mlua` does not provide absolute safety even without using `unsafe` .
This library contains a huge amount of unsafe code. There are almost
certainly bugs still lurking in this library! It is surprisingly, fiendishly
difficult to use the Lua C API without the potential for unsafety.
This library contains a huge amount of unsafe code. There are almost certainly bugs still lurking in this library!
It is surprisingly, fiendishly difficult to use the Lua C API without the potential for unsafety.
## Panic handling
@@ -149,10 +176,9 @@ let _ = lua.load(r#"
unreachable!()
```
`mlua` should also be panic safe in another way as well, which is that any `Lua`
instances or handles remains usable after a user generated panic, and such
panics should not break internal invariants or leak Lua stack space. This is
mostly important to safely use `mlua` types in Drop impls, as you should not be
`mlua` should also be panic safe in another way as well, which is that any `Lua` instances or handles
remains usable after a user generated panic, and such panics should not break internal invariants or
leak Lua stack space. This is mostly important to safely use `mlua` types in Drop impls, as you should not be
using panics for general error handling.
Below is a list of `mlua` behaviors that should be considered a bug.
+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::ffi::OsString;
use std::fs::File;
use std::io::{BufRead, BufReader, Result};
use std::ops::Bound;
use std::path::{Path, PathBuf};
pub fn probe_lua() -> PathBuf {
let include_dir = env::var_os("LUA_INC").unwrap_or(OsString::new());
let lib_dir = env::var_os("LUA_LIB").unwrap_or(OsString::new());
let lua_lib = env::var_os("LUA_LIB_NAME").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_default();
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_LIB");
println!("cargo:rerun-if-env-changed=LUA_LIB_NAME");
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();
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")]
{
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included("5.3"), Bound::Excluded("5.4")))
.cargo_metadata(need_lua_lib)
.probe("lua");
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")]
{
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included("5.2"), Bound::Excluded("5.3")))
.cargo_metadata(need_lua_lib)
.probe("lua");
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")]
{
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included("5.1"), Bound::Excluded("5.2")))
.cargo_metadata(need_lua_lib)
.probe("lua");
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")]
{
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");
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();
if env::var("LUA_LINK").unwrap_or(String::new()) == "static" {
link_lib = "static=".to_string();
}
let link_lib = match env::var("LUA_LINK") {
Ok(s) if s == "static" => "static=",
_ => "",
};
println!(
"cargo:rustc-link-search=native={}",
lib_dir.as_ref().display()
);
println!(
"cargo:rustc-link-lib={}{}",
link_lib,
lua_lib.as_ref().display()
);
if cfg!(any(not(feature = "module"), target_os = "windows")) {
println!(
"cargo:rustc-link-search=native={}",
lib_dir.as_ref().display()
);
println!(
"cargo:rustc-link-lib={}{}",
link_lib,
lua_lib.as_ref().display()
);
}
Ok(version_found)
}
+4 -5
View File
@@ -1,11 +1,8 @@
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 {
#[cfg(feature = "lua54")]
let artifacts = lua_src::Build::new().build(lua_src::Lua54);
#[cfg(feature = "lua53")]
let artifacts = lua_src::Build::new().build(lua_src::Lua53);
#[cfg(feature = "lua52")]
@@ -15,6 +12,8 @@ pub fn probe_lua() -> PathBuf {
#[cfg(feature = "luajit")]
let artifacts = luajit_src::Build::new().build();
#[cfg(not(feature = "module"))]
artifacts.print_cargo_metadata();
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::process::Command;
#[cfg_attr(feature = "vendored", path = "find_vendored.rs")]
#[cfg_attr(not(feature = "vendored"), path = "find_normal.rs")]
#[cfg_attr(
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;
trait CommandExt {
@@ -59,27 +93,43 @@ fn build_glue<P: AsRef<Path> + std::fmt::Debug>(include_path: &P) {
fn main() {
#[cfg(not(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "lua51",
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(
feature = "lua53",
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")))]
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"))]
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"))]
panic!("You can enable only one of the features: lua53, lua52, lua51, luajit");
// We don't support "vendored module" mode on windows
#[cfg(all(feature = "vendored", feature = "module", target_os = "windows"))]
compile_error!(
"Vendored (static) builds are not supported for modules on Windows.\n"
+ "Please, use `pkg-config` or custom mode to link to a Lua dll."
);
let include_dir = find::probe_lua();
build_glue(&include_dir);
+44 -17
View File
@@ -1,8 +1,33 @@
use std::collections::HashMap;
use std::sync::Arc;
use hyper::Client as HyperClient;
use bstr::BString;
use hyper::{body::Body as HyperBody, Client as HyperClient};
use tokio::{stream::StreamExt, sync::Mutex};
use mlua::{Error, Lua, Result, Thread};
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<()> {
@@ -23,10 +48,9 @@ async fn main() -> Result<()> {
.or_insert(Vec::new())
.push(value.to_str().unwrap());
}
lua_resp.set("headers", headers)?;
let buf = hyper::body::to_bytes(resp).await.map_err(Error::external)?;
lua_resp.set("body", String::from_utf8_lossy(&buf).into_owned())?;
lua_resp.set("headers", headers)?;
lua_resp.set("body", BodyReader::new(resp.into_body()))?;
Ok(lua_resp)
})?;
@@ -34,22 +58,25 @@ async fn main() -> Result<()> {
let globals = lua.globals();
globals.set("fetch_url", fetch_url)?;
let thread = lua
let f = lua
.load(
r#"
coroutine.create(function ()
local res = fetch_url("http://httpbin.org/ip");
print(res.status)
for key, vals in pairs(res.headers) do
for _, val in ipairs(vals) do
print(key..": "..val)
end
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
print(res.body)
end)
end
repeat
local body = res.body:read()
if body then
print(body)
end
until not body
"#,
)
.eval::<Thread>()?;
.into_function()?;
thread.into_async(()).await
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);
}
}
+77 -51
View File
@@ -1,5 +1,5 @@
use std::net::Shutdown;
use std::rc::Rc;
use std::sync::Arc;
use bstr::BString;
use tokio::net::{TcpListener, TcpStream};
@@ -7,24 +7,31 @@ use tokio::prelude::*;
use tokio::sync::Mutex;
use tokio::task;
use mlua::{Function, Lua, Result, Thread, UserData, UserDataMethods};
use mlua::{Function, Lua, Result, UserData, UserDataMethods};
#[derive(Clone)]
struct LuaTcpListener(Option<Rc<Mutex<TcpListener>>>);
struct LuaTcp;
#[derive(Clone)]
struct LuaTcpStream(Rc<Mutex<TcpStream>>);
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_function("bind", |_, addr: String| async {
let listener = TcpListener::bind(addr).await?;
Ok(LuaTcpListener(Some(Rc::new(Mutex::new(listener)))))
});
methods.add_async_method("accept", |_, listener, ()| async {
let (stream, _) = listener.0.unwrap().lock().await.accept().await?;
Ok(LuaTcpStream(Rc::new(Mutex::new(stream))))
methods.add_async_method("accept", |_, listener, ()| async move {
let (stream, _) = listener.0.lock().await.accept().await?;
Ok(LuaTcpStream(Arc::new(Mutex::new(stream))))
});
}
}
@@ -37,15 +44,13 @@ impl UserData for LuaTcpStream {
methods.add_async_method("read", |_, stream, size: usize| async move {
let mut buf = vec![0; size];
let mut stream = stream.0.lock().await;
let n = stream.read(&mut buf).await?;
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 mut stream = stream.0.lock().await;
let n = stream.write(&data).await?;
let n = stream.0.lock().await.write(&data).await?;
Ok(n)
});
@@ -56,48 +61,69 @@ impl UserData for LuaTcpStream {
}
}
#[tokio::main]
async fn main() -> Result<()> {
let lua = Lua::new();
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", LuaTcpListener(None))?;
globals.set("tcp", LuaTcp)?;
globals.set("spawn", spawn)?;
globals.set(
"spawn",
lua.create_function(move |lua: &Lua, func: Function| {
let fut = lua.create_thread(func)?.into_async::<_, ()>(());
task::spawn_local(async move { fut.await.unwrap() });
Ok(())
})?,
)?;
let thread = lua
let server = lua
.load(
r#"
coroutine.create(function ()
local listener = tcp.bind("0.0.0.0:1234")
print("listening on 0.0.0.0:1234")
while true do
local stream = listener:accept()
print("connected from " .. stream:peer_addr())
spawn(function()
while true do
local data = stream:read(100)
data = data:match("^%s*(.-)%s*$") -- trim
print(data)
stream:write("got: "..data.."\n")
if data == "exit" then
stream:close()
break
end
end
end)
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
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
"#,
)
.eval::<Thread>()?;
.into_function()?;
thread.into_async(()).await
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) };
}
+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)
}
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "mlua_derive"
version = "0.3.0"
version = "0.4.0"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2018"
description = "Procedural macros for the mlua crate."
+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 wrapped = quote_spanned! { span =>
mlua::require_module_feature!();
#[no_mangle]
unsafe extern "C" fn #ext_entrypoint_name(state: *mut mlua::lua_State) -> std::os::raw::c_int {
#item
+50 -6
View File
@@ -12,7 +12,7 @@ use crate::lua::Lua;
use crate::string::String;
use crate::table::Table;
use crate::thread::Thread;
use crate::types::{LightUserData, Number};
use crate::types::{LightUserData, MaybeSend, Number};
use crate::userdata::{AnyUserData, UserData};
use crate::value::{FromLua, Nil, ToLua, Value};
@@ -122,7 +122,7 @@ impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
}
}
impl<'lua, T: 'static + 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>> {
Ok(Value::UserData(lua.create_userdata(self)?))
}
@@ -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>> {
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>> {
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>> {
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>> {
Ok(Value::String(lua.create_string(&self)?))
}
@@ -373,6 +373,50 @@ macro_rules! lua_convert_float {
lua_convert_float!(f32);
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> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_sequence_from(self)?))
+42 -7
View File
@@ -2,10 +2,10 @@ use std::error::Error as StdError;
use std::fmt;
use std::io::Error as IoError;
use std::net::AddrParseError;
use std::rc::Rc;
use std::result::Result as StdResult;
use std::str::Utf8Error;
use std::string::String as StdString;
use std::sync::Arc;
/// Error type returned by `mlua` methods.
#[derive(Debug, Clone)]
@@ -36,6 +36,18 @@ pub enum Error {
/// The Lua VM returns this error when there is an error running a `__gc` metamethod.
#[cfg(any(feature = "lua53", feature = "lua52"))]
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.
///
/// This is an error because a mutable callback can only be borrowed mutably once.
@@ -118,7 +130,7 @@ pub enum Error {
/// Lua call stack backtrace.
traceback: StdString,
/// Original error returned by the Rust code.
cause: Rc<Error>,
cause: Arc<Error>,
},
/// A custom error.
///
@@ -127,7 +139,10 @@ pub enum Error {
/// Returning `Err(ExternalError(...))` from a Rust callback will raise the error as a Lua
/// error. The Rust code that originally invoked the Lua code then receives a `CallbackError`,
/// from which the original error (and a stack traceback) can be recovered.
ExternalError(Rc<dyn StdError>),
#[cfg(feature = "send")]
ExternalError(Arc<dyn StdError + Send + Sync>),
#[cfg(not(feature = "send"))]
ExternalError(Arc<dyn StdError>),
}
/// A specialized `Result` type used by `mlua`'s API.
@@ -145,6 +160,15 @@ impl fmt::Display for Error {
Error::GarbageCollectorError(ref 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::CallbackDestructed => write!(
fmt,
@@ -206,6 +230,12 @@ impl StdError for Error {
}
impl Error {
#[cfg(feature = "send")]
pub fn external<T: Into<Box<dyn StdError + Send + Sync>>>(err: T) -> Error {
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())
}
@@ -215,10 +245,15 @@ pub trait ExternalError {
fn to_lua_err(self) -> Error;
}
impl<E> ExternalError for E
where
E: Into<Box<dyn StdError>>,
{
#[cfg(feature = "send")]
impl<E: Into<Box<dyn StdError + Send + Sync>>> ExternalError for E {
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 {
Error::external(self)
}
+42 -31
View File
@@ -22,6 +22,8 @@
// Based on github.com/keplerproject/lua-compat-5.3
#![allow(clippy::needless_return)]
use std::ffi::CStr;
use std::mem;
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_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_pushnumber, lua_pushthread, lua_pushvalue, lua_rawget_old, lua_rawgeti_old, lua_rawset,
lua_replace, lua_setfield, lua_setglobal, lua_setmetatable, lua_settable, lua_toboolean,
lua_tointeger, lua_tolstring, lua_tonumber, lua_topointer, lua_tostring, lua_touserdata,
lua_type, lua_typename,
lua_pushnil, lua_pushnumber, lua_pushthread, lua_pushvalue, lua_rawget_old, lua_rawgeti_old,
lua_rawset, lua_replace, lua_setfield, lua_setglobal, lua_setmetatable, lua_settable,
lua_toboolean, lua_tointeger, lua_tolstring, lua_tonumber, lua_topointer, lua_tostring,
lua_touserdata, lua_type, lua_typename,
};
#[cfg(any(feature = "lua51", feature = "luajit"))]
use super::lua::{
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_pushlightuserdata, lua_pushnil, lua_pushstring_old, lua_rawequal, lua_remove,
lua_resume_old, lua_setfenv, lua_settop, LUA_OPADD, LUA_OPUNM,
lua_pushlightuserdata, lua_pushstring_old, lua_rawequal, lua_remove, lua_resume_old,
lua_setfenv, lua_settop, LUA_OPADD, LUA_OPUNM,
};
#[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(
L,
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"))]
static COMPAT53_ARITH_CODE: &'static str = r#"
static COMPAT53_ARITH_CODE: &str = r#"
local op,a,b=...
if op == 0 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 {
let n = lua_tonumber(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;
}
}
@@ -297,7 +299,7 @@ pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub unsafe fn lua_tonumberx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Number {
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 {
1
} 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 {
let mut ok = 0;
let n = lua_tonumberx(L, i, &mut ok);
if ok != 0 {
if n == n as lua_Integer as lua_Number {
if isnum != ptr::null_mut() {
*isnum = 1;
}
return n as lua_Integer;
let n_int = n as lua_Integer;
if ok != 0 && (n - n_int as lua_Number).abs() < lua_Number::EPSILON {
if !isnum.is_null() {
*isnum = 1;
}
return n_int;
}
if isnum != ptr::null_mut() {
if !isnum.is_null() {
*isnum = 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);
lua_rawget(L, -2);
let mut _ptr = lua_touserdata(L, -1);
if _ptr == ptr::null_mut() {
if _ptr.is_null() {
lua_pop(L, 1);
_ptr = lua_newuserdata(L, LUA_EXTRASPACE as usize);
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_rawget(L, -3);
let mptr = lua_touserdata(L, -1);
if mptr != ptr::null_mut() {
if !mptr.is_null() {
ptr::copy_nonoverlapping(mptr, _ptr, LUA_EXTRASPACE as usize)
} else {
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"))]
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 msg != ptr::null() {
if !msg.is_null() {
luaL_error(L, cstr!("stack overflow (%s)"), msg);
} else {
lua_pushliteral(L, "stack overflow");
@@ -654,7 +655,7 @@ pub unsafe fn luaL_traceback(
0
};
if msg != ptr::null() {
if !msg.is_null() {
lua_pushfstring(L, cstr!("%s\n"), msg);
}
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 {
if lua_isstring(L, -1) == 0 {
luaL_error(L, cstr!("'__tostring' must return a string"));
}
} else if lua_isstring(L, -1) == 0 {
luaL_error(L, cstr!("'__tostring' must return a string"));
}
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 {
let mut p = lua_touserdata(L, i);
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();
} else {
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"))]
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"));
while (*l).name != ptr::null() {
while !(*l).name.is_null() {
// fill the table with given functions
l = l.offset(1);
lua_pushstring(L, (*l).name);
@@ -786,13 +785,25 @@ pub unsafe fn luaL_requiref(
lua_pop(L, 1);
lua_pushcfunction(L, openf);
lua_pushstring(L, modname);
lua_call(L, 1, 1);
lua_pushvalue(L, -1);
lua_setfield(L, -3, modname);
#[cfg(any(feature = "lua52", feature = "lua51"))]
{
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_setglobal(L, modname);
}
if cfg!(feature = "luajit") && glb == 0 {
lua_pushnil(L);
lua_setglobal(L, modname);
}
lua_replace(L, -2);
}
+10 -6
View File
@@ -1,6 +1,6 @@
// The MIT License (MIT)
//
// Copyright (c) 2019 A. Orlenko
// Copyright (c) 2019-2020 A. Orlenko
// Copyright (c) 2014 J.C. Moyer
//
// 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) \
{ TY_INT, name, .int_val = val }
#if LUA_VERSION_NUM == 503
#if LUA_VERSION_NUM >= 503
#define TY_LUAINT 1
#define RS_LUAINT(name, val) \
{ TY_LUAINT, name, .lua_int_val = val }
@@ -84,6 +84,8 @@ const char *rs_int_type(int width) {
return "i32";
case 8:
return "i64";
case 16:
return "i128";
}
}
@@ -96,6 +98,8 @@ const char *rs_uint_type(int width) {
return "u32";
case 8:
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);
}
#if LUA_VERSION_NUM == 503
#if LUA_VERSION_NUM >= 503
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,
value);
@@ -152,7 +156,7 @@ int write_str_item(FILE *f, const char *name, const char *value) {
char *buf = malloc(bufsz);
int ret;
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);
return ret;
}
@@ -171,7 +175,7 @@ int write_item(FILE *f, const rs_item *c) {
switch (c->type) {
case TY_INT:
return write_int_item(f, c->name, c->int_val);
#if LUA_VERSION_NUM == 503
#if LUA_VERSION_NUM >= 503
case TY_LUAINT:
return write_lua_int_item(f, c->name, c->lua_int_val);
#endif
@@ -251,7 +255,7 @@ int main(int argc, const char **argv) {
// == lauxlib.h ==========================================================
RS_COMMENT("lauxlib.h"),
#if LUA_VERSION_NUM == 503
#if LUA_VERSION_NUM >= 503
RS_INT("LUAL_NUMSIZES", LUAL_NUMSIZES),
#endif
+25 -23
View File
@@ -1,6 +1,6 @@
// The MIT License (MIT)
//
// Copyright (c) 2019 A. Orlenko
// Copyright (c) 2019-2020 A. Orlenko
// Copyright (c) 2014 J.C. Moyer
//
// 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};
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub use super::glue::LUAL_NUMSIZES;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
@@ -49,10 +49,10 @@ pub struct luaL_Reg {
pub func: lua_CFunction,
}
#[cfg(any(feature = "lua53", feature = "lua52"))]
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)]
pub unsafe fn luaL_checkversion(L: *mut lua_State) {
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
luaL_checkversion_(
L,
lua::LUA_VERSION_NUM as lua_Number,
@@ -63,19 +63,19 @@ pub unsafe fn luaL_checkversion(L: *mut lua_State) {
}
extern "C" {
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number, sz: usize);
#[cfg(feature = "lua52")]
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;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
#[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_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_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;
@@ -90,20 +90,20 @@ extern "C" {
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;
#[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_checktype(L: *mut lua_State, arg: c_int, t: 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;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
#[link_name = "luaL_newmetatable"]
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);
#[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_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,
) -> 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;
#[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;
}
@@ -132,21 +132,21 @@ extern "C" {
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);
#[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)
-> c_int;
#[cfg(any(feature = "lua51", feature = "luajit"))]
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)]
pub unsafe fn luaL_loadfile(L: *mut lua_State, f: *const c_char) -> c_int {
luaL_loadfilex(L, f, ptr::null())
}
extern "C" {
#[cfg(any(feature = "lua53", feature = "lua52"))]
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaL_loadbufferx(
L: *mut lua_State,
buff: *const c_char,
@@ -165,9 +165,11 @@ extern "C" {
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;
// TODO (lua54): luaL_addgsub
pub fn luaL_gsub(
L: *mut lua_State,
s: *const c_char,
@@ -175,17 +177,17 @@ extern "C" {
r: *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);
#[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;
#[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);
// Skip Lua 5.2 implementation in favor of the compat53 one
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn luaL_requiref(
L: *mut lua_State,
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
#[cfg(any(feature = "lua53", feature = "lua52"))]
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)]
pub unsafe fn luaL_loadbuffer(
L: *mut lua_State,
@@ -301,4 +303,4 @@ pub unsafe fn luaL_loadbuffer(
// TODO: Add buffer API
// omitted: old module system compatibility
// omitted: old module system compatibility (removed in 5.4)
+169 -68
View File
@@ -1,6 +1,6 @@
// The MIT License (MIT)
//
// Copyright (c) 2019 A. Orlenko
// Copyright (c) 2019-2020 A. Orlenko
// Copyright (c) 2014 J.C. Moyer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -23,9 +23,11 @@
//! 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_char, c_int, c_void};
#[cfg(feature = "lua54")]
use std::os::raw::{c_uint, c_ushort};
use std::ptr;
use super::luaconf;
@@ -36,7 +38,7 @@ pub use super::glue::LUA_REGISTRYINDEX;
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub use super::glue::{LUA_ENVIRONINDEX, LUA_GLOBALSINDEX};
pub const LUA_SIGNATURE: &'static [u8] = b"\x1bLua";
pub const LUA_SIGNATURE: &[u8] = b"\x1bLua";
// option for multiple returns in 'lua_pcall' and 'lua_call'
pub const LUA_MULTRET: c_int = -1;
@@ -51,13 +53,14 @@ pub use super::compat53::{
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub use super::compat53::{
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")]
pub use super::compat53::lua_getglobal;
#[cfg(any(feature = "lua53", feature = "lua52"))]
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)]
pub fn lua_upvalueindex(i: c_int) -> c_int {
LUA_REGISTRYINDEX - i
@@ -71,7 +74,7 @@ pub const LUA_ERRSYNTAX: c_int = 3;
pub const LUA_ERRMEM: c_int = 4;
#[cfg(any(feature = "lua53", feature = "lua52"))]
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;
#[cfg(any(feature = "lua53", feature = "lua52"))]
pub const LUA_ERRERR: c_int = 6;
@@ -91,6 +94,8 @@ pub const LUA_TFUNCTION: c_int = 6;
pub const LUA_TUSERDATA: c_int = 7;
pub const LUA_TTHREAD: c_int = 8;
#[cfg(feature = "lua54")]
pub const LUA_NUMTYPES: c_int = 9;
#[cfg(any(feature = "lua53", feature = "lua52"))]
pub const LUA_NUMTAGS: c_int = 9;
@@ -98,11 +103,11 @@ pub const LUA_NUMTAGS: c_int = 9;
pub const LUA_MINSTACK: c_int = 20;
// 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;
#[cfg(any(feature = "lua53", feature = "lua52"))]
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
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;
/// A Lua number, usually equivalent to `f64`.
@@ -115,14 +120,14 @@ pub type lua_Integer = luaconf::LUA_INTEGER;
pub type lua_Unsigned = luaconf::LUA_UNSIGNED;
// type for continuation-function contexts
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub type lua_KContext = luaconf::LUA_KCONTEXT;
/// Type for native functions that can be passed to Lua.
pub type lua_CFunction = unsafe extern "C" fn(L: *mut lua_State) -> c_int;
// Type for continuation functions
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub type lua_KFunction =
unsafe extern "C" fn(L: *mut lua_State, status: c_int, ctx: lua_KContext) -> c_int;
@@ -132,7 +137,7 @@ pub type lua_Reader =
pub type lua_Writer =
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(
ud: *mut c_void,
ptr: *mut c_void,
@@ -140,19 +145,28 @@ pub type lua_Alloc = unsafe extern "C" fn(
nsize: usize,
) -> *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" {
// state manipulation
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_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;
#[cfg(feature = "lua54")]
pub fn lua_version(L: *mut lua_State) -> lua_Number;
#[cfg(feature = "lua53")]
pub fn lua_version(L: *mut lua_State) -> *const lua_Number;
// 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_gettop(L: *mut lua_State) -> c_int;
pub fn lua_settop(L: *mut lua_State, idx: c_int);
@@ -163,9 +177,9 @@ extern "C" {
pub fn lua_insert(L: *mut lua_State, idx: c_int);
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
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);
#[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_checkstack(L: *mut lua_State, sz: c_int) -> c_int;
@@ -175,7 +189,7 @@ extern "C" {
pub fn lua_isnumber(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_isstring(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_iscfunction(L: *mut lua_State, idx: c_int) -> c_int;
#[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_isuserdata(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_type(L: *mut lua_State, idx: c_int) -> c_int;
@@ -183,17 +197,17 @@ extern "C" {
#[cfg(any(feature = "lua51", feature = "luajit"))]
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;
#[cfg(any(feature = "lua51", feature = "luajit"))]
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_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;
#[cfg(any(feature = "lua51", feature = "luajit"))]
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_tocfunction(L: *mut lua_State, idx: c_int) -> lua_CFunction;
pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
@@ -215,31 +229,31 @@ pub const LUA_OPPOW: c_int = 5;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
pub const LUA_OPUNM: c_int = 6;
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPMOD: c_int = 3;
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPPOW: c_int = 4;
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPDIV: c_int = 5;
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPIDIV: c_int = 6;
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPBAND: c_int = 7;
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPBOR: c_int = 8;
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPBXOR: c_int = 9;
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPSHL: c_int = 10;
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPSHR: c_int = 11;
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPUNM: c_int = 12;
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPBNOT: c_int = 13;
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);
}
@@ -253,7 +267,7 @@ extern "C" {
pub fn lua_rawequal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
#[cfg(any(feature = "lua51", feature = "luajit"))]
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;
}
@@ -263,13 +277,13 @@ extern "C" {
pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number);
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;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
#[link_name = "lua_pushlstring"]
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;
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[link_name = "lua_pushstring"]
@@ -286,49 +300,59 @@ extern "C" {
// get functions (Lua -> stack)
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;
#[cfg(feature = "lua52")]
#[link_name = "lua_getglobal"]
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;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
#[link_name = "lua_gettable"]
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;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
#[link_name = "lua_getfield"]
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;
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
#[link_name = "lua_rawget"]
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;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
#[link_name = "lua_rawgeti"]
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;
#[cfg(feature = "lua52")]
#[link_name = "lua_rawgetp"]
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);
#[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_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")]
pub fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int;
#[cfg(feature = "lua52")]
@@ -338,28 +362,48 @@ extern "C" {
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)
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_settable(L: *mut lua_State, idx: c_int);
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_rawset(L: *mut lua_State, idx: c_int);
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_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"))]
pub fn lua_setuservalue(L: *mut lua_State, idx: c_int);
#[cfg(any(feature = "lua51", feature = "luajit"))]
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)
extern "C" {
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_callk(
L: *mut lua_State,
nargs: c_int,
@@ -376,7 +420,7 @@ extern "C" {
k: Option<lua_CFunction>,
);
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_pcallk(
L: *mut lua_State,
nargs: c_int,
@@ -412,7 +456,7 @@ extern "C" {
mode: *const c_char,
) -> c_int;
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_dump(
L: *mut lua_State,
writer: lua_Writer,
@@ -424,13 +468,13 @@ extern "C" {
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)]
pub unsafe fn lua_call(L: *mut lua_State, n: c_int, r: c_int) {
lua_callk(L, n, r, 0, None)
}
#[cfg(any(feature = "lua53", feature = "lua52"))]
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)]
pub unsafe fn lua_pcall(L: *mut lua_State, n: c_int, r: c_int, f: c_int) -> c_int {
lua_pcallk(L, n, r, f, 0, None)
@@ -438,7 +482,7 @@ pub unsafe fn lua_pcall(L: *mut lua_State, n: c_int, r: c_int, f: c_int) -> c_in
// coroutine functions
extern "C" {
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_yieldk(
L: *mut lua_State,
nresults: c_int,
@@ -455,23 +499,57 @@ extern "C" {
#[cfg(any(feature = "lua51", feature = "luajit"))]
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"))]
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"))]
#[link_name = "lua_resume"]
pub fn lua_resume_old(L: *mut lua_State, narg: c_int) -> 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;
}
#[cfg(any(feature = "lua53", feature = "lua52"))]
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)]
pub unsafe fn lua_yield(L: *mut lua_State, n: c_int) -> c_int {
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
pub const LUA_GCSTOP: c_int = 0;
pub const LUA_GCRESTART: c_int = 1;
@@ -481,10 +559,22 @@ pub const LUA_GCCOUNTB: c_int = 4;
pub const LUA_GCSTEP: c_int = 5;
pub const LUA_GCSETPAUSE: c_int = 6;
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;
#[cfg(feature = "lua54")]
pub const LUA_GCGEN: c_int = 10;
#[cfg(feature = "lua54")]
pub const LUA_GCINC: c_int = 11;
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;
}
@@ -493,29 +583,31 @@ extern "C" {
pub fn lua_error(L: *mut lua_State) -> !;
pub fn lua_next(L: *mut lua_State, idx: c_int) -> 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);
#[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_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);
#[cfg(feature = "lua54")]
pub fn lua_toclose(L: *mut lua_State, idx: c_int);
}
// some useful macros
// here, implemented as Rust functions
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
#[inline(always)]
pub unsafe fn lua_getextraspace(L: *mut lua_State) -> *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)]
pub unsafe fn lua_tonumber(L: *mut lua_State, i: c_int) -> lua_Number {
lua_tonumberx(L, i, ptr::null_mut())
}
#[cfg(any(feature = "lua53", feature = "lua52"))]
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)]
pub unsafe fn lua_tointeger(L: *mut lua_State, i: c_int) -> lua_Integer {
lua_tointegerx(L, i, ptr::null_mut())
@@ -601,7 +693,7 @@ pub unsafe fn lua_getglobal(L: *mut lua_State, var: *const c_char) -> c_int {
lua_getfield(L, LUA_GLOBALSINDEX, var)
}
#[cfg(any(feature = "lua53", feature = "lua52"))]
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)]
pub unsafe fn lua_pushglobaltable(L: *mut lua_State) -> c_int {
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)
@@ -612,20 +704,20 @@ pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
lua_tolstring(L, i, ptr::null_mut())
}
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
#[inline(always)]
pub unsafe fn lua_insert(L: *mut lua_State, idx: c_int) {
lua_rotate(L, idx, 1)
}
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
#[inline(always)]
pub unsafe fn lua_remove(L: *mut lua_State, idx: c_int) {
lua_rotate(L, idx, -1);
lua_pop(L, 1)
}
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
#[inline(always)]
pub unsafe fn lua_replace(L: *mut lua_State, idx: c_int) {
lua_copy(L, -1, idx);
@@ -647,7 +739,7 @@ pub const LUA_MASKLINE: c_int = 1 << (LUA_HOOKLINE as usize);
pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize);
/// Type for functions to be called on debug events.
pub type lua_Hook = 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" {
pub fn lua_getstack(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int;
@@ -657,18 +749,21 @@ extern "C" {
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;
#[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;
#[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_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_gethookmask(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)]
pub struct lua_Debug {
pub event: c_int,
@@ -676,6 +771,8 @@ pub struct lua_Debug {
pub namewhat: *const c_char,
pub what: *const c_char,
pub source: *const c_char,
#[cfg(feature = "lua54")]
pub srclen: usize,
pub currentline: c_int,
pub linedefined: c_int,
pub lastlinedefined: c_int,
@@ -683,6 +780,10 @@ pub struct lua_Debug {
pub nparams: c_uchar,
pub isvararg: 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],
// lua.h mentions this is for private use
i_ci: *mut c_void,
+3 -3
View File
@@ -1,6 +1,6 @@
// The MIT License (MIT)
//
// Copyright (c) 2019 A. Orlenko
// Copyright (c) 2019-2020 A. Orlenko
// Copyright (c) 2014 J.C. Moyer
//
// 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;
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub use super::glue::LUAL_NUMSIZES;
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub type LUA_KCONTEXT = isize; // intptr_t
+4 -4
View File
@@ -1,6 +1,6 @@
// The MIT License (MIT)
//
// Copyright (c) 2019 A. Orlenko
// Copyright (c) 2019-2020 A. Orlenko
// Copyright (c) 2014 J.C. Moyer
//
// 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,
};
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub use super::glue::LUA_UTF8LIBNAME;
#[cfg(any(feature = "lua52", feature = "luajit"))]
@@ -43,13 +43,13 @@ pub use super::glue::{LUA_FFILIBNAME, LUA_JITLIBNAME};
extern "C" {
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_table(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_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;
#[cfg(feature = "lua52")]
pub fn luaopen_bit32(L: *mut lua_State) -> c_int;
+48 -10
View File
@@ -1,6 +1,6 @@
// The MIT License (MIT)
//
// Copyright (c) 2019 A. Orlenko
// Copyright (c) 2019-2020 A. Orlenko
// Copyright (c) 2014 J.C. Moyer
//
// 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,
};
#[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};
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub use self::lua::lua_setfenv;
pub use self::lua::{lua_getfenv, lua_setfenv};
// C API functions
pub use self::lua::{
@@ -155,10 +158,16 @@ pub use self::lua::{
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};
#[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};
// auxiliary library types
@@ -177,7 +186,7 @@ pub use self::lauxlib::{
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};
// lualib.h functions
@@ -186,7 +195,7 @@ pub use self::lualib::{
luaopen_package, luaopen_string, luaopen_table,
};
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub use self::lualib::{luaopen_coroutine, luaopen_utf8};
#[cfg(feature = "lua52")]
@@ -206,13 +215,19 @@ pub use self::lua::{
LUA_TTABLE, LUA_TTHREAD, LUA_TUSERDATA, LUA_YIELD,
};
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub use self::lua::{
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"))]
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"))]
pub use self::lua::{LUA_ENVIRONINDEX, LUA_GLOBALSINDEX};
@@ -226,7 +241,7 @@ pub use self::lualib::{
LUA_STRLIBNAME, LUA_TABLIBNAME,
};
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub use self::lualib::LUA_UTF8LIBNAME;
#[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
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(clippy::unreadable_literal)]
mod glue {
include!(concat!(env!("OUT_DIR"), "/glue.rs"));
}
+46
View File
@@ -9,6 +9,9 @@ use crate::util::{
};
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
#[cfg(feature = "async")]
use {futures_core::future::LocalBoxFuture, futures_util::future};
/// Handle to an internal Lua function.
#[derive(Clone, Debug)]
pub struct Function<'lua>(pub(crate) LuaRef<'lua>);
@@ -86,6 +89,49 @@ impl<'lua> Function<'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
/// arguments.
///
+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())
}
}
+22 -2
View File
@@ -24,6 +24,18 @@
//! 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.
//!
//! # 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`]: struct.Lua.html
//! [executing]: struct.Lua.html#method.exec
@@ -35,6 +47,10 @@
//! [`FromLuaMulti`]: trait.FromLuaMulti.html
//! [`UserData`]: trait.UserData.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*
// warnings at all.
@@ -47,8 +63,10 @@ mod conversion;
mod error;
mod ffi;
mod function;
mod hook;
mod lua;
mod multi;
mod scope;
mod stdlib;
mod string;
mod table;
@@ -62,11 +80,13 @@ pub use crate::ffi::lua_State;
pub use crate::error::{Error, ExternalError, ExternalResult, Result};
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, GCMode, Lua};
pub use crate::multi::Variadic;
pub use crate::scope::Scope;
pub use crate::stdlib::StdLib;
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::types::{Integer, LightUserData, Number, RegistryKey};
pub use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
+731 -169
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)
};
}
#[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");
};
}
+4 -3
View File
@@ -3,9 +3,10 @@
pub use crate::{
AnyUserData as LuaAnyUserData, Chunk as LuaChunk, Error as LuaError,
ExternalError as LuaExternalError, ExternalResult as LuaExternalResult, FromLua, FromLuaMulti,
Function as LuaFunction, Integer as LuaInteger, LightUserData as LuaLightUserData, Lua,
MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber,
RegistryKey as LuaRegistryKey, Result as LuaResult, String as LuaString, Table as LuaTable,
Function as LuaFunction, GCMode as LuaGCMode, Integer as LuaInteger,
LightUserData as LuaLightUserData, Lua, MetaMethod as LuaMetaMethod,
MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber, RegistryKey as LuaRegistryKey,
Result as LuaResult, String as LuaString, Table as LuaTable, TableExt as LuaTableExt,
TablePairs as LuaTablePairs, TableSequence as LuaTableSequence, Thread as LuaThread,
ThreadStatus as LuaThreadStatus, ToLua, ToLuaMulti, UserData as LuaUserData,
UserDataMethods as LuaUserDataMethods, Value as LuaValue,
+600
View File
@@ -0,0 +1,600 @@
use std::any::Any;
use std::cell::{Cell, RefCell};
use std::marker::PhantomData;
use std::mem;
use std::os::raw::c_void;
use std::rc::Rc;
use crate::error::{Error, Result};
use crate::ffi;
use crate::function::Function;
use crate::lua::Lua;
use crate::types::{Callback, LuaRef, MaybeSend};
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
use crate::util::{
assert_stack, init_userdata_metatable, protect_lua_closure, push_string, push_userdata,
take_userdata, StackGuard,
};
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
/// callbacks that are not required to be Send or 'static.
///
/// See [`Lua::scope`] for more details.
///
/// [`Lua::scope`]: struct.Lua.html#method.scope
#[allow(clippy::type_complexity)]
pub struct Scope<'lua, 'scope> {
lua: &'lua Lua,
destructors: RefCell<Vec<(LuaRef<'lua>, fn(LuaRef<'lua>) -> Vec<Box<dyn Any>>)>>,
_scope_invariant: PhantomData<Cell<&'scope ()>>,
}
impl<'lua, 'scope> Scope<'lua, 'scope> {
pub(crate) fn new(lua: &'lua Lua) -> Scope<'lua, 'scope> {
Scope {
lua,
destructors: RefCell::new(Vec::new()),
_scope_invariant: PhantomData,
}
}
/// Wraps a Rust function or closure, creating a callable Lua function handle to it.
///
/// This is a version of [`Lua::create_function`] that creates a callback which expires on
/// scope drop. See [`Lua::scope`] for more details.
///
/// [`Lua::create_function`]: struct.Lua.html#method.create_function
/// [`Lua::scope`]: struct.Lua.html#method.scope
pub fn create_function<'callback, A, R, F>(&'callback self, func: F) -> Result<Function<'lua>>
where
A: FromLuaMulti<'callback>,
R: ToLuaMulti<'callback>,
F: 'scope + Fn(&'callback Lua, A) -> Result<R>,
{
// Safe, because 'scope must outlive 'callback (due to Self containing 'scope), however the
// callback itself must be 'scope lifetime, so the function should not be able to capture
// anything of 'callback lifetime. 'scope can't be shortened due to being invariant, and
// the 'callback lifetime here can't be enlarged due to coming from a universal
// quantification in Lua::scope.
//
// I hope I got this explanation right, but in any case this is tested with compiletest_rs
// to make sure callbacks can't capture handles with lifetime outside the scope, inside the
// scope, and owned inside the callback itself.
unsafe {
self.create_callback(Box::new(move |lua, args| {
func(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}))
}
}
/// Wraps a Rust mutable closure, creating a callable Lua function handle to it.
///
/// This is a version of [`Lua::create_function_mut`] that creates a callback which expires
/// on scope drop. See [`Lua::scope`] and [`Scope::create_function`] for more details.
///
/// [`Lua::create_function_mut`]: struct.Lua.html#method.create_function_mut
/// [`Lua::scope`]: struct.Lua.html#method.scope
/// [`Scope::create_function`]: #method.create_function
pub fn create_function_mut<'callback, A, R, F>(
&'callback self,
func: F,
) -> Result<Function<'lua>>
where
A: FromLuaMulti<'callback>,
R: ToLuaMulti<'callback>,
F: 'scope + FnMut(&'callback Lua, A) -> Result<R>,
{
let func = RefCell::new(func);
self.create_function(move |lua, args| {
(&mut *func
.try_borrow_mut()
.map_err(|_| Error::RecursiveMutCallback)?)(lua, args)
})
}
/// 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.
///
/// 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
/// UserData be 'static).
/// See [`Lua::scope`] for more details.
///
/// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata
/// [`Lua::scope`]: struct.Lua.html#method.scope
pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
where
T: 'static + UserData,
{
// Safe even though T may not be Send, because the parent Lua cannot be sent to another
// thread while the Scope is alive (or the returned AnyUserData handle even).
unsafe {
let u = self.lua.make_userdata(data)?;
self.destructors.borrow_mut().push((u.0.clone(), |u| {
let state = u.lua.state;
assert_stack(state, 2);
u.lua.push_ref(&u);
// We know the destructor has not run yet because we hold a reference to the
// userdata.
vec![Box::new(take_userdata::<RefCell<T>>(state))]
}));
Ok(u)
}
}
/// 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
/// scope drop, and does not require that the userdata type be Send or 'static. See
/// [`Lua::scope`] for more details.
///
/// 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
/// use [`Scope::create_userdata`] instead.
///
/// 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
/// 'static types. This means that it is impossible, once the userdata is created, to get a
/// reference to it back *out* of an `AnyUserData` handle. This also implies that the
/// "function" type methods that can be added via [`UserDataMethods`] (the ones that accept
/// `AnyUserData` as a first parameter) are vastly less useful. Also, there is no way to re-use
/// 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.
///
/// [`Scope::create_userdata`]: #method.create_userdata
/// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata
/// [`Lua::scope`]: struct.Lua.html#method.scope
/// [`UserDataMethods`]: trait.UserDataMethods.html
pub fn create_nonstatic_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
where
T: 'scope + UserData,
{
let data = Rc::new(RefCell::new(data));
// 'callback outliving 'scope is a lie to make the types work out, required due to the
// inability to work with the more correct callback type that is universally quantified over
// 'lua. This is safe though, because `UserData::add_methods` does not get to pick the 'lua
// lifetime, so none of the static methods UserData types can add can possibly capture
// parameters.
fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
scope: &Scope<'lua, 'scope>,
data: Rc<RefCell<T>>,
method: NonStaticMethod<'callback, T>,
) -> Result<Function<'lua>> {
// On methods that actually receive the userdata, we fake a type check on the passed in
// userdata, where we pretend there is a unique type per call to
// `Scope::create_nonstatic_userdata`. You can grab a method from a userdata and call
// it on a mismatched userdata type, which when using normal 'static userdata will fail
// with a type mismatch, but here without this check would proceed as though you had
// called the method on the original value (since we otherwise completely ignore the
// first argument).
let check_data = data.clone();
let check_ud_type = move |lua: &'callback Lua, value| {
if let Some(value) = value {
if let Value::UserData(u) = value {
unsafe {
assert_stack(lua.state, 1);
lua.push_ref(&u.0);
ffi::lua_getuservalue(lua.state, -1);
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
{
ffi::lua_rawgeti(lua.state, -1, 1);
ffi::lua_remove(lua.state, -2);
}
return ffi::lua_touserdata(lua.state, -1)
== check_data.as_ptr() as *mut c_void;
}
}
}
false
};
match method {
NonStaticMethod::Method(method) => {
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
if !check_ud_type(lua, args.pop_front()) {
return Err(Error::UserDataTypeMismatch);
}
let data = data.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
method(lua, &*data, args)
});
unsafe { scope.create_callback(f) }
}
NonStaticMethod::MethodMut(method) => {
let method = RefCell::new(method);
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
if !check_ud_type(lua, args.pop_front()) {
return Err(Error::UserDataTypeMismatch);
}
let mut method = method
.try_borrow_mut()
.map_err(|_| Error::RecursiveMutCallback)?;
let mut data = data
.try_borrow_mut()
.map_err(|_| Error::UserDataBorrowMutError)?;
(&mut *method)(lua, &mut *data, args)
});
unsafe { scope.create_callback(f) }
}
NonStaticMethod::Function(function) => unsafe { scope.create_callback(function) },
NonStaticMethod::FunctionMut(function) => {
let function = RefCell::new(function);
let f = Box::new(move |lua, args| {
(&mut *function
.try_borrow_mut()
.map_err(|_| Error::RecursiveMutCallback)?)(
lua, args
)
});
unsafe { scope.create_callback(f) }
}
}
}
let mut ud_methods = NonStaticUserDataMethods::default();
T::add_methods(&mut ud_methods);
unsafe {
let lua = self.lua;
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 6);
push_userdata(lua.state, ())?;
#[cfg(any(feature = "lua54", feature = "lua53"))]
ffi::lua_pushlightuserdata(lua.state, data.as_ptr() as *mut c_void);
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
protect_lua_closure(lua.state, 0, 1, |state| {
// Lua 5.2/5.1 allows to store only table. Then we will wrap the value.
ffi::lua_createtable(state, 1, 0);
ffi::lua_pushlightuserdata(state, data.as_ptr() as *mut c_void);
ffi::lua_rawseti(state, -2, 1);
})?;
ffi::lua_setuservalue(lua.state, -2);
protect_lua_closure(lua.state, 0, 1, move |state| {
ffi::lua_newtable(state);
})?;
for (k, m) in ud_methods.meta_methods {
push_string(lua.state, k.name())?;
lua.push_value(Value::Function(wrap_method(self, data.clone(), m)?))?;
protect_lua_closure(lua.state, 3, 1, |state| {
ffi::lua_rawset(state, -3);
})?;
}
if ud_methods.methods.is_empty() {
init_userdata_metatable::<()>(lua.state, -1, None)?;
} else {
protect_lua_closure(lua.state, 0, 1, |state| {
ffi::lua_newtable(state);
})?;
for (k, m) in ud_methods.methods {
push_string(lua.state, &k)?;
lua.push_value(Value::Function(wrap_method(self, data.clone(), m)?))?;
protect_lua_closure(lua.state, 3, 1, |state| {
ffi::lua_rawset(state, -3);
})?;
}
init_userdata_metatable::<()>(lua.state, -2, Some(-1))?;
ffi::lua_pop(lua.state, 1);
}
ffi::lua_setmetatable(lua.state, -2);
Ok(AnyUserData(lua.pop_ref()))
}
}
// Unsafe, because the callback can improperly capture any value with 'callback scope, such as
// improperly capturing an argument. Since the 'callback lifetime is chosen by the user and the
// lifetime of the callback itself is 'scope (non-'static), the borrow checker will happily pick
// a 'callback that outlives 'scope to allow this. In order for this to be safe, the callback
// must NOT capture any parameters.
unsafe fn create_callback<'callback>(
&self,
f: Callback<'callback, 'scope>,
) -> Result<Function<'lua>> {
let f = mem::transmute::<Callback<'callback, 'scope>, Callback<'lua, 'static>>(f);
let f = self.lua.create_callback(f)?;
let mut destructors = self.destructors.borrow_mut();
destructors.push((f.0.clone(), |f| {
let state = f.lua.state;
assert_stack(state, 3);
f.lua.push_ref(&f);
// We know the destructor has not run yet because we hold a reference to the callback.
ffi::lua_getupvalue(state, -1, 1);
let ud1 = take_userdata::<Callback>(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)
}
#[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> {
fn drop(&mut self) {
// We separate the action of invalidating the userdata in Lua and actually dropping the
// userdata type into two phases. This is so that, in the event a userdata drop panics, we
// can be sure that all of the userdata in Lua is actually invalidated.
// All destructors are non-panicking, so this is fine
let to_drop = self
.destructors
.get_mut()
.drain(..)
.flat_map(|(r, dest)| dest(r))
.collect::<Vec<_>>();
drop(to_drop);
}
}
enum NonStaticMethod<'lua, T> {
Method(Box<dyn Fn(&'lua Lua, &T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
MethodMut(Box<dyn FnMut(&'lua Lua, &mut T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
Function(Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
FunctionMut(Box<dyn FnMut(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
}
struct NonStaticUserDataMethods<'lua, T: UserData> {
methods: Vec<(Vec<u8>, NonStaticMethod<'lua, T>)>,
meta_methods: Vec<(MetaMethod, NonStaticMethod<'lua, T>)>,
}
impl<'lua, T: UserData> Default for NonStaticUserDataMethods<'lua, T> {
fn default() -> NonStaticUserDataMethods<'lua, T> {
NonStaticUserDataMethods {
methods: Vec::new(),
meta_methods: Vec::new(),
}
}
}
impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'lua, T> {
fn add_method<S, A, R, M>(&mut self, name: &S, method: M)
where
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
{
self.methods.push((
name.as_ref().to_vec(),
NonStaticMethod::Method(Box::new(move |lua, ud, args| {
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
));
}
fn add_method_mut<S, A, R, M>(&mut self, name: &S, mut method: M)
where
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
{
self.methods.push((
name.as_ref().to_vec(),
NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
));
}
#[cfg(feature = "async")]
fn add_async_method<S, A, R, M, MR>(&mut self, _name: &S, _method: M)
where
T: Clone,
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
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((
name.as_ref().to_vec(),
NonStaticMethod::Function(Box::new(move |lua, args| {
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
));
}
fn add_function_mut<S, A, R, F>(&mut self, name: &S, mut function: F)
where
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
{
self.methods.push((
name.as_ref().to_vec(),
NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
));
}
#[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)
where
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
{
self.meta_methods.push((
meta,
NonStaticMethod::Method(Box::new(move |lua, ud, args| {
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
));
}
fn add_meta_method_mut<A, R, M>(&mut self, meta: MetaMethod, mut method: M)
where
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
{
self.meta_methods.push((
meta,
NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
));
}
fn add_meta_function<A, R, F>(&mut self, meta: MetaMethod, function: F)
where
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
{
self.meta_methods.push((
meta,
NonStaticMethod::Function(Box::new(move |lua, args| {
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
));
}
fn add_meta_function_mut<A, R, F>(&mut self, meta: MetaMethod, mut function: F)
where
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
{
self.meta_methods.push((
meta,
NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
));
}
}
+35 -10
View File
@@ -1,31 +1,56 @@
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign};
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)]
pub struct StdLib(u32);
impl StdLib {
#[cfg(any(feature = "lua53", feature = "lua52"))]
pub const COROUTINE: StdLib = StdLib(1 << 0);
/// [`coroutine`](https://www.lua.org/manual/5.3/manual.html#6.2) library
///
/// 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);
/// [`io`](https://www.lua.org/manual/5.3/manual.html#6.8) library
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);
/// [`string`](https://www.lua.org/manual/5.3/manual.html#6.4) library
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);
#[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);
/// [`math`](https://www.lua.org/manual/5.3/manual.html#6.7) library
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);
#[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);
#[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_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 {
(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::value::{FromLua, FromLuaMulti, Nil, ToLua, ToLuaMulti, Value};
#[cfg(feature = "async")]
use {futures_core::future::LocalBoxFuture, futures_util::future};
/// Handle to an internal Lua table.
#[derive(Clone, Debug)]
pub struct Table<'lua>(pub(crate) LuaRef<'lua>);
#[allow(clippy::len_without_is_empty)]
impl<'lua> Table<'lua> {
/// 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.
///
/// Tables are compared by reference first.
@@ -262,7 +233,7 @@ impl<'lua> Table<'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.
pub fn raw_insert<V: ToLua<'lua>>(&self, idx: Integer, value: V) -> Result<()> {
let lua = self.0.lua;
@@ -292,11 +263,11 @@ impl<'lua> Table<'lua> {
/// Removes a key from the table.
///
/// If `key` is an integer, mlua shifts down the elements from table[key+1],
/// and erases element table[key]. The complexity is O(n) in worst case,
/// 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,
/// 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<()> {
let lua = self.0.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.
///
/// This struct is created by the [`Table::pairs`] method.
@@ -524,9 +610,10 @@ where
lua.push_ref(&self.table);
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
})? {
})?;
if next {
ffi::lua_pushvalue(lua.state, -2);
let key = lua.pop_value();
let value = lua.pop_value();
+22 -17
View File
@@ -45,7 +45,12 @@ pub enum ThreadStatus {
#[derive(Clone, Debug)]
pub struct Thread<'lua>(pub(crate) LuaRef<'lua>);
/// Thread (coroutine) representation as an async Future or Stream.
/// 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> {
@@ -126,7 +131,9 @@ impl<'lua> Thread<'lua> {
}
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 {
protect_lua_closure(lua.state, 0, 0, |_| {
error_traceback(thread_state);
@@ -135,7 +142,6 @@ impl<'lua> Thread<'lua> {
return Err(pop_error(thread_state, ret));
}
let nresults = ffi::lua_gettop(thread_state);
let mut results = MultiValue::new();
ffi::lua_xmove(thread_state, lua.state, nresults);
@@ -180,16 +186,18 @@ impl<'lua> Thread<'lua> {
/// values whereas Future version discards that values and poll until the final
/// one (returned from the thread function).
///
/// Requires `feature = "async"`
///
/// # Examples
///
/// ```
/// # use mlua::{Error, Lua, Result, Thread};
/// use futures_executor::block_on;
/// use futures_util::stream::TryStreamExt;
/// # fn main() -> Result<()> {
/// # 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)
/// coroutine.create(function (sum)
/// for i = 1,10 do
/// sum = sum + i
/// coroutine.yield(sum)
@@ -198,16 +206,13 @@ impl<'lua> Thread<'lua> {
/// end)
/// "#).eval()?;
///
/// let result = block_on(async {
/// let mut s = thread.into_async::<_, i64>(1);
/// let mut sum = 0;
/// while let Some(n) = s.try_next().await? {
/// sum += n;
/// }
/// Ok::<_, Error>(sum)
/// })?;
/// let mut stream = thread.into_async::<_, i64>(1);
/// let mut sum = 0;
/// while let Some(n) = stream.try_next().await? {
/// sum += n;
/// }
///
/// assert_eq!(result, 286);
/// assert_eq!(sum, 286);
///
/// # Ok(())
/// # }
+16 -3
View File
@@ -1,6 +1,6 @@
use std::cell::RefCell;
use std::os::raw::{c_int, c_void};
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::{fmt, mem, ptr};
#[cfg(feature = "async")]
@@ -8,6 +8,7 @@ use futures_core::future::LocalBoxFuture;
use crate::error::Result;
use crate::ffi;
use crate::hook::Debug;
use crate::lua::Lua;
use crate::util::{assert_stack, StackGuard};
use crate::value::MultiValue;
@@ -28,6 +29,18 @@ pub(crate) type Callback<'lua, 'a> =
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.
///
/// This is a handle to a value stored inside the Lua registry. It is not automatically
@@ -45,7 +58,7 @@ pub(crate) type AsyncCallback<'lua, 'a> =
/// [`UserData::get_user_value`]: struct.UserData.html#method.get_user_value
pub struct RegistryKey {
pub(crate) registry_id: c_int,
pub(crate) unref_list: Rc<RefCell<Option<Vec<c_int>>>>,
pub(crate) unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
}
impl fmt::Debug for RegistryKey {
@@ -56,7 +69,7 @@ impl fmt::Debug for RegistryKey {
impl Drop for RegistryKey {
fn drop(&mut self) {
let mut unref_list = mlua_expect!(self.unref_list.try_borrow_mut(), "unref list borrowed");
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);
}
+81 -33
View File
@@ -8,9 +8,9 @@ use crate::ffi;
use crate::function::Function;
use crate::lua::Lua;
use crate::table::Table;
use crate::types::LuaRef;
use crate::types::{LuaRef, MaybeSend};
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.
///
@@ -34,26 +34,31 @@ pub enum MetaMethod {
Pow,
/// The unary minus (`-`) operator.
Unm,
#[cfg(feature = "lua53")]
/// The floor division (//) operator.
/// Requires `feature = "lua54/lua53"`
#[cfg(any(feature = "lua54", feature = "lua53", doc))]
IDiv,
#[cfg(feature = "lua53")]
/// The bitwise AND (&) operator.
/// Requires `feature = "lua54/lua53"`
#[cfg(any(feature = "lua54", feature = "lua53", doc))]
BAnd,
#[cfg(feature = "lua53")]
/// The bitwise OR (|) operator.
/// Requires `feature = "lua54/lua53"`
#[cfg(any(feature = "lua54", feature = "lua53", doc))]
BOr,
#[cfg(feature = "lua53")]
/// The bitwise XOR (binary ~) operator.
/// Requires `feature = "lua54/lua53"`
#[cfg(any(feature = "lua54", feature = "lua53", doc))]
BXor,
#[cfg(feature = "lua53")]
/// The bitwise NOT (unary ~) operator.
/// Requires `feature = "lua54/lua53"`
#[cfg(any(feature = "lua54", feature = "lua53", doc))]
BNot,
#[cfg(feature = "lua53")]
/// The bitwise left shift (<<) operator.
#[cfg(any(feature = "lua54", feature = "lua53", doc))]
Shl,
#[cfg(feature = "lua53")]
/// The bitwise right shift (>>) operator.
#[cfg(any(feature = "lua54", feature = "lua53", doc))]
Shr,
/// The string concatenation operator `..`.
Concat,
@@ -75,11 +80,25 @@ pub enum MetaMethod {
///
/// This is not an operator, but will be called by methods such as `tostring` and `print`.
ToString,
#[cfg(any(feature = "lua53", feature = "lua52"))]
/// The `__pairs` metamethod.
///
/// 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,
/// 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 {
@@ -92,20 +111,22 @@ impl MetaMethod {
MetaMethod::Mod => b"__mod",
MetaMethod::Pow => b"__pow",
MetaMethod::Unm => b"__unm",
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
MetaMethod::IDiv => b"__idiv",
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
MetaMethod::BAnd => b"__band",
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
MetaMethod::BOr => b"__bor",
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
MetaMethod::BXor => b"__bxor",
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
MetaMethod::BNot => b"__bnot",
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
MetaMethod::Shl => b"__shl",
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
MetaMethod::Shr => b"__shr",
MetaMethod::Concat => b"__concat",
MetaMethod::Len => b"__len",
MetaMethod::Eq => b"__eq",
@@ -115,8 +136,12 @@ impl MetaMethod {
MetaMethod::NewIndex => b"__newindex",
MetaMethod::Call => b"__call",
MetaMethod::ToString => b"__tostring",
#[cfg(any(feature = "lua53", feature = "lua52"))]
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
MetaMethod::Pairs => b"__pairs",
#[cfg(feature = "lua54")]
MetaMethod::Close => b"__close",
}
}
}
@@ -137,7 +162,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + 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.
///
@@ -149,13 +174,15 @@ pub trait UserDataMethods<'lua, T: UserData> {
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + 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)
@@ -164,8 +191,8 @@ pub trait UserDataMethods<'lua, T: UserData> {
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + Fn(&'lua Lua, T, A) -> MR,
MR: 'static + Future<Output = Result<R>>;
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
/// be a `UserData` of type T if the method is called with Lua method syntax:
@@ -181,7 +208,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + 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.
///
@@ -193,13 +220,15 @@ pub trait UserDataMethods<'lua, T: UserData> {
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + 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)
@@ -208,8 +237,8 @@ pub trait UserDataMethods<'lua, T: UserData> {
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + Fn(&'lua Lua, A) -> FR,
FR: 'static + Future<Output = Result<R>>;
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.
///
@@ -223,7 +252,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
where
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + 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.
///
@@ -237,7 +266,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
where
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + 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.
///
@@ -248,7 +277,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
where
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + 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.
///
@@ -259,7 +288,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
where
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + FnMut(&'lua Lua, A) -> Result<R>;
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>;
}
/// Trait for custom userdata types.
@@ -387,18 +416,19 @@ impl<'lua> AnyUserData<'lua> {
/// Sets an associated value to this `AnyUserData`.
///
/// 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
pub fn set_user_value<V: ToLua<'lua>>(&self, v: V) -> Result<()> {
let lua = self.0.lua;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
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()?;
t.raw_set(1, v)?;
crate::Value::Table(t)
};
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
let v = v.to_lua(lua)?;
unsafe {
let _sg = StackGuard::new(lua.state);
@@ -412,6 +442,8 @@ impl<'lua> AnyUserData<'lua> {
/// 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
pub fn get_user_value<V: FromLua<'lua>>(&self) -> Result<V> {
let lua = self.0.lua;
@@ -424,10 +456,26 @@ impl<'lua> AnyUserData<'lua> {
};
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
return crate::Table::from_lua(res, lua)?.get(1);
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua54", feature = "lua53"))]
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>> {
unsafe {
let lua = self.0.lua;
+54 -22
View File
@@ -1,18 +1,18 @@
use std::any::{Any, TypeId};
use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::Write;
use std::os::raw::{c_char, c_int, c_void};
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::{mem, ptr, slice};
use crate::error::{Error, Result};
use crate::ffi;
thread_local! {
static METATABLE_CACHE: RefCell<HashMap<TypeId, c_int>> = RefCell::new(HashMap::new());
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
@@ -57,10 +57,11 @@ impl Drop for StackGuard {
fn drop(&mut self) {
unsafe {
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 {
ffi::lua_settop(self.state, self.top);
} else if top < self.top {
mlua_panic!("{} too many stack values popped", self.top - top);
}
}
}
@@ -468,32 +469,41 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
ud,
WrappedError(Error::CallbackError {
traceback,
cause: Rc::new(error),
cause: Arc::new(error),
}),
);
get_gc_metatable_for::<WrappedError>(state);
ffi::lua_setmetatable(state, -2);
} else if let None = get_gc_userdata::<WrappedPanic>(state, -1).as_ref() {
if 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);
ffi::lua_remove(state, -2);
}
} else if get_gc_userdata::<WrappedPanic>(state, -1).is_null()
&& 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);
ffi::lua_remove(state, -2);
}
1
}
// Does not call lua_checkstack, uses 1 stack space.
pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> *mut ffi::lua_State {
#[cfg(any(feature = "lua53", feature = "lua52"))]
pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua_State> {
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
{
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_MAINTHREAD);
let main_state = ffi::lua_tothread(state, -1);
ffi::lua_pop(state, 1);
main_state
Some(main_state)
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
state
{
// Check the current state first
let is_main_state = ffi::lua_pushthread(state) == 1;
ffi::lua_pop(state, 1);
if is_main_state {
Some(state)
} else {
None
}
}
}
// Pushes a WrappedError to the top of the stack. Uses two stack spaces and does not call
@@ -519,6 +529,16 @@ pub unsafe fn init_gc_metatable_for<T: Any>(
) {
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"));
@@ -533,15 +553,16 @@ pub unsafe fn init_gc_metatable_for<T: Any>(
f(state)
}
let ref_addr = ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX);
METATABLE_CACHE.with(|mc| mc.borrow_mut().insert(type_id, ref_addr));
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *mut c_void);
}
pub unsafe fn get_gc_metatable_for<T: Any>(state: *mut ffi::lua_State) {
let type_id = TypeId::of::<T>();
let ref_addr = METATABLE_CACHE
.with(|mc| *mlua_expect!(mc.borrow().get(&type_id), "gc metatable does not exist"));
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ref_addr as ffi::lua_Integer);
let ref_addr = {
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.
@@ -650,12 +671,19 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) {
cstr!("__mod"),
cstr!("__pow"),
cstr!("__unm"),
#[cfg(any(feature = "lua54", feature = "lua53"))]
cstr!("__idiv"),
#[cfg(any(feature = "lua54", feature = "lua53"))]
cstr!("__band"),
#[cfg(any(feature = "lua54", feature = "lua53"))]
cstr!("__bor"),
#[cfg(any(feature = "lua54", feature = "lua53"))]
cstr!("__bxor"),
#[cfg(any(feature = "lua54", feature = "lua53"))]
cstr!("__bnot"),
#[cfg(any(feature = "lua54", feature = "lua53"))]
cstr!("__shl"),
#[cfg(any(feature = "lua54", feature = "lua53"))]
cstr!("__shr"),
cstr!("__concat"),
cstr!("__len"),
@@ -666,8 +694,12 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) {
cstr!("__newindex"),
cstr!("__call"),
cstr!("__tostring"),
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
cstr!("__pairs"),
#[cfg(any(feature = "lua53", feature = "lua52"))]
cstr!("__ipairs"),
#[cfg(feature = "lua54")]
cstr!("__close"),
] {
ffi::lua_pushstring(state, method);
ffi::lua_pushcfunction(state, destructed_error);
+1 -1
View File
@@ -159,7 +159,7 @@ impl<'a, 'lua> IntoIterator for &'a MultiValue<'lua> {
type IntoIter = iter::Rev<slice::Iter<'a, Value<'lua>>>;
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 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<'_>]`
+18
View File
@@ -0,0 +1,18 @@
use mlua::{Lua, Table};
fn main() {
let lua = Lua::new();
lua.scope(|scope| {
let mut inner: Option<Table> = None;
let f = scope
.create_function_mut(move |_, t: Table| {
if let Some(old) = inner.take() {
// Access old callback `Lua`.
}
inner = Some(t);
Ok(())
})?;
f.call::<_, ()>(lua.create_table()?)?;
Ok(())
});
}
@@ -0,0 +1,45 @@
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> $DIR/scope_callback_capture.rs:8:14
|
8 | .create_function_mut(move |_, t: Table| {
| ^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 5:15...
--> $DIR/scope_callback_capture.rs:5:15
|
5 | lua.scope(|scope| {
| _______________^
6 | | let mut inner: Option<Table> = None;
7 | | let f = scope
8 | | .create_function_mut(move |_, t: Table| {
... |
16 | | Ok(())
17 | | });
| |_____^
note: ...so that reference does not outlive borrowed content
--> $DIR/scope_callback_capture.rs:7:17
|
7 | let f = scope
| ^^^^^
note: but, the lifetime must be valid for the method call at 5:5...
--> $DIR/scope_callback_capture.rs:5:5
|
5 | / lua.scope(|scope| {
6 | | let mut inner: Option<Table> = None;
7 | | let f = scope
8 | | .create_function_mut(move |_, t: Table| {
... |
16 | | Ok(())
17 | | });
| |______^
note: ...so that a type/lifetime parameter is in scope here
--> $DIR/scope_callback_capture.rs:5:5
|
5 | / lua.scope(|scope| {
6 | | let mut inner: Option<Table> = None;
7 | | let f = scope
8 | | .create_function_mut(move |_, t: Table| {
... |
16 | | Ok(())
17 | | });
| |______^
+15
View File
@@ -0,0 +1,15 @@
use mlua::{Lua, Table};
fn main() {
let lua = Lua::new();
lua.scope(|scope| {
let mut inner: Option<Table> = None;
let f = scope
.create_function_mut(|_, t: Table| {
inner = Some(t);
Ok(())
})?;
f.call::<_, ()>(lua.create_table()?)?;
Ok(())
});
}
+45
View File
@@ -0,0 +1,45 @@
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> $DIR/scope_callback_inner.rs:8:14
|
8 | .create_function_mut(|_, t: Table| {
| ^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 5:15...
--> $DIR/scope_callback_inner.rs:5:15
|
5 | lua.scope(|scope| {
| _______________^
6 | | let mut inner: Option<Table> = None;
7 | | let f = scope
8 | | .create_function_mut(|_, t: Table| {
... |
13 | | Ok(())
14 | | });
| |_____^
note: ...so that reference does not outlive borrowed content
--> $DIR/scope_callback_inner.rs:7:17
|
7 | let f = scope
| ^^^^^
note: but, the lifetime must be valid for the method call at 5:5...
--> $DIR/scope_callback_inner.rs:5:5
|
5 | / lua.scope(|scope| {
6 | | let mut inner: Option<Table> = None;
7 | | let f = scope
8 | | .create_function_mut(|_, t: Table| {
... |
13 | | Ok(())
14 | | });
| |______^
note: ...so that a type/lifetime parameter is in scope here
--> $DIR/scope_callback_inner.rs:5:5
|
5 | / lua.scope(|scope| {
6 | | let mut inner: Option<Table> = None;
7 | | let f = scope
8 | | .create_function_mut(|_, t: Table| {
... |
13 | | Ok(())
14 | | });
| |______^
+15
View File
@@ -0,0 +1,15 @@
use mlua::{Lua, Table};
fn main() {
let lua = Lua::new();
let mut outer: Option<Table> = None;
lua.scope(|scope| {
let f = scope
.create_function_mut(|_, t: Table| {
outer = Some(t);
Ok(())
})?;
f.call::<_, ()>(lua.create_table()?)?;
Ok(())
});
}
+11
View File
@@ -0,0 +1,11 @@
error: borrowed data cannot be stored outside of its closure
--> $DIR/scope_callback_outer.rs:7:17
|
5 | let mut outer: Option<Table> = None;
| --------- ...so that variable is valid at time of its declaration
6 | lua.scope(|scope| {
| ------- borrowed data cannot outlive this closure
7 | let f = scope
| ^^^^^ cannot be stored outside of its closure
8 | .create_function_mut(|_, t: Table| {
| ------------------- cannot infer an appropriate lifetime...
+23
View File
@@ -0,0 +1,23 @@
use mlua::Lua;
struct Test {
field: i32,
}
fn main() {
let lua = Lua::new();
lua.scope(|scope| {
let f = {
let mut test = Test { field: 0 };
scope
.create_function_mut(|_, ()| {
test.field = 42;
//~^ error: `test` does not live long enough
Ok(())
})?
};
f.call::<_, ()>(())
});
}
+25
View File
@@ -0,0 +1,25 @@
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
|
9 | lua.scope(|scope| {
| ----- has type `&mlua::scope::Scope<'_, '1>`
...
14 | .create_function_mut(|_, ()| {
| ^^^^^^^ may outlive borrowed value `test`
15 | test.field = 42;
| ---- `test` is borrowed here
|
note: function requires argument type to outlive `'1`
--> $DIR/scope_invariance.rs:13:13
|
13 | / scope
14 | | .create_function_mut(|_, ()| {
15 | | test.field = 42;
16 | | //~^ error: `test` does not live long enough
17 | | Ok(())
18 | | })?
| |__________________^
help: to force the closure to take ownership of `test` (and any other referenced variables), use the `move` keyword
|
14 | .create_function_mut(move |_, ()| {
| ^^^^^^^^^^^^
+15
View File
@@ -0,0 +1,15 @@
use mlua::{Lua, UserData};
fn main() {
struct MyUserData<'a>(&'a mut i32);
impl<'a> UserData for MyUserData<'a> {};
let mut i = 1;
let lua = Lua::new();
lua.scope(|scope| {
let _a = scope.create_nonstatic_userdata(MyUserData(&mut i)).unwrap();
let _b = scope.create_nonstatic_userdata(MyUserData(&mut i)).unwrap();
Ok(())
});
}
@@ -0,0 +1,9 @@
error[E0499]: cannot borrow `i` as mutable more than once at a time
--> $DIR/scope_mutable_aliasing.rs:12:61
|
11 | let _a = scope.create_nonstatic_userdata(MyUserData(&mut i)).unwrap();
| ------ first mutable borrow occurs here
12 | let _b = scope.create_nonstatic_userdata(MyUserData(&mut i)).unwrap();
| ------------------------- ^^^^^^ second mutable borrow occurs here
| |
| first borrow later used by call
+19
View File
@@ -0,0 +1,19 @@
use mlua::{Lua, UserData};
fn main() {
// 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 lua = Lua::new();
lua.scope(|scope| {
let _ugood = scope.create_nonstatic_userdata(MyUserData(&igood)).unwrap();
let _ubad = {
let ibad = 42;
scope.create_nonstatic_userdata(MyUserData(&ibad)).unwrap();
};
Ok(())
});
}
@@ -0,0 +1,13 @@
error[E0597]: `ibad` does not live long enough
--> $DIR/scope_userdata_borrow.rs:15:56
|
11 | lua.scope(|scope| {
| ----- has type `&mlua::scope::Scope<'_, '1>`
...
15 | scope.create_nonstatic_userdata(MyUserData(&ibad)).unwrap();
| -------------------------------------------^^^^^--
| | |
| | borrowed value does not live long enough
| argument requires that `ibad` is borrowed for `'1`
16 | };
| - `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,57 +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<usize>` 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<usize>` 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<usize>`
= note: required because it appears within the type `std::cell::Cell<usize>`
= note: required because it appears within the type `std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `std::marker::PhantomData<std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>>`
= note: required because it appears within the type `std::rc::Rc<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<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 `std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `std::marker::PhantomData<std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>>`
= note: required because it appears within the type `std::rc::Rc<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 `std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `std::marker::PhantomData<std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>>`
= note: required because it appears within the type `std::rc::Rc<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,65 +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<usize>` 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<usize>` 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<usize>`
= note: required because it appears within the type `std::cell::Cell<usize>`
= note: required because it appears within the type `std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `std::marker::PhantomData<std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>>`
= note: required because it appears within the type `std::rc::Rc<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<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 `std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `std::marker::PhantomData<std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>>`
= note: required because it appears within the type `std::rc::Rc<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 `std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `std::marker::PhantomData<std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>>`
= note: required because it appears within the type `std::rc::Rc<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<'_>]`
+11 -36
View File
@@ -1,10 +1,16 @@
#![allow(unused_imports)]
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
use std::{string::String as StdString, time::Duration};
#[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 futures_executor::block_on;
use mlua::{Error, Function, Lua, Result, String, Thread};
use mlua::{Function, Lua, Result, String};
#[test]
fn test_function() -> Result<()> {
@@ -81,34 +87,3 @@ fn test_rust_function() -> Result<()> {
Ok(())
}
#[cfg(feature = "async")]
#[tokio::test]
async fn test_async_function() -> Result<()> {
let lua = Lua::new();
let f = lua.create_async_function(move |_lua, n: u64| async move {
futures_timer::Delay::new(Duration::from_secs(n)).await;
Ok("hello")
})?;
lua.globals().set("rust_async_sleep", f)?;
let thread = lua
.load(
r#"
coroutine.create(function ()
ret = rust_async_sleep(1)
assert(ret == "hello")
coroutine.yield()
return "world"
end)
"#,
)
.eval::<Thread>()?;
let fut = thread.into_async(());
let ret: StdString = fut.await?;
assert_eq!(ret, "world");
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(())
})
}
+49 -6
View File
@@ -1,13 +1,56 @@
use std::rc::Rc;
#![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 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]
fn test_gc_control() -> Result<()> {
let lua = Lua::new();
let globals = lua.globals();
#[cfg(any(feature = "lua53", feature = "lua52"))]
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
{
assert!(lua.gc_is_running());
lua.gc_stop();
@@ -16,17 +59,17 @@ fn test_gc_control() -> Result<()> {
assert!(lua.gc_is_running());
}
struct MyUserdata(Rc<()>);
struct MyUserdata(Arc<()>);
impl UserData for MyUserdata {}
let rc = Rc::new(());
let rc = Arc::new(());
globals.set("userdata", lua.create_userdata(MyUserdata(rc.clone()))?)?;
globals.raw_remove("userdata")?;
assert_eq!(Rc::strong_count(&rc), 2);
assert_eq!(Arc::strong_count(&rc), 2);
lua.gc_collect()?;
lua.gc_collect()?;
assert_eq!(Rc::strong_count(&rc), 1);
assert_eq!(Arc::strong_count(&rc), 1);
Ok(())
}
+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)
}
+240
View File
@@ -0,0 +1,240 @@
#![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 mlua::{Error, Function, Lua, MetaMethod, Result, String, UserData, UserDataMethods};
#[test]
fn scope_func() -> Result<()> {
let lua = Lua::new();
let rc = Rc::new(Cell::new(0));
lua.scope(|scope| {
let r = rc.clone();
let f = scope.create_function(move |_, ()| {
r.set(42);
Ok(())
})?;
lua.globals().set("bad", f.clone())?;
f.call::<_, ()>(())?;
assert_eq!(Rc::strong_count(&rc), 2);
Ok(())
})?;
assert_eq!(rc.get(), 42);
assert_eq!(Rc::strong_count(&rc), 1);
match lua.globals().get::<_, Function>("bad")?.call::<_, ()>(()) {
Err(Error::CallbackError { .. }) => {}
r => panic!("improper return for destructed function: {:?}", r),
};
Ok(())
}
#[test]
fn scope_drop() -> Result<()> {
let lua = Lua::new();
struct MyUserdata(Rc<()>);
impl UserData for MyUserdata {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("method", |_, _, ()| Ok(()));
}
}
let rc = Rc::new(());
lua.scope(|scope| {
lua.globals()
.set("test", scope.create_userdata(MyUserdata(rc.clone()))?)?;
assert_eq!(Rc::strong_count(&rc), 2);
Ok(())
})?;
assert_eq!(Rc::strong_count(&rc), 1);
match lua.load("test:method()").exec() {
Err(Error::CallbackError { .. }) => {}
r => panic!("improper return for destructed userdata: {:?}", r),
};
Ok(())
}
#[test]
fn scope_capture() -> Result<()> {
let lua = Lua::new();
let mut i = 0;
lua.scope(|scope| {
scope
.create_function_mut(|_, ()| {
i = 42;
Ok(())
})?
.call::<_, ()>(())
})?;
assert_eq!(i, 42);
Ok(())
}
#[test]
fn outer_lua_access() -> Result<()> {
let lua = Lua::new();
let table = lua.create_table()?;
lua.scope(|scope| {
scope
.create_function_mut(|_, ()| table.set("a", "b"))?
.call::<_, ()>(())
})?;
assert_eq!(table.get::<_, String>("a")?, "b");
Ok(())
}
#[test]
fn scope_userdata_methods() -> Result<()> {
struct MyUserData<'a>(&'a Cell<i64>);
impl<'a> UserData for MyUserData<'a> {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("inc", |_, data, ()| {
data.0.set(data.0.get() + 1);
Ok(())
});
methods.add_method("dec", |_, data, ()| {
data.0.set(data.0.get() - 1);
Ok(())
});
}
}
let lua = Lua::new();
let i = Cell::new(42);
let f: Function = lua
.load(
r#"
function(u)
u:inc()
u:inc()
u:inc()
u:dec()
end
"#,
)
.eval()?;
lua.scope(|scope| f.call::<_, ()>(scope.create_nonstatic_userdata(MyUserData(&i))?))?;
assert_eq!(i.get(), 44);
Ok(())
}
#[test]
fn scope_userdata_functions() -> Result<()> {
struct MyUserData<'a>(&'a i64);
impl<'a> UserData for MyUserData<'a> {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_meta_function(MetaMethod::Add, |lua, ()| {
let globals = lua.globals();
globals.set("i", globals.get::<_, i64>("i")? + 1)?;
Ok(())
});
methods.add_meta_function(MetaMethod::Sub, |lua, ()| {
let globals = lua.globals();
globals.set("i", globals.get::<_, i64>("i")? + 1)?;
Ok(())
});
}
}
let lua = Lua::new();
let dummy = 0;
let f = lua
.load(
r#"
i = 0
return function(u)
_ = u + u
_ = u - 1
_ = 1 + u
end
"#,
)
.eval::<Function>()?;
lua.scope(|scope| f.call::<_, ()>(scope.create_nonstatic_userdata(MyUserData(&dummy))?))?;
assert_eq!(lua.globals().get::<_, i64>("i")?, 3);
Ok(())
}
#[test]
fn scope_userdata_mismatch() -> Result<()> {
struct MyUserData<'a>(&'a Cell<i64>);
impl<'a> UserData for MyUserData<'a> {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("inc", |_, data, ()| {
data.0.set(data.0.get() + 1);
Ok(())
});
}
}
let lua = Lua::new();
lua.load(
r#"
function okay(a, b)
a.inc(a)
b.inc(b)
end
function bad(a, b)
a.inc(b)
end
"#,
)
.exec()?;
let a = Cell::new(1);
let b = Cell::new(1);
let okay: Function = lua.globals().get("okay")?;
let bad: Function = lua.globals().get("bad")?;
lua.scope(|scope| {
let au = scope.create_nonstatic_userdata(MyUserData(&a))?;
let bu = scope.create_nonstatic_userdata(MyUserData(&b))?;
assert!(okay.call::<_, ()>((au.clone(), bu.clone())).is_ok());
match bad.call::<_, ()>((au, bu)) {
Err(Error::CallbackError { ref cause, .. }) => match *cause.as_ref() {
Error::UserDataTypeMismatch => {}
ref other => panic!("wrong error type {:?}", other),
},
Err(other) => panic!("wrong error type {:?}", other),
Ok(_) => panic!("incorrectly returned Ok"),
}
Ok(())
})?;
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 std::borrow::Cow;
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]
fn test_set_get() -> Result<()> {
@@ -109,6 +121,47 @@ fn test_table() -> Result<()> {
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]
fn test_table_scope() -> Result<()> {
let lua = Lua::new();
@@ -226,3 +279,33 @@ fn test_table_error() -> Result<()> {
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(())
}
+78 -9
View File
@@ -1,12 +1,64 @@
#![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::panic::catch_unwind;
use std::rc::Rc;
use std::sync::Arc;
use std::{error, f32, f64, fmt};
use mlua::{
Error, ExternalError, Function, Lua, Nil, Result, String, Table, UserData, Value, Variadic,
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"),
}
Ok(())
}
#[test]
fn test_load() -> Result<()> {
let lua = Lua::new();
@@ -389,7 +441,7 @@ fn test_num_conversion() -> Result<()> {
assert_eq!(lua.load("1.0").eval::<i64>()?, 1);
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");
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
assert_eq!(lua.load("1.0").eval::<String>()?, "1");
@@ -466,7 +518,12 @@ fn test_pcall_xpcall() -> Result<()> {
assert_eq!(globals.get::<_, String>("pcall_error")?, "testerror");
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!(
globals.get::<_, std::string::String>("xpcall_error")?,
"testerror"
@@ -584,22 +641,22 @@ fn test_registry_value() -> Result<()> {
#[test]
fn test_drop_registry_value() -> Result<()> {
struct MyUserdata(Rc<()>);
struct MyUserdata(Arc<()>);
impl UserData for MyUserdata {}
let lua = Lua::new();
let rc = Rc::new(());
let rc = Arc::new(());
let r = lua.create_registry_value(MyUserdata(rc.clone()))?;
assert_eq!(Rc::strong_count(&rc), 2);
assert_eq!(Arc::strong_count(&rc), 2);
drop(r);
lua.expire_registry_values();
lua.load(r#"collectgarbage("collect")"#).exec()?;
assert_eq!(Rc::strong_count(&rc), 1);
assert_eq!(Arc::strong_count(&rc), 1);
Ok(())
}
@@ -799,7 +856,7 @@ fn context_thread() -> Result<()> {
)
.into_function()?;
#[cfg(any(feature = "lua53", feature = "lua52"))]
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
f.call::<_, ()>(lua.current_thread())?;
#[cfg(any(feature = "lua51", feature = "luajit"))]
@@ -828,3 +885,15 @@ fn context_thread_51() -> Result<()> {
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(())
}
+17 -66
View File
@@ -1,11 +1,16 @@
#![allow(unused_imports)]
#![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::rc::Rc;
use std::time::Duration;
use futures_executor::block_on;
use futures_util::stream::TryStreamExt;
use mlua::{Error, Function, Lua, Result, Thread, ThreadStatus};
@@ -100,38 +105,6 @@ fn test_thread() -> Result<()> {
Ok(())
}
#[cfg(feature = "async")]
#[tokio::test]
async fn test_thread_stream() -> Result<()> {
let lua = Lua::new();
let thread = lua.create_thread(
lua.load(
r#"
function (s)
local sum = s
for i = 1,10 do
sum = sum + i
coroutine.yield(sum)
end
return sum
end
"#,
)
.eval()?,
)?;
let mut s = thread.into_async::<_, i64>(0);
let mut sum = 0;
while let Some(n) = s.try_next().await? {
sum += n;
}
assert_eq!(sum, 275);
Ok(())
}
#[test]
fn coroutine_from_closure() -> Result<()> {
let lua = Lua::new();
@@ -139,7 +112,12 @@ fn coroutine_from_closure() -> Result<()> {
let thrd_main = lua.create_function(|_, ()| Ok(()))?;
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()?;
#[cfg(feature = "lua51")]
let thrd: Thread = lua
@@ -167,30 +145,3 @@ fn coroutine_panic() {
Err(p) => assert!(*p.downcast::<&str>().unwrap() == "test_panic"),
}
}
#[cfg(feature = "async")]
#[tokio::test]
async fn test_thread_async() -> Result<()> {
let lua = Lua::new();
let cnt = Rc::new(1); // sleep 1 second
let cnt2 = cnt.clone();
let f = lua.create_async_function(move |_lua, ()| {
let cnt3 = cnt2.clone();
async move {
futures_timer::Delay::new(Duration::from_secs(*cnt3.as_ref())).await;
Ok("hello")
}
})?;
let mut thread_s = lua.create_thread(f)?.into_async(());
let val: String = thread_s.try_next().await?.unwrap_or_default();
// thread_s is non-resumable and subject to garbage collection
lua.gc_collect()?;
assert_eq!(Rc::strong_count(&cnt), 1);
assert_eq!(val, "hello");
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 std::os::raw::c_void;
use mlua::{Function, LightUserData, Lua, Result};
+75 -8
View File
@@ -1,4 +1,19 @@
use std::rc::Rc;
#![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;
#[cfg(feature = "lua54")]
use std::sync::atomic::{AtomicI64, Ordering};
use mlua::{
AnyUserData, ExternalError, Function, Lua, MetaMethod, Result, String, UserData,
@@ -95,7 +110,7 @@ fn test_metamethods() -> Result<()> {
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, ()| {
use std::iter::FromIterator;
let stateless_iter = lua.create_function(|_, (data, i): (MyUserData, i64)| {
@@ -120,7 +135,7 @@ fn test_metamethods() -> Result<()> {
10
);
#[cfg(any(feature = "lua53", feature = "lua52"))]
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
let pairs_it = {
lua.load(
r#"
@@ -140,7 +155,7 @@ fn test_metamethods() -> Result<()> {
assert_eq!(lua.load("userdata1 - userdata2").eval::<MyUserData>()?.0, 4);
assert_eq!(lua.load("userdata1:get()").eval::<i64>()?, 7);
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!(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.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(())
}
@@ -196,22 +263,22 @@ fn test_gc_userdata() -> Result<()> {
#[test]
fn detroys_userdata() -> Result<()> {
struct MyUserdata(Rc<()>);
struct MyUserdata(Arc<()>);
impl UserData for MyUserdata {}
let rc = Rc::new(());
let rc = Arc::new(());
let lua = Lua::new();
lua.globals().set("userdata", MyUserdata(rc.clone()))?;
assert_eq!(Rc::strong_count(&rc), 2);
assert_eq!(Arc::strong_count(&rc), 2);
// should destroy all objects
let _ = lua.globals().raw_remove("userdata")?;
lua.gc_collect()?;
assert_eq!(Rc::strong_count(&rc), 1);
assert_eq!(Arc::strong_count(&rc), 1);
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 mlua::{Lua, Result, Value};
#[test]