mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 |
@@ -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
|
||||
+33
-9
@@ -1,21 +1,26 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.2.1"
|
||||
version = "0.3.1"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
|
||||
edition = "2018"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
documentation = "https://docs.rs/mlua"
|
||||
readme = "README.md"
|
||||
keywords = ["lua", "luajit"]
|
||||
categories = ["api-bindings"]
|
||||
keywords = ["lua", "luajit", "async", "futures"]
|
||||
categories = ["api-bindings", "asynchronous"]
|
||||
license = "MIT"
|
||||
links = "lua"
|
||||
build = "build/main.rs"
|
||||
description = """
|
||||
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]
|
||||
travis-ci = { repository = "khvzak/mlua", branch = "master" }
|
||||
# github-actions = { repository = "khvzak/mlua", workflow = "CI" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["async"]
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
@@ -28,20 +33,39 @@ lua53 = []
|
||||
lua52 = []
|
||||
lua51 = []
|
||||
luajit = []
|
||||
vendored = ["lua-src", "luajit-src"]
|
||||
async = ["futures-core", "futures-task", "futures-util"]
|
||||
|
||||
[dependencies]
|
||||
num-traits = { version = "0.2.6" }
|
||||
bstr = { version = "0.2", features = ["std"], default_features = false }
|
||||
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]
|
||||
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]
|
||||
rustyline = "5.0"
|
||||
criterion = "0.2.0"
|
||||
rustyline = "6.0"
|
||||
criterion = "0.3"
|
||||
trybuild = "1.0"
|
||||
futures = "0.3.4"
|
||||
hyper = "0.13"
|
||||
tokio = { version = "0.2.18", features = ["full"] }
|
||||
futures-timer = "3.0"
|
||||
|
||||
[[bench]]
|
||||
name = "benchmark"
|
||||
harness = false
|
||||
|
||||
[[example]]
|
||||
name = "async_tcp_server"
|
||||
required-features = ["async"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_client"
|
||||
required-features = ["async"]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# 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
|
||||
[circleci]: https://circleci.com/gh/khvzak/mlua
|
||||
[Build Status]: https://github.com/khvzak/mlua/workflows/CI/badge.svg
|
||||
[github-actions]: https://github.com/khvzak/mlua/actions
|
||||
[Latest Version]: https://img.shields.io/crates/v/mlua.svg
|
||||
[crates.io]: https://crates.io/crates/mlua
|
||||
[API Documentation]: https://docs.rs/mlua/badge.svg
|
||||
@@ -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
|
||||
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` :
|
||||
|
||||
``` toml
|
||||
[dependencies]
|
||||
mlua = "0.2"
|
||||
mlua = "0.3"
|
||||
```
|
||||
|
||||
`main.rs`
|
||||
@@ -46,7 +72,7 @@ fn main() -> LuaResult<()> {
|
||||
}
|
||||
```
|
||||
|
||||
#### module mode
|
||||
### Module mode
|
||||
|
||||
Add to `Cargo.toml` :
|
||||
|
||||
@@ -55,8 +81,8 @@ Add to `Cargo.toml` :
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
mlua = "0.2"
|
||||
mlua_derive = "0.2"
|
||||
mlua = "0.3"
|
||||
mlua_derive = "0.3"
|
||||
```
|
||||
|
||||
`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
|
||||
);
|
||||
|
||||
// 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
|
||||
// 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "mlua_derive"
|
||||
version = "0.2.0"
|
||||
authors = ["Aleksandr Orlenko <zxteam@protonmail.com>"]
|
||||
version = "0.3.0"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2018"
|
||||
description = "Procedural macros for the mlua crate."
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
|
||||
+3
-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>> {
|
||||
Ok(Value::UserData(lua.create_userdata(self)?))
|
||||
}
|
||||
@@ -167,7 +167,7 @@ impl<'lua> ToLua<'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 {
|
||||
Value::Nil => Ok(false),
|
||||
Value::Boolean(b) => Ok(b),
|
||||
@@ -183,7 +183,7 @@ impl<'lua> ToLua<'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 {
|
||||
Value::LightUserData(ud) => Ok(ud),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
|
||||
+30
-9
@@ -1,8 +1,11 @@
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
use std::io::Error as IoError;
|
||||
use std::net::AddrParseError;
|
||||
use std::rc::Rc;
|
||||
use std::result::Result as StdResult;
|
||||
use std::str::Utf8Error;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Error type returned by `mlua` methods.
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -115,7 +118,7 @@ pub enum Error {
|
||||
/// Lua call stack backtrace.
|
||||
traceback: StdString,
|
||||
/// Original error returned by the Rust code.
|
||||
cause: Arc<Error>,
|
||||
cause: Rc<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
|
||||
/// error. The Rust code that originally invoked the Lua code then receives a `CallbackError`,
|
||||
/// from which the original error (and a stack traceback) can be recovered.
|
||||
ExternalError(Arc<dyn StdError + Send + Sync>),
|
||||
ExternalError(Rc<dyn StdError>),
|
||||
}
|
||||
|
||||
/// A specialized `Result` type used by `mlua`'s API.
|
||||
@@ -184,10 +187,10 @@ impl fmt::Display for Error {
|
||||
Error::MismatchedRegistryKey => {
|
||||
write!(fmt, "RegistryKey used from different Lua state")
|
||||
}
|
||||
Error::CallbackError { ref traceback, ref cause } => {
|
||||
write!(fmt, "callback error: {}: {}", cause, traceback)
|
||||
Error::CallbackError { ref 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)> {
|
||||
match *self {
|
||||
Error::CallbackError { ref cause, .. } => Some(cause.as_ref()),
|
||||
Error::ExternalError(ref err) => Some(err.as_ref()),
|
||||
Error::ExternalError(ref err) => err.source(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
@@ -214,7 +217,7 @@ pub trait ExternalError {
|
||||
|
||||
impl<E> ExternalError for E
|
||||
where
|
||||
E: Into<Box<dyn StdError + Send + Sync>>,
|
||||
E: Into<Box<dyn StdError>>,
|
||||
{
|
||||
fn to_lua_err(self) -> Error {
|
||||
Error::external(self)
|
||||
@@ -233,3 +236,21 @@ where
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@ use crate::util::{
|
||||
};
|
||||
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {futures_core::future::LocalBoxFuture, futures_util::future};
|
||||
|
||||
/// Handle to an internal Lua function.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Function<'lua>(pub(crate) LuaRef<'lua>);
|
||||
@@ -86,6 +89,47 @@ impl<'lua> Function<'lua> {
|
||||
R::from_lua_multi(results, lua)
|
||||
}
|
||||
|
||||
/// Returns a Feature that, when polled, calls `self`, passing `args` as function arguments,
|
||||
/// and drives the execution.
|
||||
///
|
||||
/// Internaly it wraps the function to an [`AsyncThread`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::time::Duration;
|
||||
/// use futures_timer::Delay;
|
||||
/// # use mlua::{Lua, Result};
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> Result<()> {
|
||||
/// # let lua = Lua::new();
|
||||
///
|
||||
/// let sleep = lua.create_async_function(move |_lua, n: u64| async move {
|
||||
/// Delay::new(Duration::from_millis(n)).await;
|
||||
/// Ok(())
|
||||
/// })?;
|
||||
///
|
||||
/// sleep.call_async(10).await?;
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`AsyncThread`]: struct.AsyncThread.html
|
||||
#[cfg(feature = "async")]
|
||||
pub fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
A: ToLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'fut,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
match lua.create_thread(self.clone()) {
|
||||
Ok(t) => Box::pin(t.into_async(args)),
|
||||
Err(e) => Box::pin(future::err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a function that, when called, calls `self`, passing `args` as the first set of
|
||||
/// arguments.
|
||||
///
|
||||
@@ -160,3 +204,9 @@ impl<'lua> Function<'lua> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> PartialEq for Function<'lua> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
+14
-3
@@ -24,6 +24,12 @@
|
||||
//! The [`UserData`] trait can be implemented by user-defined types to make them available to Lua.
|
||||
//! Methods and operators to be used from Lua can be added using the [`UserDataMethods`] API.
|
||||
//!
|
||||
//! # Async/await support
|
||||
//!
|
||||
//! The [`create_async_function`] allows creating non-blocking functions that returns [`Future`].
|
||||
//! Lua code with async capabilities can be executed by [`call_async`] family of functions or polling
|
||||
//! [`AsyncThread`] using any runtime (eg. Tokio).
|
||||
//!
|
||||
//! [Lua programming language]: https://www.lua.org/
|
||||
//! [`Lua`]: struct.Lua.html
|
||||
//! [executing]: struct.Lua.html#method.exec
|
||||
@@ -35,6 +41,10 @@
|
||||
//! [`FromLuaMulti`]: trait.FromLuaMulti.html
|
||||
//! [`UserData`]: trait.UserData.html
|
||||
//! [`UserDataMethods`]: trait.UserDataMethods.html
|
||||
//! [`create_async_function`]: struct.Lua.html#method.create_async_function
|
||||
//! [`call_async`]: struct.Function.html#method.call_async
|
||||
//! [`AsyncThread`]: struct.AsyncThread.html
|
||||
//! [`Future`]: ../futures_core/future/trait.Future.html
|
||||
|
||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||
// warnings at all.
|
||||
@@ -49,7 +59,6 @@ mod ffi;
|
||||
mod function;
|
||||
mod lua;
|
||||
mod multi;
|
||||
mod scope;
|
||||
mod stdlib;
|
||||
mod string;
|
||||
mod table;
|
||||
@@ -65,13 +74,15 @@ pub use crate::error::{Error, ExternalError, ExternalResult, Result};
|
||||
pub use crate::function::Function;
|
||||
pub use crate::lua::{Chunk, Lua};
|
||||
pub use crate::multi::Variadic;
|
||||
pub use crate::scope::Scope;
|
||||
pub use crate::stdlib::StdLib;
|
||||
pub use crate::string::String;
|
||||
pub use crate::table::{Table, TablePairs, TableSequence};
|
||||
pub use crate::table::{Table, TableExt, TablePairs, TableSequence};
|
||||
pub use crate::thread::{Thread, ThreadStatus};
|
||||
pub use crate::types::{Integer, LightUserData, Number, RegistryKey};
|
||||
pub use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
|
||||
pub use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub use crate::thread::AsyncThread;
|
||||
|
||||
pub mod prelude;
|
||||
|
||||
+427
-143
@@ -3,34 +3,43 @@ use std::cell::{RefCell, UnsafeCell};
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::CString;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::os::raw::{c_char, c_int};
|
||||
use std::rc::Rc;
|
||||
use std::{mem, ptr, str};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::scope::Scope;
|
||||
use crate::stdlib::StdLib;
|
||||
use crate::string::String;
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::types::{Callback, Integer, LightUserData, LuaRef, Number, RegistryKey};
|
||||
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
use crate::util::set_main_state;
|
||||
use crate::util::{
|
||||
assert_stack, callback_error, check_stack, get_main_state, get_userdata, get_wrapped_error,
|
||||
init_error_registry, init_userdata_metatable, pop_error, protect_lua, protect_lua_closure,
|
||||
push_string, push_userdata, push_wrapped_error, userdata_destructor, StackGuard,
|
||||
assert_stack, callback_error, check_stack, get_gc_userdata, get_main_state,
|
||||
get_meta_gc_userdata, get_wrapped_error, init_error_registry, init_gc_metatable_for,
|
||||
init_userdata_metatable, pop_error, protect_lua, protect_lua_closure, push_gc_userdata,
|
||||
push_meta_gc_userdata, push_string, push_userdata, push_wrapped_error, StackGuard,
|
||||
};
|
||||
use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::types::AsyncCallback,
|
||||
futures_core::{
|
||||
future::{Future, LocalBoxFuture},
|
||||
task::{Context, Poll, Waker},
|
||||
},
|
||||
futures_task::noop_waker,
|
||||
futures_util::future::{self, TryFutureExt},
|
||||
};
|
||||
|
||||
/// Top level Lua struct which holds the Lua state itself.
|
||||
pub struct Lua {
|
||||
pub(crate) state: *mut ffi::lua_State,
|
||||
main_state: *mut ffi::lua_State,
|
||||
extra: Arc<RefCell<ExtraData>>,
|
||||
extra: Rc<RefCell<ExtraData>>,
|
||||
ephemeral: bool,
|
||||
// Lua has lots of interior mutability, should not be RefUnwindSafe
|
||||
_no_ref_unwind_safe: PhantomData<UnsafeCell<()>>,
|
||||
@@ -39,7 +48,7 @@ pub struct Lua {
|
||||
// Data associated with the lua_State.
|
||||
struct ExtraData {
|
||||
registered_userdata: HashMap<TypeId, c_int>,
|
||||
registry_unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
||||
registry_unref_list: Rc<RefCell<Option<Vec<c_int>>>>,
|
||||
|
||||
ref_thread: *mut ffi::lua_State,
|
||||
ref_stack_size: c_int,
|
||||
@@ -47,7 +56,10 @@ struct ExtraData {
|
||||
ref_free: Vec<c_int>,
|
||||
}
|
||||
|
||||
unsafe impl Send for Lua {}
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) struct AsyncPollPending;
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) static WAKER_REGISTRY_KEY: u8 = 0;
|
||||
|
||||
impl Drop for Lua {
|
||||
fn drop(&mut self) {
|
||||
@@ -59,7 +71,10 @@ impl Drop for Lua {
|
||||
&& extra.ref_stack_max as usize == extra.ref_free.len(),
|
||||
"reference leak detected"
|
||||
);
|
||||
*mlua_expect!(extra.registry_unref_list.lock(), "unref list poisoned") = None;
|
||||
*mlua_expect!(
|
||||
extra.registry_unref_list.try_borrow_mut(),
|
||||
"unref list borrowed"
|
||||
) = None;
|
||||
ffi::lua_close(self.state);
|
||||
}
|
||||
}
|
||||
@@ -113,55 +128,25 @@ impl Lua {
|
||||
|
||||
/// Constructs a new Lua instance from the existing state.
|
||||
pub unsafe fn init_from_ptr(state: *mut ffi::lua_State) -> Lua {
|
||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||
let main_state = get_main_state(state);
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
let main_state = {
|
||||
set_main_state(state);
|
||||
state
|
||||
};
|
||||
let main_state_top = ffi::lua_gettop(state);
|
||||
|
||||
let ref_thread = mlua_expect!(
|
||||
protect_lua_closure(main_state, 0, 0, |state| {
|
||||
init_error_registry(state);
|
||||
|
||||
// Create the function metatables and place them in the registry
|
||||
// Create the internal metatables and place them in the registry
|
||||
// to prevent them from being garbage collected.
|
||||
|
||||
ffi::lua_pushlightuserdata(
|
||||
state,
|
||||
&FUNCTION_CALLBACK_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
|
||||
);
|
||||
|
||||
ffi::lua_newtable(state);
|
||||
|
||||
ffi::lua_pushstring(state, cstr!("__gc"));
|
||||
ffi::lua_pushcfunction(state, userdata_destructor::<Callback>);
|
||||
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);
|
||||
|
||||
ffi::lua_pushlightuserdata(
|
||||
state,
|
||||
&FUNCTION_EXTRA_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
|
||||
);
|
||||
|
||||
ffi::lua_newtable(state);
|
||||
|
||||
ffi::lua_pushstring(state, cstr!("__gc"));
|
||||
ffi::lua_pushcfunction(state, userdata_destructor::<Arc<RefCell<ExtraData>>>);
|
||||
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);
|
||||
init_gc_metatable_for::<Callback>(state, None);
|
||||
init_gc_metatable_for::<Lua>(state, None);
|
||||
#[cfg(feature = "async")]
|
||||
{
|
||||
init_gc_metatable_for::<AsyncCallback>(state, None);
|
||||
init_gc_metatable_for::<LocalBoxFuture<Result<MultiValue>>>(state, None);
|
||||
init_gc_metatable_for::<AsyncPollPending>(state, None);
|
||||
init_gc_metatable_for::<Waker>(state, None);
|
||||
}
|
||||
|
||||
// Create ref stack thread and place it in the registry to prevent it from being garbage
|
||||
// collected.
|
||||
@@ -175,9 +160,9 @@ impl Lua {
|
||||
|
||||
// Create ExtraData
|
||||
|
||||
let extra = Arc::new(RefCell::new(ExtraData {
|
||||
let extra = Rc::new(RefCell::new(ExtraData {
|
||||
registered_userdata: HashMap::new(),
|
||||
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
|
||||
registry_unref_list: Rc::new(RefCell::new(Some(Vec::new()))),
|
||||
ref_thread,
|
||||
// We need 1 extra stack space to move values in and out of the ref stack.
|
||||
ref_stack_size: ffi::LUA_MINSTACK - 1,
|
||||
@@ -206,7 +191,7 @@ impl Lua {
|
||||
pub fn entrypoint1<'lua, 'callback, R, F>(&'lua self, func: F) -> Result<c_int>
|
||||
where
|
||||
R: ToLua<'callback>,
|
||||
F: 'static + Send + Fn(&'callback Lua) -> Result<R>,
|
||||
F: 'static + Fn(&'callback Lua) -> Result<R>,
|
||||
{
|
||||
let cb = self.create_callback(Box::new(move |lua, _| func(lua)?.to_lua_multi(lua)))?;
|
||||
unsafe { self.push_value(cb.call(())?).map(|_| 1) }
|
||||
@@ -463,7 +448,7 @@ impl Lua {
|
||||
where
|
||||
A: FromLuaMulti<'callback>,
|
||||
R: ToLuaMulti<'callback>,
|
||||
F: 'static + Send + Fn(&'callback Lua, A) -> Result<R>,
|
||||
F: 'static + Fn(&'callback Lua, A) -> Result<R>,
|
||||
{
|
||||
self.create_callback(Box::new(move |lua, args| {
|
||||
func(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
@@ -483,7 +468,7 @@ impl Lua {
|
||||
where
|
||||
A: FromLuaMulti<'callback>,
|
||||
R: ToLuaMulti<'callback>,
|
||||
F: 'static + Send + FnMut(&'callback Lua, A) -> Result<R>,
|
||||
F: 'static + FnMut(&'callback Lua, A) -> Result<R>,
|
||||
{
|
||||
let func = RefCell::new(func);
|
||||
self.create_function(move |lua, args| {
|
||||
@@ -493,6 +478,64 @@ impl Lua {
|
||||
})
|
||||
}
|
||||
|
||||
/// Wraps a Rust async function or closure, creating a callable Lua function handle to it.
|
||||
///
|
||||
/// While executing the function Rust will poll Future and if the result is not ready, call
|
||||
/// `yield()` passing internal representation of a `Poll::Pending` value.
|
||||
///
|
||||
/// The function must be called inside Lua coroutine ([`Thread`]) to be able to suspend its execution.
|
||||
/// An executor should be used to poll [`AsyncThread`] and mlua will take a provided Waker
|
||||
/// in that case. Otherwise noop waker will be used if try to call the function outside of Rust
|
||||
/// executors.
|
||||
///
|
||||
/// The family of `call_async()` functions takes care about creating [`Thread`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Non blocking sleep:
|
||||
///
|
||||
/// ```
|
||||
/// use std::time::Duration;
|
||||
/// use futures_timer::Delay;
|
||||
/// use mlua::{Lua, Result};
|
||||
///
|
||||
/// async fn sleep(_lua: &Lua, n: u64) -> Result<&'static str> {
|
||||
/// Delay::new(Duration::from_millis(n)).await;
|
||||
/// Ok("done")
|
||||
/// }
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// lua.globals().set("sleep", lua.create_async_function(sleep)?)?;
|
||||
/// let res: String = lua.load("return sleep(...)").call_async(100).await?; // Sleep 100ms
|
||||
/// assert_eq!(res, "done");
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`Thread`]: struct.Thread.html
|
||||
/// [`ThreadStream`]: struct.ThreadStream.html
|
||||
#[cfg(feature = "async")]
|
||||
pub fn create_async_function<'lua, 'callback, A, R, F, FR>(
|
||||
&'lua self,
|
||||
func: F,
|
||||
) -> Result<Function<'lua>>
|
||||
where
|
||||
A: FromLuaMulti<'callback>,
|
||||
R: ToLuaMulti<'callback>,
|
||||
F: 'static + Fn(&'callback Lua, A) -> FR,
|
||||
FR: 'static + Future<Output = Result<R>>,
|
||||
{
|
||||
self.create_async_callback(Box::new(move |lua, args| {
|
||||
let args = match A::from_lua_multi(args, lua) {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::err(e)),
|
||||
};
|
||||
Box::pin(func(lua, args).and_then(move |ret| future::ready(ret.to_lua_multi(lua))))
|
||||
}))
|
||||
}
|
||||
|
||||
/// Wraps a Lua function into a new thread (or coroutine).
|
||||
///
|
||||
/// Equivalent to `coroutine.create`.
|
||||
@@ -513,7 +556,7 @@ impl Lua {
|
||||
/// Create a Lua userdata object from a custom userdata type.
|
||||
pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
||||
where
|
||||
T: 'static + Send + UserData,
|
||||
T: 'static + UserData,
|
||||
{
|
||||
unsafe { self.make_userdata(data) }
|
||||
}
|
||||
@@ -540,33 +583,6 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls the given function with a `Scope` parameter, giving the function the ability to create
|
||||
/// userdata and callbacks from rust types that are !Send or non-'static.
|
||||
///
|
||||
/// The lifetime of any function or userdata created through `Scope` lasts only until the
|
||||
/// completion of this method call, on completion all such created values are automatically
|
||||
/// dropped and Lua references to them are invalidated. If a script accesses a value created
|
||||
/// through `Scope` outside of this method, a Lua error will result. Since we can ensure the
|
||||
/// lifetime of values created through `Scope`, and we know that `Lua` cannot be sent to another
|
||||
/// thread while `Scope` is live, it is safe to allow !Send datatypes and whose lifetimes only
|
||||
/// outlive the scope lifetime.
|
||||
///
|
||||
/// Inside the scope callback, all handles created through Scope will share the same unique 'lua
|
||||
/// lifetime of the parent `Lua`. This allows scoped and non-scoped values to be mixed in
|
||||
/// API calls, which is very useful (e.g. passing a scoped userdata to a non-scoped function).
|
||||
/// However, this also enables handles to scoped values to be trivially leaked from the given
|
||||
/// callback. This is not dangerous, though! After the callback returns, all scoped values are
|
||||
/// invalidated, which means that though references may exist, the Rust types backing them have
|
||||
/// dropped. `Function` types will error when called, and `AnyUserData` will be typeless. It
|
||||
/// would be impossible to prevent handles to scoped values from escaping anyway, since you
|
||||
/// would always be able to smuggle them through Lua state.
|
||||
pub fn scope<'scope, 'lua: 'scope, F, R>(&'lua self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&Scope<'lua, 'scope>) -> R,
|
||||
{
|
||||
f(&Scope::new(self))
|
||||
}
|
||||
|
||||
/// Attempts to coerce a Lua value into a String in a manner consistent with Lua's internal
|
||||
/// behavior.
|
||||
///
|
||||
@@ -806,7 +822,7 @@ impl Lua {
|
||||
/// `Error::MismatchedRegistryKey` if passed a `RegistryKey` that was not created with a
|
||||
/// matching `Lua` state.
|
||||
pub fn owns_registry_value(&self, key: &RegistryKey) -> bool {
|
||||
Arc::ptr_eq(&key.unref_list, &self.extra.borrow().registry_unref_list)
|
||||
Rc::ptr_eq(&key.unref_list, &self.extra.borrow().registry_unref_list)
|
||||
}
|
||||
|
||||
/// Remove any registry values whose `RegistryKey`s have all been dropped.
|
||||
@@ -818,8 +834,8 @@ impl Lua {
|
||||
unsafe {
|
||||
let unref_list = mem::replace(
|
||||
&mut *mlua_expect!(
|
||||
self.extra.borrow().registry_unref_list.lock(),
|
||||
"unref list poisoned"
|
||||
self.extra.borrow().registry_unref_list.try_borrow_mut(),
|
||||
"unref list borrowed"
|
||||
),
|
||||
Some(Vec::new()),
|
||||
);
|
||||
@@ -1009,7 +1025,12 @@ impl Lua {
|
||||
})?;
|
||||
}
|
||||
|
||||
if methods.methods.is_empty() {
|
||||
#[cfg(feature = "async")]
|
||||
let no_methods = methods.methods.is_empty() && methods.async_methods.is_empty();
|
||||
#[cfg(not(feature = "async"))]
|
||||
let no_methods = methods.methods.is_empty();
|
||||
|
||||
if no_methods {
|
||||
init_userdata_metatable::<RefCell<T>>(self.state, -1, None)?;
|
||||
} else {
|
||||
protect_lua_closure(self.state, 0, 1, |state| {
|
||||
@@ -1022,6 +1043,14 @@ impl Lua {
|
||||
ffi::lua_rawset(state, -3);
|
||||
})?;
|
||||
}
|
||||
#[cfg(feature = "async")]
|
||||
for (k, m) in methods.async_methods {
|
||||
push_string(self.state, &k)?;
|
||||
self.push_value(Value::Function(self.create_async_callback(m)?))?;
|
||||
protect_lua_closure(self.state, 3, 1, |state| {
|
||||
ffi::lua_rawset(state, -3);
|
||||
})?;
|
||||
}
|
||||
|
||||
init_userdata_metatable::<RefCell<T>>(self.state, -2, Some(-1))?;
|
||||
ffi::lua_pop(self.state, 1);
|
||||
@@ -1053,10 +1082,10 @@ impl Lua {
|
||||
) -> Result<Function<'lua>> {
|
||||
unsafe extern "C" fn call_callback(state: *mut ffi::lua_State) -> c_int {
|
||||
callback_error(state, |nargs| {
|
||||
if ffi::lua_type(state, ffi::lua_upvalueindex(1)) == ffi::LUA_TNIL {
|
||||
return Err(Error::CallbackDestructed);
|
||||
}
|
||||
if ffi::lua_type(state, ffi::lua_upvalueindex(2)) == ffi::LUA_TNIL {
|
||||
let func =
|
||||
get_meta_gc_userdata::<Callback, Callback>(state, ffi::lua_upvalueindex(1));
|
||||
let lua = get_gc_userdata::<Lua>(state, ffi::lua_upvalueindex(2));
|
||||
if func.is_null() || lua.is_null() {
|
||||
return Err(Error::CallbackDestructed);
|
||||
}
|
||||
|
||||
@@ -1064,16 +1093,8 @@ impl Lua {
|
||||
check_stack(state, ffi::LUA_MINSTACK - nargs)?;
|
||||
}
|
||||
|
||||
let extra =
|
||||
get_userdata::<Arc<RefCell<ExtraData>>>(state, ffi::lua_upvalueindex(2));
|
||||
|
||||
let lua = Lua {
|
||||
state: state,
|
||||
main_state: get_main_state(state),
|
||||
extra: (*extra).clone(),
|
||||
ephemeral: true,
|
||||
_no_ref_unwind_safe: PhantomData,
|
||||
};
|
||||
let lua = &mut *lua;
|
||||
lua.state = state;
|
||||
|
||||
let mut args = MultiValue::new();
|
||||
args.reserve(nargs as usize);
|
||||
@@ -1081,9 +1102,7 @@ impl Lua {
|
||||
args.push_front(lua.pop_value());
|
||||
}
|
||||
|
||||
let func = get_userdata::<Callback>(state, ffi::lua_upvalueindex(1));
|
||||
|
||||
let results = (*func)(&lua, args)?;
|
||||
let results = (*func)(lua, args)?;
|
||||
let nresults = results.len() as c_int;
|
||||
|
||||
check_stack(state, nresults)?;
|
||||
@@ -1099,21 +1118,8 @@ impl Lua {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
assert_stack(self.state, 6);
|
||||
|
||||
push_userdata::<Callback>(self.state, func)?;
|
||||
ffi::lua_pushlightuserdata(
|
||||
self.state,
|
||||
&FUNCTION_CALLBACK_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
|
||||
);
|
||||
ffi::lua_rawget(self.state, ffi::LUA_REGISTRYINDEX);
|
||||
ffi::lua_setmetatable(self.state, -2);
|
||||
|
||||
push_userdata::<Arc<RefCell<ExtraData>>>(self.state, self.extra.clone())?;
|
||||
ffi::lua_pushlightuserdata(
|
||||
self.state,
|
||||
&FUNCTION_EXTRA_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
|
||||
);
|
||||
ffi::lua_rawget(self.state, ffi::LUA_REGISTRYINDEX);
|
||||
ffi::lua_setmetatable(self.state, -2);
|
||||
push_meta_gc_userdata::<Callback, _>(self.state, func)?;
|
||||
push_gc_userdata(self.state, self.clone())?;
|
||||
|
||||
protect_lua_closure(self.state, 2, 1, |state| {
|
||||
ffi::lua_pushcclosure(state, call_callback, 2);
|
||||
@@ -1123,7 +1129,140 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
// Does not require Send bounds, which can lead to unsafety.
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) fn create_async_callback<'lua, 'callback>(
|
||||
&'lua self,
|
||||
func: AsyncCallback<'callback, 'static>,
|
||||
) -> Result<Function<'lua>> {
|
||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||
self.load_from_std_lib(StdLib::COROUTINE)?;
|
||||
|
||||
unsafe extern "C" fn call_callback(state: *mut ffi::lua_State) -> c_int {
|
||||
callback_error(state, |nargs| {
|
||||
let func = get_meta_gc_userdata::<AsyncCallback, AsyncCallback>(
|
||||
state,
|
||||
ffi::lua_upvalueindex(1),
|
||||
);
|
||||
let lua = get_gc_userdata::<Lua>(state, ffi::lua_upvalueindex(2));
|
||||
if func.is_null() || lua.is_null() {
|
||||
return Err(Error::CallbackDestructed);
|
||||
}
|
||||
|
||||
if nargs < ffi::LUA_MINSTACK {
|
||||
check_stack(state, ffi::LUA_MINSTACK - nargs)?;
|
||||
}
|
||||
|
||||
let lua = &mut *lua;
|
||||
lua.state = state;
|
||||
|
||||
let mut args = MultiValue::new();
|
||||
args.reserve(nargs as usize);
|
||||
for _ in 0..nargs {
|
||||
args.push_front(lua.pop_value());
|
||||
}
|
||||
|
||||
let fut = (*func)(lua, args);
|
||||
push_gc_userdata(state, fut)?;
|
||||
push_gc_userdata(state, lua.clone())?;
|
||||
|
||||
ffi::lua_pushcclosure(state, poll_future, 2);
|
||||
|
||||
Ok(1)
|
||||
})
|
||||
}
|
||||
|
||||
unsafe extern "C" fn poll_future(state: *mut ffi::lua_State) -> c_int {
|
||||
callback_error(state, |nargs| {
|
||||
let fut = get_gc_userdata::<LocalBoxFuture<Result<MultiValue>>>(
|
||||
state,
|
||||
ffi::lua_upvalueindex(1),
|
||||
);
|
||||
let lua = get_gc_userdata::<Lua>(state, ffi::lua_upvalueindex(2));
|
||||
if fut.is_null() || lua.is_null() {
|
||||
return Err(Error::CallbackDestructed);
|
||||
}
|
||||
|
||||
if nargs < ffi::LUA_MINSTACK {
|
||||
check_stack(state, ffi::LUA_MINSTACK - nargs)?;
|
||||
}
|
||||
|
||||
let lua = &mut *lua;
|
||||
let mut waker = noop_waker();
|
||||
|
||||
// Try to get an outer poll waker
|
||||
ffi::lua_pushlightuserdata(
|
||||
state,
|
||||
&WAKER_REGISTRY_KEY as *const u8 as *mut ::std::os::raw::c_void,
|
||||
);
|
||||
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
|
||||
if let Some(w) = get_gc_userdata::<Waker>(state, -1).as_ref() {
|
||||
waker = (*w).clone();
|
||||
}
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
let mut ctx = Context::from_waker(&waker);
|
||||
|
||||
match (*fut).as_mut().poll(&mut ctx) {
|
||||
Poll::Pending => {
|
||||
check_stack(state, 6)?;
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
push_gc_userdata(state, AsyncPollPending)?;
|
||||
Ok(2)
|
||||
}
|
||||
Poll::Ready(results) => {
|
||||
let results = lua.create_sequence_from(results?)?;
|
||||
check_stack(state, 2)?;
|
||||
ffi::lua_pushboolean(state, 1);
|
||||
lua.push_value(Value::Table(results))?;
|
||||
Ok(2)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let get_poll = unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
assert_stack(self.state, 6);
|
||||
|
||||
push_meta_gc_userdata::<AsyncCallback, _>(self.state, func)?;
|
||||
push_gc_userdata(self.state, self.clone())?;
|
||||
|
||||
protect_lua_closure(self.state, 2, 1, |state| {
|
||||
ffi::lua_pushcclosure(state, call_callback, 2);
|
||||
})?;
|
||||
|
||||
Function(self.pop_ref())
|
||||
};
|
||||
|
||||
let env = self.create_table()?;
|
||||
env.set("get_poll", get_poll)?;
|
||||
env.set("coroutine", self.globals().get::<_, Value>("coroutine")?)?;
|
||||
env.set(
|
||||
"unpack",
|
||||
self.create_function(|_, tbl: Table| {
|
||||
Ok(MultiValue::from_vec(
|
||||
tbl.sequence_values().collect::<Result<Vec<Value>>>()?,
|
||||
))
|
||||
})?,
|
||||
)?;
|
||||
|
||||
self.load(
|
||||
r#"
|
||||
local poll = get_poll(...)
|
||||
while true do
|
||||
ready, res = poll()
|
||||
if ready then
|
||||
return unpack(res)
|
||||
end
|
||||
coroutine.yield(res)
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.set_name("_mlua_async_poll")?
|
||||
.set_environment(env)?
|
||||
.into_function()
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn make_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
||||
where
|
||||
T: 'static + UserData,
|
||||
@@ -1143,6 +1282,16 @@ impl Lua {
|
||||
|
||||
Ok(AnyUserData(self.pop_ref()))
|
||||
}
|
||||
|
||||
pub(crate) fn clone(&self) -> Self {
|
||||
Lua {
|
||||
state: self.state,
|
||||
main_state: self.main_state,
|
||||
extra: self.extra.clone(),
|
||||
ephemeral: true,
|
||||
_no_ref_unwind_safe: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returned from [`Lua::load`] and is used to finalize loading and executing Lua main chunks.
|
||||
@@ -1193,6 +1342,19 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Asynchronously execute this chunk of code.
|
||||
///
|
||||
/// See [`Chunk::exec`] for more details.
|
||||
///
|
||||
/// [`Chunk::exec`]: struct.Chunk.html#method.exec
|
||||
#[cfg(feature = "async")]
|
||||
pub fn exec_async<'fut>(self) -> LocalBoxFuture<'fut, Result<()>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
{
|
||||
self.call_async(())
|
||||
}
|
||||
|
||||
/// Evaluate the chunk as either an expression or block.
|
||||
///
|
||||
/// If the chunk can be parsed as an expression, this loads and executes the chunk and returns
|
||||
@@ -1202,18 +1364,39 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
// First, try interpreting the lua as an expression by adding
|
||||
// "return", then as a statement. This is the same thing the
|
||||
// actual lua repl does.
|
||||
let mut expression_source = b"return ".to_vec();
|
||||
expression_source.extend(self.source);
|
||||
if let Ok(function) =
|
||||
self.lua
|
||||
.load_chunk(&expression_source, self.name.as_ref(), self.env.clone())
|
||||
{
|
||||
if let Ok(function) = self.lua.load_chunk(
|
||||
&self.expression_source(),
|
||||
self.name.as_ref(),
|
||||
self.env.clone(),
|
||||
) {
|
||||
function.call(())
|
||||
} else {
|
||||
self.call(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Asynchronously evaluate the chunk as either an expression or block.
|
||||
///
|
||||
/// See [`Chunk::eval`] for more details.
|
||||
///
|
||||
/// [`Chunk::eval`]: struct.Chunk.html#method.eval
|
||||
#[cfg(feature = "async")]
|
||||
pub fn eval_async<'fut, R>(self) -> LocalBoxFuture<'fut, Result<R>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
R: FromLuaMulti<'lua> + 'fut,
|
||||
{
|
||||
if let Ok(function) = self.lua.load_chunk(
|
||||
&self.expression_source(),
|
||||
self.name.as_ref(),
|
||||
self.env.clone(),
|
||||
) {
|
||||
function.call_async(())
|
||||
} else {
|
||||
self.call_async(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Load the chunk function and call it with the given arguemnts.
|
||||
///
|
||||
/// This is equivalent to `into_function` and calling the resulting function.
|
||||
@@ -1221,6 +1404,24 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
self.into_function()?.call(args)
|
||||
}
|
||||
|
||||
/// Load the chunk function and asynchronously call it with the given arguemnts.
|
||||
///
|
||||
/// See [`Chunk::call`] for more details.
|
||||
///
|
||||
/// [`Chunk::call`]: struct.Chunk.html#method.call
|
||||
#[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,
|
||||
{
|
||||
match self.into_function() {
|
||||
Ok(func) => func.call_async(args),
|
||||
Err(e) => Box::pin(future::err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Load this chunk into a regular `Function`.
|
||||
///
|
||||
/// This simply compiles the chunk without actually executing it.
|
||||
@@ -1228,6 +1429,13 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
self.lua
|
||||
.load_chunk(self.source, self.name.as_ref(), self.env)
|
||||
}
|
||||
|
||||
fn expression_source(&self) -> Vec<u8> {
|
||||
let mut buf = Vec::with_capacity(b"return ".len() + self.source.len());
|
||||
buf.extend(b"return ");
|
||||
buf.extend(self.source);
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) {
|
||||
@@ -1343,11 +1551,10 @@ unsafe fn ref_stack_pop(extra: &mut ExtraData) -> c_int {
|
||||
}
|
||||
}
|
||||
|
||||
static FUNCTION_CALLBACK_METATABLE_REGISTRY_KEY: u8 = 0;
|
||||
static FUNCTION_EXTRA_METATABLE_REGISTRY_KEY: u8 = 0;
|
||||
|
||||
struct StaticUserDataMethods<'lua, T: 'static + UserData> {
|
||||
methods: Vec<(Vec<u8>, Callback<'lua, 'static>)>,
|
||||
#[cfg(feature = "async")]
|
||||
async_methods: Vec<(Vec<u8>, AsyncCallback<'lua, 'static>)>,
|
||||
meta_methods: Vec<(MetaMethod, Callback<'lua, 'static>)>,
|
||||
_type: PhantomData<T>,
|
||||
}
|
||||
@@ -1356,6 +1563,8 @@ impl<'lua, T: 'static + UserData> Default for StaticUserDataMethods<'lua, T> {
|
||||
fn default() -> StaticUserDataMethods<'lua, T> {
|
||||
StaticUserDataMethods {
|
||||
methods: Vec::new(),
|
||||
#[cfg(feature = "async")]
|
||||
async_methods: Vec::new(),
|
||||
meta_methods: Vec::new(),
|
||||
_type: PhantomData,
|
||||
}
|
||||
@@ -1368,7 +1577,7 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
|
||||
S: ?Sized + AsRef<[u8]>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>,
|
||||
M: 'static + Fn(&'lua Lua, &T, A) -> Result<R>,
|
||||
{
|
||||
self.methods
|
||||
.push((name.as_ref().to_vec(), Self::box_method(method)));
|
||||
@@ -1379,18 +1588,32 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
|
||||
S: ?Sized + AsRef<[u8]>,
|
||||
A: FromLuaMulti<'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>,
|
||||
{
|
||||
self.methods
|
||||
.push((name.as_ref().to_vec(), Self::box_method_mut(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>>,
|
||||
{
|
||||
self.async_methods
|
||||
.push((name.as_ref().to_vec(), Self::box_async_method(method)));
|
||||
}
|
||||
|
||||
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>,
|
||||
F: 'static + Fn(&'lua Lua, A) -> Result<R>,
|
||||
{
|
||||
self.methods
|
||||
.push((name.as_ref().to_vec(), Self::box_function(function)));
|
||||
@@ -1401,17 +1624,31 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
|
||||
S: ?Sized + AsRef<[u8]>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
|
||||
F: 'static + FnMut(&'lua Lua, A) -> Result<R>,
|
||||
{
|
||||
self.methods
|
||||
.push((name.as_ref().to_vec(), Self::box_function_mut(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>>,
|
||||
{
|
||||
self.async_methods
|
||||
.push((name.as_ref().to_vec(), Self::box_async_function(function)));
|
||||
}
|
||||
|
||||
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>,
|
||||
M: 'static + Fn(&'lua Lua, &T, A) -> Result<R>,
|
||||
{
|
||||
self.meta_methods.push((meta, Self::box_method(method)));
|
||||
}
|
||||
@@ -1420,7 +1657,7 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
|
||||
where
|
||||
A: FromLuaMulti<'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>,
|
||||
{
|
||||
self.meta_methods.push((meta, Self::box_method_mut(method)));
|
||||
}
|
||||
@@ -1429,7 +1666,7 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
|
||||
where
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>,
|
||||
F: 'static + Fn(&'lua Lua, A) -> Result<R>,
|
||||
{
|
||||
self.meta_methods.push((meta, Self::box_function(function)));
|
||||
}
|
||||
@@ -1438,7 +1675,7 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
|
||||
where
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
|
||||
F: 'static + FnMut(&'lua Lua, A) -> Result<R>,
|
||||
{
|
||||
self.meta_methods
|
||||
.push((meta, Self::box_function_mut(function)));
|
||||
@@ -1450,7 +1687,7 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
|
||||
where
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>,
|
||||
M: 'static + Fn(&'lua Lua, &T, A) -> Result<R>,
|
||||
{
|
||||
Box::new(move |lua, mut args| {
|
||||
if let Some(front) = args.pop_front() {
|
||||
@@ -1471,7 +1708,7 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
|
||||
where
|
||||
A: FromLuaMulti<'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>,
|
||||
{
|
||||
let method = RefCell::new(method);
|
||||
Box::new(move |lua, mut args| {
|
||||
@@ -1492,11 +1729,41 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn box_async_method<A, R, M, MR>(method: M) -> AsyncCallback<'lua, 'static>
|
||||
where
|
||||
T: Clone,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + Fn(&'lua Lua, T, A) -> MR,
|
||||
MR: 'static + Future<Output = Result<R>>,
|
||||
{
|
||||
Box::new(move |lua, mut args| {
|
||||
let fut_res = || {
|
||||
if let Some(front) = args.pop_front() {
|
||||
let userdata = AnyUserData::from_lua(front, lua)?;
|
||||
let userdata = userdata.borrow::<T>()?.clone();
|
||||
Ok(method(lua, userdata, A::from_lua_multi(args, lua)?))
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: "missing argument",
|
||||
to: "userdata",
|
||||
message: None,
|
||||
})
|
||||
}
|
||||
};
|
||||
match fut_res() {
|
||||
Ok(fut) => Box::pin(fut.and_then(move |ret| future::ready(ret.to_lua_multi(lua)))),
|
||||
Err(e) => Box::pin(future::err(e)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn box_function<A, R, F>(function: F) -> Callback<'lua, 'static>
|
||||
where
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>,
|
||||
F: 'static + Fn(&'lua Lua, A) -> Result<R>,
|
||||
{
|
||||
Box::new(move |lua, args| function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua))
|
||||
}
|
||||
@@ -1505,7 +1772,7 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
|
||||
where
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
|
||||
F: 'static + FnMut(&'lua Lua, A) -> Result<R>,
|
||||
{
|
||||
let function = RefCell::new(function);
|
||||
Box::new(move |lua, args| {
|
||||
@@ -1515,4 +1782,21 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
|
||||
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn box_async_function<A, R, F, FR>(function: F) -> AsyncCallback<'lua, 'static>
|
||||
where
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + Fn(&'lua Lua, A) -> FR,
|
||||
FR: 'static + Future<Output = Result<R>>,
|
||||
{
|
||||
Box::new(move |lua, args| {
|
||||
let args = match A::from_lua_multi(args, lua) {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::err(e)),
|
||||
};
|
||||
Box::pin(function(lua, args).and_then(move |ret| future::ready(ret.to_lua_multi(lua))))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -146,7 +146,7 @@ macro_rules! impl_tuple {
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -5,8 +5,11 @@ pub use crate::{
|
||||
ExternalError as LuaExternalError, ExternalResult as LuaExternalResult, FromLua, FromLuaMulti,
|
||||
Function as LuaFunction, Integer as LuaInteger, LightUserData as LuaLightUserData, Lua,
|
||||
MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber,
|
||||
RegistryKey as LuaRegistryKey, Result as LuaResult, Scope as LuaScope, String as LuaString,
|
||||
Table as LuaTable, TablePairs as LuaTablePairs, TableSequence as LuaTableSequence,
|
||||
RegistryKey as LuaRegistryKey, Result as LuaResult, String as LuaString, Table as LuaTable,
|
||||
TableExt as LuaTableExt, TablePairs as LuaTablePairs, TableSequence as LuaTableSequence,
|
||||
Thread as LuaThread, ThreadStatus as LuaThreadStatus, ToLua, ToLuaMulti,
|
||||
UserData as LuaUserData, UserDataMethods as LuaUserDataMethods, Value as LuaValue,
|
||||
};
|
||||
|
||||
#[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)
|
||||
})),
|
||||
));
|
||||
}
|
||||
}
|
||||
+252
-23
@@ -1,13 +1,16 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::types::{Integer, LuaRef};
|
||||
use crate::util::{assert_stack, protect_lua, protect_lua_closure, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, Nil, ToLua, ToLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {futures_core::future::LocalBoxFuture, futures_util::future};
|
||||
|
||||
/// Handle to an internal Lua table.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Table<'lua>(pub(crate) LuaRef<'lua>);
|
||||
@@ -134,26 +137,15 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
|
||||
/// 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
|
||||
///
|
||||
/// 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 function is deprecated since 0.3.1 in favor of [`call_method`]
|
||||
/// in the `TableExt` trait.
|
||||
///
|
||||
/// 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>
|
||||
where
|
||||
K: ToLua<'lua>,
|
||||
@@ -166,11 +158,60 @@ impl<'lua> Table<'lua> {
|
||||
self.get::<_, Function>(key)?.call(args)
|
||||
}
|
||||
|
||||
/// Removes a key from the table, returning the value at the key
|
||||
/// if the key was previously in the table.
|
||||
pub fn raw_remove<K: ToLua<'lua>>(&self, key: K) -> Result<()> {
|
||||
self.raw_set(key, Nil)?;
|
||||
Ok(())
|
||||
/// Compares two tables for equality.
|
||||
///
|
||||
/// Tables are compared by reference first.
|
||||
/// If they are not primitively equals, then mlua will try to invoke the `__eq` metamethod.
|
||||
/// 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.
|
||||
@@ -213,6 +254,70 @@ impl<'lua> Table<'lua> {
|
||||
V::from_lua(value, lua)
|
||||
}
|
||||
|
||||
/// Inserts element value at position `idx` to the table, shifting up the elements from `table[idx]`.
|
||||
/// 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.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// This struct is created by the [`Table::pairs`] method.
|
||||
|
||||
+214
@@ -8,6 +8,24 @@ use crate::util::{
|
||||
};
|
||||
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).
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum ThreadStatus {
|
||||
@@ -27,6 +45,18 @@ pub enum ThreadStatus {
|
||||
#[derive(Clone, Debug)]
|
||||
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> {
|
||||
/// 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::sync::{Arc, Mutex};
|
||||
use std::rc::Rc;
|
||||
use std::{fmt, mem, ptr};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use futures_core::future::LocalBoxFuture;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::ffi;
|
||||
use crate::lua::Lua;
|
||||
use crate::util::{assert_stack, StackGuard};
|
||||
use crate::value::MultiValue;
|
||||
|
||||
/// Type of Lua integer numbers.
|
||||
@@ -19,29 +24,28 @@ pub struct LightUserData(pub *mut c_void);
|
||||
pub(crate) type Callback<'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.
|
||||
///
|
||||
/// This is a handle to a value stored inside the Lua registry. It is not directly usable like the
|
||||
/// `Table` or `Function` handle types, but since it doesn't hold a reference to a parent Lua and is
|
||||
/// Send + Sync + 'static, it is much more flexible and can be used in many situations where it is
|
||||
/// 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`].
|
||||
/// This is a handle to a value stored inside the Lua registry. 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
|
||||
/// 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
|
||||
/// [`UserData::set_user_value`] / [`UserData::get_user_value`], and instead of moving a RegistryKey
|
||||
/// into a callback, prefer [`Lua::scope`].
|
||||
/// [`UserData::set_user_value`] / [`UserData::get_user_value`].
|
||||
///
|
||||
/// [`Lua::remove_registry_value`]: struct.Lua.html#method.remove_registry_value
|
||||
/// [`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::get_user_value`]: struct.UserData.html#method.get_user_value
|
||||
pub struct RegistryKey {
|
||||
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 {
|
||||
@@ -52,7 +56,8 @@ impl fmt::Debug for RegistryKey {
|
||||
|
||||
impl Drop for RegistryKey {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -92,3 +97,16 @@ impl<'lua> Drop for LuaRef<'lua> {
|
||||
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};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use std::future::Future;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::types::LuaRef;
|
||||
use crate::util::{assert_stack, get_userdata, StackGuard};
|
||||
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.
|
||||
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 {
|
||||
@@ -117,8 +117,6 @@ impl MetaMethod {
|
||||
MetaMethod::ToString => b"__tostring",
|
||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||
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]>,
|
||||
A: FromLuaMulti<'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.
|
||||
///
|
||||
@@ -151,7 +149,23 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
S: ?Sized + AsRef<[u8]>,
|
||||
A: FromLuaMulti<'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
|
||||
/// 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]>,
|
||||
A: FromLuaMulti<'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.
|
||||
///
|
||||
@@ -179,7 +193,23 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
S: ?Sized + AsRef<[u8]>,
|
||||
A: FromLuaMulti<'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.
|
||||
///
|
||||
@@ -193,7 +223,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
where
|
||||
A: FromLuaMulti<'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.
|
||||
///
|
||||
@@ -207,7 +237,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
where
|
||||
A: FromLuaMulti<'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.
|
||||
///
|
||||
@@ -218,7 +248,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
where
|
||||
A: FromLuaMulti<'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.
|
||||
///
|
||||
@@ -229,7 +259,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
where
|
||||
A: FromLuaMulti<'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.
|
||||
@@ -298,7 +328,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
/// [`UserDataMethods`]: trait.UserDataMethods.html
|
||||
pub trait UserData: Sized {
|
||||
/// 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`].
|
||||
@@ -398,6 +428,42 @@ impl<'lua> AnyUserData<'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>
|
||||
where
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
+102
-122
@@ -1,14 +1,20 @@
|
||||
use std::any::Any;
|
||||
use std::any::{Any, TypeId};
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Write;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
||||
use std::sync::Arc;
|
||||
use std::rc::Rc;
|
||||
use std::{mem, ptr, slice};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
|
||||
thread_local! {
|
||||
static METATABLE_CACHE: RefCell<HashMap<TypeId, c_int>> = RefCell::new(HashMap::new());
|
||||
}
|
||||
|
||||
// Checks that Lua has enough free stack space for future stack operations. On failure, this will
|
||||
// panic with an internal error message.
|
||||
pub unsafe fn assert_stack(state: *mut ffi::lua_State, amount: c_int) {
|
||||
@@ -175,8 +181,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() {
|
||||
ffi::lua_pop(state, 1);
|
||||
err.clone()
|
||||
} else if is_wrapped_panic(state, -1) {
|
||||
let panic = get_userdata::<WrappedPanic>(state, -1);
|
||||
} else if let Some(panic) = get_gc_userdata::<WrappedPanic>(state, -1).as_mut() {
|
||||
if let Some(p) = (*panic).0.take() {
|
||||
resume_unwind(p);
|
||||
} else {
|
||||
@@ -255,6 +260,41 @@ pub unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
|
||||
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
|
||||
// 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.
|
||||
@@ -380,14 +420,14 @@ where
|
||||
Ok(Err(err)) => {
|
||||
ffi::lua_settop(state, 1);
|
||||
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_error(state)
|
||||
}
|
||||
Err(p) => {
|
||||
ffi::lua_settop(state, 1);
|
||||
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_error(state)
|
||||
}
|
||||
@@ -428,12 +468,12 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
|
||||
ud,
|
||||
WrappedError(Error::CallbackError {
|
||||
traceback,
|
||||
cause: Arc::new(error),
|
||||
cause: Rc::new(error),
|
||||
}),
|
||||
);
|
||||
get_error_metatable(state);
|
||||
get_gc_metatable_for::<WrappedError>(state);
|
||||
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 {
|
||||
let s = ffi::luaL_tolstring(state, -1, ptr::null_mut());
|
||||
ffi::luaL_traceback(state, state, s, 0);
|
||||
@@ -443,68 +483,72 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
|
||||
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.
|
||||
pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> *mut ffi::lua_State {
|
||||
#[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_rawget(state, ffi::LUA_REGISTRYINDEX);
|
||||
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_MAINTHREAD);
|
||||
let main_state = ffi::lua_tothread(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
main_state
|
||||
}
|
||||
let main_state = ffi::lua_tothread(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
main_state
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
state
|
||||
}
|
||||
|
||||
// Pushes a WrappedError to the top of the stack. Uses two stack spaces and does not call
|
||||
// lua_checkstack.
|
||||
pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) -> Result<()> {
|
||||
let ud = protect_lua_closure(state, 0, 1, move |state| {
|
||||
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(())
|
||||
push_gc_userdata::<WrappedError>(state, WrappedError(err))
|
||||
}
|
||||
|
||||
// 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.
|
||||
pub unsafe fn get_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> *const Error {
|
||||
let userdata = ffi::lua_touserdata(state, index);
|
||||
if userdata.is_null() {
|
||||
let ud = get_gc_userdata::<WrappedError>(state, index);
|
||||
if ud.is_null() {
|
||||
return ptr::null();
|
||||
}
|
||||
&(*ud).0
|
||||
}
|
||||
|
||||
if ffi::lua_getmetatable(state, index) == 0 {
|
||||
return ptr::null();
|
||||
// Initialize the internal (with __gc) metatable for a type T
|
||||
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>();
|
||||
|
||||
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);
|
||||
let res = ffi::lua_rawequal(state, -1, -2) != 0;
|
||||
ffi::lua_pop(state, 2);
|
||||
let ref_addr = ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX);
|
||||
METATABLE_CACHE.with(|mc| mc.borrow_mut().insert(type_id, ref_addr));
|
||||
}
|
||||
|
||||
if res {
|
||||
&(*get_userdata::<WrappedError>(state, -1)).0
|
||||
} else {
|
||||
ptr::null()
|
||||
}
|
||||
pub unsafe fn get_gc_metatable_for<T: Any>(state: *mut ffi::lua_State) {
|
||||
let type_id = TypeId::of::<T>();
|
||||
let ref_addr = METATABLE_CACHE
|
||||
.with(|mc| *mlua_expect!(mc.borrow().get(&type_id), "gc metatable does not exist"));
|
||||
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ref_addr as ffi::lua_Integer);
|
||||
}
|
||||
|
||||
// Initialize the error, panic, and destructed userdata metatables.
|
||||
pub unsafe fn init_error_registry(state: *mut ffi::lua_State) {
|
||||
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 {
|
||||
let err_buf = callback_error(state, |_| {
|
||||
@@ -524,8 +568,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) {
|
||||
// kind of recursive error structure?)
|
||||
let _ = write!(&mut (*err_buf), "{}", error);
|
||||
Ok(err_buf)
|
||||
} else if is_wrapped_panic(state, -1) {
|
||||
let panic = get_userdata::<WrappedPanic>(state, -1);
|
||||
} else if let Some(panic) = get_gc_userdata::<WrappedPanic>(state, -1).as_ref() {
|
||||
if let Some(ref p) = (*panic).0 {
|
||||
ffi::lua_pushlightuserdata(
|
||||
state,
|
||||
@@ -564,56 +607,31 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) {
|
||||
1
|
||||
}
|
||||
|
||||
ffi::lua_pushlightuserdata(
|
||||
init_gc_metatable_for::<WrappedError>(
|
||||
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"));
|
||||
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(
|
||||
init_gc_metatable_for::<WrappedPanic>(
|
||||
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
|
||||
|
||||
unsafe extern "C" fn destructed_error(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedError>()) as *mut WrappedError;
|
||||
|
||||
ptr::write(ud, WrappedError(Error::CallbackDestructed));
|
||||
get_error_metatable(state);
|
||||
get_gc_metatable_for::<WrappedError>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
@@ -709,40 +727,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) {
|
||||
ffi::lua_pushlightuserdata(
|
||||
state,
|
||||
@@ -751,9 +735,5 @@ unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) {
|
||||
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 ERROR_PRINT_BUFFER_KEY: u8 = 0;
|
||||
|
||||
+50
-3
@@ -2,6 +2,7 @@ use std::iter::{self, FromIterator};
|
||||
use std::{slice, str, vec};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
@@ -47,20 +48,66 @@ pub enum Value<'lua> {
|
||||
pub use self::Value::Nil;
|
||||
|
||||
impl<'lua> Value<'lua> {
|
||||
pub(crate) fn type_name(&self) -> &'static str {
|
||||
pub fn type_name(&self) -> &'static str {
|
||||
match *self {
|
||||
Value::Nil => "nil",
|
||||
Value::Boolean(_) => "boolean",
|
||||
Value::LightUserData(_) => "light userdata",
|
||||
Value::LightUserData(_) => "lightuserdata",
|
||||
Value::Integer(_) => "integer",
|
||||
Value::Number(_) => "number",
|
||||
Value::String(_) => "string",
|
||||
Value::Table(_) => "table",
|
||||
Value::Function(_) => "function",
|
||||
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`.
|
||||
|
||||
+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 mlua::{Lua, Result};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#[cfg(any(feature = "lua53", feature = "lua51"))]
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_compile_fail() {
|
||||
let t = trybuild::TestCases::new();
|
||||
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 it appears within the type `[closure@$DIR/tests/compile_fail/lua_norefunwindsafe.rs:7:18: 7:48 lua:&mlua::lua::Lua]`
|
||||
|
||||
error[E0277]: the type `std::cell::UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> $DIR/lua_norefunwindsafe.rs:7:5
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ^^^^^^^^^^^^ `std::cell::UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
|
|
||||
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<usize>`
|
||||
= note: required because it appears within the type `std::cell::Cell<usize>`
|
||||
= note: required because it appears within the type `std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>`
|
||||
= note: required because it appears within the type `std::marker::PhantomData<std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>>`
|
||||
= note: required because it appears within the type `std::rc::Rc<std::cell::RefCell<mlua::lua::ExtraData>>`
|
||||
= note: required because it appears within the type `mlua::lua::Lua`
|
||||
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile_fail/lua_norefunwindsafe.rs:7:18: 7:48 lua:&mlua::lua::Lua]`
|
||||
|
||||
error[E0277]: the type `std::cell::UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> $DIR/lua_norefunwindsafe.rs:7:5
|
||||
|
|
||||
@@ -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>`
|
||||
= 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::sync::Arc<std::cell::RefCell<mlua::lua::ExtraData>>`
|
||||
= note: required because it appears within the type `std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>`
|
||||
= note: required because it appears within the type `std::marker::PhantomData<std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>>`
|
||||
= note: required because it appears within the type `std::rc::Rc<std::cell::RefCell<mlua::lua::ExtraData>>`
|
||||
= note: required because it appears within the type `mlua::lua::Lua`
|
||||
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile_fail/lua_norefunwindsafe.rs:7:18: 7:48 lua:&mlua::lua::Lua]`
|
||||
@@ -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>`
|
||||
= 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::marker::PhantomData<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::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]`
|
||||
|
||||
@@ -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 `[closure@$DIR/tests/compile_fail/ref_nounwindsafe.rs:8:18: 8:54 table:mlua::table::Table<'_>]`
|
||||
|
||||
error[E0277]: the type `std::cell::UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> $DIR/ref_nounwindsafe.rs:8:5
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ^^^^^^^^^^^^ `std::cell::UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
|
|
||||
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<usize>`
|
||||
= note: required because it appears within the type `std::cell::Cell<usize>`
|
||||
= note: required because it appears within the type `std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>`
|
||||
= note: required because it appears within the type `std::marker::PhantomData<std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>>`
|
||||
= note: required because it appears within the type `std::rc::Rc<std::cell::RefCell<mlua::lua::ExtraData>>`
|
||||
= note: required because it appears within the type `mlua::lua::Lua`
|
||||
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
|
||||
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
|
||||
= note: required because it appears within the type `mlua::table::Table<'_>`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile_fail/ref_nounwindsafe.rs:8:18: 8:54 table:mlua::table::Table<'_>]`
|
||||
|
||||
error[E0277]: the type `std::cell::UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> $DIR/ref_nounwindsafe.rs:8:5
|
||||
|
|
||||
@@ -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>`
|
||||
= 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::sync::Arc<std::cell::RefCell<mlua::lua::ExtraData>>`
|
||||
= note: required because it appears within the type `std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>`
|
||||
= note: required because it appears within the type `std::marker::PhantomData<std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>>`
|
||||
= note: required because it appears within the type `std::rc::Rc<std::cell::RefCell<mlua::lua::ExtraData>>`
|
||||
= note: required because it appears within the type `mlua::lua::Lua`
|
||||
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
|
||||
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
|
||||
@@ -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>`
|
||||
= 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::marker::PhantomData<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::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<'_>`
|
||||
|
||||
@@ -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};
|
||||
|
||||
#[test]
|
||||
|
||||
+17
-28
@@ -1,21 +1,11 @@
|
||||
#![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::sync::Arc;
|
||||
use std::rc::Rc;
|
||||
|
||||
use mlua::{Lua, Result, UserData};
|
||||
|
||||
#[test]
|
||||
fn test_gc_control() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||
{
|
||||
@@ -26,18 +16,17 @@ fn test_gc_control() -> Result<()> {
|
||||
assert!(lua.gc_is_running());
|
||||
}
|
||||
|
||||
struct MyUserdata(Arc<()>);
|
||||
struct MyUserdata(Rc<()>);
|
||||
impl UserData for MyUserdata {}
|
||||
|
||||
let rc = Arc::new(());
|
||||
lua.globals()
|
||||
.set("userdata", lua.create_userdata(MyUserdata(rc.clone()))?)?;
|
||||
lua.globals().raw_remove("userdata")?;
|
||||
let rc = Rc::new(());
|
||||
globals.set("userdata", lua.create_userdata(MyUserdata(rc.clone()))?)?;
|
||||
globals.raw_remove("userdata")?;
|
||||
|
||||
assert_eq!(Arc::strong_count(&rc), 2);
|
||||
assert_eq!(Rc::strong_count(&rc), 2);
|
||||
lua.gc_collect()?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&rc), 1);
|
||||
assert_eq!(Rc::strong_count(&rc), 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -51,15 +40,15 @@ fn test_gc_error() {
|
||||
match lua
|
||||
.load(
|
||||
r#"
|
||||
val = nil
|
||||
table = {}
|
||||
setmetatable(table, {
|
||||
__gc = function()
|
||||
error("gcwascalled")
|
||||
end
|
||||
})
|
||||
table = nil
|
||||
collectgarbage("collect")
|
||||
val = nil
|
||||
table = {}
|
||||
setmetatable(table, {
|
||||
__gc = function()
|
||||
error("gcwascalled")
|
||||
end
|
||||
})
|
||||
table = nil
|
||||
collectgarbage("collect")
|
||||
"#,
|
||||
)
|
||||
.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 mlua::{Lua, Result, String};
|
||||
|
||||
+88
-13
@@ -1,15 +1,4 @@
|
||||
#![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::{Lua, Nil, Result, Table, Value};
|
||||
use mlua::{Lua, Nil, Result, Table, TableExt, Value};
|
||||
|
||||
#[test]
|
||||
fn test_set_get() -> Result<()> {
|
||||
@@ -91,10 +80,32 @@ fn test_table() -> Result<()> {
|
||||
globals.set("table4", lua.create_sequence_from(vec![1, 2, 3, 4, 5])?)?;
|
||||
let table4 = globals.get::<_, Table>("table4")?;
|
||||
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)]
|
||||
);
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -148,6 +159,40 @@ fn test_metatable() -> Result<()> {
|
||||
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]
|
||||
fn test_table_error() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -181,3 +226,33 @@ fn test_table_error() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_call() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
table = {a = 1}
|
||||
|
||||
function table.func(key)
|
||||
return "func_"..key
|
||||
end
|
||||
|
||||
function table:method(key)
|
||||
return "method_"..self[key]
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
let table: Table = lua.globals().get("table")?;
|
||||
|
||||
assert_eq!(table.call_function::<_, _, String>("func", "a")?, "func_a");
|
||||
assert_eq!(
|
||||
table.call_method::<_, _, String>("method", "a")?,
|
||||
"method_1"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+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::panic::catch_unwind;
|
||||
use std::sync::Arc;
|
||||
use std::rc::Rc;
|
||||
use std::{error, f32, f64, fmt};
|
||||
|
||||
use mlua::{
|
||||
@@ -92,13 +81,13 @@ fn test_lua_multi() -> Result<()> {
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
function concat(arg1, arg2)
|
||||
return arg1 .. arg2
|
||||
end
|
||||
function concat(arg1, arg2)
|
||||
return arg1 .. arg2
|
||||
end
|
||||
|
||||
function mreturn()
|
||||
return 1, 2, 3, 4, 5, 6
|
||||
end
|
||||
function mreturn()
|
||||
return 1, 2, 3, 4, 5, 6
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
@@ -595,22 +584,22 @@ fn test_registry_value() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
fn test_drop_registry_value() -> Result<()> {
|
||||
struct MyUserdata(Arc<()>);
|
||||
struct MyUserdata(Rc<()>);
|
||||
|
||||
impl UserData for MyUserdata {}
|
||||
|
||||
let lua = Lua::new();
|
||||
let rc = Arc::new(());
|
||||
let rc = Rc::new(());
|
||||
|
||||
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);
|
||||
lua.expire_registry_values();
|
||||
|
||||
lua.load(r#"collectgarbage("collect")"#).exec()?;
|
||||
|
||||
assert_eq!(Arc::strong_count(&rc), 1);
|
||||
assert_eq!(Rc::strong_count(&rc), 1);
|
||||
|
||||
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 mlua::{Error, Function, Lua, Result, Thread, ThreadStatus};
|
||||
@@ -20,13 +9,13 @@ fn test_thread() -> Result<()> {
|
||||
let thread = lua.create_thread(
|
||||
lua.load(
|
||||
r#"
|
||||
function (s)
|
||||
local sum = s
|
||||
for i = 1,4 do
|
||||
sum = sum + coroutine.yield(sum)
|
||||
end
|
||||
return sum
|
||||
function (s)
|
||||
local sum = s
|
||||
for i = 1,4 do
|
||||
sum = sum + coroutine.yield(sum)
|
||||
end
|
||||
return sum
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.eval()?,
|
||||
@@ -47,11 +36,11 @@ fn test_thread() -> Result<()> {
|
||||
let accumulate = lua.create_thread(
|
||||
lua.load(
|
||||
r#"
|
||||
function (sum)
|
||||
while true do
|
||||
sum = sum + coroutine.yield(sum)
|
||||
end
|
||||
function (sum)
|
||||
while true do
|
||||
sum = sum + coroutine.yield(sum)
|
||||
end
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.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 mlua::{Function, LightUserData, Lua, Result};
|
||||
|
||||
+35
-34
@@ -1,19 +1,8 @@
|
||||
#![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::sync::Arc;
|
||||
use std::rc::Rc;
|
||||
|
||||
use mlua::{
|
||||
AnyUserData, ExternalError, Function, Lua, MetaMethod, Result, String, UserData,
|
||||
UserDataMethods,
|
||||
UserDataMethods, Value,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -96,6 +85,9 @@ fn test_metamethods() -> Result<()> {
|
||||
MetaMethod::Sub,
|
||||
|_, (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| {
|
||||
if index.to_str()? == "inner" {
|
||||
Ok(data.0)
|
||||
@@ -104,7 +96,7 @@ fn test_metamethods() -> Result<()> {
|
||||
}
|
||||
});
|
||||
#[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;
|
||||
let stateless_iter = lua.create_function(|_, (data, i): (MyUserData, i64)| {
|
||||
let i = i + 1;
|
||||
@@ -122,18 +114,19 @@ fn test_metamethods() -> Result<()> {
|
||||
let globals = lua.globals();
|
||||
globals.set("userdata1", MyUserData(7))?;
|
||||
globals.set("userdata2", MyUserData(3))?;
|
||||
globals.set("userdata3", MyUserData(3))?;
|
||||
assert_eq!(
|
||||
lua.load("userdata1 + userdata2").eval::<MyUserData>()?.0,
|
||||
10
|
||||
);
|
||||
|
||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||
let ipairs_it = {
|
||||
let pairs_it = {
|
||||
lua.load(
|
||||
r#"
|
||||
function ipairs_it()
|
||||
function pairs_it()
|
||||
local r = 0
|
||||
for i, v in ipairs(userdata1) do
|
||||
for i, v in pairs(userdata1) do
|
||||
r = r + v
|
||||
end
|
||||
return r
|
||||
@@ -141,16 +134,23 @@ fn test_metamethods() -> Result<()> {
|
||||
"#,
|
||||
)
|
||||
.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:get()").eval::<i64>()?, 7);
|
||||
assert_eq!(lua.load("userdata2.inner").eval::<i64>()?, 3);
|
||||
#[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());
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -175,18 +175,18 @@ fn test_gc_userdata() -> Result<()> {
|
||||
assert!(lua
|
||||
.load(
|
||||
r#"
|
||||
local tbl = setmetatable({
|
||||
userdata = userdata
|
||||
}, { __gc = function(self)
|
||||
-- resurrect userdata
|
||||
hatch = self.userdata
|
||||
end })
|
||||
local tbl = setmetatable({
|
||||
userdata = userdata
|
||||
}, { __gc = function(self)
|
||||
-- resurrect userdata
|
||||
hatch = self.userdata
|
||||
end })
|
||||
|
||||
tbl = nil
|
||||
userdata = nil -- make table and userdata collectable
|
||||
collectgarbage("collect")
|
||||
hatch:access()
|
||||
"#
|
||||
tbl = nil
|
||||
userdata = nil -- make table and userdata collectable
|
||||
collectgarbage("collect")
|
||||
hatch:access()
|
||||
"#
|
||||
)
|
||||
.exec()
|
||||
.is_err());
|
||||
@@ -196,22 +196,22 @@ fn test_gc_userdata() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
fn detroys_userdata() -> Result<()> {
|
||||
struct MyUserdata(Arc<()>);
|
||||
struct MyUserdata(Rc<()>);
|
||||
|
||||
impl UserData for MyUserdata {}
|
||||
|
||||
let rc = Arc::new(());
|
||||
let rc = Rc::new(());
|
||||
|
||||
let lua = Lua::new();
|
||||
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
|
||||
let _ = lua.globals().raw_remove("userdata")?;
|
||||
lua.gc_collect()?;
|
||||
|
||||
assert_eq!(Arc::strong_count(&rc), 1);
|
||||
assert_eq!(Rc::strong_count(&rc), 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -219,6 +219,7 @@ fn detroys_userdata() -> Result<()> {
|
||||
#[test]
|
||||
fn user_value() -> Result<()> {
|
||||
struct MyUserData;
|
||||
|
||||
impl UserData for MyUserData {}
|
||||
|
||||
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