mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e9c2b8d306 | |||
| d672e19365 | |||
| bda399a5b4 | |||
| fe5e87b0f5 | |||
| 0f32e9cb43 | |||
| 0efa0fcb6a | |||
| 4e19ae6ccf | |||
| c826798a6d | |||
| ee08050c1f | |||
| d8897d867b | |||
| 222f4df668 | |||
| afaa0eb639 | |||
| db5ad6bc60 | |||
| ef06c5eec9 | |||
| f7dc9da107 | |||
| 47e8a80c1c | |||
| 1a788c48f1 | |||
| d8b9c46d28 | |||
| 9bd6d6fa92 | |||
| ea6e6e00b2 | |||
| 03ed7f6f99 | |||
| d5bc9c8c24 | |||
| 5cd0b645c4 | |||
| bf36eb72f8 | |||
| 1f7f8f62ab | |||
| e4dc773aa3 | |||
| 07fc4642ae | |||
| 27121c779d | |||
| 5eec0ef56b | |||
| 831161bfda | |||
| 52be96677b | |||
| 42b33849e1 |
@@ -1,49 +0,0 @@
|
|||||||
version: 2
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
docker:
|
|
||||||
- image: circleci/rust:latest
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- run:
|
|
||||||
name: Version information
|
|
||||||
command: rustc --version; cargo --version; rustup --version
|
|
||||||
- run:
|
|
||||||
name: Calculate dependencies
|
|
||||||
command: cargo generate-lockfile
|
|
||||||
- restore_cache:
|
|
||||||
keys:
|
|
||||||
- cargo-cache-{{ arch }}-{{ checksum "Cargo.lock" }}
|
|
||||||
- run:
|
|
||||||
name: Check Formatting
|
|
||||||
command: |
|
|
||||||
rustup component add rustfmt
|
|
||||||
rustfmt --version
|
|
||||||
cargo fmt --all -- --check --color=auto
|
|
||||||
- run:
|
|
||||||
name: Install Lua
|
|
||||||
command: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get -y --no-install-recommends install liblua5.3-dev liblua5.2-dev liblua5.1-0-dev libluajit-5.1-dev
|
|
||||||
- run:
|
|
||||||
name: Build all targets
|
|
||||||
command: cargo build --all --all-targets
|
|
||||||
- run:
|
|
||||||
name: Run all tests / Lua 5.3
|
|
||||||
command: cargo test --all --no-default-features --features lua53
|
|
||||||
- run:
|
|
||||||
name: Run all tests / Lua 5.2
|
|
||||||
command: cargo test --all --no-default-features --features lua52
|
|
||||||
- run:
|
|
||||||
name: Run all tests / Lua 5.1
|
|
||||||
command: cargo test --all --no-default-features --features lua51
|
|
||||||
- run:
|
|
||||||
name: Run all tests / LuaJIT
|
|
||||||
command: cargo test --all --no-default-features --features luajit
|
|
||||||
- save_cache:
|
|
||||||
paths:
|
|
||||||
- /usr/local/cargo/registry
|
|
||||||
- target/debug/.fingerprint
|
|
||||||
- target/debug/build
|
|
||||||
- target/debug/deps
|
|
||||||
key: cargo-cache-{{ arch }}-{{ checksum "Cargo.lock" }}
|
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
name: CI
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
thing:
|
||||||
|
- stable
|
||||||
|
- macos-x86_64
|
||||||
|
- x86_64-msvc
|
||||||
|
include:
|
||||||
|
- thing: stable
|
||||||
|
target: x86_64-unknown-linux-gnu
|
||||||
|
rust: stable
|
||||||
|
os: ubuntu-latest
|
||||||
|
- thing: macos-x86_64
|
||||||
|
target: x86_64-apple-darwin
|
||||||
|
rust: stable
|
||||||
|
os: macos-latest
|
||||||
|
- thing: x86_64-msvc
|
||||||
|
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 }}
|
||||||
|
shell: bash
|
||||||
|
- name: Install Lua (ubuntu)
|
||||||
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
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
|
||||||
|
|
||||||
|
test_linux:
|
||||||
|
name: Test on Linux
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
|
||||||
|
test_macos:
|
||||||
|
name: Test on MacOS
|
||||||
|
runs-on: macos-latest
|
||||||
|
needs: build
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- name: Install Rust
|
||||||
|
run: |
|
||||||
|
curl https://sh.rustup.rs | sh -s -- -y
|
||||||
|
echo ::add-path::$HOME/.cargo/bin
|
||||||
|
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
|
||||||
|
|
||||||
|
test_windows:
|
||||||
|
name: Test on Windows
|
||||||
|
runs-on: windows-latest
|
||||||
|
needs: build
|
||||||
|
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
|
||||||
|
|
||||||
|
rustfmt:
|
||||||
|
name: Rustfmt
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- name: Install Rust
|
||||||
|
run: rustup update stable && rustup default stable && rustup component add rustfmt
|
||||||
|
- run: cargo fmt -- --check
|
||||||
+34
-9
@@ -1,21 +1,26 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mlua"
|
name = "mlua"
|
||||||
version = "0.2.0"
|
version = "0.3.2"
|
||||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
|
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
repository = "https://github.com/khvzak/mlua"
|
repository = "https://github.com/khvzak/mlua"
|
||||||
documentation = "https://docs.rs/mlua"
|
documentation = "https://docs.rs/mlua"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
keywords = ["lua", "luajit"]
|
keywords = ["lua", "luajit", "async", "futures"]
|
||||||
categories = ["api-bindings"]
|
categories = ["api-bindings", "asynchronous"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
links = "lua"
|
||||||
|
build = "build/main.rs"
|
||||||
description = """
|
description = """
|
||||||
High level bindings to Lua 5.1/5.2/5.3 (including LuaJIT)
|
High level bindings to Lua 5.1/5.2/5.3 (including LuaJIT)
|
||||||
with support of writing native lua modules in Rust.
|
with async/await features and support of writing native lua modules in Rust.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
travis-ci = { repository = "khvzak/mlua", branch = "master" }
|
# github-actions = { repository = "khvzak/mlua", workflow = "CI" }
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
features = ["async"]
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
@@ -28,20 +33,40 @@ lua53 = []
|
|||||||
lua52 = []
|
lua52 = []
|
||||||
lua51 = []
|
lua51 = []
|
||||||
luajit = []
|
luajit = []
|
||||||
|
vendored = ["lua-src", "luajit-src"]
|
||||||
|
async = ["futures-core", "futures-task", "futures-util"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
num-traits = { version = "0.2.6" }
|
|
||||||
bstr = { version = "0.2", features = ["std"], default_features = false }
|
bstr = { version = "0.2", features = ["std"], default_features = false }
|
||||||
|
lazy_static = { version = "1.4" }
|
||||||
|
num-traits = { version = "0.2.11" }
|
||||||
|
futures-core = { version = "0.3.4", optional = true }
|
||||||
|
futures-task = { version = "0.3.4", optional = true }
|
||||||
|
futures-util = { version = "0.3.4", optional = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cc = { version = "1.0" }
|
cc = { version = "1.0" }
|
||||||
pkg-config = { version = "0.3.11" }
|
pkg-config = { version = "0.3.17" }
|
||||||
|
lua-src = { version = "535.0.1", optional = true }
|
||||||
|
luajit-src = { version = "210.0.0", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rustyline = "5.0"
|
rustyline = "6.0"
|
||||||
criterion = "0.2.0"
|
criterion = "0.3"
|
||||||
trybuild = "1.0"
|
trybuild = "1.0"
|
||||||
|
futures = "0.3.4"
|
||||||
|
hyper = "0.13"
|
||||||
|
tokio = { version = "0.2.18", features = ["full"] }
|
||||||
|
futures-timer = "3.0"
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "benchmark"
|
name = "benchmark"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "async_tcp_server"
|
||||||
|
required-features = ["async"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "async_http_client"
|
||||||
|
required-features = ["async"]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# mlua
|
# mlua
|
||||||
[![Build Status]][circleci] [![Latest Version]][crates.io] [![API Documentation]][docs.rs]
|
[![Build Status]][github-actions] [![Latest Version]][crates.io] [![API Documentation]][docs.rs]
|
||||||
|
|
||||||
[Build Status]: https://img.shields.io/circleci/project/github/khvzak/mlua.svg
|
[Build Status]: https://github.com/khvzak/mlua/workflows/CI/badge.svg
|
||||||
[circleci]: https://circleci.com/gh/khvzak/mlua
|
[github-actions]: https://github.com/khvzak/mlua/actions
|
||||||
[Latest Version]: https://img.shields.io/crates/v/mlua.svg
|
[Latest Version]: https://img.shields.io/crates/v/mlua.svg
|
||||||
[crates.io]: https://crates.io/crates/mlua
|
[crates.io]: https://crates.io/crates/mlua
|
||||||
[API Documentation]: https://docs.rs/mlua/badge.svg
|
[API Documentation]: https://docs.rs/mlua/badge.svg
|
||||||
@@ -16,14 +16,40 @@ The `mlua` goal is to be an easy to use, practical and flexible API between Rust
|
|||||||
*__not__* always 100% safe due to the Lua VM nature. Also, `mlua` provides a way to write native lua
|
*__not__* always 100% safe due to the Lua VM nature. Also, `mlua` provides a way to write native lua
|
||||||
modules in Rust.
|
modules in Rust.
|
||||||
|
|
||||||
### Usage
|
## Usage
|
||||||
|
|
||||||
#### standalone mode
|
### 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`.
|
||||||
|
|
||||||
|
**Examples**:
|
||||||
|
- [HTTP Client](examples/async_http_client.rs)
|
||||||
|
- [TCP Server](examples/async_tcp_server.rs)
|
||||||
|
|
||||||
|
### Choosing Lua version
|
||||||
|
|
||||||
|
The following features could be used to choose Lua version: `lua53` (default), `lua52`, `lua51` and `luajit`.
|
||||||
|
|
||||||
|
By default mlua uses `pkg-config` tool to find lua includes and lib.
|
||||||
|
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).
|
||||||
|
|
||||||
|
An example how to use them:
|
||||||
|
``` sh
|
||||||
|
my_project $ LUA_INC=$HOME/tmp/lua-5.2.4/src LUA_LIB=$HOME/tmp/lua-5.2.4/src LUA_LIB_NAME=lua LUA_LINK=static cargo build
|
||||||
|
```
|
||||||
|
|
||||||
|
`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.
|
||||||
|
|
||||||
|
### Standalone mode
|
||||||
Add to `Cargo.toml` :
|
Add to `Cargo.toml` :
|
||||||
|
|
||||||
``` toml
|
``` toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
mlua = "0.2"
|
mlua = "0.3"
|
||||||
```
|
```
|
||||||
|
|
||||||
`main.rs`
|
`main.rs`
|
||||||
@@ -46,7 +72,7 @@ fn main() -> LuaResult<()> {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### module mode
|
### Module mode
|
||||||
|
|
||||||
Add to `Cargo.toml` :
|
Add to `Cargo.toml` :
|
||||||
|
|
||||||
@@ -55,8 +81,8 @@ Add to `Cargo.toml` :
|
|||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
mlua = "0.2"
|
mlua = "0.3"
|
||||||
mlua_derive = "0.2"
|
mlua_derive = "0.3"
|
||||||
```
|
```
|
||||||
|
|
||||||
`lib.rs` :
|
`lib.rs` :
|
||||||
|
|||||||
@@ -1,189 +0,0 @@
|
|||||||
use std::env;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{BufRead, BufReader, Error, ErrorKind, Result};
|
|
||||||
use std::ops::Bound;
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
trait CommandExt {
|
|
||||||
fn execute(&mut self) -> Result<()>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CommandExt for Command {
|
|
||||||
/// Execute the command and return an error if it exited with a failure status.
|
|
||||||
fn execute(&mut self) -> Result<()> {
|
|
||||||
self.status()
|
|
||||||
.and_then(|status| {
|
|
||||||
if status.success() {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(Error::new(ErrorKind::Other, "non-zero exit code"))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map_err(|_| {
|
|
||||||
Error::new(
|
|
||||||
ErrorKind::Other,
|
|
||||||
format!("The command {:?} did not run successfully.", self),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn use_custom_lua<S: AsRef<str>>(include_dir: &S, lib_dir: &S, lua_lib: &S) -> Result<String> {
|
|
||||||
let mut version_found = String::new();
|
|
||||||
|
|
||||||
// Find LUA_VERSION_NUM
|
|
||||||
let mut lua_h_path = PathBuf::from(include_dir.as_ref());
|
|
||||||
lua_h_path.push("lua.h");
|
|
||||||
let f = File::open(lua_h_path)?;
|
|
||||||
let reader = BufReader::new(f);
|
|
||||||
for line in reader.lines() {
|
|
||||||
let line = line?;
|
|
||||||
let parts = line.split_whitespace().collect::<Vec<_>>();
|
|
||||||
if parts.len() == 3 && parts[1] == "LUA_VERSION_NUM" {
|
|
||||||
version_found = parts[2].to_string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut static_link = "";
|
|
||||||
if env::var("LUA_LINK").unwrap_or(String::new()) == "static" {
|
|
||||||
static_link = "static=";
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("cargo:rustc-link-search=native={}", lib_dir.as_ref());
|
|
||||||
println!("cargo:rustc-link-lib={}{}", static_link, lua_lib.as_ref());
|
|
||||||
|
|
||||||
Ok(version_found)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_glue<P: AsRef<Path> + std::fmt::Debug>(include_paths: &[P]) {
|
|
||||||
let build_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
|
||||||
|
|
||||||
// Ensure the presence of glue.rs
|
|
||||||
// if build_dir.join("glue.rs").exists() {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
let mut config = cc::Build::new();
|
|
||||||
|
|
||||||
for path in include_paths {
|
|
||||||
config.include(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compile and run glue.c
|
|
||||||
let glue = build_dir.join("glue");
|
|
||||||
|
|
||||||
config
|
|
||||||
.get_compiler()
|
|
||||||
.to_command()
|
|
||||||
.arg("src/ffi/glue/glue.c")
|
|
||||||
.arg("-o")
|
|
||||||
.arg(&glue)
|
|
||||||
.execute()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Command::new(glue)
|
|
||||||
.arg(build_dir.join("glue.rs"))
|
|
||||||
.execute()
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let include_dir = env::var("LUA_INC").unwrap_or(String::new());
|
|
||||||
let lib_dir = env::var("LUA_LIB").unwrap_or(String::new());
|
|
||||||
let lua_lib = env::var("LUA_LIB_NAME").unwrap_or(String::new());
|
|
||||||
|
|
||||||
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");
|
|
||||||
println!("cargo:rerun-if-changed=src/ffi/glue/glue.c");
|
|
||||||
|
|
||||||
if include_dir != "" && lib_dir != "" && lua_lib != "" {
|
|
||||||
let _version = use_custom_lua(&include_dir, &lib_dir, &lua_lib).unwrap();
|
|
||||||
build_glue(&[include_dir]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find lua via pkg-config
|
|
||||||
|
|
||||||
#[cfg(not(any(
|
|
||||||
feature = "lua53",
|
|
||||||
feature = "lua52",
|
|
||||||
feature = "lua51",
|
|
||||||
feature = "luajit"
|
|
||||||
)))]
|
|
||||||
panic!("You must enable one of the features: 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");
|
|
||||||
|
|
||||||
#[cfg(all(feature = "lua52", any(feature = "lua51", feature = "luajit")))]
|
|
||||||
panic!("You can enable only one of the features: lua53, lua52, lua51, luajit");
|
|
||||||
|
|
||||||
#[cfg(all(feature = "lua51", feature = "luajit"))]
|
|
||||||
panic!("You can enable only one of the features: lua53, lua52, lua51, luajit");
|
|
||||||
|
|
||||||
#[cfg(feature = "lua53")]
|
|
||||||
{
|
|
||||||
let mut lua = pkg_config::Config::new()
|
|
||||||
.range_version((Bound::Included("5.3"), Bound::Excluded("5.4")))
|
|
||||||
.probe("lua");
|
|
||||||
|
|
||||||
if lua.is_err() {
|
|
||||||
lua = pkg_config::Config::new().probe("lua5.3");
|
|
||||||
}
|
|
||||||
|
|
||||||
match lua {
|
|
||||||
Ok(lua) => build_glue(&lua.include_paths),
|
|
||||||
Err(err) => panic!(err),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "lua52")]
|
|
||||||
{
|
|
||||||
let mut lua = pkg_config::Config::new()
|
|
||||||
.range_version((Bound::Included("5.2"), Bound::Excluded("5.3")))
|
|
||||||
.probe("lua");
|
|
||||||
|
|
||||||
if lua.is_err() {
|
|
||||||
lua = pkg_config::Config::new().probe("lua5.2");
|
|
||||||
}
|
|
||||||
|
|
||||||
match lua {
|
|
||||||
Ok(lua) => build_glue(&lua.include_paths),
|
|
||||||
Err(_) => panic!("Lua 5.2 not found"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "lua51")]
|
|
||||||
{
|
|
||||||
let mut lua = pkg_config::Config::new()
|
|
||||||
.range_version((Bound::Included("5.1"), Bound::Excluded("5.2")))
|
|
||||||
.probe("lua");
|
|
||||||
|
|
||||||
if lua.is_err() {
|
|
||||||
lua = pkg_config::Config::new().probe("lua5.1");
|
|
||||||
}
|
|
||||||
|
|
||||||
match lua {
|
|
||||||
Ok(lua) => build_glue(&lua.include_paths),
|
|
||||||
Err(err) => panic!(err),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "luajit")]
|
|
||||||
{
|
|
||||||
let lua = pkg_config::Config::new()
|
|
||||||
.range_version((Bound::Included("2.0.5"), Bound::Unbounded))
|
|
||||||
.probe("luajit");
|
|
||||||
|
|
||||||
match lua {
|
|
||||||
Ok(lua) => build_glue(&lua.include_paths),
|
|
||||||
Err(err) => panic!(err),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
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());
|
||||||
|
|
||||||
|
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 _version = use_custom_lua(&include_dir, &lib_dir, &lua_lib).unwrap();
|
||||||
|
return PathBuf::from(include_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find using via pkg-config
|
||||||
|
|
||||||
|
#[cfg(feature = "lua53")]
|
||||||
|
{
|
||||||
|
let mut lua = pkg_config::Config::new()
|
||||||
|
.range_version((Bound::Included("5.3"), Bound::Excluded("5.4")))
|
||||||
|
.probe("lua");
|
||||||
|
|
||||||
|
if lua.is_err() {
|
||||||
|
lua = pkg_config::Config::new().probe("lua5.3");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 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")))
|
||||||
|
.probe("lua");
|
||||||
|
|
||||||
|
if lua.is_err() {
|
||||||
|
lua = pkg_config::Config::new().probe("lua5.2");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 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")))
|
||||||
|
.probe("lua");
|
||||||
|
|
||||||
|
if lua.is_err() {
|
||||||
|
lua = pkg_config::Config::new().probe("lua5.1");
|
||||||
|
}
|
||||||
|
|
||||||
|
return lua.unwrap().include_paths[0].clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "luajit")]
|
||||||
|
{
|
||||||
|
let lua = pkg_config::Config::new()
|
||||||
|
.range_version((Bound::Included("2.1.0"), Bound::Unbounded))
|
||||||
|
.probe("luajit");
|
||||||
|
|
||||||
|
return lua.unwrap().include_paths[0].clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn use_custom_lua<S: AsRef<Path>>(include_dir: &S, lib_dir: &S, lua_lib: &S) -> Result<String> {
|
||||||
|
let mut version_found = String::new();
|
||||||
|
|
||||||
|
// Find LUA_VERSION_NUM
|
||||||
|
let mut lua_h_path = include_dir.as_ref().to_owned();
|
||||||
|
lua_h_path.push("lua.h");
|
||||||
|
let f = File::open(lua_h_path)?;
|
||||||
|
let reader = BufReader::new(f);
|
||||||
|
for line in reader.lines() {
|
||||||
|
let line = line?;
|
||||||
|
let parts = line.split_whitespace().collect::<Vec<_>>();
|
||||||
|
if parts.len() == 3 && parts[1] == "LUA_VERSION_NUM" {
|
||||||
|
version_found = parts[2].to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut link_lib = String::new();
|
||||||
|
if env::var("LUA_LINK").unwrap_or(String::new()) == "static" {
|
||||||
|
link_lib = "static=".to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
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 = "lua53")]
|
||||||
|
let artifacts = lua_src::Build::new().build(lua_src::Lua53);
|
||||||
|
#[cfg(feature = "lua52")]
|
||||||
|
let artifacts = lua_src::Build::new().build(lua_src::Lua52);
|
||||||
|
#[cfg(feature = "lua51")]
|
||||||
|
let artifacts = lua_src::Build::new().build(lua_src::Lua51);
|
||||||
|
#[cfg(feature = "luajit")]
|
||||||
|
let artifacts = luajit_src::Build::new().build();
|
||||||
|
|
||||||
|
artifacts.print_cargo_metadata();
|
||||||
|
artifacts.include_dir().to_owned()
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
#![allow(unreachable_code)]
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
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")]
|
||||||
|
mod find;
|
||||||
|
|
||||||
|
trait CommandExt {
|
||||||
|
fn execute(&mut self) -> Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommandExt for Command {
|
||||||
|
/// Execute the command and return an error if it exited with a failure status.
|
||||||
|
fn execute(&mut self) -> Result<()> {
|
||||||
|
self.status()
|
||||||
|
.and_then(|status| {
|
||||||
|
if status.success() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::new(ErrorKind::Other, "non-zero exit code"))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map_err(|_| {
|
||||||
|
Error::new(
|
||||||
|
ErrorKind::Other,
|
||||||
|
format!("The command {:?} did not run successfully.", self),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_glue<P: AsRef<Path> + std::fmt::Debug>(include_path: &P) {
|
||||||
|
let build_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||||
|
|
||||||
|
let mut config = cc::Build::new();
|
||||||
|
config.include(include_path);
|
||||||
|
|
||||||
|
// Compile and run glue.c
|
||||||
|
let glue = build_dir.join("glue");
|
||||||
|
|
||||||
|
config
|
||||||
|
.get_compiler()
|
||||||
|
.to_command()
|
||||||
|
.arg("src/ffi/glue/glue.c")
|
||||||
|
.arg("-o")
|
||||||
|
.arg(&glue)
|
||||||
|
.execute()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Command::new(glue)
|
||||||
|
.arg(build_dir.join("glue.rs"))
|
||||||
|
.execute()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
#[cfg(not(any(
|
||||||
|
feature = "lua53",
|
||||||
|
feature = "lua52",
|
||||||
|
feature = "lua51",
|
||||||
|
feature = "luajit"
|
||||||
|
)))]
|
||||||
|
panic!("You must enable one of the features: 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");
|
||||||
|
|
||||||
|
#[cfg(all(feature = "lua52", any(feature = "lua51", feature = "luajit")))]
|
||||||
|
panic!("You can enable only one of the features: lua53, lua52, lua51, luajit");
|
||||||
|
|
||||||
|
#[cfg(all(feature = "lua51", feature = "luajit"))]
|
||||||
|
panic!("You can enable only one of the features: lua53, lua52, lua51, luajit");
|
||||||
|
|
||||||
|
#[cfg(all(feature = "lua51", feature = "luajit"))]
|
||||||
|
panic!("You can enable only one of the features: lua53, lua52, lua51, luajit");
|
||||||
|
|
||||||
|
let include_dir = find::probe_lua();
|
||||||
|
build_glue(&include_dir);
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use bstr::BString;
|
||||||
|
use hyper::{body::Body as HyperBody, Client as HyperClient};
|
||||||
|
use tokio::stream::StreamExt;
|
||||||
|
|
||||||
|
use mlua::{Error, Lua, Result, UserData, UserDataMethods};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct BodyReader(Rc<RefCell<HyperBody>>);
|
||||||
|
|
||||||
|
impl BodyReader {
|
||||||
|
fn new(body: HyperBody) -> Self {
|
||||||
|
BodyReader(Rc::new(RefCell::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.borrow_mut();
|
||||||
|
let bytes = reader.try_next().await.map_err(Error::external)?;
|
||||||
|
if let Some(bytes) = bytes {
|
||||||
|
return Ok(Some(BString::from(bytes.as_ref())));
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
let fetch_url = lua.create_async_function(|lua, uri: String| async move {
|
||||||
|
let client = HyperClient::new();
|
||||||
|
let uri = uri.parse().map_err(Error::external)?;
|
||||||
|
let resp = client.get(uri).await.map_err(Error::external)?;
|
||||||
|
|
||||||
|
let lua_resp = lua.create_table()?;
|
||||||
|
lua_resp.set("status", resp.status().as_u16())?;
|
||||||
|
|
||||||
|
let mut headers = HashMap::new();
|
||||||
|
for (key, value) in resp.headers().iter() {
|
||||||
|
headers
|
||||||
|
.entry(key.as_str())
|
||||||
|
.or_insert(Vec::new())
|
||||||
|
.push(value.to_str().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_resp.set("headers", headers)?;
|
||||||
|
lua_resp.set("body", BodyReader::new(resp.into_body()))?;
|
||||||
|
|
||||||
|
Ok(lua_resp)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let globals = lua.globals();
|
||||||
|
globals.set("fetch_url", fetch_url)?;
|
||||||
|
|
||||||
|
let f = lua
|
||||||
|
.load(
|
||||||
|
r#"
|
||||||
|
local res = fetch_url(...);
|
||||||
|
print(res.status)
|
||||||
|
for key, vals in pairs(res.headers) do
|
||||||
|
for _, val in ipairs(vals) do
|
||||||
|
print(key..": "..val)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
repeat
|
||||||
|
local body = res.body:read()
|
||||||
|
if body then
|
||||||
|
print(body)
|
||||||
|
end
|
||||||
|
until not body
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.into_function()?;
|
||||||
|
|
||||||
|
f.call_async("http://httpbin.org/ip").await
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
use std::net::Shutdown;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use bstr::BString;
|
||||||
|
use tokio::net::{TcpListener, TcpStream};
|
||||||
|
use tokio::prelude::*;
|
||||||
|
use tokio::task;
|
||||||
|
|
||||||
|
use mlua::{Function, Lua, Result, UserData, UserDataMethods};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct LuaTcp;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct LuaTcpListener(Rc<RefCell<TcpListener>>);
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct LuaTcpStream(Rc<RefCell<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(Rc::new(RefCell::new(listener))))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserData for LuaTcpListener {
|
||||||
|
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_async_method("accept", |_, listener, ()| async move {
|
||||||
|
let (stream, _) = listener.0.borrow_mut().accept().await?;
|
||||||
|
Ok(LuaTcpStream(Rc::new(RefCell::new(stream))))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserData for LuaTcpStream {
|
||||||
|
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_async_method("peer_addr", |_, stream, ()| async move {
|
||||||
|
Ok(stream.0.borrow().peer_addr()?.to_string())
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_async_method("read", |_, stream, size: usize| async move {
|
||||||
|
let mut buf = vec![0; size];
|
||||||
|
let n = stream.0.borrow_mut().read(&mut buf).await?;
|
||||||
|
buf.truncate(n);
|
||||||
|
Ok(BString::from(buf))
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_async_method("write", |_, stream, data: BString| async move {
|
||||||
|
let n = stream.0.borrow_mut().write(&data).await?;
|
||||||
|
Ok(n)
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_method("close", |_, stream, ()| {
|
||||||
|
stream.0.borrow().shutdown(Shutdown::Both)?;
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
let spawn = lua.create_function(move |_, func: Function| {
|
||||||
|
task::spawn_local(async move { func.call_async::<_, ()>(()).await.unwrap() });
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let globals = lua.globals();
|
||||||
|
globals.set("tcp", LuaTcp)?;
|
||||||
|
globals.set("spawn", spawn)?;
|
||||||
|
|
||||||
|
let server = lua
|
||||||
|
.load(
|
||||||
|
r#"
|
||||||
|
local addr = ...
|
||||||
|
local listener = tcp.bind(addr)
|
||||||
|
print("listening on "..addr)
|
||||||
|
while true do
|
||||||
|
local stream = listener:accept()
|
||||||
|
local peer_addr = stream:peer_addr()
|
||||||
|
print("connected from "..peer_addr)
|
||||||
|
spawn(function()
|
||||||
|
while true do
|
||||||
|
local data = stream:read(100)
|
||||||
|
data = data:match("^%s*(.-)%s*$") -- trim
|
||||||
|
print("["..peer_addr.."] "..data)
|
||||||
|
stream:write("got: "..data.."\n")
|
||||||
|
if data == "exit" then
|
||||||
|
stream:close()
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.into_function()?;
|
||||||
|
|
||||||
|
task::LocalSet::new()
|
||||||
|
.run_until(server.call_async::<_, ()>("0.0.0.0:1234"))
|
||||||
|
.await
|
||||||
|
}
|
||||||
@@ -164,34 +164,6 @@ fn main() -> Result<()> {
|
|||||||
< f32::EPSILON
|
< f32::EPSILON
|
||||||
);
|
);
|
||||||
|
|
||||||
// Normally, Rust types passed to `Lua` must be `Send`, because `Lua` itself is `Send`, and
|
|
||||||
// must be `'static`, because there is no way to be sure of their lifetime inside the Lua
|
|
||||||
// state. There is, however, a limited way to lift both of these requirements. You can
|
|
||||||
// call `Lua::scope` to create userdata and callbacks types that only live for as long
|
|
||||||
// as the call to scope, but do not have to be `Send` OR `'static`.
|
|
||||||
|
|
||||||
{
|
|
||||||
let mut rust_val = 0;
|
|
||||||
|
|
||||||
lua.scope(|scope| {
|
|
||||||
// We create a 'sketchy' Lua callback that holds a mutable reference to the variable
|
|
||||||
// `rust_val`. Outside of a `Lua::scope` call, this would not be allowed
|
|
||||||
// because it could be unsafe.
|
|
||||||
|
|
||||||
lua.globals().set(
|
|
||||||
"sketchy",
|
|
||||||
scope.create_function_mut(|_, ()| {
|
|
||||||
rust_val = 42;
|
|
||||||
Ok(())
|
|
||||||
})?,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
lua.load("sketchy()").exec()
|
|
||||||
})?;
|
|
||||||
|
|
||||||
assert_eq!(rust_val, 42);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We were able to run our 'sketchy' function inside the scope just fine. However, if we
|
// We were able to run our 'sketchy' function inside the scope just fine. However, if we
|
||||||
// try to run our 'sketchy' function outside of the scope, the function we created will have
|
// try to run our 'sketchy' function outside of the scope, the function we created will have
|
||||||
// been invalidated and we will generate an error. If our function wasn't invalidated, we
|
// been invalidated and we will generate an error. If our function wasn't invalidated, we
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mlua_derive"
|
name = "mlua_derive"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
authors = ["Aleksandr Orlenko <zxteam@protonmail.com>"]
|
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "Procedural macros for the mlua crate."
|
description = "Procedural macros for the mlua crate."
|
||||||
repository = "https://github.com/khvzak/mlua"
|
repository = "https://github.com/khvzak/mlua"
|
||||||
|
|||||||
+47
-3
@@ -122,7 +122,7 @@ impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua, T: 'static + Send + UserData> ToLua<'lua> for T {
|
impl<'lua, T: 'static + UserData> ToLua<'lua> for T {
|
||||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
Ok(Value::UserData(lua.create_userdata(self)?))
|
Ok(Value::UserData(lua.create_userdata(self)?))
|
||||||
}
|
}
|
||||||
@@ -167,7 +167,7 @@ impl<'lua> ToLua<'lua> for bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> FromLua<'lua> for bool {
|
impl<'lua> FromLua<'lua> for bool {
|
||||||
fn from_lua(v: Value, _: &'lua Lua) -> Result<Self> {
|
fn from_lua(v: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||||
match v {
|
match v {
|
||||||
Value::Nil => Ok(false),
|
Value::Nil => Ok(false),
|
||||||
Value::Boolean(b) => Ok(b),
|
Value::Boolean(b) => Ok(b),
|
||||||
@@ -183,7 +183,7 @@ impl<'lua> ToLua<'lua> for LightUserData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> FromLua<'lua> for LightUserData {
|
impl<'lua> FromLua<'lua> for LightUserData {
|
||||||
fn from_lua(value: Value, _: &'lua Lua) -> Result<Self> {
|
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||||
match value {
|
match value {
|
||||||
Value::LightUserData(ud) => Ok(ud),
|
Value::LightUserData(ud) => Ok(ud),
|
||||||
_ => Err(Error::FromLuaConversionError {
|
_ => Err(Error::FromLuaConversionError {
|
||||||
@@ -373,6 +373,50 @@ macro_rules! lua_convert_float {
|
|||||||
lua_convert_float!(f32);
|
lua_convert_float!(f32);
|
||||||
lua_convert_float!(f64);
|
lua_convert_float!(f64);
|
||||||
|
|
||||||
|
impl<'lua, T> ToLua<'lua> for &'_ [T]
|
||||||
|
where
|
||||||
|
T: Clone + ToLua<'lua>,
|
||||||
|
{
|
||||||
|
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
|
Ok(Value::Table(
|
||||||
|
lua.create_sequence_from(self.into_iter().cloned())?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! lua_convert_array {
|
||||||
|
($($N:literal)+) => {
|
||||||
|
$(
|
||||||
|
impl<'lua, T> ToLua<'lua> for [T; $N]
|
||||||
|
where
|
||||||
|
T: Clone + ToLua<'lua>,
|
||||||
|
{
|
||||||
|
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
|
(&self).to_lua(lua)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua, T> ToLua<'lua> for &'_ [T; $N]
|
||||||
|
where
|
||||||
|
T: Clone + ToLua<'lua>,
|
||||||
|
{
|
||||||
|
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
|
Ok(Value::Table(
|
||||||
|
lua.create_sequence_from(self.iter().cloned())?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_convert_array! {
|
||||||
|
0 1 2 3 4 5 6 7 8 9
|
||||||
|
10 11 12 13 14 15 16 17 18 19
|
||||||
|
20 21 22 23 24 25 26 27 28 29
|
||||||
|
30 31 32
|
||||||
|
}
|
||||||
|
|
||||||
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Vec<T> {
|
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Vec<T> {
|
||||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
Ok(Value::Table(lua.create_sequence_from(self)?))
|
Ok(Value::Table(lua.create_sequence_from(self)?))
|
||||||
|
|||||||
+30
-9
@@ -1,8 +1,11 @@
|
|||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::io::Error as IoError;
|
||||||
|
use std::net::AddrParseError;
|
||||||
|
use std::rc::Rc;
|
||||||
use std::result::Result as StdResult;
|
use std::result::Result as StdResult;
|
||||||
|
use std::str::Utf8Error;
|
||||||
use std::string::String as StdString;
|
use std::string::String as StdString;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
/// Error type returned by `mlua` methods.
|
/// Error type returned by `mlua` methods.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -115,7 +118,7 @@ pub enum Error {
|
|||||||
/// Lua call stack backtrace.
|
/// Lua call stack backtrace.
|
||||||
traceback: StdString,
|
traceback: StdString,
|
||||||
/// Original error returned by the Rust code.
|
/// Original error returned by the Rust code.
|
||||||
cause: Arc<Error>,
|
cause: Rc<Error>,
|
||||||
},
|
},
|
||||||
/// A custom error.
|
/// A custom error.
|
||||||
///
|
///
|
||||||
@@ -124,7 +127,7 @@ pub enum Error {
|
|||||||
/// Returning `Err(ExternalError(...))` from a Rust callback will raise the error as a Lua
|
/// Returning `Err(ExternalError(...))` from a Rust callback will raise the error as a Lua
|
||||||
/// error. The Rust code that originally invoked the Lua code then receives a `CallbackError`,
|
/// error. The Rust code that originally invoked the Lua code then receives a `CallbackError`,
|
||||||
/// from which the original error (and a stack traceback) can be recovered.
|
/// from which the original error (and a stack traceback) can be recovered.
|
||||||
ExternalError(Arc<dyn StdError + Send + Sync>),
|
ExternalError(Rc<dyn StdError>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A specialized `Result` type used by `mlua`'s API.
|
/// A specialized `Result` type used by `mlua`'s API.
|
||||||
@@ -184,10 +187,10 @@ impl fmt::Display for Error {
|
|||||||
Error::MismatchedRegistryKey => {
|
Error::MismatchedRegistryKey => {
|
||||||
write!(fmt, "RegistryKey used from different Lua state")
|
write!(fmt, "RegistryKey used from different Lua state")
|
||||||
}
|
}
|
||||||
Error::CallbackError { ref traceback, ref cause } => {
|
Error::CallbackError { ref traceback, .. } => {
|
||||||
write!(fmt, "callback error: {}: {}", cause, traceback)
|
write!(fmt, "callback error: {}", traceback)
|
||||||
}
|
}
|
||||||
Error::ExternalError(ref err) => write!(fmt, "external error: {}", err),
|
Error::ExternalError(ref err) => write!(fmt, "{}", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,14 +199,14 @@ impl StdError for Error {
|
|||||||
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
||||||
match *self {
|
match *self {
|
||||||
Error::CallbackError { ref cause, .. } => Some(cause.as_ref()),
|
Error::CallbackError { ref cause, .. } => Some(cause.as_ref()),
|
||||||
Error::ExternalError(ref err) => Some(err.as_ref()),
|
Error::ExternalError(ref err) => err.source(),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
pub fn external<T: Into<Box<dyn StdError + Send + Sync>>>(err: T) -> Error {
|
pub fn external<T: Into<Box<dyn StdError>>>(err: T) -> Error {
|
||||||
Error::ExternalError(err.into().into())
|
Error::ExternalError(err.into().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -214,7 +217,7 @@ pub trait ExternalError {
|
|||||||
|
|
||||||
impl<E> ExternalError for E
|
impl<E> ExternalError for E
|
||||||
where
|
where
|
||||||
E: Into<Box<dyn StdError + Send + Sync>>,
|
E: Into<Box<dyn StdError>>,
|
||||||
{
|
{
|
||||||
fn to_lua_err(self) -> Error {
|
fn to_lua_err(self) -> Error {
|
||||||
Error::external(self)
|
Error::external(self)
|
||||||
@@ -233,3 +236,21 @@ where
|
|||||||
self.map_err(|e| e.to_lua_err())
|
self.map_err(|e| e.to_lua_err())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::convert::From<AddrParseError> for Error {
|
||||||
|
fn from(err: AddrParseError) -> Self {
|
||||||
|
Error::external(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::From<IoError> for Error {
|
||||||
|
fn from(err: IoError) -> Self {
|
||||||
|
Error::external(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::From<Utf8Error> for Error {
|
||||||
|
fn from(err: Utf8Error) -> Self {
|
||||||
|
Error::external(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+8
-2
@@ -263,15 +263,21 @@ int main(int argc, const char **argv) {
|
|||||||
RS_STR("LUA_IOLIBNAME", LUA_IOLIBNAME),
|
RS_STR("LUA_IOLIBNAME", LUA_IOLIBNAME),
|
||||||
RS_STR("LUA_OSLIBNAME", LUA_OSLIBNAME),
|
RS_STR("LUA_OSLIBNAME", LUA_OSLIBNAME),
|
||||||
RS_STR("LUA_STRLIBNAME", LUA_STRLIBNAME),
|
RS_STR("LUA_STRLIBNAME", LUA_STRLIBNAME),
|
||||||
#if LUA_VERSION_NUM == 503
|
#ifdef LUA_UTF8LIBNAME
|
||||||
RS_STR("LUA_UTF8LIBNAME", LUA_UTF8LIBNAME),
|
RS_STR("LUA_UTF8LIBNAME", LUA_UTF8LIBNAME),
|
||||||
#endif
|
#endif
|
||||||
#if LUA_VERSION_NUM >= 502
|
#ifdef LUA_BITLIBNAME
|
||||||
RS_STR("LUA_BITLIBNAME", LUA_BITLIBNAME),
|
RS_STR("LUA_BITLIBNAME", LUA_BITLIBNAME),
|
||||||
#endif
|
#endif
|
||||||
RS_STR("LUA_MATHLIBNAME", LUA_MATHLIBNAME),
|
RS_STR("LUA_MATHLIBNAME", LUA_MATHLIBNAME),
|
||||||
RS_STR("LUA_DBLIBNAME", LUA_DBLIBNAME),
|
RS_STR("LUA_DBLIBNAME", LUA_DBLIBNAME),
|
||||||
RS_STR("LUA_LOADLIBNAME", LUA_LOADLIBNAME),
|
RS_STR("LUA_LOADLIBNAME", LUA_LOADLIBNAME),
|
||||||
|
#ifdef LUA_JITLIBNAME
|
||||||
|
RS_STR("LUA_JITLIBNAME", LUA_JITLIBNAME),
|
||||||
|
#endif
|
||||||
|
#ifdef LUA_FFILIBNAME
|
||||||
|
RS_STR("LUA_FFILIBNAME", LUA_FFILIBNAME),
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!write_items(f, glue_entries)) {
|
if (!write_items(f, glue_entries)) {
|
||||||
|
|||||||
+13
-4
@@ -32,12 +32,15 @@ pub use super::glue::{
|
|||||||
LUA_STRLIBNAME, LUA_TABLIBNAME,
|
LUA_STRLIBNAME, LUA_TABLIBNAME,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
|
||||||
pub use super::glue::LUA_BITLIBNAME;
|
|
||||||
|
|
||||||
#[cfg(feature = "lua53")]
|
#[cfg(feature = "lua53")]
|
||||||
pub use super::glue::LUA_UTF8LIBNAME;
|
pub use super::glue::LUA_UTF8LIBNAME;
|
||||||
|
|
||||||
|
#[cfg(any(feature = "lua52", feature = "luajit"))]
|
||||||
|
pub use super::glue::LUA_BITLIBNAME;
|
||||||
|
|
||||||
|
#[cfg(feature = "luajit")]
|
||||||
|
pub use super::glue::{LUA_FFILIBNAME, LUA_JITLIBNAME};
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn luaopen_base(L: *mut lua_State) -> c_int;
|
pub fn luaopen_base(L: *mut lua_State) -> c_int;
|
||||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||||
@@ -48,11 +51,17 @@ extern "C" {
|
|||||||
pub fn luaopen_string(L: *mut lua_State) -> c_int;
|
pub fn luaopen_string(L: *mut lua_State) -> c_int;
|
||||||
#[cfg(feature = "lua53")]
|
#[cfg(feature = "lua53")]
|
||||||
pub fn luaopen_utf8(L: *mut lua_State) -> c_int;
|
pub fn luaopen_utf8(L: *mut lua_State) -> c_int;
|
||||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
#[cfg(feature = "lua52")]
|
||||||
pub fn luaopen_bit32(L: *mut lua_State) -> c_int;
|
pub fn luaopen_bit32(L: *mut lua_State) -> c_int;
|
||||||
pub fn luaopen_math(L: *mut lua_State) -> c_int;
|
pub fn luaopen_math(L: *mut lua_State) -> c_int;
|
||||||
pub fn luaopen_debug(L: *mut lua_State) -> c_int;
|
pub fn luaopen_debug(L: *mut lua_State) -> c_int;
|
||||||
pub fn luaopen_package(L: *mut lua_State) -> c_int;
|
pub fn luaopen_package(L: *mut lua_State) -> c_int;
|
||||||
|
#[cfg(feature = "luajit")]
|
||||||
|
pub fn luaopen_bit(L: *mut lua_State) -> c_int;
|
||||||
|
#[cfg(feature = "luajit")]
|
||||||
|
pub fn luaopen_jit(L: *mut lua_State) -> c_int;
|
||||||
|
#[cfg(feature = "luajit")]
|
||||||
|
pub fn luaopen_ffi(L: *mut lua_State) -> c_int;
|
||||||
|
|
||||||
pub fn luaL_openlibs(L: *mut lua_State);
|
pub fn luaL_openlibs(L: *mut lua_State);
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-12
@@ -155,12 +155,12 @@ pub use self::lua::{
|
|||||||
lua_yield,
|
lua_yield,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
|
||||||
pub use self::lua::{lua_callk, lua_pcallk, lua_upvalueid, lua_upvaluejoin, lua_yieldk};
|
|
||||||
|
|
||||||
#[cfg(feature = "lua53")]
|
#[cfg(feature = "lua53")]
|
||||||
pub use self::lua::{lua_isyieldable, lua_version};
|
pub use self::lua::{lua_isyieldable, lua_version};
|
||||||
|
|
||||||
|
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||||
|
pub use self::lua::{lua_callk, lua_pcallk, lua_upvalueid, lua_upvaluejoin, lua_yieldk};
|
||||||
|
|
||||||
// auxiliary library types
|
// auxiliary library types
|
||||||
pub use self::lauxlib::luaL_Reg;
|
pub use self::lauxlib::luaL_Reg;
|
||||||
|
|
||||||
@@ -186,11 +186,14 @@ pub use self::lualib::{
|
|||||||
luaopen_package, luaopen_string, luaopen_table,
|
luaopen_package, luaopen_string, luaopen_table,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
#[cfg(feature = "lua53")]
|
||||||
|
pub use self::lualib::{luaopen_coroutine, luaopen_utf8};
|
||||||
|
|
||||||
|
#[cfg(feature = "lua52")]
|
||||||
pub use self::lualib::{luaopen_bit32, luaopen_coroutine};
|
pub use self::lualib::{luaopen_bit32, luaopen_coroutine};
|
||||||
|
|
||||||
#[cfg(feature = "lua53")]
|
#[cfg(feature = "luajit")]
|
||||||
pub use self::lualib::luaopen_utf8;
|
pub use self::lualib::{luaopen_bit, luaopen_ffi, luaopen_jit};
|
||||||
|
|
||||||
// constants from lua.h
|
// constants from lua.h
|
||||||
pub use self::lua::{
|
pub use self::lua::{
|
||||||
@@ -203,14 +206,14 @@ pub use self::lua::{
|
|||||||
LUA_TTABLE, LUA_TTHREAD, LUA_TUSERDATA, LUA_YIELD,
|
LUA_TTABLE, LUA_TTHREAD, LUA_TUSERDATA, LUA_YIELD,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
|
||||||
pub use self::lua::{LUA_ERRGCMM, LUA_GCISRUNNING, LUA_RIDX_GLOBALS, LUA_RIDX_MAINTHREAD};
|
|
||||||
|
|
||||||
#[cfg(feature = "lua53")]
|
#[cfg(feature = "lua53")]
|
||||||
pub use self::lua::{
|
pub use self::lua::{
|
||||||
LUA_OPBAND, LUA_OPBNOT, LUA_OPBOR, LUA_OPBXOR, LUA_OPIDIV, LUA_OPSHL, LUA_OPSHR,
|
LUA_OPBAND, LUA_OPBNOT, LUA_OPBOR, LUA_OPBXOR, LUA_OPIDIV, LUA_OPSHL, LUA_OPSHR,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||||
|
pub use self::lua::{LUA_ERRGCMM, LUA_GCISRUNNING, LUA_RIDX_GLOBALS, LUA_RIDX_MAINTHREAD};
|
||||||
|
|
||||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||||
pub use self::lua::{LUA_ENVIRONINDEX, LUA_GLOBALSINDEX};
|
pub use self::lua::{LUA_ENVIRONINDEX, LUA_GLOBALSINDEX};
|
||||||
|
|
||||||
@@ -223,12 +226,15 @@ pub use self::lualib::{
|
|||||||
LUA_STRLIBNAME, LUA_TABLIBNAME,
|
LUA_STRLIBNAME, LUA_TABLIBNAME,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
|
||||||
pub use self::lualib::LUA_BITLIBNAME;
|
|
||||||
|
|
||||||
#[cfg(feature = "lua53")]
|
#[cfg(feature = "lua53")]
|
||||||
pub use self::lualib::LUA_UTF8LIBNAME;
|
pub use self::lualib::LUA_UTF8LIBNAME;
|
||||||
|
|
||||||
|
#[cfg(any(feature = "lua52", feature = "luajit"))]
|
||||||
|
pub use self::lualib::LUA_BITLIBNAME;
|
||||||
|
|
||||||
|
#[cfg(feature = "luajit")]
|
||||||
|
pub use self::lualib::{LUA_FFILIBNAME, LUA_JITLIBNAME};
|
||||||
|
|
||||||
// Not actually defined in lua.h / luaconf.h
|
// Not actually defined in lua.h / luaconf.h
|
||||||
pub const LUA_MAX_UPVALUES: c_int = 255;
|
pub const LUA_MAX_UPVALUES: c_int = 255;
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ use crate::util::{
|
|||||||
};
|
};
|
||||||
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
|
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
use {futures_core::future::LocalBoxFuture, futures_util::future};
|
||||||
|
|
||||||
/// Handle to an internal Lua function.
|
/// Handle to an internal Lua function.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Function<'lua>(pub(crate) LuaRef<'lua>);
|
pub struct Function<'lua>(pub(crate) LuaRef<'lua>);
|
||||||
@@ -86,6 +89,47 @@ impl<'lua> Function<'lua> {
|
|||||||
R::from_lua_multi(results, lua)
|
R::from_lua_multi(results, lua)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a Feature that, when polled, calls `self`, passing `args` as function arguments,
|
||||||
|
/// and drives the execution.
|
||||||
|
///
|
||||||
|
/// Internaly it wraps the function to an [`AsyncThread`].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::time::Duration;
|
||||||
|
/// use futures_timer::Delay;
|
||||||
|
/// # use mlua::{Lua, Result};
|
||||||
|
/// # #[tokio::main]
|
||||||
|
/// # async fn main() -> Result<()> {
|
||||||
|
/// # let lua = Lua::new();
|
||||||
|
///
|
||||||
|
/// let sleep = lua.create_async_function(move |_lua, n: u64| async move {
|
||||||
|
/// Delay::new(Duration::from_millis(n)).await;
|
||||||
|
/// Ok(())
|
||||||
|
/// })?;
|
||||||
|
///
|
||||||
|
/// sleep.call_async(10).await?;
|
||||||
|
///
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`AsyncThread`]: struct.AsyncThread.html
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
pub fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||||
|
where
|
||||||
|
'lua: 'fut,
|
||||||
|
A: ToLuaMulti<'lua>,
|
||||||
|
R: FromLuaMulti<'lua> + 'fut,
|
||||||
|
{
|
||||||
|
let lua = self.0.lua;
|
||||||
|
match lua.create_thread(self.clone()) {
|
||||||
|
Ok(t) => Box::pin(t.into_async(args)),
|
||||||
|
Err(e) => Box::pin(future::err(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a function that, when called, calls `self`, passing `args` as the first set of
|
/// Returns a function that, when called, calls `self`, passing `args` as the first set of
|
||||||
/// arguments.
|
/// arguments.
|
||||||
///
|
///
|
||||||
@@ -160,3 +204,9 @@ impl<'lua> Function<'lua> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'lua> PartialEq for Function<'lua> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.0 == other.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+16
-3
@@ -24,6 +24,12 @@
|
|||||||
//! The [`UserData`] trait can be implemented by user-defined types to make them available to Lua.
|
//! The [`UserData`] trait can be implemented by user-defined types to make them available to Lua.
|
||||||
//! Methods and operators to be used from Lua can be added using the [`UserDataMethods`] API.
|
//! Methods and operators to be used from Lua can be added using the [`UserDataMethods`] API.
|
||||||
//!
|
//!
|
||||||
|
//! # Async/await support
|
||||||
|
//!
|
||||||
|
//! The [`create_async_function`] allows creating non-blocking functions that returns [`Future`].
|
||||||
|
//! Lua code with async capabilities can be executed by [`call_async`] family of functions or polling
|
||||||
|
//! [`AsyncThread`] using any runtime (eg. Tokio).
|
||||||
|
//!
|
||||||
//! [Lua programming language]: https://www.lua.org/
|
//! [Lua programming language]: https://www.lua.org/
|
||||||
//! [`Lua`]: struct.Lua.html
|
//! [`Lua`]: struct.Lua.html
|
||||||
//! [executing]: struct.Lua.html#method.exec
|
//! [executing]: struct.Lua.html#method.exec
|
||||||
@@ -35,6 +41,10 @@
|
|||||||
//! [`FromLuaMulti`]: trait.FromLuaMulti.html
|
//! [`FromLuaMulti`]: trait.FromLuaMulti.html
|
||||||
//! [`UserData`]: trait.UserData.html
|
//! [`UserData`]: trait.UserData.html
|
||||||
//! [`UserDataMethods`]: trait.UserDataMethods.html
|
//! [`UserDataMethods`]: trait.UserDataMethods.html
|
||||||
|
//! [`create_async_function`]: struct.Lua.html#method.create_async_function
|
||||||
|
//! [`call_async`]: struct.Function.html#method.call_async
|
||||||
|
//! [`AsyncThread`]: struct.AsyncThread.html
|
||||||
|
//! [`Future`]: ../futures_core/future/trait.Future.html
|
||||||
|
|
||||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||||
// warnings at all.
|
// warnings at all.
|
||||||
@@ -49,7 +59,7 @@ mod ffi;
|
|||||||
mod function;
|
mod function;
|
||||||
mod lua;
|
mod lua;
|
||||||
mod multi;
|
mod multi;
|
||||||
mod scope;
|
mod stdlib;
|
||||||
mod string;
|
mod string;
|
||||||
mod table;
|
mod table;
|
||||||
mod thread;
|
mod thread;
|
||||||
@@ -64,12 +74,15 @@ pub use crate::error::{Error, ExternalError, ExternalResult, Result};
|
|||||||
pub use crate::function::Function;
|
pub use crate::function::Function;
|
||||||
pub use crate::lua::{Chunk, Lua};
|
pub use crate::lua::{Chunk, Lua};
|
||||||
pub use crate::multi::Variadic;
|
pub use crate::multi::Variadic;
|
||||||
pub use crate::scope::Scope;
|
pub use crate::stdlib::StdLib;
|
||||||
pub use crate::string::String;
|
pub use crate::string::String;
|
||||||
pub use crate::table::{Table, TablePairs, TableSequence};
|
pub use crate::table::{Table, TableExt, TablePairs, TableSequence};
|
||||||
pub use crate::thread::{Thread, ThreadStatus};
|
pub use crate::thread::{Thread, ThreadStatus};
|
||||||
pub use crate::types::{Integer, LightUserData, Number, RegistryKey};
|
pub use crate::types::{Integer, LightUserData, Number, RegistryKey};
|
||||||
pub use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
|
pub use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
|
||||||
pub use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
|
pub use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
pub use crate::thread::AsyncThread;
|
||||||
|
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
|
|||||||
+556
-161
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -146,7 +146,7 @@ macro_rules! impl_tuple {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> FromLuaMulti<'lua> for () {
|
impl<'lua> FromLuaMulti<'lua> for () {
|
||||||
fn from_lua_multi(_: MultiValue, _: &'lua Lua) -> Result<Self> {
|
fn from_lua_multi(_: MultiValue<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -5,8 +5,11 @@ pub use crate::{
|
|||||||
ExternalError as LuaExternalError, ExternalResult as LuaExternalResult, FromLua, FromLuaMulti,
|
ExternalError as LuaExternalError, ExternalResult as LuaExternalResult, FromLua, FromLuaMulti,
|
||||||
Function as LuaFunction, Integer as LuaInteger, LightUserData as LuaLightUserData, Lua,
|
Function as LuaFunction, Integer as LuaInteger, LightUserData as LuaLightUserData, Lua,
|
||||||
MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber,
|
MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber,
|
||||||
RegistryKey as LuaRegistryKey, Result as LuaResult, Scope as LuaScope, String as LuaString,
|
RegistryKey as LuaRegistryKey, Result as LuaResult, String as LuaString, Table as LuaTable,
|
||||||
Table as LuaTable, TablePairs as LuaTablePairs, TableSequence as LuaTableSequence,
|
TableExt as LuaTableExt, TablePairs as LuaTablePairs, TableSequence as LuaTableSequence,
|
||||||
Thread as LuaThread, ThreadStatus as LuaThreadStatus, ToLua, ToLuaMulti,
|
Thread as LuaThread, ThreadStatus as LuaThreadStatus, ToLua, ToLuaMulti,
|
||||||
UserData as LuaUserData, UserDataMethods as LuaUserDataMethods, Value as LuaValue,
|
UserData as LuaUserData, UserDataMethods as LuaUserDataMethods, Value as LuaValue,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
pub use crate::AsyncThread as LuaAsyncThread;
|
||||||
|
|||||||
-484
@@ -1,484 +0,0 @@
|
|||||||
use std::any::Any;
|
|
||||||
use std::cell::Cell;
|
|
||||||
use std::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};
|
|
||||||
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};
|
|
||||||
|
|
||||||
/// 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
|
|
||||||
pub struct Scope<'lua, 'scope> {
|
|
||||||
lua: &'lua Lua,
|
|
||||||
destructors: RefCell<Vec<(LuaRef<'lua>, fn(LuaRef<'lua>) -> 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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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_static_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.
|
|
||||||
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_static_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.
|
|
||||||
///
|
|
||||||
/// [`create_static_userdata`]: #method.create_static_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_pushinteger(lua.state, 1);
|
|
||||||
ffi::lua_gettable(lua.state, -2);
|
|
||||||
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 method_data = data.clone();
|
|
||||||
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
|
|
||||||
if !check_ud_type(lua, args.pop_front()) {
|
|
||||||
return Err(Error::UserDataTypeMismatch);
|
|
||||||
}
|
|
||||||
let data = method_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 method_data = data.clone();
|
|
||||||
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 = method_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(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_pushinteger(state, 1);
|
|
||||||
ffi::lua_pushlightuserdata(state, data.as_ptr() as *mut c_void);
|
|
||||||
ffi::lua_settable(state, -3);
|
|
||||||
})?;
|
|
||||||
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);
|
|
||||||
|
|
||||||
ffi::lua_getupvalue(state, -1, 1);
|
|
||||||
// We know the destructor has not run yet because we hold a reference to the callback.
|
|
||||||
let ud = take_userdata::<Callback>(state);
|
|
||||||
|
|
||||||
ffi::lua_pushnil(state);
|
|
||||||
ffi::lua_setupvalue(state, -2, 1);
|
|
||||||
|
|
||||||
ffi::lua_pop(state, 1);
|
|
||||||
Box::new(ud)
|
|
||||||
}));
|
|
||||||
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(..)
|
|
||||||
.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: ?Sized + AsRef<[u8]>,
|
|
||||||
A: FromLuaMulti<'lua>,
|
|
||||||
R: ToLuaMulti<'lua>,
|
|
||||||
M: 'static + Send + 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: ?Sized + AsRef<[u8]>,
|
|
||||||
A: FromLuaMulti<'lua>,
|
|
||||||
R: ToLuaMulti<'lua>,
|
|
||||||
M: 'static + Send + 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)
|
|
||||||
})),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_function<S, A, R, F>(&mut self, name: &S, function: F)
|
|
||||||
where
|
|
||||||
S: ?Sized + AsRef<[u8]>,
|
|
||||||
A: FromLuaMulti<'lua>,
|
|
||||||
R: ToLuaMulti<'lua>,
|
|
||||||
F: 'static + Send + 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: ?Sized + AsRef<[u8]>,
|
|
||||||
A: FromLuaMulti<'lua>,
|
|
||||||
R: ToLuaMulti<'lua>,
|
|
||||||
F: 'static + Send + 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)
|
|
||||||
})),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_meta_method<A, R, M>(&mut self, meta: MetaMethod, method: M)
|
|
||||||
where
|
|
||||||
A: FromLuaMulti<'lua>,
|
|
||||||
R: ToLuaMulti<'lua>,
|
|
||||||
M: 'static + Send + 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 + Send + 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 + Send + 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 + Send + 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)
|
|
||||||
})),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign};
|
||||||
|
use std::u32;
|
||||||
|
|
||||||
|
/// Flags describing the set of lua modules 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);
|
||||||
|
pub const TABLE: StdLib = StdLib(1 << 1);
|
||||||
|
pub const IO: StdLib = StdLib(1 << 2);
|
||||||
|
pub const OS: StdLib = StdLib(1 << 3);
|
||||||
|
pub const STRING: StdLib = StdLib(1 << 4);
|
||||||
|
#[cfg(feature = "lua53")]
|
||||||
|
pub const UTF8: StdLib = StdLib(1 << 5);
|
||||||
|
#[cfg(any(feature = "lua52", feature = "luajit"))]
|
||||||
|
pub const BIT: StdLib = StdLib(1 << 6);
|
||||||
|
pub const MATH: StdLib = StdLib(1 << 7);
|
||||||
|
pub const PACKAGE: StdLib = StdLib(1 << 8);
|
||||||
|
#[cfg(feature = "luajit")]
|
||||||
|
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
|
||||||
|
|
||||||
|
pub const ALL: StdLib = StdLib(u32::MAX);
|
||||||
|
pub const ALL_NO_DEBUG: StdLib = StdLib((1 << 31) - 1);
|
||||||
|
|
||||||
|
pub fn contains(self, lib: Self) -> bool {
|
||||||
|
(self & lib).0 != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitAnd for StdLib {
|
||||||
|
type Output = Self;
|
||||||
|
fn bitand(self, rhs: Self) -> Self::Output {
|
||||||
|
StdLib(self.0 & rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitAndAssign for StdLib {
|
||||||
|
fn bitand_assign(&mut self, rhs: Self) {
|
||||||
|
*self = StdLib(self.0 & rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitOr for StdLib {
|
||||||
|
type Output = Self;
|
||||||
|
fn bitor(self, rhs: Self) -> Self::Output {
|
||||||
|
StdLib(self.0 | rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitOrAssign for StdLib {
|
||||||
|
fn bitor_assign(&mut self, rhs: Self) {
|
||||||
|
*self = StdLib(self.0 | rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitXor for StdLib {
|
||||||
|
type Output = Self;
|
||||||
|
fn bitxor(self, rhs: Self) -> Self::Output {
|
||||||
|
StdLib(self.0 ^ rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitXorAssign for StdLib {
|
||||||
|
fn bitxor_assign(&mut self, rhs: Self) {
|
||||||
|
*self = StdLib(self.0 ^ rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
+252
-23
@@ -1,13 +1,16 @@
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
use crate::error::Result;
|
use crate::error::{Error, Result};
|
||||||
use crate::ffi;
|
use crate::ffi;
|
||||||
use crate::function::Function;
|
use crate::function::Function;
|
||||||
use crate::types::{Integer, LuaRef};
|
use crate::types::{Integer, LuaRef};
|
||||||
use crate::util::{assert_stack, protect_lua, protect_lua_closure, StackGuard};
|
use crate::util::{assert_stack, protect_lua, protect_lua_closure, StackGuard};
|
||||||
use crate::value::{FromLua, FromLuaMulti, Nil, ToLua, ToLuaMulti, Value};
|
use crate::value::{FromLua, FromLuaMulti, Nil, ToLua, ToLuaMulti, Value};
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
use {futures_core::future::LocalBoxFuture, futures_util::future};
|
||||||
|
|
||||||
/// Handle to an internal Lua table.
|
/// Handle to an internal Lua table.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Table<'lua>(pub(crate) LuaRef<'lua>);
|
pub struct Table<'lua>(pub(crate) LuaRef<'lua>);
|
||||||
@@ -134,26 +137,15 @@ impl<'lua> Table<'lua> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the function associated to `key` from the table and executes it,
|
/// Gets the function associated to `key` from the table and executes it,
|
||||||
/// passing the table itself as the first argument.
|
/// passing the table itself along with `args` as function arguments.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// This function is deprecated since 0.3.1 in favor of [`call_method`]
|
||||||
///
|
/// in the `TableExt` trait.
|
||||||
/// 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.
|
/// This might invoke the `__index` metamethod.
|
||||||
|
///
|
||||||
|
/// [`call_method`]: trait.TableExt.html#tymethod.call_method
|
||||||
|
#[deprecated(since = "0.3.1", note = "Please use `call_method` instead")]
|
||||||
pub fn call<K, A, R>(&self, key: K, args: A) -> Result<R>
|
pub fn call<K, A, R>(&self, key: K, args: A) -> Result<R>
|
||||||
where
|
where
|
||||||
K: ToLua<'lua>,
|
K: ToLua<'lua>,
|
||||||
@@ -166,11 +158,60 @@ impl<'lua> Table<'lua> {
|
|||||||
self.get::<_, Function>(key)?.call(args)
|
self.get::<_, Function>(key)?.call(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a key from the table, returning the value at the key
|
/// Compares two tables for equality.
|
||||||
/// if the key was previously in the table.
|
///
|
||||||
pub fn raw_remove<K: ToLua<'lua>>(&self, key: K) -> Result<()> {
|
/// Tables are compared by reference first.
|
||||||
self.raw_set(key, Nil)?;
|
/// If they are not primitively equals, then mlua will try to invoke the `__eq` metamethod.
|
||||||
Ok(())
|
/// mlua will check `self` first for the metamethod, then `other` if not found.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Compare two tables using `__eq` metamethod:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use mlua::{Lua, Result, Table};
|
||||||
|
/// # fn main() -> Result<()> {
|
||||||
|
/// # let lua = Lua::new();
|
||||||
|
/// let table1 = lua.create_table()?;
|
||||||
|
/// table1.set(1, "value")?;
|
||||||
|
///
|
||||||
|
/// let table2 = lua.create_table()?;
|
||||||
|
/// table2.set(2, "value")?;
|
||||||
|
///
|
||||||
|
/// let always_equals_mt = lua.create_table()?;
|
||||||
|
/// always_equals_mt.set("__eq", lua.create_function(|_, (_t1, _t2): (Table, Table)| Ok(true))?)?;
|
||||||
|
/// table2.set_metatable(Some(always_equals_mt));
|
||||||
|
///
|
||||||
|
/// assert!(table1.equals(&table1.clone())?);
|
||||||
|
/// assert!(table1.equals(&table2)?);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
|
||||||
|
let other = other.as_ref();
|
||||||
|
if self == other {
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare using __eq metamethod if exists
|
||||||
|
// First, check the self for the metamethod.
|
||||||
|
// If self does not define it, then check the other table.
|
||||||
|
if let Some(mt) = self.get_metatable() {
|
||||||
|
if mt.contains_key("__eq")? {
|
||||||
|
return mt
|
||||||
|
.get::<_, Function>("__eq")?
|
||||||
|
.call((self.clone(), other.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(mt) = other.get_metatable() {
|
||||||
|
if mt.contains_key("__eq")? {
|
||||||
|
return mt
|
||||||
|
.get::<_, Function>("__eq")?
|
||||||
|
.call((self.clone(), other.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a key-value pair without invoking metamethods.
|
/// Sets a key-value pair without invoking metamethods.
|
||||||
@@ -213,6 +254,70 @@ impl<'lua> Table<'lua> {
|
|||||||
V::from_lua(value, lua)
|
V::from_lua(value, lua)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Inserts element value at position `idx` to the table, shifting up the elements from `table[idx]`.
|
||||||
|
/// 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;
|
||||||
|
let size = self.raw_len();
|
||||||
|
if idx < 1 || idx > size + 1 {
|
||||||
|
return Err(Error::RuntimeError("index out of bounds".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = value.to_lua(lua)?;
|
||||||
|
unsafe {
|
||||||
|
let _sg = StackGuard::new(lua.state);
|
||||||
|
assert_stack(lua.state, 6);
|
||||||
|
|
||||||
|
lua.push_ref(&self.0);
|
||||||
|
lua.push_value(value)?;
|
||||||
|
|
||||||
|
protect_lua_closure(lua.state, 2, 0, |state| {
|
||||||
|
for i in (idx..size + 1).rev() {
|
||||||
|
// table[i+1] = table[i]
|
||||||
|
ffi::lua_rawgeti(state, -2, i);
|
||||||
|
ffi::lua_rawseti(state, -3, i + 1);
|
||||||
|
}
|
||||||
|
ffi::lua_rawseti(state, -2, idx);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
/// where n is the table length.
|
||||||
|
///
|
||||||
|
/// 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)?;
|
||||||
|
match key {
|
||||||
|
Value::Integer(idx) => {
|
||||||
|
let size = self.raw_len();
|
||||||
|
if idx < 1 || idx > size {
|
||||||
|
return Err(Error::RuntimeError("index out of bounds".to_string()));
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
let _sg = StackGuard::new(lua.state);
|
||||||
|
assert_stack(lua.state, 6);
|
||||||
|
|
||||||
|
lua.push_ref(&self.0);
|
||||||
|
|
||||||
|
protect_lua_closure(lua.state, 1, 0, |state| {
|
||||||
|
for i in idx..size {
|
||||||
|
ffi::lua_rawgeti(state, -1, i + 1);
|
||||||
|
ffi::lua_rawseti(state, -2, i);
|
||||||
|
}
|
||||||
|
ffi::lua_pushnil(state);
|
||||||
|
ffi::lua_rawseti(state, -2, size);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => self.raw_set(key, Nil),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the result of the Lua `#` operator.
|
/// Returns the result of the Lua `#` operator.
|
||||||
///
|
///
|
||||||
/// This might invoke the `__len` metamethod. Use the [`raw_len`] method if that is not desired.
|
/// This might invoke the `__len` metamethod. Use the [`raw_len`] method if that is not desired.
|
||||||
@@ -368,6 +473,130 @@ impl<'lua> Table<'lua> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'lua> PartialEq for Table<'lua> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.0 == other.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> AsRef<Table<'lua>> for Table<'lua> {
|
||||||
|
#[inline]
|
||||||
|
fn as_ref(&self) -> &Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// This might invoke the `__index` metamethod.
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
fn call_async_function<'fut, K, A, R>(
|
||||||
|
&self,
|
||||||
|
key: K,
|
||||||
|
args: A,
|
||||||
|
) -> LocalBoxFuture<'fut, Result<R>>
|
||||||
|
where
|
||||||
|
'lua: 'fut,
|
||||||
|
K: ToLua<'lua>,
|
||||||
|
A: ToLuaMulti<'lua>,
|
||||||
|
R: FromLuaMulti<'lua> + 'fut;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> TableExt<'lua> for Table<'lua> {
|
||||||
|
fn call_method<K, A, R>(&self, key: K, args: A) -> Result<R>
|
||||||
|
where
|
||||||
|
K: ToLua<'lua>,
|
||||||
|
A: ToLuaMulti<'lua>,
|
||||||
|
R: FromLuaMulti<'lua>,
|
||||||
|
{
|
||||||
|
let lua = self.0.lua;
|
||||||
|
let mut args = args.to_lua_multi(lua)?;
|
||||||
|
args.push_front(Value::Table(self.clone()));
|
||||||
|
self.get::<_, Function>(key)?.call(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call_function<K, A, R>(&self, key: K, args: A) -> Result<R>
|
||||||
|
where
|
||||||
|
K: ToLua<'lua>,
|
||||||
|
A: ToLuaMulti<'lua>,
|
||||||
|
R: FromLuaMulti<'lua>,
|
||||||
|
{
|
||||||
|
self.get::<_, Function>(key)?.call(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
fn call_async_method<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||||
|
where
|
||||||
|
'lua: 'fut,
|
||||||
|
K: ToLua<'lua>,
|
||||||
|
A: ToLuaMulti<'lua>,
|
||||||
|
R: FromLuaMulti<'lua> + 'fut,
|
||||||
|
{
|
||||||
|
let lua = self.0.lua;
|
||||||
|
let mut args = match args.to_lua_multi(lua) {
|
||||||
|
Ok(args) => args,
|
||||||
|
Err(e) => return Box::pin(future::err(e)),
|
||||||
|
};
|
||||||
|
args.push_front(Value::Table(self.clone()));
|
||||||
|
self.call_async_function(key, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
fn call_async_function<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||||
|
where
|
||||||
|
'lua: 'fut,
|
||||||
|
K: ToLua<'lua>,
|
||||||
|
A: ToLuaMulti<'lua>,
|
||||||
|
R: FromLuaMulti<'lua> + 'fut,
|
||||||
|
{
|
||||||
|
match self.get::<_, Function>(key) {
|
||||||
|
Ok(func) => func.call_async(args),
|
||||||
|
Err(e) => Box::pin(future::err(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An iterator over the pairs of a Lua table.
|
/// An iterator over the pairs of a Lua table.
|
||||||
///
|
///
|
||||||
/// This struct is created by the [`Table::pairs`] method.
|
/// This struct is created by the [`Table::pairs`] method.
|
||||||
|
|||||||
+214
@@ -8,6 +8,24 @@ use crate::util::{
|
|||||||
};
|
};
|
||||||
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
|
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
error::ExternalError,
|
||||||
|
lua::{AsyncPollPending, Lua, WAKER_REGISTRY_KEY},
|
||||||
|
util::{get_gc_userdata, push_gc_userdata},
|
||||||
|
value::Value,
|
||||||
|
},
|
||||||
|
futures_core::{future::Future, stream::Stream},
|
||||||
|
std::{
|
||||||
|
cell::RefCell,
|
||||||
|
marker::PhantomData,
|
||||||
|
os::raw::c_void,
|
||||||
|
pin::Pin,
|
||||||
|
task::{Context, Poll, Waker},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/// Status of a Lua thread (or coroutine).
|
/// Status of a Lua thread (or coroutine).
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub enum ThreadStatus {
|
pub enum ThreadStatus {
|
||||||
@@ -27,6 +45,18 @@ pub enum ThreadStatus {
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Thread<'lua>(pub(crate) LuaRef<'lua>);
|
pub struct Thread<'lua>(pub(crate) LuaRef<'lua>);
|
||||||
|
|
||||||
|
/// Thread (coroutine) representation as an async [`Future`] or [`Stream`].
|
||||||
|
///
|
||||||
|
/// [`Future`]: ../futures_core/future/trait.Future.html
|
||||||
|
/// [`Stream`]: ../futures_core/stream/trait.Stream.html
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AsyncThread<'lua, R> {
|
||||||
|
thread: Thread<'lua>,
|
||||||
|
args0: RefCell<Option<Result<MultiValue<'lua>>>>,
|
||||||
|
ret: PhantomData<R>,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'lua> Thread<'lua> {
|
impl<'lua> Thread<'lua> {
|
||||||
/// Resumes execution of this thread.
|
/// Resumes execution of this thread.
|
||||||
///
|
///
|
||||||
@@ -142,4 +172,188 @@ impl<'lua> Thread<'lua> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts Thread to an AsyncThread which implements Future and Stream traits.
|
||||||
|
///
|
||||||
|
/// `args` are passed as arguments to the thread function for first call.
|
||||||
|
/// The object call `resume()` while polling and also allows to run rust futures
|
||||||
|
/// to completion using an executor.
|
||||||
|
///
|
||||||
|
/// Using AsyncThread as a Stream allows to iterate through `coroutine.yield()`
|
||||||
|
/// values whereas Future version discards that values and poll until the final
|
||||||
|
/// one (returned from the thread function).
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use mlua::{Lua, Result, Thread};
|
||||||
|
/// use futures::stream::TryStreamExt;
|
||||||
|
/// # #[tokio::main]
|
||||||
|
/// # async fn main() -> Result<()> {
|
||||||
|
/// # let lua = Lua::new();
|
||||||
|
/// let thread: Thread = lua.load(r#"
|
||||||
|
/// coroutine.create(function (sum)
|
||||||
|
/// for i = 1,10 do
|
||||||
|
/// sum = sum + i
|
||||||
|
/// coroutine.yield(sum)
|
||||||
|
/// end
|
||||||
|
/// return sum
|
||||||
|
/// end)
|
||||||
|
/// "#).eval()?;
|
||||||
|
///
|
||||||
|
/// let mut stream = thread.into_async::<_, i64>(1);
|
||||||
|
/// let mut sum = 0;
|
||||||
|
/// while let Some(n) = stream.try_next().await? {
|
||||||
|
/// sum += n;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(sum, 286);
|
||||||
|
///
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
pub fn into_async<A, R>(self, args: A) -> AsyncThread<'lua, R>
|
||||||
|
where
|
||||||
|
A: ToLuaMulti<'lua>,
|
||||||
|
R: FromLuaMulti<'lua>,
|
||||||
|
{
|
||||||
|
let args = args.to_lua_multi(&self.0.lua);
|
||||||
|
AsyncThread {
|
||||||
|
thread: self,
|
||||||
|
args0: RefCell::new(Some(args)),
|
||||||
|
ret: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> PartialEq for Thread<'lua> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.0 == other.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
impl<'lua, R> Stream for AsyncThread<'lua, R>
|
||||||
|
where
|
||||||
|
R: FromLuaMulti<'lua>,
|
||||||
|
{
|
||||||
|
type Item = Result<R>;
|
||||||
|
|
||||||
|
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
|
let lua = self.thread.0.lua;
|
||||||
|
|
||||||
|
match self.thread.status() {
|
||||||
|
ThreadStatus::Resumable => {}
|
||||||
|
_ => return Poll::Ready(None),
|
||||||
|
};
|
||||||
|
|
||||||
|
let _wg = WakerGuard::new(lua.state, cx.waker().clone());
|
||||||
|
let ret: MultiValue = if let Some(args) = self.args0.borrow_mut().take() {
|
||||||
|
self.thread.resume(args?)?
|
||||||
|
} else {
|
||||||
|
self.thread.resume(())?
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_poll_pending(lua, &ret) {
|
||||||
|
return Poll::Pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
cx.waker().wake_by_ref();
|
||||||
|
Poll::Ready(Some(R::from_lua_multi(ret, lua)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
impl<'lua, R> Future for AsyncThread<'lua, R>
|
||||||
|
where
|
||||||
|
R: FromLuaMulti<'lua>,
|
||||||
|
{
|
||||||
|
type Output = Result<R>;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let lua = self.thread.0.lua;
|
||||||
|
|
||||||
|
match self.thread.status() {
|
||||||
|
ThreadStatus::Resumable => {}
|
||||||
|
_ => return Poll::Ready(Err("Thread already finished".to_lua_err())),
|
||||||
|
};
|
||||||
|
|
||||||
|
let _wg = WakerGuard::new(lua.state, cx.waker().clone());
|
||||||
|
let ret: MultiValue = if let Some(args) = self.args0.borrow_mut().take() {
|
||||||
|
self.thread.resume(args?)?
|
||||||
|
} else {
|
||||||
|
self.thread.resume(())?
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_poll_pending(lua, &ret) {
|
||||||
|
return Poll::Pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let ThreadStatus::Resumable = self.thread.status() {
|
||||||
|
// Ignore value returned via yield()
|
||||||
|
cx.waker().wake_by_ref();
|
||||||
|
return Poll::Pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Ready(R::from_lua_multi(ret, lua))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
fn is_poll_pending(lua: &Lua, val: &MultiValue) -> bool {
|
||||||
|
if val.len() != 1 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(Value::UserData(ud)) = val.iter().next() {
|
||||||
|
unsafe {
|
||||||
|
let _sg = StackGuard::new(lua.state);
|
||||||
|
assert_stack(lua.state, 3);
|
||||||
|
|
||||||
|
lua.push_ref(&ud.0);
|
||||||
|
let is_pending = get_gc_userdata::<AsyncPollPending>(lua.state, -1)
|
||||||
|
.as_ref()
|
||||||
|
.is_some();
|
||||||
|
ffi::lua_pop(lua.state, 1);
|
||||||
|
|
||||||
|
return is_pending;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
struct WakerGuard(*mut ffi::lua_State);
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
impl WakerGuard {
|
||||||
|
pub fn new(state: *mut ffi::lua_State, waker: Waker) -> Result<WakerGuard> {
|
||||||
|
unsafe {
|
||||||
|
let _sg = StackGuard::new(state);
|
||||||
|
assert_stack(state, 6);
|
||||||
|
|
||||||
|
ffi::lua_pushlightuserdata(state, &WAKER_REGISTRY_KEY as *const u8 as *mut c_void);
|
||||||
|
push_gc_userdata(state, waker)?;
|
||||||
|
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
||||||
|
|
||||||
|
Ok(WakerGuard(state))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
impl Drop for WakerGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
let state = self.0;
|
||||||
|
let _sg = StackGuard::new(state);
|
||||||
|
assert_stack(state, 2);
|
||||||
|
|
||||||
|
ffi::lua_pushlightuserdata(state, &WAKER_REGISTRY_KEY as *const u8 as *mut c_void);
|
||||||
|
ffi::lua_pushnil(state);
|
||||||
|
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-12
@@ -1,10 +1,15 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
use std::os::raw::{c_int, c_void};
|
use std::os::raw::{c_int, c_void};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::rc::Rc;
|
||||||
use std::{fmt, mem, ptr};
|
use std::{fmt, mem, ptr};
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
use futures_core::future::LocalBoxFuture;
|
||||||
|
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::ffi;
|
use crate::ffi;
|
||||||
use crate::lua::Lua;
|
use crate::lua::Lua;
|
||||||
|
use crate::util::{assert_stack, StackGuard};
|
||||||
use crate::value::MultiValue;
|
use crate::value::MultiValue;
|
||||||
|
|
||||||
/// Type of Lua integer numbers.
|
/// Type of Lua integer numbers.
|
||||||
@@ -19,29 +24,28 @@ pub struct LightUserData(pub *mut c_void);
|
|||||||
pub(crate) type Callback<'lua, 'a> =
|
pub(crate) type Callback<'lua, 'a> =
|
||||||
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>> + 'a>;
|
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>> + 'a>;
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
pub(crate) type AsyncCallback<'lua, 'a> =
|
||||||
|
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> LocalBoxFuture<'lua, Result<MultiValue<'lua>>> + 'a>;
|
||||||
|
|
||||||
/// An auto generated key into the Lua registry.
|
/// An auto generated key into the Lua registry.
|
||||||
///
|
///
|
||||||
/// This is a handle to a value stored inside the Lua registry. It is not directly usable like the
|
/// This is a handle to a value stored inside the Lua registry. It is not automatically
|
||||||
/// `Table` or `Function` handle types, but since it doesn't hold a reference to a parent Lua and is
|
/// garbage collected on Drop, but it can be removed with [`Lua::remove_registry_value`],
|
||||||
/// Send + Sync + 'static, it is much more flexible and can be used in many situations where it is
|
/// and instances not manually removed can be garbage collected with [`Lua::expire_registry_values`].
|
||||||
/// impossible to directly store a normal handle type. It is not automatically garbage collected on
|
|
||||||
/// Drop, but it can be removed with [`Lua::remove_registry_value`], and instances not manually
|
|
||||||
/// removed can be garbage collected with [`Lua::expire_registry_values`].
|
|
||||||
///
|
///
|
||||||
/// Be warned, If you place this into Lua via a `UserData` type or a rust callback, it is *very
|
/// Be warned, If you place this into Lua via a `UserData` type or a rust callback, it is *very
|
||||||
/// easy* to accidentally cause reference cycles that the Lua garbage collector cannot resolve.
|
/// easy* to accidentally cause reference cycles that the Lua garbage collector cannot resolve.
|
||||||
/// Instead of placing a `RegistryKey` into a `UserData` type, prefer instead to use
|
/// Instead of placing a `RegistryKey` into a `UserData` type, prefer instead to use
|
||||||
/// [`UserData::set_user_value`] / [`UserData::get_user_value`], and instead of moving a RegistryKey
|
/// [`UserData::set_user_value`] / [`UserData::get_user_value`].
|
||||||
/// into a callback, prefer [`Lua::scope`].
|
|
||||||
///
|
///
|
||||||
/// [`Lua::remove_registry_value`]: struct.Lua.html#method.remove_registry_value
|
/// [`Lua::remove_registry_value`]: struct.Lua.html#method.remove_registry_value
|
||||||
/// [`Lua::expire_registry_values`]: struct.Lua.html#method.expire_registry_values
|
/// [`Lua::expire_registry_values`]: struct.Lua.html#method.expire_registry_values
|
||||||
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
|
||||||
/// [`UserData::set_user_value`]: struct.UserData.html#method.set_user_value
|
/// [`UserData::set_user_value`]: struct.UserData.html#method.set_user_value
|
||||||
/// [`UserData::get_user_value`]: struct.UserData.html#method.get_user_value
|
/// [`UserData::get_user_value`]: struct.UserData.html#method.get_user_value
|
||||||
pub struct RegistryKey {
|
pub struct RegistryKey {
|
||||||
pub(crate) registry_id: c_int,
|
pub(crate) registry_id: c_int,
|
||||||
pub(crate) unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
pub(crate) unref_list: Rc<RefCell<Option<Vec<c_int>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for RegistryKey {
|
impl fmt::Debug for RegistryKey {
|
||||||
@@ -52,7 +56,8 @@ impl fmt::Debug for RegistryKey {
|
|||||||
|
|
||||||
impl Drop for RegistryKey {
|
impl Drop for RegistryKey {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if let Some(list) = mlua_expect!(self.unref_list.lock(), "unref_list poisoned").as_mut() {
|
let mut unref_list = mlua_expect!(self.unref_list.try_borrow_mut(), "unref list borrowed");
|
||||||
|
if let Some(list) = unref_list.as_mut() {
|
||||||
list.push(self.registry_id);
|
list.push(self.registry_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,3 +97,16 @@ impl<'lua> Drop for LuaRef<'lua> {
|
|||||||
self.lua.drop_ref(self)
|
self.lua.drop_ref(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'lua> PartialEq for LuaRef<'lua> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
let lua = self.lua;
|
||||||
|
unsafe {
|
||||||
|
let _sg = StackGuard::new(lua.state);
|
||||||
|
assert_stack(lua.state, 2);
|
||||||
|
lua.push_ref(&self);
|
||||||
|
lua.push_ref(&other);
|
||||||
|
ffi::lua_rawequal(lua.state, -1, -2) == 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+95
-16
@@ -1,8 +1,13 @@
|
|||||||
use std::cell::{Ref, RefCell, RefMut};
|
use std::cell::{Ref, RefCell, RefMut};
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::ffi;
|
use crate::ffi;
|
||||||
|
use crate::function::Function;
|
||||||
use crate::lua::Lua;
|
use crate::lua::Lua;
|
||||||
|
use crate::table::Table;
|
||||||
use crate::types::LuaRef;
|
use crate::types::LuaRef;
|
||||||
use crate::util::{assert_stack, get_userdata, StackGuard};
|
use crate::util::{assert_stack, get_userdata, StackGuard};
|
||||||
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti};
|
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti};
|
||||||
@@ -75,11 +80,6 @@ pub enum MetaMethod {
|
|||||||
///
|
///
|
||||||
/// This is not an operator, but it will be called by the built-in `pairs` function.
|
/// This is not an operator, but it will be called by the built-in `pairs` function.
|
||||||
Pairs,
|
Pairs,
|
||||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
|
||||||
/// The `__ipairs` metamethod.
|
|
||||||
///
|
|
||||||
/// This is not an operator, but it will be called by the built-in `ipairs` function.
|
|
||||||
IPairs,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MetaMethod {
|
impl MetaMethod {
|
||||||
@@ -117,8 +117,6 @@ impl MetaMethod {
|
|||||||
MetaMethod::ToString => b"__tostring",
|
MetaMethod::ToString => b"__tostring",
|
||||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||||
MetaMethod::Pairs => b"__pairs",
|
MetaMethod::Pairs => b"__pairs",
|
||||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
|
||||||
MetaMethod::IPairs => b"__ipairs",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,7 +137,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
|||||||
S: ?Sized + AsRef<[u8]>,
|
S: ?Sized + AsRef<[u8]>,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>;
|
M: 'static + Fn(&'lua Lua, &T, A) -> Result<R>;
|
||||||
|
|
||||||
/// Add a regular method which accepts a `&mut T` as the first parameter.
|
/// Add a regular method which accepts a `&mut T` as the first parameter.
|
||||||
///
|
///
|
||||||
@@ -151,7 +149,23 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
|||||||
S: ?Sized + AsRef<[u8]>,
|
S: ?Sized + AsRef<[u8]>,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>;
|
M: 'static + 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.
|
||||||
|
///
|
||||||
|
/// [`add_method`]: #method.add_method
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
fn add_async_method<S, A, R, M, MR>(&mut self, name: &S, method: M)
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
S: ?Sized + AsRef<[u8]>,
|
||||||
|
A: FromLuaMulti<'lua>,
|
||||||
|
R: ToLuaMulti<'lua>,
|
||||||
|
M: 'static + Fn(&'lua Lua, T, A) -> MR,
|
||||||
|
MR: 'static + Future<Output = Result<R>>;
|
||||||
|
|
||||||
/// Add a regular method as a function which accepts generic arguments, the first argument will
|
/// Add a regular method as a function which accepts generic arguments, the first argument will
|
||||||
/// be a `UserData` of type T if the method is called with Lua method syntax:
|
/// be a `UserData` of type T if the method is called with Lua method syntax:
|
||||||
@@ -167,7 +181,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
|||||||
S: ?Sized + AsRef<[u8]>,
|
S: ?Sized + AsRef<[u8]>,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>;
|
F: 'static + Fn(&'lua Lua, A) -> Result<R>;
|
||||||
|
|
||||||
/// Add a regular method as a mutable function which accepts generic arguments.
|
/// Add a regular method as a mutable function which accepts generic arguments.
|
||||||
///
|
///
|
||||||
@@ -179,7 +193,23 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
|||||||
S: ?Sized + AsRef<[u8]>,
|
S: ?Sized + AsRef<[u8]>,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>;
|
F: 'static + 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`].
|
||||||
|
///
|
||||||
|
/// [`add_function`]: #method.add_function
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
fn add_async_function<S, A, R, F, FR>(&mut self, name: &S, function: F)
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
S: ?Sized + AsRef<[u8]>,
|
||||||
|
A: FromLuaMulti<'lua>,
|
||||||
|
R: ToLuaMulti<'lua>,
|
||||||
|
F: 'static + Fn(&'lua Lua, A) -> FR,
|
||||||
|
FR: 'static + Future<Output = Result<R>>;
|
||||||
|
|
||||||
/// Add a metamethod which accepts a `&T` as the first parameter.
|
/// Add a metamethod which accepts a `&T` as the first parameter.
|
||||||
///
|
///
|
||||||
@@ -193,7 +223,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
|||||||
where
|
where
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>;
|
M: 'static + Fn(&'lua Lua, &T, A) -> Result<R>;
|
||||||
|
|
||||||
/// Add a metamethod as a function which accepts a `&mut T` as the first parameter.
|
/// Add a metamethod as a function which accepts a `&mut T` as the first parameter.
|
||||||
///
|
///
|
||||||
@@ -207,7 +237,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
|||||||
where
|
where
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>;
|
M: 'static + FnMut(&'lua Lua, &mut T, A) -> Result<R>;
|
||||||
|
|
||||||
/// Add a metamethod which accepts generic arguments.
|
/// Add a metamethod which accepts generic arguments.
|
||||||
///
|
///
|
||||||
@@ -218,7 +248,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
|||||||
where
|
where
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>;
|
F: 'static + Fn(&'lua Lua, A) -> Result<R>;
|
||||||
|
|
||||||
/// Add a metamethod as a mutable function which accepts generic arguments.
|
/// Add a metamethod as a mutable function which accepts generic arguments.
|
||||||
///
|
///
|
||||||
@@ -229,7 +259,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
|||||||
where
|
where
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>;
|
F: 'static + FnMut(&'lua Lua, A) -> Result<R>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for custom userdata types.
|
/// Trait for custom userdata types.
|
||||||
@@ -298,7 +328,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
|||||||
/// [`UserDataMethods`]: trait.UserDataMethods.html
|
/// [`UserDataMethods`]: trait.UserDataMethods.html
|
||||||
pub trait UserData: Sized {
|
pub trait UserData: Sized {
|
||||||
/// Adds custom methods and operators specific to this userdata.
|
/// Adds custom methods and operators specific to this userdata.
|
||||||
fn add_methods<'lua, T: UserDataMethods<'lua, Self>>(_methods: &mut T) {}
|
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle to an internal Lua userdata for any type that implements [`UserData`].
|
/// Handle to an internal Lua userdata for any type that implements [`UserData`].
|
||||||
@@ -398,6 +428,42 @@ impl<'lua> AnyUserData<'lua> {
|
|||||||
V::from_lua(res, lua)
|
V::from_lua(res, lua)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_metatable(&self) -> Result<Table<'lua>> {
|
||||||
|
unsafe {
|
||||||
|
let lua = self.0.lua;
|
||||||
|
let _sg = StackGuard::new(lua.state);
|
||||||
|
assert_stack(lua.state, 3);
|
||||||
|
|
||||||
|
lua.push_ref(&self.0);
|
||||||
|
|
||||||
|
if ffi::lua_getmetatable(lua.state, -1) == 0 {
|
||||||
|
return Err(Error::UserDataTypeMismatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Table(lua.pop_ref()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
|
||||||
|
let other = other.as_ref();
|
||||||
|
if self == other {
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mt = self.get_metatable()?;
|
||||||
|
if mt != other.get_metatable()? {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if mt.contains_key("__eq")? {
|
||||||
|
return mt
|
||||||
|
.get::<_, Function>("__eq")?
|
||||||
|
.call((self.clone(), other.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
|
||||||
fn inspect<'a, T, R, F>(&'a self, func: F) -> Result<R>
|
fn inspect<'a, T, R, F>(&'a self, func: F) -> Result<R>
|
||||||
where
|
where
|
||||||
T: 'static + UserData,
|
T: 'static + UserData,
|
||||||
@@ -428,3 +494,16 @@ impl<'lua> AnyUserData<'lua> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'lua> PartialEq for AnyUserData<'lua> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.0 == other.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> AsRef<AnyUserData<'lua>> for AnyUserData<'lua> {
|
||||||
|
#[inline]
|
||||||
|
fn as_ref(&self) -> &Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+114
-122
@@ -1,14 +1,21 @@
|
|||||||
use std::any::Any;
|
use std::any::{Any, TypeId};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
||||||
use std::sync::Arc;
|
use std::rc::Rc;
|
||||||
|
use std::sync::Mutex;
|
||||||
use std::{mem, ptr, slice};
|
use std::{mem, ptr, slice};
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::ffi;
|
use crate::ffi;
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
// The capacity must(!) be greater than number of stored keys
|
||||||
|
static ref METATABLE_CACHE: Mutex<HashMap<TypeId, u8>> = Mutex::new(HashMap::with_capacity(32));
|
||||||
|
}
|
||||||
|
|
||||||
// Checks that Lua has enough free stack space for future stack operations. On failure, this will
|
// Checks that Lua has enough free stack space for future stack operations. On failure, this will
|
||||||
// panic with an internal error message.
|
// panic with an internal error message.
|
||||||
pub unsafe fn assert_stack(state: *mut ffi::lua_State, amount: c_int) {
|
pub unsafe fn assert_stack(state: *mut ffi::lua_State, amount: c_int) {
|
||||||
@@ -175,8 +182,7 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
|
|||||||
if let Some(err) = get_wrapped_error(state, -1).as_ref() {
|
if let Some(err) = get_wrapped_error(state, -1).as_ref() {
|
||||||
ffi::lua_pop(state, 1);
|
ffi::lua_pop(state, 1);
|
||||||
err.clone()
|
err.clone()
|
||||||
} else if is_wrapped_panic(state, -1) {
|
} else if let Some(panic) = get_gc_userdata::<WrappedPanic>(state, -1).as_mut() {
|
||||||
let panic = get_userdata::<WrappedPanic>(state, -1);
|
|
||||||
if let Some(p) = (*panic).0.take() {
|
if let Some(p) = (*panic).0.take() {
|
||||||
resume_unwind(p);
|
resume_unwind(p);
|
||||||
} else {
|
} else {
|
||||||
@@ -255,6 +261,41 @@ pub unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
|
|||||||
ptr::read(ud)
|
ptr::read(ud)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pushes the userdata and attaches a metatable with __gc method
|
||||||
|
// Internally uses 5 stack spaces, does not call checkstack
|
||||||
|
pub unsafe fn push_gc_userdata<T: Any>(state: *mut ffi::lua_State, t: T) -> Result<()> {
|
||||||
|
push_meta_gc_userdata::<T, T>(state, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn push_meta_gc_userdata<MT: Any, T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
|
||||||
|
let ud = protect_lua_closure(state, 0, 1, move |state| {
|
||||||
|
ffi::lua_newuserdata(state, mem::size_of::<T>()) as *mut T
|
||||||
|
})?;
|
||||||
|
ptr::write(ud, t);
|
||||||
|
get_gc_metatable_for::<MT>(state);
|
||||||
|
ffi::lua_setmetatable(state, -2);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uses 2 stack spaces, does not call checkstack
|
||||||
|
pub unsafe fn get_gc_userdata<T: Any>(state: *mut ffi::lua_State, index: c_int) -> *mut T {
|
||||||
|
get_meta_gc_userdata::<T, T>(state, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn get_meta_gc_userdata<MT: Any, T>(state: *mut ffi::lua_State, index: c_int) -> *mut T {
|
||||||
|
let ud = ffi::lua_touserdata(state, index) as *mut T;
|
||||||
|
if ud.is_null() || ffi::lua_getmetatable(state, index) == 0 {
|
||||||
|
return ptr::null_mut();
|
||||||
|
}
|
||||||
|
get_gc_metatable_for::<MT>(state);
|
||||||
|
let res = ffi::lua_rawequal(state, -1, -2) != 0;
|
||||||
|
ffi::lua_pop(state, 2);
|
||||||
|
if !res {
|
||||||
|
return ptr::null_mut();
|
||||||
|
}
|
||||||
|
ud
|
||||||
|
}
|
||||||
|
|
||||||
// Populates the given table with the appropriate members to be a userdata metatable for the given
|
// Populates the given table with the appropriate members to be a userdata metatable for the given
|
||||||
// type. This function takes the given table at the `metatable` index, and adds an appropriate __gc
|
// type. This function takes the given table at the `metatable` index, and adds an appropriate __gc
|
||||||
// member to it for the given type and a __metatable entry to protect the table from script access.
|
// member to it for the given type and a __metatable entry to protect the table from script access.
|
||||||
@@ -380,14 +421,14 @@ where
|
|||||||
Ok(Err(err)) => {
|
Ok(Err(err)) => {
|
||||||
ffi::lua_settop(state, 1);
|
ffi::lua_settop(state, 1);
|
||||||
ptr::write(ud as *mut WrappedError, WrappedError(err));
|
ptr::write(ud as *mut WrappedError, WrappedError(err));
|
||||||
get_error_metatable(state);
|
get_gc_metatable_for::<WrappedError>(state);
|
||||||
ffi::lua_setmetatable(state, -2);
|
ffi::lua_setmetatable(state, -2);
|
||||||
ffi::lua_error(state)
|
ffi::lua_error(state)
|
||||||
}
|
}
|
||||||
Err(p) => {
|
Err(p) => {
|
||||||
ffi::lua_settop(state, 1);
|
ffi::lua_settop(state, 1);
|
||||||
ptr::write(ud as *mut WrappedPanic, WrappedPanic(Some(p)));
|
ptr::write(ud as *mut WrappedPanic, WrappedPanic(Some(p)));
|
||||||
get_panic_metatable(state);
|
get_gc_metatable_for::<WrappedPanic>(state);
|
||||||
ffi::lua_setmetatable(state, -2);
|
ffi::lua_setmetatable(state, -2);
|
||||||
ffi::lua_error(state)
|
ffi::lua_error(state)
|
||||||
}
|
}
|
||||||
@@ -428,12 +469,12 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
|
|||||||
ud,
|
ud,
|
||||||
WrappedError(Error::CallbackError {
|
WrappedError(Error::CallbackError {
|
||||||
traceback,
|
traceback,
|
||||||
cause: Arc::new(error),
|
cause: Rc::new(error),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
get_error_metatable(state);
|
get_gc_metatable_for::<WrappedError>(state);
|
||||||
ffi::lua_setmetatable(state, -2);
|
ffi::lua_setmetatable(state, -2);
|
||||||
} else if !is_wrapped_panic(state, -1) {
|
} else if let None = get_gc_userdata::<WrappedPanic>(state, -1).as_ref() {
|
||||||
if ffi::lua_checkstack(state, LUA_TRACEBACK_STACK) != 0 {
|
if ffi::lua_checkstack(state, LUA_TRACEBACK_STACK) != 0 {
|
||||||
let s = ffi::luaL_tolstring(state, -1, ptr::null_mut());
|
let s = ffi::luaL_tolstring(state, -1, ptr::null_mut());
|
||||||
ffi::luaL_traceback(state, state, s, 0);
|
ffi::luaL_traceback(state, state, s, 0);
|
||||||
@@ -443,68 +484,83 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
|
|||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Does not call lua_checkstack, uses 2 stack spaces.
|
|
||||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
|
||||||
pub unsafe fn set_main_state(state: *mut ffi::lua_State) {
|
|
||||||
ffi::lua_pushlightuserdata(state, &MAIN_THREAD_REGISTRY_KEY as *const u8 as *mut c_void);
|
|
||||||
ffi::lua_pushthread(state);
|
|
||||||
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Does not call lua_checkstack, uses 1 stack space.
|
// Does not call lua_checkstack, uses 1 stack space.
|
||||||
pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> *mut ffi::lua_State {
|
pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> *mut ffi::lua_State {
|
||||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||||
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_MAINTHREAD);
|
|
||||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
|
||||||
{
|
{
|
||||||
ffi::lua_pushlightuserdata(state, &MAIN_THREAD_REGISTRY_KEY as *const u8 as *mut c_void);
|
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_MAINTHREAD);
|
||||||
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
|
let main_state = ffi::lua_tothread(state, -1);
|
||||||
|
ffi::lua_pop(state, 1);
|
||||||
|
main_state
|
||||||
}
|
}
|
||||||
let main_state = ffi::lua_tothread(state, -1);
|
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||||
ffi::lua_pop(state, 1);
|
state
|
||||||
main_state
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pushes a WrappedError to the top of the stack. Uses two stack spaces and does not call
|
// Pushes a WrappedError to the top of the stack. Uses two stack spaces and does not call
|
||||||
// lua_checkstack.
|
// lua_checkstack.
|
||||||
pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) -> Result<()> {
|
pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) -> Result<()> {
|
||||||
let ud = protect_lua_closure(state, 0, 1, move |state| {
|
push_gc_userdata::<WrappedError>(state, WrappedError(err))
|
||||||
ffi::lua_newuserdata(state, mem::size_of::<WrappedError>()) as *mut WrappedError
|
|
||||||
})?;
|
|
||||||
ptr::write(ud, WrappedError(err));
|
|
||||||
get_error_metatable(state);
|
|
||||||
ffi::lua_setmetatable(state, -2);
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if the value at the given index is a WrappedError, and if it is returns a pointer to it,
|
// Checks if the value at the given index is a WrappedError, and if it is returns a pointer to it,
|
||||||
// otherwise returns null. Uses 2 stack spaces and does not call lua_checkstack.
|
// otherwise returns null. Uses 2 stack spaces and does not call lua_checkstack.
|
||||||
pub unsafe fn get_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> *const Error {
|
pub unsafe fn get_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> *const Error {
|
||||||
let userdata = ffi::lua_touserdata(state, index);
|
let ud = get_gc_userdata::<WrappedError>(state, index);
|
||||||
if userdata.is_null() {
|
if ud.is_null() {
|
||||||
return ptr::null();
|
return ptr::null();
|
||||||
}
|
}
|
||||||
|
&(*ud).0
|
||||||
|
}
|
||||||
|
|
||||||
if ffi::lua_getmetatable(state, index) == 0 {
|
// Initialize the internal (with __gc) metatable for a type T
|
||||||
return ptr::null();
|
pub unsafe fn init_gc_metatable_for<T: Any>(
|
||||||
|
state: *mut ffi::lua_State,
|
||||||
|
customize_fn: Option<fn(*mut ffi::lua_State)>,
|
||||||
|
) {
|
||||||
|
let type_id = TypeId::of::<T>();
|
||||||
|
|
||||||
|
let ref_addr = {
|
||||||
|
let mut mt_cache = mlua_expect!(METATABLE_CACHE.lock(), "cannot lock metatable cache");
|
||||||
|
mlua_assert!(
|
||||||
|
mt_cache.capacity() - mt_cache.len() > 0,
|
||||||
|
"out of metatable cache capacity"
|
||||||
|
);
|
||||||
|
mt_cache.insert(type_id, 0);
|
||||||
|
&mt_cache[&type_id] as *const u8
|
||||||
|
};
|
||||||
|
|
||||||
|
ffi::lua_newtable(state);
|
||||||
|
|
||||||
|
ffi::lua_pushstring(state, cstr!("__gc"));
|
||||||
|
ffi::lua_pushcfunction(state, userdata_destructor::<T>);
|
||||||
|
ffi::lua_rawset(state, -3);
|
||||||
|
|
||||||
|
ffi::lua_pushstring(state, cstr!("__metatable"));
|
||||||
|
ffi::lua_pushboolean(state, 0);
|
||||||
|
ffi::lua_rawset(state, -3);
|
||||||
|
|
||||||
|
if let Some(f) = customize_fn {
|
||||||
|
f(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
get_error_metatable(state);
|
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *mut c_void);
|
||||||
let res = ffi::lua_rawequal(state, -1, -2) != 0;
|
}
|
||||||
ffi::lua_pop(state, 2);
|
|
||||||
|
|
||||||
if res {
|
pub unsafe fn get_gc_metatable_for<T: Any>(state: *mut ffi::lua_State) {
|
||||||
&(*get_userdata::<WrappedError>(state, -1)).0
|
let type_id = TypeId::of::<T>();
|
||||||
} else {
|
let ref_addr = {
|
||||||
ptr::null()
|
let mt_cache = mlua_expect!(METATABLE_CACHE.lock(), "cannot lock metatable cache");
|
||||||
}
|
mlua_expect!(mt_cache.get(&type_id), "gc metatable does not exist") as *const u8
|
||||||
|
};
|
||||||
|
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *mut c_void);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the error, panic, and destructed userdata metatables.
|
// Initialize the error, panic, and destructed userdata metatables.
|
||||||
pub unsafe fn init_error_registry(state: *mut ffi::lua_State) {
|
pub unsafe fn init_error_registry(state: *mut ffi::lua_State) {
|
||||||
assert_stack(state, 8);
|
assert_stack(state, 8);
|
||||||
|
|
||||||
// Create error metatable
|
// Create error and panic metatables
|
||||||
|
|
||||||
unsafe extern "C" fn error_tostring(state: *mut ffi::lua_State) -> c_int {
|
unsafe extern "C" fn error_tostring(state: *mut ffi::lua_State) -> c_int {
|
||||||
let err_buf = callback_error(state, |_| {
|
let err_buf = callback_error(state, |_| {
|
||||||
@@ -524,8 +580,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) {
|
|||||||
// kind of recursive error structure?)
|
// kind of recursive error structure?)
|
||||||
let _ = write!(&mut (*err_buf), "{}", error);
|
let _ = write!(&mut (*err_buf), "{}", error);
|
||||||
Ok(err_buf)
|
Ok(err_buf)
|
||||||
} else if is_wrapped_panic(state, -1) {
|
} else if let Some(panic) = get_gc_userdata::<WrappedPanic>(state, -1).as_ref() {
|
||||||
let panic = get_userdata::<WrappedPanic>(state, -1);
|
|
||||||
if let Some(ref p) = (*panic).0 {
|
if let Some(ref p) = (*panic).0 {
|
||||||
ffi::lua_pushlightuserdata(
|
ffi::lua_pushlightuserdata(
|
||||||
state,
|
state,
|
||||||
@@ -564,56 +619,31 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) {
|
|||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
ffi::lua_pushlightuserdata(
|
init_gc_metatable_for::<WrappedError>(
|
||||||
state,
|
state,
|
||||||
&ERROR_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
|
Some(|state| {
|
||||||
|
ffi::lua_pushstring(state, cstr!("__tostring"));
|
||||||
|
ffi::lua_pushcfunction(state, error_tostring);
|
||||||
|
ffi::lua_rawset(state, -3);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
ffi::lua_newtable(state);
|
|
||||||
|
|
||||||
ffi::lua_pushstring(state, cstr!("__gc"));
|
init_gc_metatable_for::<WrappedPanic>(
|
||||||
ffi::lua_pushcfunction(state, userdata_destructor::<WrappedError>);
|
|
||||||
ffi::lua_rawset(state, -3);
|
|
||||||
|
|
||||||
ffi::lua_pushstring(state, cstr!("__tostring"));
|
|
||||||
ffi::lua_pushcfunction(state, error_tostring);
|
|
||||||
ffi::lua_rawset(state, -3);
|
|
||||||
|
|
||||||
ffi::lua_pushstring(state, cstr!("__metatable"));
|
|
||||||
ffi::lua_pushboolean(state, 0);
|
|
||||||
ffi::lua_rawset(state, -3);
|
|
||||||
|
|
||||||
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
|
||||||
|
|
||||||
// Create panic metatable
|
|
||||||
|
|
||||||
ffi::lua_pushlightuserdata(
|
|
||||||
state,
|
state,
|
||||||
&PANIC_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
|
Some(|state| {
|
||||||
|
ffi::lua_pushstring(state, cstr!("__tostring"));
|
||||||
|
ffi::lua_pushcfunction(state, error_tostring);
|
||||||
|
ffi::lua_rawset(state, -3);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
ffi::lua_newtable(state);
|
|
||||||
|
|
||||||
ffi::lua_pushstring(state, cstr!("__gc"));
|
|
||||||
ffi::lua_pushcfunction(state, userdata_destructor::<WrappedPanic>);
|
|
||||||
ffi::lua_rawset(state, -3);
|
|
||||||
|
|
||||||
ffi::lua_pushstring(state, cstr!("__tostring"));
|
|
||||||
ffi::lua_pushcfunction(state, error_tostring);
|
|
||||||
ffi::lua_rawset(state, -3);
|
|
||||||
|
|
||||||
ffi::lua_pushstring(state, cstr!("__metatable"));
|
|
||||||
ffi::lua_pushboolean(state, 0);
|
|
||||||
ffi::lua_rawset(state, -3);
|
|
||||||
|
|
||||||
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
|
||||||
|
|
||||||
// Create destructed userdata metatable
|
// Create destructed userdata metatable
|
||||||
|
|
||||||
unsafe extern "C" fn destructed_error(state: *mut ffi::lua_State) -> c_int {
|
unsafe extern "C" fn destructed_error(state: *mut ffi::lua_State) -> c_int {
|
||||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||||
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedError>()) as *mut WrappedError;
|
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedError>()) as *mut WrappedError;
|
||||||
|
|
||||||
ptr::write(ud, WrappedError(Error::CallbackDestructed));
|
ptr::write(ud, WrappedError(Error::CallbackDestructed));
|
||||||
get_error_metatable(state);
|
get_gc_metatable_for::<WrappedError>(state);
|
||||||
ffi::lua_setmetatable(state, -2);
|
ffi::lua_setmetatable(state, -2);
|
||||||
ffi::lua_error(state)
|
ffi::lua_error(state)
|
||||||
}
|
}
|
||||||
@@ -709,40 +739,6 @@ unsafe fn to_string<'a>(state: *mut ffi::lua_State, index: c_int) -> Cow<'a, str
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if the value at the given index is a WrappedPanic. Uses 2 stack spaces and does not call
|
|
||||||
// lua_checkstack.
|
|
||||||
unsafe fn is_wrapped_panic(state: *mut ffi::lua_State, index: c_int) -> bool {
|
|
||||||
let userdata = ffi::lua_touserdata(state, index);
|
|
||||||
if userdata.is_null() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ffi::lua_getmetatable(state, index) == 0 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
get_panic_metatable(state);
|
|
||||||
let res = ffi::lua_rawequal(state, -1, -2) != 0;
|
|
||||||
ffi::lua_pop(state, 2);
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn get_error_metatable(state: *mut ffi::lua_State) {
|
|
||||||
ffi::lua_pushlightuserdata(
|
|
||||||
state,
|
|
||||||
&ERROR_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
|
|
||||||
);
|
|
||||||
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn get_panic_metatable(state: *mut ffi::lua_State) {
|
|
||||||
ffi::lua_pushlightuserdata(
|
|
||||||
state,
|
|
||||||
&PANIC_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
|
|
||||||
);
|
|
||||||
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) {
|
unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) {
|
||||||
ffi::lua_pushlightuserdata(
|
ffi::lua_pushlightuserdata(
|
||||||
state,
|
state,
|
||||||
@@ -751,9 +747,5 @@ unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) {
|
|||||||
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
|
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
|
||||||
static MAIN_THREAD_REGISTRY_KEY: u8 = 0;
|
|
||||||
static ERROR_METATABLE_REGISTRY_KEY: u8 = 0;
|
|
||||||
static PANIC_METATABLE_REGISTRY_KEY: u8 = 0;
|
|
||||||
static DESTRUCTED_USERDATA_METATABLE: u8 = 0;
|
static DESTRUCTED_USERDATA_METATABLE: u8 = 0;
|
||||||
static ERROR_PRINT_BUFFER_KEY: u8 = 0;
|
static ERROR_PRINT_BUFFER_KEY: u8 = 0;
|
||||||
|
|||||||
+50
-3
@@ -2,6 +2,7 @@ use std::iter::{self, FromIterator};
|
|||||||
use std::{slice, str, vec};
|
use std::{slice, str, vec};
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
|
use crate::ffi;
|
||||||
use crate::function::Function;
|
use crate::function::Function;
|
||||||
use crate::lua::Lua;
|
use crate::lua::Lua;
|
||||||
use crate::string::String;
|
use crate::string::String;
|
||||||
@@ -47,20 +48,66 @@ pub enum Value<'lua> {
|
|||||||
pub use self::Value::Nil;
|
pub use self::Value::Nil;
|
||||||
|
|
||||||
impl<'lua> Value<'lua> {
|
impl<'lua> Value<'lua> {
|
||||||
pub(crate) fn type_name(&self) -> &'static str {
|
pub fn type_name(&self) -> &'static str {
|
||||||
match *self {
|
match *self {
|
||||||
Value::Nil => "nil",
|
Value::Nil => "nil",
|
||||||
Value::Boolean(_) => "boolean",
|
Value::Boolean(_) => "boolean",
|
||||||
Value::LightUserData(_) => "light userdata",
|
Value::LightUserData(_) => "lightuserdata",
|
||||||
Value::Integer(_) => "integer",
|
Value::Integer(_) => "integer",
|
||||||
Value::Number(_) => "number",
|
Value::Number(_) => "number",
|
||||||
Value::String(_) => "string",
|
Value::String(_) => "string",
|
||||||
Value::Table(_) => "table",
|
Value::Table(_) => "table",
|
||||||
Value::Function(_) => "function",
|
Value::Function(_) => "function",
|
||||||
Value::Thread(_) => "thread",
|
Value::Thread(_) => "thread",
|
||||||
Value::UserData(_) | Value::Error(_) => "userdata",
|
Value::UserData(_) => "userdata",
|
||||||
|
Value::Error(_) => "error",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compares two values for equality.
|
||||||
|
///
|
||||||
|
/// Equality comparisons do not convert strings to numbers or vice versa.
|
||||||
|
/// Tables, Functions, Threads, and Userdata are compared by reference:
|
||||||
|
/// two objects are considered equal only if they are the same object.
|
||||||
|
///
|
||||||
|
/// If Tables or Userdata have `__eq` metamethod then mlua will try to invoke it.
|
||||||
|
/// The first value is checked first. If that value does not define a metamethod
|
||||||
|
/// for `__eq`, then mlua will check the second value.
|
||||||
|
/// Then mlua calls the metamethod with the two values as arguments, if found.
|
||||||
|
pub fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
|
||||||
|
match (self, other.as_ref()) {
|
||||||
|
(Value::Table(a), Value::Table(b)) => a.equals(b),
|
||||||
|
(Value::UserData(a), Value::UserData(b)) => a.equals(b),
|
||||||
|
_ => Ok(self == other.as_ref()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> PartialEq for Value<'lua> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
match (self, other) {
|
||||||
|
(Value::Nil, Value::Nil) => true,
|
||||||
|
(Value::Boolean(a), Value::Boolean(b)) => a == b,
|
||||||
|
(Value::LightUserData(a), Value::LightUserData(b)) => a == b,
|
||||||
|
(Value::Integer(a), Value::Integer(b)) => *a == *b,
|
||||||
|
(Value::Integer(a), Value::Number(b)) => *a as ffi::lua_Number == *b,
|
||||||
|
(Value::Number(a), Value::Integer(b)) => *a == *b as ffi::lua_Number,
|
||||||
|
(Value::Number(a), Value::Number(b)) => *a == *b,
|
||||||
|
(Value::String(a), Value::String(b)) => a == b,
|
||||||
|
(Value::Table(a), Value::Table(b)) => a == b,
|
||||||
|
(Value::Function(a), Value::Function(b)) => a == b,
|
||||||
|
(Value::Thread(a), Value::Thread(b)) => a == b,
|
||||||
|
(Value::UserData(a), Value::UserData(b)) => a == b,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> AsRef<Value<'lua>> for Value<'lua> {
|
||||||
|
#[inline]
|
||||||
|
fn as_ref(&self) -> &Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for types convertible to `Value`.
|
/// Trait for types convertible to `Value`.
|
||||||
|
|||||||
+295
@@ -0,0 +1,295 @@
|
|||||||
|
#![cfg(feature = "async")]
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
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 = Rc::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!(Rc::strong_count(&cnt), 2);
|
||||||
|
lua.gc_collect()?; // thread_s is non-resumable and subject to garbage collection
|
||||||
|
assert_eq!(Rc::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(Rc<Cell<i64>>);
|
||||||
|
|
||||||
|
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.get())
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_async_method("set_value", |_, data, n| async move {
|
||||||
|
Delay::new(Duration::from_millis(10)).await;
|
||||||
|
data.0.set(n);
|
||||||
|
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(Rc::new(Cell::new(11))))?;
|
||||||
|
globals.set("userdata", userdata.clone())?;
|
||||||
|
|
||||||
|
lua.load(
|
||||||
|
r#"
|
||||||
|
assert(userdata:get_value() == 11)
|
||||||
|
userdata:set_value(12)
|
||||||
|
assert(userdata:get_value() == 12)
|
||||||
|
assert(userdata.sleep(5) == "elapsed:5ms")
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.exec_async()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -1,14 +1,3 @@
|
|||||||
#![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"
|
|
||||||
)]
|
|
||||||
extern "system" {}
|
|
||||||
|
|
||||||
use bstr::{BStr, BString};
|
use bstr::{BStr, BString};
|
||||||
use mlua::{Lua, Result};
|
use mlua::{Lua, Result};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#[cfg(any(feature = "lua53", feature = "lua51"))]
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore]
|
||||||
fn test_compile_fail() {
|
fn test_compile_fail() {
|
||||||
let t = trybuild::TestCases::new();
|
let t = trybuild::TestCases::new();
|
||||||
t.compile_fail("tests/compile_fail/*.rs");
|
t.compile_fail("tests/compile_fail/*.rs");
|
||||||
|
|||||||
@@ -10,6 +10,21 @@ error[E0277]: the type `std::cell::UnsafeCell<()>` may contain interior mutabili
|
|||||||
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&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]`
|
= 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
|
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
|
--> $DIR/lua_norefunwindsafe.rs:7:5
|
||||||
|
|
|
|
||||||
@@ -18,8 +33,9 @@ error[E0277]: the type `std::cell::UnsafeCell<mlua::lua::ExtraData>` may contain
|
|||||||
|
|
|
|
||||||
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<mlua::lua::ExtraData>`
|
= 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::cell::RefCell<mlua::lua::ExtraData>`
|
||||||
= note: required because it appears within the type `std::marker::PhantomData<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::sync::Arc<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 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 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]`
|
= note: required because it appears within the type `[closure@$DIR/tests/compile_fail/lua_norefunwindsafe.rs:7:18: 7:48 lua:&mlua::lua::Lua]`
|
||||||
@@ -33,8 +49,9 @@ error[E0277]: the type `std::cell::UnsafeCell<isize>` may contain interior mutab
|
|||||||
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<isize>`
|
= 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::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::cell::RefCell<mlua::lua::ExtraData>`
|
||||||
= note: required because it appears within the type `std::marker::PhantomData<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::sync::Arc<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 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 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]`
|
= note: required because it appears within the type `[closure@$DIR/tests/compile_fail/lua_norefunwindsafe.rs:7:18: 7:48 lua:&mlua::lua::Lua]`
|
||||||
|
|||||||
@@ -12,6 +12,23 @@ error[E0277]: the type `std::cell::UnsafeCell<()>` may contain interior mutabili
|
|||||||
= note: required because it appears within the type `mlua::table::Table<'_>`
|
= 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<'_>]`
|
= 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
|
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
|
--> $DIR/ref_nounwindsafe.rs:8:5
|
||||||
|
|
|
|
||||||
@@ -20,8 +37,9 @@ error[E0277]: the type `std::cell::UnsafeCell<mlua::lua::ExtraData>` may contain
|
|||||||
|
|
|
|
||||||
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<mlua::lua::ExtraData>`
|
= 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::cell::RefCell<mlua::lua::ExtraData>`
|
||||||
= note: required because it appears within the type `std::marker::PhantomData<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::sync::Arc<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 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 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::types::LuaRef<'_>`
|
||||||
@@ -37,8 +55,9 @@ error[E0277]: the type `std::cell::UnsafeCell<isize>` may contain interior mutab
|
|||||||
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<isize>`
|
= 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::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::cell::RefCell<mlua::lua::ExtraData>`
|
||||||
= note: required because it appears within the type `std::marker::PhantomData<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::sync::Arc<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 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 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::types::LuaRef<'_>`
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
use mlua::{Lua, Table, Result};
|
|
||||||
|
|
||||||
struct Test {
|
|
||||||
field: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let lua = Lua::new();
|
|
||||||
lua.scope(|scope| -> Result<()> {
|
|
||||||
let mut inner: Option<Table> = None;
|
|
||||||
let f = scope
|
|
||||||
.create_function_mut(move |lua, t: Table| {
|
|
||||||
if let Some(old) = inner.take() {
|
|
||||||
// Access old callback `Lua`.
|
|
||||||
}
|
|
||||||
inner = Some(t);
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
f.call::<_, ()>(lua.create_table()?)?;
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
|
|
||||||
--> $DIR/scope_callback_capture.rs:12:14
|
|
||||||
|
|
|
||||||
12 | .create_function_mut(move |lua, t: Table| {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 9:15...
|
|
||||||
--> $DIR/scope_callback_capture.rs:9:15
|
|
||||||
|
|
|
||||||
9 | lua.scope(|scope| -> Result<()> {
|
|
||||||
| _______________^
|
|
||||||
10 | | let mut inner: Option<Table> = None;
|
|
||||||
11 | | let f = scope
|
|
||||||
12 | | .create_function_mut(move |lua, t: Table| {
|
|
||||||
... |
|
|
||||||
20 | | Ok(())
|
|
||||||
21 | | });
|
|
||||||
| |_____^
|
|
||||||
note: ...so that reference does not outlive borrowed content
|
|
||||||
--> $DIR/scope_callback_capture.rs:11:17
|
|
||||||
|
|
|
||||||
11 | let f = scope
|
|
||||||
| ^^^^^
|
|
||||||
note: but, the lifetime must be valid for the method call at 9:5...
|
|
||||||
--> $DIR/scope_callback_capture.rs:9:5
|
|
||||||
|
|
|
||||||
9 | / lua.scope(|scope| -> Result<()> {
|
|
||||||
10 | | let mut inner: Option<Table> = None;
|
|
||||||
11 | | let f = scope
|
|
||||||
12 | | .create_function_mut(move |lua, t: Table| {
|
|
||||||
... |
|
|
||||||
20 | | Ok(())
|
|
||||||
21 | | });
|
|
||||||
| |______^
|
|
||||||
note: ...so that a type/lifetime parameter is in scope here
|
|
||||||
--> $DIR/scope_callback_capture.rs:9:5
|
|
||||||
|
|
|
||||||
9 | / lua.scope(|scope| -> Result<()> {
|
|
||||||
10 | | let mut inner: Option<Table> = None;
|
|
||||||
11 | | let f = scope
|
|
||||||
12 | | .create_function_mut(move |lua, t: Table| {
|
|
||||||
... |
|
|
||||||
20 | | Ok(())
|
|
||||||
21 | | });
|
|
||||||
| |______^
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
use mlua::{Lua, Table, Result};
|
|
||||||
|
|
||||||
struct Test {
|
|
||||||
field: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let lua = Lua::new();
|
|
||||||
lua.scope(|scope| -> Result<()> {
|
|
||||||
let mut inner: Option<Table> = None;
|
|
||||||
let f = scope
|
|
||||||
.create_function_mut(|_, t: Table| {
|
|
||||||
inner = Some(t);
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
f.call::<_, ()>(lua.create_table()?)?;
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
|
|
||||||
--> $DIR/scope_callback_inner.rs:12:14
|
|
||||||
|
|
|
||||||
12 | .create_function_mut(|_, t: Table| {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 9:15...
|
|
||||||
--> $DIR/scope_callback_inner.rs:9:15
|
|
||||||
|
|
|
||||||
9 | lua.scope(|scope| -> Result<()> {
|
|
||||||
| _______________^
|
|
||||||
10 | | let mut inner: Option<Table> = None;
|
|
||||||
11 | | let f = scope
|
|
||||||
12 | | .create_function_mut(|_, t: Table| {
|
|
||||||
... |
|
|
||||||
17 | | Ok(())
|
|
||||||
18 | | });
|
|
||||||
| |_____^
|
|
||||||
note: ...so that reference does not outlive borrowed content
|
|
||||||
--> $DIR/scope_callback_inner.rs:11:17
|
|
||||||
|
|
|
||||||
11 | let f = scope
|
|
||||||
| ^^^^^
|
|
||||||
note: but, the lifetime must be valid for the method call at 9:5...
|
|
||||||
--> $DIR/scope_callback_inner.rs:9:5
|
|
||||||
|
|
|
||||||
9 | / lua.scope(|scope| -> Result<()> {
|
|
||||||
10 | | let mut inner: Option<Table> = None;
|
|
||||||
11 | | let f = scope
|
|
||||||
12 | | .create_function_mut(|_, t: Table| {
|
|
||||||
... |
|
|
||||||
17 | | Ok(())
|
|
||||||
18 | | });
|
|
||||||
| |______^
|
|
||||||
note: ...so that a type/lifetime parameter is in scope here
|
|
||||||
--> $DIR/scope_callback_inner.rs:9:5
|
|
||||||
|
|
|
||||||
9 | / lua.scope(|scope| -> Result<()> {
|
|
||||||
10 | | let mut inner: Option<Table> = None;
|
|
||||||
11 | | let f = scope
|
|
||||||
12 | | .create_function_mut(|_, t: Table| {
|
|
||||||
... |
|
|
||||||
17 | | Ok(())
|
|
||||||
18 | | });
|
|
||||||
| |______^
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
use mlua::{Lua, Table, Result};
|
|
||||||
|
|
||||||
struct Test {
|
|
||||||
field: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let lua = Lua::new();
|
|
||||||
let mut outer: Option<Table> = None;
|
|
||||||
lua.scope(|scope| -> Result<()> {
|
|
||||||
let f = scope
|
|
||||||
.create_function_mut(|_, t: Table| {
|
|
||||||
outer = Some(t);
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
f.call::<_, ()>(lua.create_table()?)?;
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
error: borrowed data cannot be stored outside of its closure
|
|
||||||
--> $DIR/scope_callback_outer.rs:11:17
|
|
||||||
|
|
|
||||||
9 | let mut outer: Option<Table> = None;
|
|
||||||
| --------- ...so that variable is valid at time of its declaration
|
|
||||||
10 | lua.scope(|scope| -> Result<()> {
|
|
||||||
| --------------------- borrowed data cannot outlive this closure
|
|
||||||
11 | let f = scope
|
|
||||||
| ^^^^^ cannot be stored outside of its closure
|
|
||||||
12 | .create_function_mut(|_, t: Table| {
|
|
||||||
| ------------------- cannot infer an appropriate lifetime...
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
use mlua::{Lua, Result};
|
|
||||||
|
|
||||||
struct Test {
|
|
||||||
field: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let lua = Lua::new();
|
|
||||||
lua.scope(|scope| -> Result<()> {
|
|
||||||
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::<_, ()>(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
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| -> Result<()> {
|
|
||||||
| ----- 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 |_, ()| {
|
|
||||||
| ^^^^^^^^^^^^
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
use mlua::{Lua, UserData, Result};
|
|
||||||
|
|
||||||
struct MyUserData<'a>(&'a mut i32);
|
|
||||||
impl<'a> UserData for MyUserData<'a> {}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let mut i = 1;
|
|
||||||
|
|
||||||
let lua = Lua::new();
|
|
||||||
lua.scope(|scope| -> Result<()> {
|
|
||||||
let _a = scope.create_nonstatic_userdata(MyUserData(&mut i))?;
|
|
||||||
let _b = scope.create_nonstatic_userdata(MyUserData(&mut i))?;
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
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))?;
|
|
||||||
| ------ first mutable borrow occurs here
|
|
||||||
12 | let _b = scope.create_nonstatic_userdata(MyUserData(&mut i))?;
|
|
||||||
| ------------------------- ^^^^^^ second mutable borrow occurs here
|
|
||||||
| |
|
|
||||||
| first borrow later used by call
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
use mlua::{Lua, UserData, Result};
|
|
||||||
|
|
||||||
struct MyUserData<'a>(&'a i32);
|
|
||||||
impl<'a> UserData for MyUserData<'a> {}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// Should not allow userdata borrow to outlive lifetime of AnyUserData handle
|
|
||||||
|
|
||||||
let igood = 1;
|
|
||||||
|
|
||||||
let lua = Lua::new();
|
|
||||||
lua.scope(|scope| -> Result<()> {
|
|
||||||
let _ugood = scope.create_nonstatic_userdata(MyUserData(&igood))?;
|
|
||||||
let _ubad = {
|
|
||||||
let ibad = 42;
|
|
||||||
scope.create_nonstatic_userdata(MyUserData(&ibad))?;
|
|
||||||
};
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
error[E0597]: `ibad` does not live long enough
|
|
||||||
--> $DIR/scope_userdata_borrow.rs:16:56
|
|
||||||
|
|
|
||||||
12 | lua.scope(|scope| -> Result<()> {
|
|
||||||
| ----- has type `&mlua::scope::Scope<'_, '1>`
|
|
||||||
...
|
|
||||||
16 | scope.create_nonstatic_userdata(MyUserData(&ibad))?;
|
|
||||||
| -------------------------------------------^^^^^--
|
|
||||||
| | |
|
|
||||||
| | borrowed value does not live long enough
|
|
||||||
| argument requires that `ibad` is borrowed for `'1`
|
|
||||||
17 | };
|
|
||||||
| - `ibad` dropped here while still borrowed
|
|
||||||
@@ -1,14 +1,3 @@
|
|||||||
#![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"
|
|
||||||
)]
|
|
||||||
extern "system" {}
|
|
||||||
|
|
||||||
use mlua::{Function, Lua, Result, String};
|
use mlua::{Function, Lua, Result, String};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
+17
-28
@@ -1,21 +1,11 @@
|
|||||||
#![cfg_attr(
|
use std::rc::Rc;
|
||||||
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"
|
|
||||||
)]
|
|
||||||
extern "system" {}
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use mlua::{Lua, Result, UserData};
|
use mlua::{Lua, Result, UserData};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_gc_control() -> Result<()> {
|
fn test_gc_control() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
let globals = lua.globals();
|
||||||
|
|
||||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||||
{
|
{
|
||||||
@@ -26,18 +16,17 @@ fn test_gc_control() -> Result<()> {
|
|||||||
assert!(lua.gc_is_running());
|
assert!(lua.gc_is_running());
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MyUserdata(Arc<()>);
|
struct MyUserdata(Rc<()>);
|
||||||
impl UserData for MyUserdata {}
|
impl UserData for MyUserdata {}
|
||||||
|
|
||||||
let rc = Arc::new(());
|
let rc = Rc::new(());
|
||||||
lua.globals()
|
globals.set("userdata", lua.create_userdata(MyUserdata(rc.clone()))?)?;
|
||||||
.set("userdata", lua.create_userdata(MyUserdata(rc.clone()))?)?;
|
globals.raw_remove("userdata")?;
|
||||||
lua.globals().raw_remove("userdata")?;
|
|
||||||
|
|
||||||
assert_eq!(Arc::strong_count(&rc), 2);
|
assert_eq!(Rc::strong_count(&rc), 2);
|
||||||
lua.gc_collect()?;
|
lua.gc_collect()?;
|
||||||
lua.gc_collect()?;
|
lua.gc_collect()?;
|
||||||
assert_eq!(Arc::strong_count(&rc), 1);
|
assert_eq!(Rc::strong_count(&rc), 1);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -51,15 +40,15 @@ fn test_gc_error() {
|
|||||||
match lua
|
match lua
|
||||||
.load(
|
.load(
|
||||||
r#"
|
r#"
|
||||||
val = nil
|
val = nil
|
||||||
table = {}
|
table = {}
|
||||||
setmetatable(table, {
|
setmetatable(table, {
|
||||||
__gc = function()
|
__gc = function()
|
||||||
error("gcwascalled")
|
error("gcwascalled")
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
table = nil
|
table = nil
|
||||||
collectgarbage("collect")
|
collectgarbage("collect")
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.exec()
|
.exec()
|
||||||
|
|||||||
-242
@@ -1,242 +0,0 @@
|
|||||||
#![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"
|
|
||||||
)]
|
|
||||||
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_static_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(())
|
|
||||||
}
|
|
||||||
@@ -1,14 +1,3 @@
|
|||||||
#![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"
|
|
||||||
)]
|
|
||||||
extern "system" {}
|
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use mlua::{Lua, Result, String};
|
use mlua::{Lua, Result, String};
|
||||||
|
|||||||
+129
-13
@@ -1,15 +1,4 @@
|
|||||||
#![cfg_attr(
|
use mlua::{Lua, Nil, Result, Table, TableExt, Value};
|
||||||
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"
|
|
||||||
)]
|
|
||||||
extern "system" {}
|
|
||||||
|
|
||||||
use mlua::{Lua, Nil, Result, Table, Value};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_set_get() -> Result<()> {
|
fn test_set_get() -> Result<()> {
|
||||||
@@ -91,10 +80,73 @@ fn test_table() -> Result<()> {
|
|||||||
globals.set("table4", lua.create_sequence_from(vec![1, 2, 3, 4, 5])?)?;
|
globals.set("table4", lua.create_sequence_from(vec![1, 2, 3, 4, 5])?)?;
|
||||||
let table4 = globals.get::<_, Table>("table4")?;
|
let table4 = globals.get::<_, Table>("table4")?;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
table4.pairs().collect::<Result<Vec<(i64, i64)>>>()?,
|
table4
|
||||||
|
.clone()
|
||||||
|
.pairs()
|
||||||
|
.collect::<Result<Vec<(i64, i64)>>>()?,
|
||||||
vec![(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]
|
vec![(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
table4.raw_insert(4, 35)?;
|
||||||
|
table4.raw_insert(7, 7)?;
|
||||||
|
assert_eq!(
|
||||||
|
table4
|
||||||
|
.clone()
|
||||||
|
.pairs()
|
||||||
|
.collect::<Result<Vec<(i64, i64)>>>()?,
|
||||||
|
vec![(1, 1), (2, 2), (3, 3), (4, 35), (5, 4), (6, 5), (7, 7)]
|
||||||
|
);
|
||||||
|
|
||||||
|
table4.raw_remove(1)?;
|
||||||
|
assert_eq!(
|
||||||
|
table4
|
||||||
|
.clone()
|
||||||
|
.pairs()
|
||||||
|
.collect::<Result<Vec<(i64, i64)>>>()?,
|
||||||
|
vec![(1, 2), (2, 3), (3, 35), (4, 4), (5, 5), (6, 7)]
|
||||||
|
);
|
||||||
|
|
||||||
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,6 +200,40 @@ fn test_metatable() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_table_eq() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
let globals = lua.globals();
|
||||||
|
|
||||||
|
lua.load(
|
||||||
|
r#"
|
||||||
|
table1 = {1}
|
||||||
|
table2 = {1}
|
||||||
|
table3 = table1
|
||||||
|
table4 = {1}
|
||||||
|
|
||||||
|
setmetatable(table4, {
|
||||||
|
__eq = function(a, b) return a[1] == b[1] end
|
||||||
|
})
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.exec()?;
|
||||||
|
|
||||||
|
let table1 = globals.get::<_, Table>("table1")?;
|
||||||
|
let table2 = globals.get::<_, Table>("table2")?;
|
||||||
|
let table3 = globals.get::<_, Table>("table3")?;
|
||||||
|
let table4 = globals.get::<_, Table>("table4")?;
|
||||||
|
|
||||||
|
assert!(table1 != table2);
|
||||||
|
assert!(!table1.equals(&table2)?);
|
||||||
|
assert!(table1 == table3);
|
||||||
|
assert!(table1.equals(&table3)?);
|
||||||
|
assert!(table1 != table4);
|
||||||
|
assert!(table1.equals(&table4)?);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_table_error() -> Result<()> {
|
fn test_table_error() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
@@ -181,3 +267,33 @@ fn test_table_error() -> Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_table_call() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
lua.load(
|
||||||
|
r#"
|
||||||
|
table = {a = 1}
|
||||||
|
|
||||||
|
function table.func(key)
|
||||||
|
return "func_"..key
|
||||||
|
end
|
||||||
|
|
||||||
|
function table:method(key)
|
||||||
|
return "method_"..self[key]
|
||||||
|
end
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.exec()?;
|
||||||
|
|
||||||
|
let table: Table = lua.globals().get("table")?;
|
||||||
|
|
||||||
|
assert_eq!(table.call_function::<_, _, String>("func", "a")?, "func_a");
|
||||||
|
assert_eq!(
|
||||||
|
table.call_method::<_, _, String>("method", "a")?,
|
||||||
|
"method_1"
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
+11
-22
@@ -1,17 +1,6 @@
|
|||||||
#![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"
|
|
||||||
)]
|
|
||||||
extern "system" {}
|
|
||||||
|
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
use std::panic::catch_unwind;
|
use std::panic::catch_unwind;
|
||||||
use std::sync::Arc;
|
use std::rc::Rc;
|
||||||
use std::{error, f32, f64, fmt};
|
use std::{error, f32, f64, fmt};
|
||||||
|
|
||||||
use mlua::{
|
use mlua::{
|
||||||
@@ -92,13 +81,13 @@ fn test_lua_multi() -> Result<()> {
|
|||||||
|
|
||||||
lua.load(
|
lua.load(
|
||||||
r#"
|
r#"
|
||||||
function concat(arg1, arg2)
|
function concat(arg1, arg2)
|
||||||
return arg1 .. arg2
|
return arg1 .. arg2
|
||||||
end
|
end
|
||||||
|
|
||||||
function mreturn()
|
function mreturn()
|
||||||
return 1, 2, 3, 4, 5, 6
|
return 1, 2, 3, 4, 5, 6
|
||||||
end
|
end
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.exec()?;
|
.exec()?;
|
||||||
@@ -595,22 +584,22 @@ fn test_registry_value() -> Result<()> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_drop_registry_value() -> Result<()> {
|
fn test_drop_registry_value() -> Result<()> {
|
||||||
struct MyUserdata(Arc<()>);
|
struct MyUserdata(Rc<()>);
|
||||||
|
|
||||||
impl UserData for MyUserdata {}
|
impl UserData for MyUserdata {}
|
||||||
|
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
let rc = Arc::new(());
|
let rc = Rc::new(());
|
||||||
|
|
||||||
let r = lua.create_registry_value(MyUserdata(rc.clone()))?;
|
let r = lua.create_registry_value(MyUserdata(rc.clone()))?;
|
||||||
assert_eq!(Arc::strong_count(&rc), 2);
|
assert_eq!(Rc::strong_count(&rc), 2);
|
||||||
|
|
||||||
drop(r);
|
drop(r);
|
||||||
lua.expire_registry_values();
|
lua.expire_registry_values();
|
||||||
|
|
||||||
lua.load(r#"collectgarbage("collect")"#).exec()?;
|
lua.load(r#"collectgarbage("collect")"#).exec()?;
|
||||||
|
|
||||||
assert_eq!(Arc::strong_count(&rc), 1);
|
assert_eq!(Rc::strong_count(&rc), 1);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-21
@@ -1,14 +1,3 @@
|
|||||||
#![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"
|
|
||||||
)]
|
|
||||||
extern "system" {}
|
|
||||||
|
|
||||||
use std::panic::catch_unwind;
|
use std::panic::catch_unwind;
|
||||||
|
|
||||||
use mlua::{Error, Function, Lua, Result, Thread, ThreadStatus};
|
use mlua::{Error, Function, Lua, Result, Thread, ThreadStatus};
|
||||||
@@ -20,13 +9,13 @@ fn test_thread() -> Result<()> {
|
|||||||
let thread = lua.create_thread(
|
let thread = lua.create_thread(
|
||||||
lua.load(
|
lua.load(
|
||||||
r#"
|
r#"
|
||||||
function (s)
|
function (s)
|
||||||
local sum = s
|
local sum = s
|
||||||
for i = 1,4 do
|
for i = 1,4 do
|
||||||
sum = sum + coroutine.yield(sum)
|
sum = sum + coroutine.yield(sum)
|
||||||
end
|
|
||||||
return sum
|
|
||||||
end
|
end
|
||||||
|
return sum
|
||||||
|
end
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.eval()?,
|
.eval()?,
|
||||||
@@ -47,11 +36,11 @@ fn test_thread() -> Result<()> {
|
|||||||
let accumulate = lua.create_thread(
|
let accumulate = lua.create_thread(
|
||||||
lua.load(
|
lua.load(
|
||||||
r#"
|
r#"
|
||||||
function (sum)
|
function (sum)
|
||||||
while true do
|
while true do
|
||||||
sum = sum + coroutine.yield(sum)
|
sum = sum + coroutine.yield(sum)
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.eval::<Function>()?,
|
.eval::<Function>()?,
|
||||||
|
|||||||
@@ -1,14 +1,3 @@
|
|||||||
#![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"
|
|
||||||
)]
|
|
||||||
extern "system" {}
|
|
||||||
|
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
|
|
||||||
use mlua::{Function, LightUserData, Lua, Result};
|
use mlua::{Function, LightUserData, Lua, Result};
|
||||||
|
|||||||
+35
-34
@@ -1,19 +1,8 @@
|
|||||||
#![cfg_attr(
|
use std::rc::Rc;
|
||||||
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"
|
|
||||||
)]
|
|
||||||
extern "system" {}
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use mlua::{
|
use mlua::{
|
||||||
AnyUserData, ExternalError, Function, Lua, MetaMethod, Result, String, UserData,
|
AnyUserData, ExternalError, Function, Lua, MetaMethod, Result, String, UserData,
|
||||||
UserDataMethods,
|
UserDataMethods, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -96,6 +85,9 @@ fn test_metamethods() -> Result<()> {
|
|||||||
MetaMethod::Sub,
|
MetaMethod::Sub,
|
||||||
|_, (lhs, rhs): (MyUserData, MyUserData)| Ok(MyUserData(lhs.0 - rhs.0)),
|
|_, (lhs, rhs): (MyUserData, MyUserData)| Ok(MyUserData(lhs.0 - rhs.0)),
|
||||||
);
|
);
|
||||||
|
methods.add_meta_function(MetaMethod::Eq, |_, (lhs, rhs): (MyUserData, MyUserData)| {
|
||||||
|
Ok(lhs.0 == rhs.0)
|
||||||
|
});
|
||||||
methods.add_meta_method(MetaMethod::Index, |_, data, index: String| {
|
methods.add_meta_method(MetaMethod::Index, |_, data, index: String| {
|
||||||
if index.to_str()? == "inner" {
|
if index.to_str()? == "inner" {
|
||||||
Ok(data.0)
|
Ok(data.0)
|
||||||
@@ -104,7 +96,7 @@ fn test_metamethods() -> Result<()> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||||
methods.add_meta_method(MetaMethod::IPairs, |lua, data, ()| {
|
methods.add_meta_method(MetaMethod::Pairs, |lua, data, ()| {
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
let stateless_iter = lua.create_function(|_, (data, i): (MyUserData, i64)| {
|
let stateless_iter = lua.create_function(|_, (data, i): (MyUserData, i64)| {
|
||||||
let i = i + 1;
|
let i = i + 1;
|
||||||
@@ -122,18 +114,19 @@ fn test_metamethods() -> Result<()> {
|
|||||||
let globals = lua.globals();
|
let globals = lua.globals();
|
||||||
globals.set("userdata1", MyUserData(7))?;
|
globals.set("userdata1", MyUserData(7))?;
|
||||||
globals.set("userdata2", MyUserData(3))?;
|
globals.set("userdata2", MyUserData(3))?;
|
||||||
|
globals.set("userdata3", MyUserData(3))?;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lua.load("userdata1 + userdata2").eval::<MyUserData>()?.0,
|
lua.load("userdata1 + userdata2").eval::<MyUserData>()?.0,
|
||||||
10
|
10
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||||
let ipairs_it = {
|
let pairs_it = {
|
||||||
lua.load(
|
lua.load(
|
||||||
r#"
|
r#"
|
||||||
function ipairs_it()
|
function pairs_it()
|
||||||
local r = 0
|
local r = 0
|
||||||
for i, v in ipairs(userdata1) do
|
for i, v in pairs(userdata1) do
|
||||||
r = r + v
|
r = r + v
|
||||||
end
|
end
|
||||||
return r
|
return r
|
||||||
@@ -141,16 +134,23 @@ fn test_metamethods() -> Result<()> {
|
|||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.exec()?;
|
.exec()?;
|
||||||
globals.get::<_, Function>("ipairs_it")?
|
globals.get::<_, Function>("pairs_it")?
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(lua.load("userdata1 - userdata2").eval::<MyUserData>()?.0, 4);
|
assert_eq!(lua.load("userdata1 - userdata2").eval::<MyUserData>()?.0, 4);
|
||||||
assert_eq!(lua.load("userdata1:get()").eval::<i64>()?, 7);
|
assert_eq!(lua.load("userdata1:get()").eval::<i64>()?, 7);
|
||||||
assert_eq!(lua.load("userdata2.inner").eval::<i64>()?, 3);
|
assert_eq!(lua.load("userdata2.inner").eval::<i64>()?, 3);
|
||||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||||
assert_eq!(ipairs_it.call::<_, i64>(())?, 28);
|
assert_eq!(pairs_it.call::<_, i64>(())?, 28);
|
||||||
assert!(lua.load("userdata2.nonexist_field").eval::<()>().is_err());
|
assert!(lua.load("userdata2.nonexist_field").eval::<()>().is_err());
|
||||||
|
|
||||||
|
let userdata2: Value = globals.get("userdata2")?;
|
||||||
|
let userdata3: Value = globals.get("userdata3")?;
|
||||||
|
|
||||||
|
assert!(lua.load("userdata2 == userdata3").eval::<bool>()?);
|
||||||
|
assert!(userdata2 != userdata3); // because references are differ
|
||||||
|
assert!(userdata2.equals(userdata3)?);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,18 +175,18 @@ fn test_gc_userdata() -> Result<()> {
|
|||||||
assert!(lua
|
assert!(lua
|
||||||
.load(
|
.load(
|
||||||
r#"
|
r#"
|
||||||
local tbl = setmetatable({
|
local tbl = setmetatable({
|
||||||
userdata = userdata
|
userdata = userdata
|
||||||
}, { __gc = function(self)
|
}, { __gc = function(self)
|
||||||
-- resurrect userdata
|
-- resurrect userdata
|
||||||
hatch = self.userdata
|
hatch = self.userdata
|
||||||
end })
|
end })
|
||||||
|
|
||||||
tbl = nil
|
tbl = nil
|
||||||
userdata = nil -- make table and userdata collectable
|
userdata = nil -- make table and userdata collectable
|
||||||
collectgarbage("collect")
|
collectgarbage("collect")
|
||||||
hatch:access()
|
hatch:access()
|
||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
.exec()
|
.exec()
|
||||||
.is_err());
|
.is_err());
|
||||||
@@ -196,22 +196,22 @@ fn test_gc_userdata() -> Result<()> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn detroys_userdata() -> Result<()> {
|
fn detroys_userdata() -> Result<()> {
|
||||||
struct MyUserdata(Arc<()>);
|
struct MyUserdata(Rc<()>);
|
||||||
|
|
||||||
impl UserData for MyUserdata {}
|
impl UserData for MyUserdata {}
|
||||||
|
|
||||||
let rc = Arc::new(());
|
let rc = Rc::new(());
|
||||||
|
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
lua.globals().set("userdata", MyUserdata(rc.clone()))?;
|
lua.globals().set("userdata", MyUserdata(rc.clone()))?;
|
||||||
|
|
||||||
assert_eq!(Arc::strong_count(&rc), 2);
|
assert_eq!(Rc::strong_count(&rc), 2);
|
||||||
|
|
||||||
// should destroy all objects
|
// should destroy all objects
|
||||||
let _ = lua.globals().raw_remove("userdata")?;
|
let _ = lua.globals().raw_remove("userdata")?;
|
||||||
lua.gc_collect()?;
|
lua.gc_collect()?;
|
||||||
|
|
||||||
assert_eq!(Arc::strong_count(&rc), 1);
|
assert_eq!(Rc::strong_count(&rc), 1);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -219,6 +219,7 @@ fn detroys_userdata() -> Result<()> {
|
|||||||
#[test]
|
#[test]
|
||||||
fn user_value() -> Result<()> {
|
fn user_value() -> Result<()> {
|
||||||
struct MyUserData;
|
struct MyUserData;
|
||||||
|
|
||||||
impl UserData for MyUserData {}
|
impl UserData for MyUserData {}
|
||||||
|
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
use mlua::{Lua, Result, Value};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_value_eq() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
let globals = lua.globals();
|
||||||
|
|
||||||
|
lua.load(
|
||||||
|
r#"
|
||||||
|
table1 = {1}
|
||||||
|
table2 = {1}
|
||||||
|
string1 = "hello"
|
||||||
|
string2 = "hello"
|
||||||
|
num1 = 1
|
||||||
|
num2 = 1.0
|
||||||
|
num3 = "1"
|
||||||
|
func1 = function() end
|
||||||
|
func2 = func1
|
||||||
|
func3 = function() end
|
||||||
|
thread1 = coroutine.create(function() end)
|
||||||
|
thread2 = thread1
|
||||||
|
|
||||||
|
setmetatable(table1, {
|
||||||
|
__eq = function(a, b) return a[1] == b[1] end
|
||||||
|
})
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.exec()?;
|
||||||
|
|
||||||
|
let table1: Value = globals.get("table1")?;
|
||||||
|
let table2: Value = globals.get("table2")?;
|
||||||
|
let string1: Value = globals.get("string1")?;
|
||||||
|
let string2: Value = globals.get("string2")?;
|
||||||
|
let num1: Value = globals.get("num1")?;
|
||||||
|
let num2: Value = globals.get("num2")?;
|
||||||
|
let num3: Value = globals.get("num3")?;
|
||||||
|
let func1: Value = globals.get("func1")?;
|
||||||
|
let func2: Value = globals.get("func2")?;
|
||||||
|
let func3: Value = globals.get("func3")?;
|
||||||
|
let thread1: Value = globals.get("thread1")?;
|
||||||
|
let thread2: Value = globals.get("thread2")?;
|
||||||
|
|
||||||
|
assert!(table1 != table2);
|
||||||
|
assert!(table1.equals(table2)?);
|
||||||
|
assert!(string1 == string2);
|
||||||
|
assert!(string1.equals(string2)?);
|
||||||
|
assert!(num1 == num2);
|
||||||
|
assert!(num1.equals(num2)?);
|
||||||
|
assert!(num1 != num3);
|
||||||
|
assert!(func1 == func2);
|
||||||
|
assert!(func1 != func3);
|
||||||
|
assert!(!func1.equals(func3)?);
|
||||||
|
assert!(thread1 == thread2);
|
||||||
|
assert!(thread1.equals(thread2)?);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user