mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 973b5c3bf5 | |||
| b610a79d66 | |||
| fe39ae09bf | |||
| 01714d2510 | |||
| 0bad4a0ff9 | |||
| 35b7504076 | |||
| c9b8eb5418 | |||
| ef7d123f80 | |||
| 20cba5de5b | |||
| 3e03f4201c | |||
| 5199b02346 | |||
| 5293b8d6d2 | |||
| 7541b6f3f3 | |||
| 205510a540 | |||
| 2250421438 | |||
| 8e57e6fa5a | |||
| f2dbbb091f | |||
| 13cfb4bd51 | |||
| 33ebacab49 | |||
| 3829b72212 | |||
| af67971e0d | |||
| 0f4bcca7ce | |||
| 585c0a25d8 | |||
| 64346ce56c | |||
| 108682cc71 | |||
| 4af7bcf0d9 | |||
| 1bb3c5c19f | |||
| a4567cb5f7 | |||
| 26d8d899f2 | |||
| 67bc0b1196 | |||
| e8505b5239 | |||
| 3f55958bdd | |||
| 463fc646bc | |||
| b5f1325f2f | |||
| 0625991a48 | |||
| 269ef9c55d | |||
| f5b88624ce | |||
| 2fae94586d | |||
| bc81d1016f | |||
| c19f12898d | |||
| c7541ef7d3 | |||
| 41a1a0d15a | |||
| c10169a380 | |||
| ced808d5ab | |||
| c95ac32741 | |||
| 14169eadb1 | |||
| 5a7ad9f7cd | |||
| decb5b9e37 | |||
| 1635903d3f | |||
| 2b2df708f9 | |||
| cb1ac28f2a | |||
| 3e7f25670a | |||
| 0d404ce4c3 | |||
| e26cec5db9 | |||
| 0bd36b42e7 | |||
| e0da6ac929 | |||
| 0c7db4916c | |||
| b9589491e4 | |||
| 58cb371f06 | |||
| 8add60b019 | |||
| c363fb9288 | |||
| 3900e23839 | |||
| 726fde7e1f | |||
| 7cb9c4f39c |
@@ -14,10 +14,10 @@ jobs:
|
||||
|
||||
- name: Generate code coverage
|
||||
run: |
|
||||
cargo tarpaulin --verbose --features lua53,vendored,async,send,serialize --out xml --exclude-files benches --exclude-files tests --exclude-files build --exclude-files src/ffi
|
||||
cargo tarpaulin --verbose --features lua53,vendored,async,send,serialize,macros --out xml --exclude-files benches --exclude-files build --exclude-files mlua_derive --exclude-files src/ffi --exclude-files tests
|
||||
|
||||
- name: Upload to codecov.io
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
token: ${{secrets.CODECOV_TOKEN}}
|
||||
fail_ci_if_error: true
|
||||
fail_ci_if_error: false
|
||||
|
||||
+15
-37
@@ -26,8 +26,8 @@ jobs:
|
||||
override: true
|
||||
- name: Build ${{ matrix.lua }} vendored
|
||||
run: |
|
||||
cargo build --release --features "${{ matrix.lua }} vendored"
|
||||
cargo build --release --features "${{ matrix.lua }} vendored async send serialize"
|
||||
cargo build --release --features "${{ matrix.lua }},vendored"
|
||||
cargo build --release --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
- name: Build ${{ matrix.lua }} pkg-config
|
||||
if: ${{ matrix.os == 'ubuntu-18.04' && matrix.lua != 'lua54' }}
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
target: aarch64-apple-darwin
|
||||
override: true
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }} async send serialize vendored"
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
|
||||
build_aarch64_cross_ubuntu:
|
||||
name: Cross-compile to aarch64-unknown-linux-gnu
|
||||
@@ -73,7 +73,7 @@ jobs:
|
||||
sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }} async send serialize vendored"
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
|
||||
build_armv7_cross_ubuntu:
|
||||
@@ -96,7 +96,7 @@ jobs:
|
||||
sudo apt-get install -y --no-install-recommends gcc-arm-linux-gnueabihf libc-dev-armhf-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }} async send serialize vendored"
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
|
||||
test:
|
||||
@@ -106,7 +106,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04, macos-latest, windows-latest]
|
||||
rust: [stable]
|
||||
rust: [stable, nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
@@ -123,37 +123,15 @@ jobs:
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
if: ${{ matrix.os != 'macos-latest' || matrix.lua != 'luajit' }}
|
||||
run: |
|
||||
cargo test --release --features "${{ matrix.lua }} vendored"
|
||||
cargo test --release --features "${{ matrix.lua }} vendored async send serialize"
|
||||
cargo test --release --features "${{ matrix.lua }},vendored"
|
||||
cargo test --release --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
- name: Run compile tests (macos lua53)
|
||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua53' }}
|
||||
run: |
|
||||
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }} vendored" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }} vendored async send serialize" -- --ignored
|
||||
shell: bash
|
||||
|
||||
test_luajit_macos:
|
||||
name: Test LuaJIT on macOS
|
||||
runs-on: macos-latest
|
||||
needs: build
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
target: x86_64-apple-darwin
|
||||
override: true
|
||||
- name: Run LuaJIT 2.0.5 tests
|
||||
run: |
|
||||
brew install luajit
|
||||
cargo test --tests --release --features "luajit async send serialize" -- --test-threads=1
|
||||
shell: bash
|
||||
- name: Run LuaJIT vendored tests
|
||||
run: |
|
||||
cargo test --release --features "luajit vendored async send serialize"
|
||||
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }},vendored" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }},vendored,async,send,serialize,macros" -- --ignored
|
||||
shell: bash
|
||||
|
||||
test_modules:
|
||||
@@ -163,7 +141,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04, macos-latest]
|
||||
rust: [stable]
|
||||
rust: [stable, nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
@@ -179,8 +157,8 @@ jobs:
|
||||
override: true
|
||||
- name: Run ${{ matrix.lua }} module tests
|
||||
run: |
|
||||
(cd examples/module && cargo build --release --features "${{ matrix.lua }} vendored")
|
||||
(cd tests/module && cargo test --release --features "${{ matrix.lua }} vendored")
|
||||
(cd examples/module && cargo build --release --features "${{ matrix.lua }},vendored")
|
||||
(cd tests/module && cargo test --release --features "${{ matrix.lua }},vendored")
|
||||
shell: bash
|
||||
|
||||
test_modules_windows:
|
||||
@@ -189,7 +167,7 @@ jobs:
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua53, luajit]
|
||||
lua: [lua54, luajit]
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
@@ -232,4 +210,4 @@ jobs:
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --features "${{ matrix.lua }},vendored,async,send,serialize
|
||||
args: --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
|
||||
@@ -1,3 +1,36 @@
|
||||
## v0.6.0-beta.2
|
||||
|
||||
- [**Breaking**] Removed `AnyUserData::has_metamethod()`
|
||||
- Added `Thread::reset()` for luajit/lua54 to recycle threads.
|
||||
It's possible to attach a new function to a thread (coroutine).
|
||||
- Added `chunk!` macro support to load chunks of Lua code using the Rust tokenizer and optinally capturing Rust variables.
|
||||
- Improved error reporting (`Error`'s `__tostring` method formats full stacktraces). This is useful in the module mode.
|
||||
|
||||
## v0.6.0-beta.1
|
||||
|
||||
- New `UserDataFields` API
|
||||
- Allow to define arbitrary MetaMethods
|
||||
- `MetaMethods::name()` is public
|
||||
- Do not trigger longjmp in Rust to prevent unwinding across FFI boundaries. See https://github.com/rust-lang/rust/issues/83541
|
||||
- Added `SerializeOptions` to to change default Lua serializer behaviour (eg. nil/null/array serialization)
|
||||
- [**Breaking**] Removed `Result` from `LuaSerdeExt::null()` and `LuaSerdeExt::array_metatable()` (never fails)
|
||||
- [**Breaking**] Removed `Result` from `Function::dump()` (never fails)
|
||||
- `ToLua`/`FromLua` implementation for `Box<str>` and `Box<[T]>`
|
||||
- [**Breaking**] Added `LuaOptions` to customize Lua/Rust behaviour (currently panic handling)
|
||||
- Various bugfixes and performance improvements
|
||||
|
||||
## v0.5.4
|
||||
|
||||
- Build script improvements
|
||||
- Improvements in panic handling (resume panic on value popping)
|
||||
- Fixed bug serializing 3rd party userdata (causes segfault)
|
||||
- Make error::Error non exhaustive
|
||||
|
||||
## v0.5.3
|
||||
|
||||
- Fixed bug when returning nil-prefixed multi values from async function (+ test)
|
||||
- Performance optimisation for async callbacks (polling)
|
||||
|
||||
## v0.5.2
|
||||
|
||||
- Some performance optimisations (callbacks)
|
||||
|
||||
+7
-5
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.5.2" # remember to update html_root_url and mlua_derive
|
||||
version = "0.6.0-beta.2" # remember to update html_root_url and mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
|
||||
edition = "2018"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
@@ -17,7 +17,7 @@ with async/await features and support of writing native lua modules in Rust.
|
||||
"""
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["lua53", "async", "send", "serialize"]
|
||||
features = ["lua53", "async", "send", "serialize", "macros"]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
@@ -38,11 +38,12 @@ module = ["mlua_derive"]
|
||||
async = ["futures-core", "futures-task", "futures-util"]
|
||||
send = []
|
||||
serialize = ["serde", "erased-serde"]
|
||||
macros = ["mlua_derive/macros"]
|
||||
|
||||
[dependencies]
|
||||
mlua_derive = { version = "0.5", optional = true, path = "mlua_derive" }
|
||||
mlua_derive = { version = "=0.6.0-beta.2", optional = true, path = "mlua_derive" }
|
||||
bstr = { version = "0.2", features = ["std"], default_features = false }
|
||||
lazy_static = { version = "1.4" }
|
||||
once_cell = { version = "1.7" }
|
||||
num-traits = { version = "0.2.14" }
|
||||
futures-core = { version = "0.3.5", optional = true }
|
||||
futures-task = { version = "0.3.5", optional = true }
|
||||
@@ -57,7 +58,7 @@ lua-src = { version = ">= 540.0.0, < 550.0.0", optional = true }
|
||||
luajit-src = { version = ">= 210.1.2, < 220.0.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rustyline = "7.0"
|
||||
rustyline = "8.0"
|
||||
criterion = { version = "0.3.4", features = ["html_reports", "async_tokio"] }
|
||||
trybuild = "1.0"
|
||||
futures = "0.3.5"
|
||||
@@ -66,6 +67,7 @@ reqwest = { version = "0.11", features = ["json"] }
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
futures-timer = "3.0"
|
||||
serde_json = "1.0"
|
||||
maplit = "1.0"
|
||||
|
||||
[[bench]]
|
||||
name = "benchmark"
|
||||
|
||||
@@ -17,7 +17,7 @@ _safe_ (as far as it's possible), high level, easy to use, practical and flexibl
|
||||
|
||||
Started as [rlua](https://github.com/amethyst/rlua/tree/0.15.3) fork, `mlua` supports Lua 5.4, 5.3, 5.2 and 5.1 including LuaJIT (2.0.5 and 2.1 beta) and allows to write native Lua modules in Rust as well as use Lua in a standalone mode.
|
||||
|
||||
`mlua` tested on Windows/macOS/Linux including module mode in [GitHub Actions] on `x86_64` platform and cross-compilation to `aarch64` (other targes are also supported).
|
||||
`mlua` tested on Windows/macOS/Linux including module mode in [GitHub Actions] on `x86_64` platform and cross-compilation to `aarch64` (other targets are also supported).
|
||||
|
||||
[GitHub Actions]: https://github.com/khvzak/mlua/actions
|
||||
|
||||
@@ -25,7 +25,7 @@ Started as [rlua](https://github.com/amethyst/rlua/tree/0.15.3) fork, `mlua` sup
|
||||
|
||||
### Feature flags
|
||||
|
||||
`mlua` uses feature flags to reduce the amount of depenendies, compiled code and allow to choose only required set of features.
|
||||
`mlua` uses feature flags to reduce the amount of dependencies, compiled code and allow to choose only required set of features.
|
||||
Below is a list of the available feature flags. By default `mlua` does not enable any features.
|
||||
|
||||
* `lua54`: activate Lua [5.4] support
|
||||
@@ -37,7 +37,7 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
|
||||
* `module`: enable module mode (building loadable `cdylib` library for Lua)
|
||||
* `async`: enable async/await support (any executor can be used, eg. [tokio] or [async-std])
|
||||
* `send`: make `mlua::Lua` transferable across thread boundaries (adds [`Send`] requirement to `mlua::Function` and `mlua::UserData`)
|
||||
* `serialize`: add serialization and deserialization support to `mlua` types usign [serde] framework
|
||||
* `serialize`: add serialization and deserialization support to `mlua` types using [serde] framework
|
||||
|
||||
[5.4]: https://www.lua.org/manual/5.4/manual.html
|
||||
[5.3]: https://www.lua.org/manual/5.3/manual.html
|
||||
@@ -73,7 +73,7 @@ With `serialize` feature flag enabled, `mlua` allows you to serialize/deserializ
|
||||
|
||||
### Compiling
|
||||
|
||||
You have to enable one of the features `lua54`, `lua53`, `lua52`, `lua51` or `luajit`, according to the choosen Lua version.
|
||||
You have to enable one of the features `lua54`, `lua53`, `lua52`, `lua51` or `luajit`, according to the chosen Lua version.
|
||||
|
||||
By default `mlua` uses `pkg-config` tool to find lua includes and libraries for the chosen Lua version.
|
||||
In most cases it works as desired, although sometimes could be more preferable to use a custom lua library.
|
||||
@@ -85,7 +85,7 @@ An example how to use them:
|
||||
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
|
||||
`mlua` also supports vendored lua/luajit using the auxiliary crates [lua-src](https://crates.io/crates/lua-src) and
|
||||
[luajit-src](https://crates.io/crates/luajit-src).
|
||||
Just enable the `vendored` feature and cargo will automatically build and link specified lua/luajit version. This is the easiest way to get started with `mlua`.
|
||||
|
||||
@@ -198,7 +198,7 @@ It is surprisingly, fiendishly difficult to use the Lua C API without the potent
|
||||
## Panic handling
|
||||
|
||||
`mlua` wraps panics that are generated inside Rust callbacks in a regular Lua error. Panics could be
|
||||
resumed then by propagating the Lua error to Rust code.
|
||||
resumed then by returning or propagating the Lua error to Rust code.
|
||||
|
||||
For example:
|
||||
``` rust
|
||||
@@ -217,6 +217,10 @@ let _ = lua.load(r#"
|
||||
unreachable!()
|
||||
```
|
||||
|
||||
Optionally `mlua` can disable Rust panics catching in Lua via `pcall`/`xpcall` and automatically resume
|
||||
them across the Lua API boundary. This is controlled via `LuaOptions` and done by wrapping the Lua `pcall`/`xpcall`
|
||||
functions on a way to prevent catching errors that are wrapped Rust panics.
|
||||
|
||||
`mlua` should also be panic safe in another way as well, which is that any `Lua` instances or handles
|
||||
remains usable after a user generated panic, and such panics should not break internal invariants or
|
||||
leak Lua stack space. This is mostly important to safely use `mlua` types in Drop impls, as you should not be
|
||||
@@ -225,15 +229,14 @@ using panics for general error handling.
|
||||
Below is a list of `mlua` behaviors that should be considered a bug.
|
||||
If you encounter them, a bug report would be very welcome:
|
||||
|
||||
+ If your program panics with a message that contains the string "mlua internal error", this is a bug.
|
||||
+ If you can cause UB with `mlua` without typing the word "unsafe", this is a bug.
|
||||
|
||||
+ The above is true even for the internal panic about running out of stack space! There are a few ways to generate normal script errors by running out of stack, but if you encounter a *panic* based on running out of stack, this is a bug.
|
||||
+ If your program panics with a message that contains the string "mlua internal error", this is a bug.
|
||||
|
||||
+ Lua C API errors are handled by lonjmp. All instances where the Lua C API would otherwise longjmp over calling stack frames should be guarded against, except in internal callbacks where this is intentional. If you detect that `mlua` is triggering a longjmp over your Rust stack frames, this is a bug!
|
||||
+ Lua C API errors are handled by longjmp. All instances where the Lua C API would otherwise longjmp over calling stack frames should be guarded against, except in internal callbacks where this is intentional. If you detect that `mlua` is triggering a longjmp over your Rust stack frames, this is a bug!
|
||||
|
||||
+ If you detect that, after catching a panic or during a Drop triggered from a panic, a `Lua` or handle method is triggering other bugs or there is a Lua stack space leak, this is a bug. `mlua` instances are supposed to remain fully usable in the face of user generated panics. This guarantee does not extend to panics marked with "mlua internal error" simply because that is already indicative of a separate bug.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the [MIT license](LICENSE)
|
||||
|
||||
|
||||
+5
-13
@@ -1,18 +1,7 @@
|
||||
#![cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
feature(link_args)
|
||||
)]
|
||||
|
||||
#[cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
use std::time::Duration;
|
||||
use tokio::runtime::Runtime;
|
||||
use tokio::task;
|
||||
|
||||
use mlua::prelude::*;
|
||||
|
||||
@@ -117,7 +106,10 @@ fn call_sum_callback(c: &mut Criterion) {
|
||||
fn call_async_sum_callback(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
let callback = lua
|
||||
.create_async_function(|_, (a, b, c): (i64, i64, i64)| async move { Ok(a + b + c) })
|
||||
.create_async_function(|_, (a, b, c): (i64, i64, i64)| async move {
|
||||
task::yield_now().await;
|
||||
Ok(a + b + c)
|
||||
})
|
||||
.unwrap();
|
||||
lua.globals().set("callback", callback).unwrap();
|
||||
|
||||
|
||||
+27
-46
@@ -1,13 +1,19 @@
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader, Result};
|
||||
use std::ops::Bound;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn get_env_var(name: &str) -> String {
|
||||
match env::var(name) {
|
||||
Ok(val) => val,
|
||||
Err(env::VarError::NotPresent) => String::new(),
|
||||
Err(err) => panic!("cannot get {}: {}", name, err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn probe_lua() -> PathBuf {
|
||||
let include_dir = env::var_os("LUA_INC").unwrap_or_default();
|
||||
let lib_dir = env::var_os("LUA_LIB").unwrap_or_default();
|
||||
let lua_lib = env::var_os("LUA_LIB_NAME").unwrap_or_default();
|
||||
let include_dir = get_env_var("LUA_INC");
|
||||
let lib_dir = get_env_var("LUA_LIB");
|
||||
let lua_lib = get_env_var("LUA_LIB_NAME");
|
||||
|
||||
println!("cargo:rerun-if-env-changed=LUA_INC");
|
||||
println!("cargo:rerun-if-env-changed=LUA_LIB");
|
||||
@@ -16,11 +22,22 @@ pub fn probe_lua() -> PathBuf {
|
||||
|
||||
let need_lua_lib = cfg!(any(not(feature = "module"), target_os = "windows"));
|
||||
|
||||
if include_dir != "" && (!need_lua_lib || lib_dir != "") {
|
||||
if lua_lib == "" {
|
||||
panic!("LUA_LIB_NAME is not set");
|
||||
if !include_dir.is_empty() {
|
||||
if need_lua_lib {
|
||||
if lib_dir.is_empty() {
|
||||
panic!("LUA_LIB is not set");
|
||||
}
|
||||
if lua_lib.is_empty() {
|
||||
panic!("LUA_LIB_NAME is not set");
|
||||
}
|
||||
|
||||
let mut link_lib = "";
|
||||
if get_env_var("LUA_LINK") == "static" {
|
||||
link_lib = "static=";
|
||||
};
|
||||
println!("cargo:rustc-link-search=native={}", lib_dir);
|
||||
println!("cargo:rustc-link-lib={}{}", link_lib, lua_lib);
|
||||
}
|
||||
let _version = use_custom_lua(&include_dir, &lib_dir, &lua_lib).unwrap();
|
||||
return PathBuf::from(include_dir);
|
||||
}
|
||||
|
||||
@@ -100,39 +117,3 @@ pub fn probe_lua() -> PathBuf {
|
||||
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 link_lib = match env::var("LUA_LINK") {
|
||||
Ok(s) if s == "static" => "static=",
|
||||
_ => "",
|
||||
};
|
||||
|
||||
if cfg!(any(not(feature = "module"), target_os = "windows")) {
|
||||
println!(
|
||||
"cargo:rustc-link-search=native={}",
|
||||
lib_dir.as_ref().display()
|
||||
);
|
||||
println!(
|
||||
"cargo:rustc-link-lib={}{}",
|
||||
link_lib,
|
||||
lua_lib.as_ref().display()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(version_found)
|
||||
}
|
||||
|
||||
@@ -236,5 +236,17 @@ fn main() {
|
||||
generate_glue().unwrap();
|
||||
} else {
|
||||
build_glue(&include_dir);
|
||||
println!("cargo:rerun-if-changed=src/ffi/glue/glue.c");
|
||||
}
|
||||
|
||||
let mut shim_cc = cc::Build::new();
|
||||
shim_cc
|
||||
.include(include_dir)
|
||||
.define("COMPAT53_INCLUDE_SOURCE", None);
|
||||
#[cfg(feature = "luajit")]
|
||||
shim_cc.define("COMPAT53_LUAJIT", None);
|
||||
shim_cc.file("src/ffi/shim/shim.c").compile("shim");
|
||||
|
||||
println!("cargo:rerun-if-changed=src/ffi/shim");
|
||||
println!("cargo:rerun-if-changed=build");
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use mlua::{Error, Lua, LuaSerdeExt, Result};
|
||||
async fn main() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("null", lua.null()?)?;
|
||||
globals.set("null", lua.null())?;
|
||||
|
||||
let fetch_json = lua.create_async_function(|lua, uri: String| async move {
|
||||
let resp = reqwest::get(&uri)
|
||||
|
||||
@@ -33,8 +33,8 @@ fn main() -> Result<()> {
|
||||
"#).eval()?)?;
|
||||
|
||||
// Set it as (serializable) userdata
|
||||
globals.set("null", lua.null()?)?;
|
||||
globals.set("array_mt", lua.array_metatable()?)?;
|
||||
globals.set("null", lua.null())?;
|
||||
globals.set("array_mt", lua.array_metatable())?;
|
||||
globals.set("car", lua.create_ser_userdata(car)?)?;
|
||||
|
||||
// Create a Lua table with multiple data types
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua_derive"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0-beta.2"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2018"
|
||||
description = "Procedural macros for the mlua crate."
|
||||
@@ -11,7 +11,14 @@ license = "MIT"
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[features]
|
||||
macros = ["proc-macro-error", "itertools", "regex", "once_cell"]
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
proc-macro2 = { version = "1.0", features = ["span-locations"] }
|
||||
proc-macro-error = { version = "1.0", optional = true }
|
||||
syn = { version = "1.0", features = ["full"] }
|
||||
itertools = { version = "0.10", optional = true }
|
||||
regex = { version = "1.4", optional = true }
|
||||
once_cell = { version = "1.5", optional = true }
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
use proc_macro::{TokenStream, TokenTree};
|
||||
|
||||
use crate::token::{Pos, Token, Tokens};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct Capture {
|
||||
key: Token,
|
||||
rust: TokenTree,
|
||||
}
|
||||
|
||||
impl Capture {
|
||||
fn new(key: Token, rust: TokenTree) -> Self {
|
||||
Self { key, rust }
|
||||
}
|
||||
|
||||
/// Token string inside `chunk!`
|
||||
pub(crate) fn key(&self) -> &Token {
|
||||
&self.key
|
||||
}
|
||||
|
||||
/// As rust variable, e.g. `x`
|
||||
pub(crate) fn as_rust(&self) -> &TokenTree {
|
||||
&self.rust
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Captures(Vec<Capture>);
|
||||
|
||||
impl Captures {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self(Vec::new())
|
||||
}
|
||||
|
||||
pub(crate) fn add(&mut self, token: &Token) -> Capture {
|
||||
let tt = token.tree();
|
||||
let key = token.clone();
|
||||
|
||||
match self.0.iter().find(|arg| arg.key() == &key) {
|
||||
Some(arg) => arg.clone(),
|
||||
None => {
|
||||
let arg = Capture::new(key, tt.clone());
|
||||
self.0.push(arg.clone());
|
||||
arg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn captures(&self) -> &[Capture] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Chunk {
|
||||
source: String,
|
||||
caps: Captures,
|
||||
}
|
||||
|
||||
impl Chunk {
|
||||
pub(crate) fn new(tokens: TokenStream) -> Self {
|
||||
let tokens = Tokens::retokenize(tokens);
|
||||
|
||||
let mut source = String::new();
|
||||
let mut caps = Captures::new();
|
||||
|
||||
let mut pos: Option<Pos> = None;
|
||||
for t in tokens {
|
||||
if t.is_cap() {
|
||||
caps.add(&t);
|
||||
}
|
||||
|
||||
let (line, col) = (t.start().line, t.start().column);
|
||||
let (prev_line, prev_col) = pos
|
||||
.take()
|
||||
.map(|lc| (lc.line, lc.column))
|
||||
.unwrap_or_else(|| (line, col));
|
||||
|
||||
#[allow(clippy::comparison_chain)]
|
||||
if line > prev_line {
|
||||
source.push('\n');
|
||||
} else if line == prev_line {
|
||||
for _ in 0..col.saturating_sub(prev_col) {
|
||||
source.push(' ');
|
||||
}
|
||||
}
|
||||
source.push_str(&t.to_string());
|
||||
|
||||
pos = Some(t.end());
|
||||
}
|
||||
|
||||
Self {
|
||||
source: source.trim_end().to_string(),
|
||||
caps,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn source(&self) -> &str {
|
||||
&self.source
|
||||
}
|
||||
|
||||
pub(crate) fn captures(&self) -> &[Capture] {
|
||||
self.caps.captures()
|
||||
}
|
||||
}
|
||||
+86
-3
@@ -1,16 +1,20 @@
|
||||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::{Ident, Span};
|
||||
use quote::quote_spanned;
|
||||
use syn::{parse_macro_input, spanned::Spanned, AttributeArgs, Error, ItemFn};
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
use {
|
||||
crate::chunk::Chunk, proc_macro::TokenTree, proc_macro2::TokenStream as TokenStream2,
|
||||
proc_macro_error::proc_macro_error, quote::quote,
|
||||
};
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let args = parse_macro_input!(attr as AttributeArgs);
|
||||
let item = parse_macro_input!(item as ItemFn);
|
||||
|
||||
if args.len() > 0 {
|
||||
if !args.is_empty() {
|
||||
let err = Error::new(Span::call_site(), "the number of arguments must be zero")
|
||||
.to_compile_error();
|
||||
return err.into();
|
||||
@@ -35,3 +39,82 @@ pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
|
||||
wrapped.into()
|
||||
}
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
fn to_ident(tt: &TokenTree) -> TokenStream2 {
|
||||
let s: TokenStream = tt.clone().into();
|
||||
s.into()
|
||||
}
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
#[proc_macro]
|
||||
#[proc_macro_error]
|
||||
pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
let chunk = Chunk::new(input);
|
||||
|
||||
let source = chunk.source();
|
||||
|
||||
let caps_len = chunk.captures().len();
|
||||
let caps = chunk.captures().iter().map(|cap| {
|
||||
let cap_name = cap.as_rust().to_string();
|
||||
let cap = to_ident(cap.as_rust());
|
||||
quote! { env.raw_set(#cap_name, #cap)?; }
|
||||
});
|
||||
|
||||
let wrapped_code = quote! {{
|
||||
use ::mlua::{AsChunk, ChunkMode, Lua, Result, Value};
|
||||
use ::std::marker::PhantomData;
|
||||
use ::std::sync::Mutex;
|
||||
|
||||
fn annotate<'a, F: FnOnce(&'a Lua) -> Result<Value<'a>>>(f: F) -> F { f }
|
||||
|
||||
struct InnerChunk<'a, F: FnOnce(&'a Lua) -> Result<Value<'a>>>(Mutex<Option<F>>, PhantomData<&'a ()>);
|
||||
|
||||
impl<'lua, F> AsChunk<'lua> for InnerChunk<'lua, F>
|
||||
where
|
||||
F: FnOnce(&'lua Lua) -> Result<Value<'lua>>,
|
||||
{
|
||||
fn source(&self) -> &[u8] {
|
||||
(#source).as_bytes()
|
||||
}
|
||||
|
||||
fn env(&self, lua: &'lua Lua) -> Option<Result<Value<'lua>>> {
|
||||
if #caps_len > 0 {
|
||||
if let Ok(mut make_env) = self.0.lock() {
|
||||
if let Some(make_env) = make_env.take() {
|
||||
return Some(make_env(lua));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn mode(&self) -> Option<ChunkMode> {
|
||||
Some(ChunkMode::Text)
|
||||
}
|
||||
}
|
||||
|
||||
let make_env = annotate(move |lua: &Lua| -> Result<Value> {
|
||||
let globals = lua.globals();
|
||||
let env = lua.create_table()?;
|
||||
let meta = lua.create_table()?;
|
||||
meta.raw_set("__index", globals.clone())?;
|
||||
meta.raw_set("__newindex", globals)?;
|
||||
|
||||
// Add captured variables
|
||||
#(#caps)*
|
||||
|
||||
env.set_metatable(Some(meta));
|
||||
Ok(Value::Table(env))
|
||||
});
|
||||
|
||||
&InnerChunk(Mutex::new(Some(make_env)), PhantomData)
|
||||
}};
|
||||
|
||||
wrapped_code.into()
|
||||
}
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
mod chunk;
|
||||
#[cfg(feature = "macros")]
|
||||
mod token;
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
use std::{
|
||||
cmp::{Eq, PartialEq},
|
||||
fmt::{self, Display, Formatter},
|
||||
iter::IntoIterator,
|
||||
vec::IntoIter,
|
||||
};
|
||||
|
||||
use itertools::Itertools;
|
||||
use once_cell::sync::Lazy;
|
||||
use proc_macro::{Delimiter, Span, TokenStream, TokenTree};
|
||||
use proc_macro2::Span as Span2;
|
||||
use regex::Regex;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) struct Pos {
|
||||
pub(crate) line: usize,
|
||||
pub(crate) column: usize,
|
||||
}
|
||||
|
||||
impl Pos {
|
||||
fn new(line: usize, column: usize) -> Self {
|
||||
Self { line, column }
|
||||
}
|
||||
|
||||
fn left(&self) -> Self {
|
||||
Self {
|
||||
line: self.line,
|
||||
column: self.column.saturating_sub(1),
|
||||
}
|
||||
}
|
||||
|
||||
fn right(&self) -> Self {
|
||||
Self {
|
||||
line: self.line,
|
||||
column: self.column.saturating_add(1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn span_pos(span: &Span) -> (Pos, Pos) {
|
||||
let span2: Span2 = (*span).into();
|
||||
let start = span2.start();
|
||||
let end = span2.end();
|
||||
|
||||
// In stable, line/column information is not provided
|
||||
// and set to 0 (line is 1-indexed)
|
||||
if start.line == 0 || end.line == 0 {
|
||||
return fallback_span_pos(span);
|
||||
}
|
||||
|
||||
(
|
||||
Pos::new(start.line, start.column),
|
||||
Pos::new(end.line, end.column),
|
||||
)
|
||||
}
|
||||
|
||||
fn parse_pos(span: &Span) -> Option<(usize, usize)> {
|
||||
// Workaround to somehow retrieve location information in span in stable rust :(
|
||||
|
||||
static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"bytes\(([0-9]+)\.\.([0-9]+)\)").unwrap());
|
||||
|
||||
match RE.captures(&format!("{:?}", span)) {
|
||||
Some(caps) => match (caps.get(1), caps.get(2)) {
|
||||
(Some(start), Some(end)) => Some((
|
||||
match start.as_str().parse() {
|
||||
Ok(v) => v,
|
||||
_ => return None,
|
||||
},
|
||||
match end.as_str().parse() {
|
||||
Ok(v) => v,
|
||||
_ => return None,
|
||||
},
|
||||
)),
|
||||
_ => None,
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn fallback_span_pos(span: &Span) -> (Pos, Pos) {
|
||||
let (start, end) = match parse_pos(span) {
|
||||
Some(v) => v,
|
||||
None => proc_macro_error::abort_call_site!(
|
||||
"Cannot retrieve span information; please use nightly"
|
||||
),
|
||||
};
|
||||
(Pos::new(1, start), Pos::new(1, end))
|
||||
}
|
||||
|
||||
/// Attribute of token.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum TokenAttr {
|
||||
/// No attribute
|
||||
None,
|
||||
/// Starts with `$`
|
||||
Cap,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct Token {
|
||||
source: String,
|
||||
tree: TokenTree,
|
||||
start: Pos,
|
||||
end: Pos,
|
||||
attr: TokenAttr,
|
||||
}
|
||||
|
||||
impl PartialEq for Token {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.source == other.source && self.attr == other.attr
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Token {}
|
||||
|
||||
impl Token {
|
||||
fn new(tree: TokenTree) -> Self {
|
||||
let (start, end) = span_pos(&tree.span());
|
||||
Self {
|
||||
source: tree.to_string(),
|
||||
start,
|
||||
end,
|
||||
tree,
|
||||
attr: TokenAttr::None,
|
||||
}
|
||||
}
|
||||
|
||||
fn new_delim(source: String, tree: TokenTree, open: bool) -> Self {
|
||||
let (start, end) = span_pos(&tree.span());
|
||||
let (start, end) = if open {
|
||||
(start, start.right())
|
||||
} else {
|
||||
(end.left(), end)
|
||||
};
|
||||
|
||||
Self {
|
||||
source,
|
||||
tree,
|
||||
start,
|
||||
end,
|
||||
attr: TokenAttr::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn tree(&self) -> &TokenTree {
|
||||
&self.tree
|
||||
}
|
||||
|
||||
pub(crate) fn is_cap(&self) -> bool {
|
||||
self.attr == TokenAttr::Cap
|
||||
}
|
||||
|
||||
pub(crate) fn start(&self) -> Pos {
|
||||
self.start
|
||||
}
|
||||
|
||||
pub(crate) fn end(&self) -> Pos {
|
||||
self.end
|
||||
}
|
||||
|
||||
fn is(&self, s: &str) -> bool {
|
||||
self.source == s
|
||||
}
|
||||
|
||||
fn attr(mut self, attr: TokenAttr) -> Self {
|
||||
self.attr = attr;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Tokens(pub(crate) Vec<Token>);
|
||||
|
||||
impl Tokens {
|
||||
pub(crate) fn retokenize(tt: TokenStream) -> Tokens {
|
||||
Tokens(
|
||||
tt.into_iter()
|
||||
.map(Tokens::from)
|
||||
.flatten()
|
||||
.peekable()
|
||||
.batching(|iter| {
|
||||
// Find variable tokens
|
||||
let t = iter.next()?;
|
||||
if t.is("$") {
|
||||
// `$` + `ident` => `$ident`
|
||||
let t = iter.next().expect("$ must trail an identifier");
|
||||
Some(t.attr(TokenAttr::Cap))
|
||||
} else {
|
||||
Some(t)
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Tokens {
|
||||
type Item = Token;
|
||||
type IntoIter = IntoIter<Token>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TokenTree> for Tokens {
|
||||
fn from(tt: TokenTree) -> Self {
|
||||
let tts = match tt.clone() {
|
||||
TokenTree::Group(g) => {
|
||||
let (b, e) = match g.delimiter() {
|
||||
Delimiter::Parenthesis => ("(", ")"),
|
||||
Delimiter::Brace => ("{", "}"),
|
||||
Delimiter::Bracket => ("[", "]"),
|
||||
Delimiter::None => ("", ""),
|
||||
};
|
||||
let (b, e) = (b.into(), e.into());
|
||||
|
||||
vec![Token::new_delim(b, tt.clone(), true)]
|
||||
.into_iter()
|
||||
.chain(g.stream().into_iter().map(Tokens::from).flatten())
|
||||
.chain(vec![Token::new_delim(e, tt, false)])
|
||||
.collect()
|
||||
}
|
||||
_ => vec![Token::new(tt)],
|
||||
};
|
||||
Tokens(tts)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Token {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.source)
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::wrong_self_convention)]
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
use std::ffi::{CStr, CString};
|
||||
@@ -229,6 +231,28 @@ impl<'lua> ToLua<'lua> for Cow<'_, str> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for Box<str> {
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(&*self)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for Box<str> {
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
Ok(lua
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "Box<str>",
|
||||
message: Some("expected string or number".to_string()),
|
||||
})?
|
||||
.to_str()?
|
||||
.to_owned()
|
||||
.into_boxed_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for CString {
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(self.as_bytes())?))
|
||||
@@ -436,6 +460,26 @@ lua_convert_array! {
|
||||
30 31 32
|
||||
}
|
||||
|
||||
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Box<[T]> {
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(lua.create_sequence_from(self.into_vec())?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Box<[T]> {
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
if let Value::Table(table) = value {
|
||||
table.sequence_values().collect()
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "Box<[T]>",
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Vec<T> {
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(lua.create_sequence_from(self)?))
|
||||
|
||||
+35
-12
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::wrong_self_convention)]
|
||||
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
use std::io::Error as IoError;
|
||||
@@ -9,6 +11,7 @@ use std::sync::Arc;
|
||||
|
||||
/// Error type returned by `mlua` methods.
|
||||
#[derive(Debug, Clone)]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
/// Syntax error while parsing Lua source code.
|
||||
SyntaxError {
|
||||
@@ -87,7 +90,7 @@ pub enum Error {
|
||||
},
|
||||
/// [`Thread::resume`] was called on an inactive coroutine.
|
||||
///
|
||||
/// A coroutine is inactive if its main function has returned or if an error has occured inside
|
||||
/// A coroutine is inactive if its main function has returned or if an error has occurred inside
|
||||
/// the coroutine.
|
||||
///
|
||||
/// [`Thread::status`] can be used to check if the coroutine can be resumed without causing this
|
||||
@@ -130,6 +133,18 @@ pub enum Error {
|
||||
/// [`AnyUserData`]: struct.AnyUserData.html
|
||||
/// [`UserData`]: trait.UserData.html
|
||||
UserDataBorrowMutError,
|
||||
/// A [`MetaMethod`] operation is restricted (typically for `__gc` or `__metatable`).
|
||||
///
|
||||
/// [`MetaMethod`]: enum.MetaMethod.html
|
||||
MetaMethodRestricted(StdString),
|
||||
/// A [`MetaMethod`] (eg. `__index` or `__newindex`) has invalid type.
|
||||
///
|
||||
/// [`MetaMethod`]: enum.MetaMethod.html
|
||||
MetaMethodTypeError {
|
||||
method: StdString,
|
||||
type_name: &'static str,
|
||||
message: Option<StdString>,
|
||||
},
|
||||
/// A `RegistryKey` produced from a different Lua state was used.
|
||||
MismatchedRegistryKey,
|
||||
/// A Rust callback returned `Err`, raising the contained `Error` as a Lua error.
|
||||
@@ -139,6 +154,11 @@ pub enum Error {
|
||||
/// Original error returned by the Rust code.
|
||||
cause: Arc<Error>,
|
||||
},
|
||||
/// A Rust panic that was previously resumed, returned again.
|
||||
///
|
||||
/// This error can occur only when a Rust panic resumed previously was recovered
|
||||
/// and returned again.
|
||||
PreviouslyResumedPanic,
|
||||
/// Serialization error.
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
@@ -197,22 +217,14 @@ impl fmt::Display for Error {
|
||||
fmt,
|
||||
"too many arguments to Function::bind"
|
||||
),
|
||||
Error::ToLuaConversionError {
|
||||
from,
|
||||
to,
|
||||
ref message,
|
||||
} => {
|
||||
Error::ToLuaConversionError { from, to, ref message } => {
|
||||
write!(fmt, "error converting {} to Lua {}", from, to)?;
|
||||
match *message {
|
||||
None => Ok(()),
|
||||
Some(ref message) => write!(fmt, " ({})", message),
|
||||
}
|
||||
}
|
||||
Error::FromLuaConversionError {
|
||||
from,
|
||||
to,
|
||||
ref message,
|
||||
} => {
|
||||
Error::FromLuaConversionError { from, to, ref message } => {
|
||||
write!(fmt, "error converting Lua {} to {}", from, to)?;
|
||||
match *message {
|
||||
None => Ok(()),
|
||||
@@ -224,11 +236,22 @@ impl fmt::Display for Error {
|
||||
Error::UserDataDestructed => write!(fmt, "userdata has been destructed"),
|
||||
Error::UserDataBorrowError => write!(fmt, "userdata already mutably borrowed"),
|
||||
Error::UserDataBorrowMutError => write!(fmt, "userdata already borrowed"),
|
||||
Error::MetaMethodRestricted(ref method) => write!(fmt, "metamethod {} is restricted", method),
|
||||
Error::MetaMethodTypeError { ref method, type_name, ref message } => {
|
||||
write!(fmt, "metamethod {} has unsupported type {}", method, type_name)?;
|
||||
match *message {
|
||||
None => Ok(()),
|
||||
Some(ref message) => write!(fmt, " ({})", message),
|
||||
}
|
||||
}
|
||||
Error::MismatchedRegistryKey => {
|
||||
write!(fmt, "RegistryKey used from different Lua state")
|
||||
}
|
||||
Error::CallbackError { ref traceback, .. } => {
|
||||
write!(fmt, "callback error: {}", traceback)
|
||||
write!(fmt, "callback error\n{}", traceback)
|
||||
}
|
||||
Error::PreviouslyResumedPanic => {
|
||||
write!(fmt, "previously resumed panic returned again")
|
||||
}
|
||||
#[cfg(feature = "serialize")]
|
||||
Error::SerializeError(ref err) => {
|
||||
|
||||
+39
-2
@@ -228,6 +228,7 @@ pub fn lua_upvalueindex(i: c_int) -> c_int {
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_absindex(L: *mut lua_State, mut idx: c_int) -> c_int {
|
||||
if idx < 0 && idx > lua::LUA_REGISTRYINDEX {
|
||||
idx += lua_gettop(L) + 1;
|
||||
@@ -250,6 +251,7 @@ end
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
pub unsafe fn lua_arith(L: *mut lua_State, op: c_int) {
|
||||
#[allow(clippy::manual_range_contains)]
|
||||
if op < LUA_OPADD || op > LUA_OPUNM {
|
||||
luaL_error(L, cstr!("invalid 'op' argument for lua_arith"));
|
||||
}
|
||||
@@ -278,6 +280,7 @@ pub unsafe fn lua_rotate(L: *mut lua_State, mut idx: c_int, mut n: c_int) {
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int) {
|
||||
let abs_to = lua_absindex(L, toidx);
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||
@@ -285,6 +288,7 @@ pub unsafe fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int) {
|
||||
lua_replace(L, abs_to);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
|
||||
if lua_type(L, idx) == lua::LUA_TNUMBER {
|
||||
let n = lua_tonumber(L, idx);
|
||||
@@ -297,6 +301,7 @@ pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tonumberx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Number {
|
||||
let n = lua_tonumber(L, i);
|
||||
if !isnum.is_null() {
|
||||
@@ -311,6 +316,7 @@ pub unsafe fn lua_tonumberx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> l
|
||||
|
||||
// Implemented for Lua 5.2 as well
|
||||
// See https://github.com/keplerproject/lua-compat-5.3/issues/40
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tointegerx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Integer {
|
||||
let mut ok = 0;
|
||||
let n = lua_tonumberx(L, i, &mut ok);
|
||||
@@ -328,11 +334,13 @@ pub unsafe fn lua_tointegerx(L: *mut lua_State, i: c_int, isnum: *mut c_int) ->
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize {
|
||||
lua_objlen(L, idx)
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_compare(L: *mut lua_State, mut idx1: c_int, mut idx2: c_int, op: c_int) -> c_int {
|
||||
match op {
|
||||
lua::LUA_OPEQ => lua_equal(L, idx1, idx2),
|
||||
@@ -353,6 +361,7 @@ pub unsafe fn lua_compare(L: *mut lua_State, mut idx1: c_int, mut idx2: c_int, o
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char {
|
||||
if l == 0 {
|
||||
lua_pushlstring_old(L, cstr!(""), 0);
|
||||
@@ -363,6 +372,7 @@ pub unsafe fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) ->
|
||||
}
|
||||
|
||||
#[cfg(feature = "lua52")]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char {
|
||||
if l == 0 {
|
||||
lua_pushlstring_old(L, cstr!(""), 0)
|
||||
@@ -372,27 +382,32 @@ pub unsafe fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) ->
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushstring(L: *mut lua_State, s: *const c_char) -> *const c_char {
|
||||
lua_pushstring_old(L, s);
|
||||
lua_tostring(L, -1)
|
||||
}
|
||||
|
||||
#[cfg(feature = "lua52")]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_getglobal(L: *mut lua_State, var: *const c_char) -> c_int {
|
||||
lua_getglobal_old(L, var);
|
||||
lua_type(L, -1)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_gettable(L: *mut lua_State, idx: c_int) -> c_int {
|
||||
lua_gettable_old(L, idx);
|
||||
lua_type(L, -1)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_getfield(L: *mut lua_State, idx: c_int, k: *const c_char) -> c_int {
|
||||
lua_getfield_old(L, idx, k);
|
||||
lua_type(L, -1)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_geti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) -> c_int {
|
||||
idx = lua_absindex(L, idx);
|
||||
lua_pushinteger(L, n);
|
||||
@@ -401,18 +416,21 @@ pub unsafe fn lua_geti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) -> c_i
|
||||
}
|
||||
|
||||
// A new version which returns c_int
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int {
|
||||
lua_rawget_old(L, idx);
|
||||
lua_type(L, -1)
|
||||
}
|
||||
|
||||
// A new version which returns c_int
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int {
|
||||
lua_rawgeti_old(L, idx, n);
|
||||
lua_type(L, -1)
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int {
|
||||
let abs_i = lua_absindex(L, idx);
|
||||
lua_pushlightuserdata(L, p as *mut c_void);
|
||||
@@ -421,23 +439,27 @@ pub unsafe fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_
|
||||
}
|
||||
|
||||
#[cfg(feature = "lua52")]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int {
|
||||
lua_rawgetp_old(L, idx, p);
|
||||
lua_type(L, -1)
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int {
|
||||
lua_getfenv(L, idx);
|
||||
lua_type(L, -1)
|
||||
}
|
||||
|
||||
#[cfg(feature = "lua52")]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int {
|
||||
lua_getuservalue_old(L, idx);
|
||||
lua_type(L, -1)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_seti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) {
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
|
||||
idx = lua_absindex(L, idx);
|
||||
@@ -447,6 +469,7 @@ pub unsafe fn lua_seti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) {
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void) {
|
||||
let abs_i = lua_absindex(L, idx);
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||
@@ -456,11 +479,13 @@ pub unsafe fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void) {
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_setuservalue(L: *mut lua_State, idx: c_int) {
|
||||
luaL_checktype(L, -1, lua::LUA_TTABLE);
|
||||
lua_setfenv(L, idx);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_dump(
|
||||
L: *mut lua_State,
|
||||
writer: lua_Writer,
|
||||
@@ -471,11 +496,13 @@ pub unsafe fn lua_dump(
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_resume(L: *mut lua_State, _from: *mut lua_State, narg: c_int) -> c_int {
|
||||
lua_resume_old(L, narg)
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_len(L: *mut lua_State, idx: c_int) {
|
||||
match lua_type(L, idx) {
|
||||
lua::LUA_TSTRING => {
|
||||
@@ -497,6 +524,7 @@ pub unsafe fn lua_len(L: *mut lua_State, idx: c_int) {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_stringtonumber(L: *mut lua_State, s: *const c_char) -> usize {
|
||||
use std::str::FromStr;
|
||||
|
||||
@@ -510,6 +538,7 @@ pub unsafe fn lua_stringtonumber(L: *mut lua_State, s: *const c_char) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
#[allow(clippy::branches_sharing_code)]
|
||||
pub unsafe fn lua_getextraspace(L: *mut lua_State) -> *mut c_void {
|
||||
use super::glue::LUA_EXTRASPACE;
|
||||
|
||||
@@ -574,6 +603,7 @@ pub unsafe fn lua_pushglobaltable(L: *mut lua_State) {
|
||||
//
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char) {
|
||||
if lua_checkstack(L, sz + lua::LUA_MINSTACK) == 0 {
|
||||
if !msg.is_null() {
|
||||
@@ -589,6 +619,7 @@ pub unsafe fn luaL_checkversion(_L: *mut lua_State) {
|
||||
// Void
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int {
|
||||
if luaL_getmetafield_old(L, obj, e) != 0 {
|
||||
lua_type(L, -1)
|
||||
@@ -597,6 +628,7 @@ pub unsafe fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_int {
|
||||
if luaL_newmetatable_old(L, tname) != 0 {
|
||||
lua_pushstring(L, tname);
|
||||
@@ -608,6 +640,7 @@ pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_in
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_loadbufferx(
|
||||
L: *mut lua_State,
|
||||
buff: *const c_char,
|
||||
@@ -627,6 +660,7 @@ pub unsafe fn luaL_loadbufferx(
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
|
||||
let mut isnum = 0;
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||
@@ -646,7 +680,7 @@ pub unsafe fn luaL_traceback(
|
||||
msg: *const c_char,
|
||||
mut level: c_int,
|
||||
) {
|
||||
let mut ar: lua_Debug = std::mem::zeroed();
|
||||
let mut ar: lua_Debug = mem::zeroed();
|
||||
let top = lua_gettop(L);
|
||||
let numlevels = compat53_countlevels(L1);
|
||||
let mark = if numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2 {
|
||||
@@ -667,7 +701,7 @@ pub unsafe fn luaL_traceback(
|
||||
level = numlevels - COMPAT53_LEVELS2; // and skip to last ones
|
||||
} else {
|
||||
lua_getinfo(L1, cstr!("Slnt"), &mut ar);
|
||||
lua_pushfstring(L, cstr!("\n\t%s:"), cstr!("ok") /*ar.short_src*/);
|
||||
lua_pushfstring(L, cstr!("\n\t%s:"), ar.short_src.as_ptr());
|
||||
if ar.currentline > 0 {
|
||||
lua_pushfstring(L, cstr!("%d:"), ar.currentline);
|
||||
}
|
||||
@@ -716,6 +750,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char) {
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||
luaL_getmetatable(L, tname);
|
||||
@@ -723,6 +758,7 @@ pub unsafe fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char) {
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_testudata(L: *mut lua_State, i: c_int, tname: *const c_char) -> *mut c_void {
|
||||
let mut p = lua_touserdata(L, i);
|
||||
luaL_checkstack(L, 2, cstr!("not enough stack slots"));
|
||||
@@ -740,6 +776,7 @@ pub unsafe fn luaL_testudata(L: *mut lua_State, i: c_int, tname: *const c_char)
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_setfuncs(L: *mut lua_State, mut l: *const luaL_Reg, nup: c_int) {
|
||||
luaL_checkstack(L, nup + 1, cstr!("too many upvalues"));
|
||||
while !(*l).name.is_null() {
|
||||
|
||||
+18
-1
@@ -156,8 +156,13 @@ extern "C" {
|
||||
pub fn lua_newstate(f: lua_Alloc, ud: *mut c_void) -> *mut lua_State;
|
||||
pub fn lua_close(L: *mut lua_State);
|
||||
pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State;
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
pub fn lua_resetthread(L: *mut lua_State) -> c_int;
|
||||
#[link_name = "lua_resetthread"]
|
||||
pub fn lua_resetthread_54(L: *mut lua_State) -> c_int;
|
||||
#[cfg(all(feature = "luajit", feature = "vendored"))]
|
||||
#[link_name = "lua_resetthread"]
|
||||
pub fn lua_resetthread_jit(L: *mut lua_State, th: *mut lua_State);
|
||||
|
||||
pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction;
|
||||
|
||||
@@ -216,6 +221,17 @@ extern "C" {
|
||||
pub fn lua_topointer(L: *mut lua_State, idx: c_int) -> *const c_void;
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored")))]
|
||||
pub unsafe fn lua_resetthread(_L: *mut lua_State, th: *mut lua_State) -> c_int {
|
||||
#[cfg(all(feature = "luajit", feature = "vendored"))]
|
||||
{
|
||||
lua_resetthread_jit(_L, th);
|
||||
LUA_OK
|
||||
}
|
||||
#[cfg(feature = "lua54")]
|
||||
lua_resetthread_54(th)
|
||||
}
|
||||
|
||||
// Comparison and arithmetic functions
|
||||
pub const LUA_OPADD: c_int = 0;
|
||||
pub const LUA_OPSUB: c_int = 1;
|
||||
@@ -531,6 +547,7 @@ pub unsafe fn lua_yield(L: *mut lua_State, n: c_int) -> c_int {
|
||||
feature = "lua51",
|
||||
feature = "luajit"
|
||||
))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_resume(
|
||||
L: *mut lua_State,
|
||||
from: *mut lua_State,
|
||||
|
||||
+11
-4
@@ -160,8 +160,8 @@ pub use self::lua::{
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
pub use self::lua::{
|
||||
lua_getiuservalue, lua_newuserdatauv, lua_resetthread, lua_setcstacklimit, lua_setiuservalue,
|
||||
lua_setwarnf, lua_toclose, lua_warning,
|
||||
lua_getiuservalue, lua_newuserdatauv, lua_setcstacklimit, lua_setiuservalue, lua_setwarnf,
|
||||
lua_toclose, lua_warning,
|
||||
};
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
@@ -170,6 +170,9 @@ pub use self::lua::{lua_isyieldable, lua_version};
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
pub use self::lua::{lua_callk, lua_pcallk, lua_upvalueid, lua_upvaluejoin, lua_yieldk};
|
||||
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored")))]
|
||||
pub use self::lua::lua_resetthread;
|
||||
|
||||
// auxiliary library types
|
||||
pub use self::lauxlib::luaL_Reg;
|
||||
|
||||
@@ -253,16 +256,18 @@ pub use self::lualib::{LUA_FFILIBNAME, LUA_JITLIBNAME};
|
||||
// Not actually defined in lua.h / luaconf.h
|
||||
pub const LUA_MAX_UPVALUES: c_int = 255;
|
||||
|
||||
// Copied from https://github.com/rust-lang/rust/blob/master/src/libstd/sys_common/alloc.rs
|
||||
// Copied from https://github.com/rust-lang/rust/blob/master/library/std/src/sys/common/alloc.rs
|
||||
#[cfg(all(any(
|
||||
target_arch = "x86",
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "powerpc64",
|
||||
target_arch = "sparc",
|
||||
target_arch = "asmjs",
|
||||
target_arch = "wasm32",
|
||||
target_arch = "hexagon"
|
||||
target_arch = "hexagon",
|
||||
target_arch = "riscv32"
|
||||
)))]
|
||||
pub const SYS_MIN_ALIGN: usize = 8;
|
||||
#[cfg(all(any(
|
||||
@@ -288,3 +293,5 @@ mod lauxlib;
|
||||
mod lua;
|
||||
mod luaconf;
|
||||
mod lualib;
|
||||
|
||||
pub mod safe;
|
||||
|
||||
+278
@@ -0,0 +1,278 @@
|
||||
use std::ffi::CString;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::util::protect_lua;
|
||||
|
||||
use super::lua::{lua_CFunction, lua_Debug, lua_Integer, lua_State};
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "MLUA_WRAPPED_ERROR_SIZE"]
|
||||
pub static mut WRAPPED_ERROR_SIZE: usize;
|
||||
#[link_name = "MLUA_WRAPPED_PANIC_SIZE"]
|
||||
pub static mut WRAPPED_PANIC_SIZE: usize;
|
||||
#[link_name = "MLUA_WRAPPED_ERROR_KEY"]
|
||||
pub static mut WRAPPED_ERROR_KEY: *const c_void;
|
||||
#[link_name = "MLUA_WRAPPED_PANIC_KEY"]
|
||||
pub static mut WRAPPED_PANIC_KEY: *const c_void;
|
||||
|
||||
pub fn lua_call_mlua_hook_proc(L: *mut lua_State, ar: *mut lua_Debug);
|
||||
|
||||
pub fn meta_index_impl(state: *mut lua_State) -> c_int;
|
||||
pub fn meta_newindex_impl(state: *mut lua_State) -> c_int;
|
||||
pub fn bind_call_impl(state: *mut lua_State) -> c_int;
|
||||
pub fn error_traceback(state: *mut lua_State) -> c_int;
|
||||
pub fn lua_nopanic_pcall(state: *mut lua_State) -> c_int;
|
||||
pub fn lua_nopanic_xpcall(state: *mut lua_State) -> c_int;
|
||||
|
||||
fn lua_gc_s(L: *mut lua_State) -> c_int;
|
||||
fn luaL_ref_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_pushlstring_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_tolstring_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_newthread_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_newuserdata_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_newwrappederror_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_pushcclosure_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_pushrclosure_s(L: *mut lua_State) -> c_int;
|
||||
fn luaL_requiref_s(L: *mut lua_State) -> c_int;
|
||||
fn error_traceback_s(L: *mut lua_State) -> c_int;
|
||||
|
||||
fn lua_newtable_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_createtable_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_gettable_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_settable_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_geti_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_rawset_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_rawseti_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_rawsetp_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_rawsetfield_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_rawinsert_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_rawremove_s(L: *mut lua_State) -> c_int;
|
||||
fn luaL_len_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_next_s(L: *mut lua_State) -> c_int;
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct StringArg {
|
||||
data: *const c_char,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
//
|
||||
// Common functions
|
||||
//
|
||||
|
||||
// Uses 4 stack spaces
|
||||
pub unsafe fn lua_gc(state: *mut lua_State, what: c_int, data: c_int) -> Result<c_int> {
|
||||
super::lua_pushinteger(state, what as lua_Integer);
|
||||
super::lua_pushinteger(state, data as lua_Integer);
|
||||
protect_lua(state, 2, lua_gc_s)?;
|
||||
let ret = super::lua_tointeger(state, -1) as c_int;
|
||||
super::lua_pop(state, 1);
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces
|
||||
pub unsafe fn luaL_ref(state: *mut lua_State, table: c_int) -> Result<c_int> {
|
||||
super::lua_pushvalue(state, table);
|
||||
super::lua_rotate(state, -2, 1);
|
||||
protect_lua(state, 2, luaL_ref_s)?;
|
||||
let ret = super::lua_tointeger(state, -1) as c_int;
|
||||
super::lua_pop(state, 1);
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces
|
||||
pub unsafe fn lua_pushstring<S: AsRef<[u8]> + ?Sized>(state: *mut lua_State, s: &S) -> Result<()> {
|
||||
let s = s.as_ref();
|
||||
let s = StringArg {
|
||||
data: s.as_ptr() as *const c_char,
|
||||
len: s.len(),
|
||||
};
|
||||
super::lua_pushlightuserdata(state, &s as *const StringArg as *mut c_void);
|
||||
protect_lua(state, 1, lua_pushlstring_s)
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces
|
||||
pub unsafe fn lua_tolstring(
|
||||
state: *mut lua_State,
|
||||
index: c_int,
|
||||
len: *mut usize,
|
||||
) -> Result<*const c_char> {
|
||||
let index = super::lua_absindex(state, index);
|
||||
super::lua_pushvalue(state, index);
|
||||
super::lua_pushlightuserdata(state, len as *mut c_void);
|
||||
protect_lua(state, 2, lua_tolstring_s)?;
|
||||
let s = super::lua_touserdata(state, -1);
|
||||
super::lua_pop(state, 1);
|
||||
super::lua_replace(state, index);
|
||||
Ok(s as *const c_char)
|
||||
}
|
||||
|
||||
// Uses 2 stack spaces
|
||||
pub unsafe fn lua_newthread(state: *mut lua_State) -> Result<*mut lua_State> {
|
||||
protect_lua(state, 0, lua_newthread_s)?;
|
||||
Ok(super::lua_tothread(state, -1))
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces
|
||||
pub unsafe fn lua_newuserdata(state: *mut lua_State, size: usize) -> Result<*mut c_void> {
|
||||
super::lua_pushinteger(state, size as lua_Integer);
|
||||
protect_lua(state, 1, lua_newuserdata_s)?;
|
||||
Ok(super::lua_touserdata(state, -1))
|
||||
}
|
||||
|
||||
// Uses 2 stack spaces
|
||||
pub unsafe fn lua_newwrappederror(state: *mut lua_State) -> Result<*mut c_void> {
|
||||
protect_lua(state, 0, lua_newwrappederror_s)?;
|
||||
Ok(super::lua_touserdata(state, -1))
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces
|
||||
pub unsafe fn lua_pushcclosure(state: *mut lua_State, f: lua_CFunction, n: c_int) -> Result<()> {
|
||||
super::lua_pushlightuserdata(state, f as *mut c_void);
|
||||
protect_lua(state, n + 1, lua_pushcclosure_s)
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces
|
||||
pub unsafe fn lua_pushrclosure(state: *mut lua_State, f: lua_CFunction, n: c_int) -> Result<()> {
|
||||
super::lua_pushlightuserdata(state, f as *mut c_void);
|
||||
if n > 0 {
|
||||
super::lua_rotate(state, -n - 1, 1);
|
||||
}
|
||||
protect_lua(state, n + 1, lua_pushrclosure_s)
|
||||
}
|
||||
|
||||
// Uses 5 stack spaces
|
||||
pub unsafe fn luaL_requiref<S: AsRef<[u8]> + ?Sized>(
|
||||
state: *mut lua_State,
|
||||
modname: &S,
|
||||
openf: lua_CFunction,
|
||||
glb: c_int,
|
||||
) -> Result<()> {
|
||||
let modname = mlua_expect!(CString::new(modname.as_ref()), "modname contains nil bytes");
|
||||
super::lua_pushlightuserdata(state, modname.as_ptr() as *mut c_void);
|
||||
super::lua_pushlightuserdata(state, openf as *mut c_void);
|
||||
super::lua_pushinteger(state, glb as lua_Integer);
|
||||
protect_lua(state, 3, luaL_requiref_s)
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces
|
||||
pub unsafe fn error_traceback2(state: *mut lua_State, state2: *mut lua_State) -> Result<()> {
|
||||
mlua_assert!(
|
||||
state != state2,
|
||||
"error_traceback2 must be used with two different states"
|
||||
);
|
||||
super::lua_pushlightuserdata(state, state2);
|
||||
protect_lua(state, 1, error_traceback_s)
|
||||
}
|
||||
|
||||
//
|
||||
// Table functions
|
||||
//
|
||||
|
||||
// Uses 2 stack spaces
|
||||
pub unsafe fn lua_newtable(state: *mut lua_State) -> Result<()> {
|
||||
protect_lua(state, 0, lua_newtable_s)
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces
|
||||
pub unsafe fn lua_createtable(state: *mut lua_State, narr: c_int, nrec: c_int) -> Result<()> {
|
||||
super::lua_pushinteger(state, narr as lua_Integer);
|
||||
super::lua_pushinteger(state, nrec as lua_Integer);
|
||||
protect_lua(state, 2, lua_createtable_s)
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces
|
||||
pub unsafe fn lua_gettable(state: *mut lua_State, table: c_int) -> Result<()> {
|
||||
super::lua_pushvalue(state, table);
|
||||
super::lua_rotate(state, -2, 1);
|
||||
protect_lua(state, 2, lua_gettable_s)
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces
|
||||
pub unsafe fn lua_settable(state: *mut lua_State, table: c_int) -> Result<()> {
|
||||
super::lua_pushvalue(state, table);
|
||||
super::lua_rotate(state, -3, 1);
|
||||
protect_lua(state, 3, lua_settable_s)
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces
|
||||
pub unsafe fn lua_geti(state: *mut lua_State, table: c_int, i: lua_Integer) -> Result<c_int> {
|
||||
super::lua_pushvalue(state, table);
|
||||
super::lua_pushinteger(state, i);
|
||||
protect_lua(state, 2, lua_geti_s).map(|_| super::lua_type(state, -1))
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces
|
||||
pub unsafe fn lua_rawset(state: *mut lua_State, table: c_int) -> Result<()> {
|
||||
super::lua_pushvalue(state, table);
|
||||
super::lua_rotate(state, -3, 1);
|
||||
protect_lua(state, 3, lua_rawset_s)
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces
|
||||
pub unsafe fn lua_rawseti(state: *mut lua_State, table: c_int, i: lua_Integer) -> Result<()> {
|
||||
super::lua_pushvalue(state, table);
|
||||
super::lua_rotate(state, -2, 1);
|
||||
super::lua_pushinteger(state, i);
|
||||
protect_lua(state, 3, lua_rawseti_s)
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces
|
||||
pub unsafe fn lua_rawsetp(state: *mut lua_State, table: c_int, ptr: *const c_void) -> Result<()> {
|
||||
super::lua_pushvalue(state, table);
|
||||
super::lua_rotate(state, -2, 1);
|
||||
super::lua_pushlightuserdata(state, ptr as *mut c_void);
|
||||
protect_lua(state, 3, lua_rawsetp_s)
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces
|
||||
pub unsafe fn lua_rawsetfield<S>(state: *mut lua_State, table: c_int, field: &S) -> Result<()>
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
let field = field.as_ref();
|
||||
let s = StringArg {
|
||||
data: field.as_ptr() as *const c_char,
|
||||
len: field.len(),
|
||||
};
|
||||
super::lua_pushvalue(state, table);
|
||||
super::lua_pushlightuserdata(state, &s as *const StringArg as *mut c_void);
|
||||
super::lua_rotate(state, -3, 2);
|
||||
protect_lua(state, 3, lua_rawsetfield_s)
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces
|
||||
pub unsafe fn lua_rawinsert(state: *mut lua_State, table: c_int, i: lua_Integer) -> Result<()> {
|
||||
super::lua_pushvalue(state, table);
|
||||
super::lua_rotate(state, -2, 1);
|
||||
super::lua_pushinteger(state, i);
|
||||
protect_lua(state, 3, lua_rawinsert_s)
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces
|
||||
pub unsafe fn lua_rawremove(state: *mut lua_State, table: c_int, i: lua_Integer) -> Result<()> {
|
||||
super::lua_pushvalue(state, table);
|
||||
super::lua_pushinteger(state, i);
|
||||
protect_lua(state, 2, lua_rawremove_s)
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces
|
||||
pub unsafe fn luaL_len(state: *mut lua_State, table: c_int) -> Result<lua_Integer> {
|
||||
super::lua_pushvalue(state, table);
|
||||
protect_lua(state, 1, luaL_len_s)?;
|
||||
let ret = super::lua_tointeger(state, -1);
|
||||
super::lua_pop(state, 1);
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces
|
||||
pub unsafe fn lua_next(state: *mut lua_State, table: c_int) -> Result<lua_Integer> {
|
||||
super::lua_pushvalue(state, table);
|
||||
super::lua_rotate(state, -2, 1);
|
||||
protect_lua(state, 2, lua_next_s)?;
|
||||
let ret = super::lua_tointeger(state, -1);
|
||||
super::lua_pop(state, 1);
|
||||
Ok(ret)
|
||||
}
|
||||
@@ -0,0 +1,953 @@
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include "compat-5.3.h"
|
||||
|
||||
/* don't compile it again if it already is included via compat53.h */
|
||||
#ifndef COMPAT53_C_
|
||||
#define COMPAT53_C_
|
||||
|
||||
|
||||
|
||||
/* definitions for Lua 5.1 only */
|
||||
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
|
||||
|
||||
#ifndef COMPAT53_FOPEN_NO_LOCK
|
||||
# if defined(_MSC_VER)
|
||||
# define COMPAT53_FOPEN_NO_LOCK 1
|
||||
# else /* otherwise */
|
||||
# define COMPAT53_FOPEN_NO_LOCK 0
|
||||
# endif /* VC++ only so far */
|
||||
#endif /* No-lock fopen_s usage if possible */
|
||||
|
||||
#if defined(_MSC_VER) && COMPAT53_FOPEN_NO_LOCK
|
||||
# include <share.h>
|
||||
#endif /* VC++ _fsopen for share-allowed file read */
|
||||
|
||||
#ifndef COMPAT53_HAVE_STRERROR_R
|
||||
# if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || \
|
||||
(defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600) || \
|
||||
defined(__APPLE__)
|
||||
# define COMPAT53_HAVE_STRERROR_R 1
|
||||
# else /* none of the defines matched: define to 0 */
|
||||
# define COMPAT53_HAVE_STRERROR_R 0
|
||||
# endif /* have strerror_r of some form */
|
||||
#endif /* strerror_r */
|
||||
|
||||
#ifndef COMPAT53_HAVE_STRERROR_S
|
||||
# if defined(_MSC_VER) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && \
|
||||
defined(__STDC_LIB_EXT1__) && __STDC_LIB_EXT1__)
|
||||
# define COMPAT53_HAVE_STRERROR_S 1
|
||||
# else /* not VC++ or C11 */
|
||||
# define COMPAT53_HAVE_STRERROR_S 0
|
||||
# endif /* strerror_s from VC++ or C11 */
|
||||
#endif /* strerror_s */
|
||||
|
||||
#ifndef COMPAT53_LUA_FILE_BUFFER_SIZE
|
||||
# define COMPAT53_LUA_FILE_BUFFER_SIZE 4096
|
||||
#endif /* Lua File Buffer Size */
|
||||
|
||||
|
||||
static char* compat53_strerror (int en, char* buff, size_t sz) {
|
||||
#if COMPAT53_HAVE_STRERROR_R
|
||||
/* use strerror_r here, because it's available on these specific platforms */
|
||||
if (sz > 0) {
|
||||
buff[0] = '\0';
|
||||
/* we don't care whether the GNU version or the XSI version is used: */
|
||||
if (strerror_r(en, buff, sz)) {
|
||||
/* Yes, we really DO want to ignore the return value!
|
||||
* GCC makes that extra hard, not even a (void) cast will do. */
|
||||
}
|
||||
if (buff[0] == '\0') {
|
||||
/* Buffer is unchanged, so we probably have called GNU strerror_r which
|
||||
* returned a static constant string. Chances are that strerror will
|
||||
* return the same static constant string and therefore be thread-safe. */
|
||||
return strerror(en);
|
||||
}
|
||||
}
|
||||
return buff; /* sz is 0 *or* strerror_r wrote into the buffer */
|
||||
#elif COMPAT53_HAVE_STRERROR_S
|
||||
/* for MSVC and other C11 implementations, use strerror_s since it's
|
||||
* provided by default by the libraries */
|
||||
strerror_s(buff, sz, en);
|
||||
return buff;
|
||||
#else
|
||||
/* fallback, but strerror is not guaranteed to be threadsafe due to modifying
|
||||
* errno itself and some impls not locking a static buffer for it ... but most
|
||||
* known systems have threadsafe errno: this might only change if the locale
|
||||
* is changed out from under someone while this function is being called */
|
||||
(void)buff;
|
||||
(void)sz;
|
||||
return strerror(en);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API int lua_absindex (lua_State *L, int i) {
|
||||
if (i < 0 && i > LUA_REGISTRYINDEX)
|
||||
i += lua_gettop(L) + 1;
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
static void compat53_call_lua (lua_State *L, char const code[], size_t len,
|
||||
int nargs, int nret) {
|
||||
lua_rawgetp(L, LUA_REGISTRYINDEX, (void*)code);
|
||||
if (lua_type(L, -1) != LUA_TFUNCTION) {
|
||||
lua_pop(L, 1);
|
||||
if (luaL_loadbuffer(L, code, len, "=none"))
|
||||
lua_error(L);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_rawsetp(L, LUA_REGISTRYINDEX, (void*)code);
|
||||
}
|
||||
lua_insert(L, -nargs-1);
|
||||
lua_call(L, nargs, nret);
|
||||
}
|
||||
|
||||
|
||||
static const char compat53_arith_code[] =
|
||||
"local op,a,b=...\n"
|
||||
"if op==0 then return a+b\n"
|
||||
"elseif op==1 then return a-b\n"
|
||||
"elseif op==2 then return a*b\n"
|
||||
"elseif op==3 then return a/b\n"
|
||||
"elseif op==4 then return a%b\n"
|
||||
"elseif op==5 then return a^b\n"
|
||||
"elseif op==6 then return -a\n"
|
||||
"end\n";
|
||||
|
||||
COMPAT53_API void lua_arith (lua_State *L, int op) {
|
||||
if (op < LUA_OPADD || op > LUA_OPUNM)
|
||||
luaL_error(L, "invalid 'op' argument for lua_arith");
|
||||
luaL_checkstack(L, 5, "not enough stack slots");
|
||||
if (op == LUA_OPUNM)
|
||||
lua_pushvalue(L, -1);
|
||||
lua_pushnumber(L, op);
|
||||
lua_insert(L, -3);
|
||||
compat53_call_lua(L, compat53_arith_code,
|
||||
sizeof(compat53_arith_code)-1, 3, 1);
|
||||
}
|
||||
|
||||
|
||||
static const char compat53_compare_code[] =
|
||||
"local a,b=...\n"
|
||||
"return a<=b\n";
|
||||
|
||||
COMPAT53_API int lua_compare (lua_State *L, int idx1, int idx2, int op) {
|
||||
int result = 0;
|
||||
switch (op) {
|
||||
case LUA_OPEQ:
|
||||
return lua_equal(L, idx1, idx2);
|
||||
case LUA_OPLT:
|
||||
return lua_lessthan(L, idx1, idx2);
|
||||
case LUA_OPLE:
|
||||
luaL_checkstack(L, 5, "not enough stack slots");
|
||||
idx1 = lua_absindex(L, idx1);
|
||||
idx2 = lua_absindex(L, idx2);
|
||||
lua_pushvalue(L, idx1);
|
||||
lua_pushvalue(L, idx2);
|
||||
compat53_call_lua(L, compat53_compare_code,
|
||||
sizeof(compat53_compare_code)-1, 2, 1);
|
||||
result = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return result;
|
||||
default:
|
||||
luaL_error(L, "invalid 'op' argument for lua_compare");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void lua_copy (lua_State *L, int from, int to) {
|
||||
int abs_to = lua_absindex(L, to);
|
||||
luaL_checkstack(L, 1, "not enough stack slots");
|
||||
lua_pushvalue(L, from);
|
||||
lua_replace(L, abs_to);
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void lua_len (lua_State *L, int i) {
|
||||
switch (lua_type(L, i)) {
|
||||
case LUA_TSTRING:
|
||||
lua_pushnumber(L, (lua_Number)lua_objlen(L, i));
|
||||
break;
|
||||
case LUA_TTABLE:
|
||||
if (!luaL_callmeta(L, i, "__len"))
|
||||
lua_pushnumber(L, (lua_Number)lua_objlen(L, i));
|
||||
break;
|
||||
case LUA_TUSERDATA:
|
||||
if (luaL_callmeta(L, i, "__len"))
|
||||
break;
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
luaL_error(L, "attempt to get length of a %s value",
|
||||
lua_typename(L, lua_type(L, i)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API int lua_rawgetp (lua_State *L, int i, const void *p) {
|
||||
int abs_i = lua_absindex(L, i);
|
||||
lua_pushlightuserdata(L, (void*)p);
|
||||
lua_rawget(L, abs_i);
|
||||
return lua_type(L, -1);
|
||||
}
|
||||
|
||||
COMPAT53_API void lua_rawsetp (lua_State *L, int i, const void *p) {
|
||||
int abs_i = lua_absindex(L, i);
|
||||
luaL_checkstack(L, 1, "not enough stack slots");
|
||||
lua_pushlightuserdata(L, (void*)p);
|
||||
lua_insert(L, -2);
|
||||
lua_rawset(L, abs_i);
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API lua_Number lua_tonumberx (lua_State *L, int i, int *isnum) {
|
||||
lua_Number n = lua_tonumber(L, i);
|
||||
if (isnum != NULL) {
|
||||
*isnum = (n != 0 || lua_isnumber(L, i));
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void luaL_checkversion (lua_State *L) {
|
||||
(void)L;
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void luaL_checkstack (lua_State *L, int sp, const char *msg) {
|
||||
if (!lua_checkstack(L, sp+LUA_MINSTACK)) {
|
||||
if (msg != NULL)
|
||||
luaL_error(L, "stack overflow (%s)", msg);
|
||||
else {
|
||||
lua_pushliteral(L, "stack overflow");
|
||||
lua_error(L);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API int luaL_getsubtable (lua_State *L, int i, const char *name) {
|
||||
int abs_i = lua_absindex(L, i);
|
||||
luaL_checkstack(L, 3, "not enough stack slots");
|
||||
lua_pushstring(L, name);
|
||||
lua_gettable(L, abs_i);
|
||||
if (lua_istable(L, -1))
|
||||
return 1;
|
||||
lua_pop(L, 1);
|
||||
lua_newtable(L);
|
||||
lua_pushstring(L, name);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_settable(L, abs_i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API lua_Integer luaL_len (lua_State *L, int i) {
|
||||
lua_Integer res = 0;
|
||||
int isnum = 0;
|
||||
luaL_checkstack(L, 1, "not enough stack slots");
|
||||
lua_len(L, i);
|
||||
res = lua_tointegerx(L, -1, &isnum);
|
||||
lua_pop(L, 1);
|
||||
if (!isnum)
|
||||
luaL_error(L, "object length is not an integer");
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
|
||||
luaL_checkstack(L, nup+1, "too many upvalues");
|
||||
for (; l->name != NULL; l++) { /* fill the table with given functions */
|
||||
int i;
|
||||
lua_pushstring(L, l->name);
|
||||
for (i = 0; i < nup; i++) /* copy upvalues to the top */
|
||||
lua_pushvalue(L, -(nup + 1));
|
||||
lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
|
||||
lua_settable(L, -(nup + 3)); /* table must be below the upvalues, the name and the closure */
|
||||
}
|
||||
lua_pop(L, nup); /* remove upvalues */
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void luaL_setmetatable (lua_State *L, const char *tname) {
|
||||
luaL_checkstack(L, 1, "not enough stack slots");
|
||||
luaL_getmetatable(L, tname);
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void *luaL_testudata (lua_State *L, int i, const char *tname) {
|
||||
void *p = lua_touserdata(L, i);
|
||||
luaL_checkstack(L, 2, "not enough stack slots");
|
||||
if (p == NULL || !lua_getmetatable(L, i))
|
||||
return NULL;
|
||||
else {
|
||||
int res = 0;
|
||||
luaL_getmetatable(L, tname);
|
||||
res = lua_rawequal(L, -1, -2);
|
||||
lua_pop(L, 2);
|
||||
if (!res)
|
||||
p = NULL;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
static int compat53_countlevels (lua_State *L) {
|
||||
lua_Debug ar;
|
||||
int li = 1, le = 1;
|
||||
/* find an upper bound */
|
||||
while (lua_getstack(L, le, &ar)) { li = le; le *= 2; }
|
||||
/* do a binary search */
|
||||
while (li < le) {
|
||||
int m = (li + le)/2;
|
||||
if (lua_getstack(L, m, &ar)) li = m + 1;
|
||||
else le = m;
|
||||
}
|
||||
return le - 1;
|
||||
}
|
||||
|
||||
static int compat53_findfield (lua_State *L, int objidx, int level) {
|
||||
if (level == 0 || !lua_istable(L, -1))
|
||||
return 0; /* not found */
|
||||
lua_pushnil(L); /* start 'next' loop */
|
||||
while (lua_next(L, -2)) { /* for each pair in table */
|
||||
if (lua_type(L, -2) == LUA_TSTRING) { /* ignore non-string keys */
|
||||
if (lua_rawequal(L, objidx, -1)) { /* found object? */
|
||||
lua_pop(L, 1); /* remove value (but keep name) */
|
||||
return 1;
|
||||
}
|
||||
else if (compat53_findfield(L, objidx, level - 1)) { /* try recursively */
|
||||
lua_remove(L, -2); /* remove table (but keep name) */
|
||||
lua_pushliteral(L, ".");
|
||||
lua_insert(L, -2); /* place '.' between the two names */
|
||||
lua_concat(L, 3);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1); /* remove value */
|
||||
}
|
||||
return 0; /* not found */
|
||||
}
|
||||
|
||||
static int compat53_pushglobalfuncname (lua_State *L, lua_Debug *ar) {
|
||||
int top = lua_gettop(L);
|
||||
lua_getinfo(L, "f", ar); /* push function */
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||
if (compat53_findfield(L, top + 1, 2)) {
|
||||
lua_copy(L, -1, top + 1); /* move name to proper place */
|
||||
lua_pop(L, 2); /* remove pushed values */
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
lua_settop(L, top); /* remove function and global table */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void compat53_pushfuncname (lua_State *L, lua_Debug *ar) {
|
||||
if (*ar->namewhat != '\0') /* is there a name? */
|
||||
lua_pushfstring(L, "function " LUA_QS, ar->name);
|
||||
else if (*ar->what == 'm') /* main? */
|
||||
lua_pushliteral(L, "main chunk");
|
||||
else if (*ar->what == 'C') {
|
||||
if (compat53_pushglobalfuncname(L, ar)) {
|
||||
lua_pushfstring(L, "function " LUA_QS, lua_tostring(L, -1));
|
||||
lua_remove(L, -2); /* remove name */
|
||||
}
|
||||
else
|
||||
lua_pushliteral(L, "?");
|
||||
}
|
||||
else
|
||||
lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined);
|
||||
}
|
||||
|
||||
#define COMPAT53_LEVELS1 12 /* size of the first part of the stack */
|
||||
#define COMPAT53_LEVELS2 10 /* size of the second part of the stack */
|
||||
|
||||
COMPAT53_API void luaL_traceback (lua_State *L, lua_State *L1,
|
||||
const char *msg, int level) {
|
||||
lua_Debug ar;
|
||||
int top = lua_gettop(L);
|
||||
int numlevels = compat53_countlevels(L1);
|
||||
int mark = (numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2) ? COMPAT53_LEVELS1 : 0;
|
||||
if (msg) lua_pushfstring(L, "%s\n", msg);
|
||||
lua_pushliteral(L, "stack traceback:");
|
||||
while (lua_getstack(L1, level++, &ar)) {
|
||||
if (level == mark) { /* too many levels? */
|
||||
lua_pushliteral(L, "\n\t..."); /* add a '...' */
|
||||
level = numlevels - COMPAT53_LEVELS2; /* and skip to last ones */
|
||||
}
|
||||
else {
|
||||
lua_getinfo(L1, "Slnt", &ar);
|
||||
lua_pushfstring(L, "\n\t%s:", ar.short_src);
|
||||
if (ar.currentline > 0)
|
||||
lua_pushfstring(L, "%d:", ar.currentline);
|
||||
lua_pushliteral(L, " in ");
|
||||
compat53_pushfuncname(L, &ar);
|
||||
lua_concat(L, lua_gettop(L) - top);
|
||||
}
|
||||
}
|
||||
lua_concat(L, lua_gettop(L) - top);
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API int luaL_fileresult (lua_State *L, int stat, const char *fname) {
|
||||
const char *serr = NULL;
|
||||
int en = errno; /* calls to Lua API may change this value */
|
||||
char buf[512] = { 0 };
|
||||
if (stat) {
|
||||
lua_pushboolean(L, 1);
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
lua_pushnil(L);
|
||||
serr = compat53_strerror(en, buf, sizeof(buf));
|
||||
if (fname)
|
||||
lua_pushfstring(L, "%s: %s", fname, serr);
|
||||
else
|
||||
lua_pushstring(L, serr);
|
||||
lua_pushnumber(L, (lua_Number)en);
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int compat53_checkmode (lua_State *L, const char *mode, const char *modename, int err) {
|
||||
if (mode && strchr(mode, modename[0]) == NULL) {
|
||||
lua_pushfstring(L, "attempt to load a %s chunk (mode is '%s')", modename, mode);
|
||||
return err;
|
||||
}
|
||||
return LUA_OK;
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
lua_Reader reader;
|
||||
void *ud;
|
||||
int has_peeked_data;
|
||||
const char *peeked_data;
|
||||
size_t peeked_data_size;
|
||||
} compat53_reader_data;
|
||||
|
||||
|
||||
static const char *compat53_reader (lua_State *L, void *ud, size_t *size) {
|
||||
compat53_reader_data *data = (compat53_reader_data *)ud;
|
||||
if (data->has_peeked_data) {
|
||||
data->has_peeked_data = 0;
|
||||
*size = data->peeked_data_size;
|
||||
return data->peeked_data;
|
||||
} else
|
||||
return data->reader(L, data->ud, size);
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char *source, const char *mode) {
|
||||
int status = LUA_OK;
|
||||
compat53_reader_data compat53_data = { 0, NULL, 1, 0, 0 };
|
||||
compat53_data.reader = reader;
|
||||
compat53_data.ud = data;
|
||||
compat53_data.peeked_data = reader(L, data, &(compat53_data.peeked_data_size));
|
||||
if (compat53_data.peeked_data && compat53_data.peeked_data_size &&
|
||||
compat53_data.peeked_data[0] == LUA_SIGNATURE[0]) /* binary file? */
|
||||
status = compat53_checkmode(L, mode, "binary", LUA_ERRSYNTAX);
|
||||
else
|
||||
status = compat53_checkmode(L, mode, "text", LUA_ERRSYNTAX);
|
||||
if (status != LUA_OK)
|
||||
return status;
|
||||
/* we need to call the original 5.1 version of lua_load! */
|
||||
#undef lua_load
|
||||
return lua_load(L, compat53_reader, &compat53_data, source);
|
||||
#define lua_load COMPAT53_CONCAT(COMPAT53_PREFIX, _load_53)
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
int n; /* number of pre-read characters */
|
||||
FILE *f; /* file being read */
|
||||
char buff[COMPAT53_LUA_FILE_BUFFER_SIZE]; /* area for reading file */
|
||||
} compat53_LoadF;
|
||||
|
||||
|
||||
static const char *compat53_getF (lua_State *L, void *ud, size_t *size) {
|
||||
compat53_LoadF *lf = (compat53_LoadF *)ud;
|
||||
(void)L; /* not used */
|
||||
if (lf->n > 0) { /* are there pre-read characters to be read? */
|
||||
*size = lf->n; /* return them (chars already in buffer) */
|
||||
lf->n = 0; /* no more pre-read characters */
|
||||
}
|
||||
else { /* read a block from file */
|
||||
/* 'fread' can return > 0 *and* set the EOF flag. If next call to
|
||||
'compat53_getF' called 'fread', it might still wait for user input.
|
||||
The next check avoids this problem. */
|
||||
if (feof(lf->f)) return NULL;
|
||||
*size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* read block */
|
||||
}
|
||||
return lf->buff;
|
||||
}
|
||||
|
||||
|
||||
static int compat53_errfile (lua_State *L, const char *what, int fnameindex) {
|
||||
char buf[512] = {0};
|
||||
const char *serr = compat53_strerror(errno, buf, sizeof(buf));
|
||||
const char *filename = lua_tostring(L, fnameindex) + 1;
|
||||
lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr);
|
||||
lua_remove(L, fnameindex);
|
||||
return LUA_ERRFILE;
|
||||
}
|
||||
|
||||
|
||||
static int compat53_skipBOM (compat53_LoadF *lf) {
|
||||
const char *p = "\xEF\xBB\xBF"; /* UTF-8 BOM mark */
|
||||
int c;
|
||||
lf->n = 0;
|
||||
do {
|
||||
c = getc(lf->f);
|
||||
if (c == EOF || c != *(const unsigned char *)p++) return c;
|
||||
lf->buff[lf->n++] = (char)c; /* to be read by the parser */
|
||||
} while (*p != '\0');
|
||||
lf->n = 0; /* prefix matched; discard it */
|
||||
return getc(lf->f); /* return next character */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** reads the first character of file 'f' and skips an optional BOM mark
|
||||
** in its beginning plus its first line if it starts with '#'. Returns
|
||||
** true if it skipped the first line. In any case, '*cp' has the
|
||||
** first "valid" character of the file (after the optional BOM and
|
||||
** a first-line comment).
|
||||
*/
|
||||
static int compat53_skipcomment (compat53_LoadF *lf, int *cp) {
|
||||
int c = *cp = compat53_skipBOM(lf);
|
||||
if (c == '#') { /* first line is a comment (Unix exec. file)? */
|
||||
do { /* skip first line */
|
||||
c = getc(lf->f);
|
||||
} while (c != EOF && c != '\n');
|
||||
*cp = getc(lf->f); /* skip end-of-line, if present */
|
||||
return 1; /* there was a comment */
|
||||
}
|
||||
else return 0; /* no comment */
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API int luaL_loadfilex (lua_State *L, const char *filename, const char *mode) {
|
||||
compat53_LoadF lf;
|
||||
int status, readstatus;
|
||||
int c;
|
||||
int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */
|
||||
if (filename == NULL) {
|
||||
lua_pushliteral(L, "=stdin");
|
||||
lf.f = stdin;
|
||||
}
|
||||
else {
|
||||
lua_pushfstring(L, "@%s", filename);
|
||||
#if defined(_MSC_VER)
|
||||
/* This code is here to stop a deprecation error that stops builds
|
||||
* if a certain macro is defined. While normally not caring would
|
||||
* be best, some header-only libraries and builds can't afford to
|
||||
* dictate this to the user. A quick check shows that fopen_s this
|
||||
* goes back to VS 2005, and _fsopen goes back to VS 2003 .NET,
|
||||
* possibly even before that so we don't need to do any version
|
||||
* number checks, since this has been there since forever. */
|
||||
|
||||
/* TO USER: if you want the behavior of typical fopen_s/fopen,
|
||||
* which does lock the file on VC++, define the macro used below to 0 */
|
||||
#if COMPAT53_FOPEN_NO_LOCK
|
||||
lf.f = _fsopen(filename, "r", _SH_DENYNO); /* do not lock the file in any way */
|
||||
if (lf.f == NULL)
|
||||
return compat53_errfile(L, "open", fnameindex);
|
||||
#else /* use default locking version */
|
||||
if (fopen_s(&lf.f, filename, "r") != 0)
|
||||
return compat53_errfile(L, "open", fnameindex);
|
||||
#endif /* Locking vs. No-locking fopen variants */
|
||||
#else
|
||||
lf.f = fopen(filename, "r"); /* default stdlib doesn't forcefully lock files here */
|
||||
if (lf.f == NULL) return compat53_errfile(L, "open", fnameindex);
|
||||
#endif
|
||||
}
|
||||
if (compat53_skipcomment(&lf, &c)) /* read initial portion */
|
||||
lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */
|
||||
if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */
|
||||
#if defined(_MSC_VER)
|
||||
if (freopen_s(&lf.f, filename, "rb", lf.f) != 0)
|
||||
return compat53_errfile(L, "reopen", fnameindex);
|
||||
#else
|
||||
lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
|
||||
if (lf.f == NULL) return compat53_errfile(L, "reopen", fnameindex);
|
||||
#endif
|
||||
compat53_skipcomment(&lf, &c); /* re-read initial portion */
|
||||
}
|
||||
if (c != EOF)
|
||||
lf.buff[lf.n++] = (char)c; /* 'c' is the first character of the stream */
|
||||
status = lua_load(L, &compat53_getF, &lf, lua_tostring(L, -1), mode);
|
||||
readstatus = ferror(lf.f);
|
||||
if (filename) fclose(lf.f); /* close file (even in case of errors) */
|
||||
if (readstatus) {
|
||||
lua_settop(L, fnameindex); /* ignore results from 'lua_load' */
|
||||
return compat53_errfile(L, "read", fnameindex);
|
||||
}
|
||||
lua_remove(L, fnameindex);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t sz, const char *name, const char *mode) {
|
||||
int status = LUA_OK;
|
||||
if (sz > 0 && buff[0] == LUA_SIGNATURE[0]) {
|
||||
status = compat53_checkmode(L, mode, "binary", LUA_ERRSYNTAX);
|
||||
}
|
||||
else {
|
||||
status = compat53_checkmode(L, mode, "text", LUA_ERRSYNTAX);
|
||||
}
|
||||
if (status != LUA_OK)
|
||||
return status;
|
||||
return luaL_loadbuffer(L, buff, sz, name);
|
||||
}
|
||||
|
||||
|
||||
#if !defined(l_inspectstat) && \
|
||||
(defined(unix) || defined(__unix) || defined(__unix__) || \
|
||||
defined(__TOS_AIX__) || defined(_SYSTYPE_BSD) || \
|
||||
(defined(__APPLE__) && defined(__MACH__)))
|
||||
/* some form of unix; check feature macros in unistd.h for details */
|
||||
# include <unistd.h>
|
||||
/* check posix version; the relevant include files and macros probably
|
||||
* were available before 2001, but I'm not sure */
|
||||
# if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L
|
||||
# include <sys/wait.h>
|
||||
# define l_inspectstat(stat,what) \
|
||||
if (WIFEXITED(stat)) { stat = WEXITSTATUS(stat); } \
|
||||
else if (WIFSIGNALED(stat)) { stat = WTERMSIG(stat); what = "signal"; }
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* provide default (no-op) version */
|
||||
#if !defined(l_inspectstat)
|
||||
# define l_inspectstat(stat,what) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
COMPAT53_API int luaL_execresult (lua_State *L, int stat) {
|
||||
const char *what = "exit";
|
||||
if (stat == -1)
|
||||
return luaL_fileresult(L, 0, NULL);
|
||||
else {
|
||||
l_inspectstat(stat, what);
|
||||
if (*what == 'e' && stat == 0)
|
||||
lua_pushboolean(L, 1);
|
||||
else
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, what);
|
||||
lua_pushinteger(L, stat);
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void luaL_buffinit (lua_State *L, luaL_Buffer_53 *B) {
|
||||
/* make it crash if used via pointer to a 5.1-style luaL_Buffer */
|
||||
B->b.p = NULL;
|
||||
B->b.L = NULL;
|
||||
B->b.lvl = 0;
|
||||
/* reuse the buffer from the 5.1-style luaL_Buffer though! */
|
||||
B->ptr = B->b.buffer;
|
||||
B->capacity = LUAL_BUFFERSIZE;
|
||||
B->nelems = 0;
|
||||
B->L2 = L;
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API char *luaL_prepbuffsize (luaL_Buffer_53 *B, size_t s) {
|
||||
if (B->capacity - B->nelems < s) { /* needs to grow */
|
||||
char* newptr = NULL;
|
||||
size_t newcap = B->capacity * 2;
|
||||
if (newcap - B->nelems < s)
|
||||
newcap = B->nelems + s;
|
||||
if (newcap < B->capacity) /* overflow */
|
||||
luaL_error(B->L2, "buffer too large");
|
||||
newptr = (char*)lua_newuserdata(B->L2, newcap);
|
||||
memcpy(newptr, B->ptr, B->nelems);
|
||||
if (B->ptr != B->b.buffer)
|
||||
lua_replace(B->L2, -2); /* remove old buffer */
|
||||
B->ptr = newptr;
|
||||
B->capacity = newcap;
|
||||
}
|
||||
return B->ptr+B->nelems;
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void luaL_addlstring (luaL_Buffer_53 *B, const char *s, size_t l) {
|
||||
memcpy(luaL_prepbuffsize(B, l), s, l);
|
||||
luaL_addsize(B, l);
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void luaL_addvalue (luaL_Buffer_53 *B) {
|
||||
size_t len = 0;
|
||||
const char *s = lua_tolstring(B->L2, -1, &len);
|
||||
if (!s)
|
||||
luaL_error(B->L2, "cannot convert value to string");
|
||||
if (B->ptr != B->b.buffer)
|
||||
lua_insert(B->L2, -2); /* userdata buffer must be at stack top */
|
||||
luaL_addlstring(B, s, len);
|
||||
lua_remove(B->L2, B->ptr != B->b.buffer ? -2 : -1);
|
||||
}
|
||||
|
||||
|
||||
void luaL_pushresult (luaL_Buffer_53 *B) {
|
||||
lua_pushlstring(B->L2, B->ptr, B->nelems);
|
||||
if (B->ptr != B->b.buffer)
|
||||
lua_replace(B->L2, -2); /* remove userdata buffer */
|
||||
}
|
||||
|
||||
|
||||
#endif /* Lua 5.1 */
|
||||
|
||||
|
||||
|
||||
/* definitions for Lua 5.1 and Lua 5.2 */
|
||||
#if defined( LUA_VERSION_NUM ) && LUA_VERSION_NUM <= 502
|
||||
|
||||
|
||||
COMPAT53_API int lua_geti (lua_State *L, int index, lua_Integer i) {
|
||||
index = lua_absindex(L, index);
|
||||
lua_pushinteger(L, i);
|
||||
lua_gettable(L, index);
|
||||
return lua_type(L, -1);
|
||||
}
|
||||
|
||||
|
||||
#ifndef LUA_EXTRASPACE
|
||||
#define LUA_EXTRASPACE (sizeof(void*))
|
||||
#endif
|
||||
|
||||
COMPAT53_API void *lua_getextraspace (lua_State *L) {
|
||||
int is_main = 0;
|
||||
void *ptr = NULL;
|
||||
luaL_checkstack(L, 4, "not enough stack slots available");
|
||||
lua_pushliteral(L, "__compat53_extraspace");
|
||||
lua_pushvalue(L, -1);
|
||||
lua_rawget(L, LUA_REGISTRYINDEX);
|
||||
if (!lua_istable(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
lua_createtable(L, 0, 2);
|
||||
lua_createtable(L, 0, 1);
|
||||
lua_pushliteral(L, "k");
|
||||
lua_setfield(L, -2, "__mode");
|
||||
lua_setmetatable(L, -2);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_rawset(L, LUA_REGISTRYINDEX);
|
||||
}
|
||||
lua_replace(L, -2);
|
||||
is_main = lua_pushthread(L);
|
||||
lua_rawget(L, -2);
|
||||
ptr = lua_touserdata(L, -1);
|
||||
if (!ptr) {
|
||||
lua_pop(L, 1);
|
||||
ptr = lua_newuserdata(L, LUA_EXTRASPACE);
|
||||
if (is_main) {
|
||||
memset(ptr, '\0', LUA_EXTRASPACE);
|
||||
lua_pushthread(L);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_rawset(L, -4);
|
||||
lua_pushboolean(L, 1);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_rawset(L, -4);
|
||||
} else {
|
||||
void* mptr = NULL;
|
||||
lua_pushboolean(L, 1);
|
||||
lua_rawget(L, -3);
|
||||
mptr = lua_touserdata(L, -1);
|
||||
if (mptr)
|
||||
memcpy(ptr, mptr, LUA_EXTRASPACE);
|
||||
else
|
||||
memset(ptr, '\0', LUA_EXTRASPACE);
|
||||
lua_pop(L, 1);
|
||||
lua_pushthread(L);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_rawset(L, -4);
|
||||
}
|
||||
}
|
||||
lua_pop(L, 2);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API int lua_isinteger (lua_State *L, int index) {
|
||||
if (lua_type(L, index) == LUA_TNUMBER) {
|
||||
lua_Number n = lua_tonumber(L, index);
|
||||
lua_Integer i = lua_tointeger(L, index);
|
||||
if (i == n)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API lua_Integer lua_tointegerx (lua_State *L, int i, int *isnum) {
|
||||
int ok = 0;
|
||||
lua_Number n = lua_tonumberx(L, i, &ok);
|
||||
if (ok) {
|
||||
if (n == (lua_Integer)n) {
|
||||
if (isnum)
|
||||
*isnum = 1;
|
||||
return (lua_Integer)n;
|
||||
}
|
||||
}
|
||||
if (isnum)
|
||||
*isnum = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void compat53_reverse (lua_State *L, int a, int b) {
|
||||
for (; a < b; ++a, --b) {
|
||||
lua_pushvalue(L, a);
|
||||
lua_pushvalue(L, b);
|
||||
lua_replace(L, a);
|
||||
lua_replace(L, b);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void lua_rotate (lua_State *L, int idx, int n) {
|
||||
int n_elems = 0;
|
||||
idx = lua_absindex(L, idx);
|
||||
n_elems = lua_gettop(L)-idx+1;
|
||||
if (n < 0)
|
||||
n += n_elems;
|
||||
if ( n > 0 && n < n_elems) {
|
||||
luaL_checkstack(L, 2, "not enough stack slots available");
|
||||
n = n_elems - n;
|
||||
compat53_reverse(L, idx, idx+n-1);
|
||||
compat53_reverse(L, idx+n, idx+n_elems-1);
|
||||
compat53_reverse(L, idx, idx+n_elems-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void lua_seti (lua_State *L, int index, lua_Integer i) {
|
||||
luaL_checkstack(L, 1, "not enough stack slots available");
|
||||
index = lua_absindex(L, index);
|
||||
lua_pushinteger(L, i);
|
||||
lua_insert(L, -2);
|
||||
lua_settable(L, index);
|
||||
}
|
||||
|
||||
|
||||
#if !defined(lua_str2number)
|
||||
# define lua_str2number(s, p) strtod((s), (p))
|
||||
#endif
|
||||
|
||||
COMPAT53_API size_t lua_stringtonumber (lua_State *L, const char *s) {
|
||||
char* endptr;
|
||||
lua_Number n = lua_str2number(s, &endptr);
|
||||
if (endptr != s) {
|
||||
while (*endptr != '\0' && isspace((unsigned char)*endptr))
|
||||
++endptr;
|
||||
if (*endptr == '\0') {
|
||||
lua_pushnumber(L, n);
|
||||
return endptr - s + 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) {
|
||||
if (!luaL_callmeta(L, idx, "__tostring")) {
|
||||
int t = lua_type(L, idx), tt = 0;
|
||||
char const* name = NULL;
|
||||
switch (t) {
|
||||
case LUA_TNIL:
|
||||
lua_pushliteral(L, "nil");
|
||||
break;
|
||||
case LUA_TSTRING:
|
||||
case LUA_TNUMBER:
|
||||
lua_pushvalue(L, idx);
|
||||
break;
|
||||
case LUA_TBOOLEAN:
|
||||
if (lua_toboolean(L, idx))
|
||||
lua_pushliteral(L, "true");
|
||||
else
|
||||
lua_pushliteral(L, "false");
|
||||
break;
|
||||
default:
|
||||
tt = luaL_getmetafield(L, idx, "__name");
|
||||
name = (tt == LUA_TSTRING) ? lua_tostring(L, -1) : lua_typename(L, t);
|
||||
lua_pushfstring(L, "%s: %p", name, lua_topointer(L, idx));
|
||||
if (tt != LUA_TNIL)
|
||||
lua_replace(L, -2);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!lua_isstring(L, -1))
|
||||
luaL_error(L, "'__tostring' must return a string");
|
||||
}
|
||||
return lua_tolstring(L, -1, len);
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void luaL_requiref (lua_State *L, const char *modname,
|
||||
lua_CFunction openf, int glb) {
|
||||
luaL_checkstack(L, 3, "not enough stack slots available");
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, "_LOADED");
|
||||
if (lua_getfield(L, -1, modname) == LUA_TNIL) {
|
||||
lua_pop(L, 1);
|
||||
lua_pushcfunction(L, openf);
|
||||
lua_pushstring(L, modname);
|
||||
#ifndef COMPAT53_LUAJIT
|
||||
lua_call(L, 1, 1);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, -3, modname);
|
||||
#else
|
||||
lua_call(L, 1, 0);
|
||||
lua_getfield(L, -1, modname);
|
||||
#endif /* COMPAT53_LUAJIT */
|
||||
}
|
||||
if (glb) {
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setglobal(L, modname);
|
||||
}
|
||||
lua_replace(L, -2);
|
||||
}
|
||||
|
||||
|
||||
#endif /* Lua 5.1 and 5.2 */
|
||||
|
||||
|
||||
#endif /* COMPAT53_C_ */
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
* This file contains parts of Lua 5.2's and Lua 5.3's source code:
|
||||
*
|
||||
* Copyright (C) 1994-2014 Lua.org, PUC-Rio.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
|
||||
@@ -0,0 +1,424 @@
|
||||
#ifndef COMPAT53_H_
|
||||
#define COMPAT53_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#if defined(__cplusplus) && !defined(COMPAT53_LUA_CPP)
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <lualib.h>
|
||||
#if defined(__cplusplus) && !defined(COMPAT53_LUA_CPP)
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#undef COMPAT53_INCLUDE_SOURCE
|
||||
#if defined(COMPAT53_PREFIX)
|
||||
/* - change the symbol names of functions to avoid linker conflicts
|
||||
* - compat-5.3.c needs to be compiled (and linked) separately
|
||||
*/
|
||||
# if !defined(COMPAT53_API)
|
||||
# define COMPAT53_API extern
|
||||
# endif
|
||||
#else /* COMPAT53_PREFIX */
|
||||
/* - make all functions static and include the source.
|
||||
* - compat-5.3.c doesn't need to be compiled (and linked) separately
|
||||
*/
|
||||
# define COMPAT53_PREFIX compat53
|
||||
# undef COMPAT53_API
|
||||
# if defined(__GNUC__) || defined(__clang__)
|
||||
# define COMPAT53_API __attribute__((__unused__)) static
|
||||
# else
|
||||
# define COMPAT53_API static
|
||||
# endif
|
||||
# define COMPAT53_INCLUDE_SOURCE
|
||||
#endif /* COMPAT53_PREFIX */
|
||||
|
||||
#define COMPAT53_CONCAT_HELPER(a, b) a##b
|
||||
#define COMPAT53_CONCAT(a, b) COMPAT53_CONCAT_HELPER(a, b)
|
||||
|
||||
|
||||
|
||||
/* declarations for Lua 5.1 */
|
||||
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
|
||||
|
||||
/* XXX not implemented:
|
||||
* lua_arith (new operators)
|
||||
* lua_upvalueid
|
||||
* lua_upvaluejoin
|
||||
* lua_version
|
||||
* lua_yieldk
|
||||
*/
|
||||
|
||||
#ifndef LUA_OK
|
||||
# define LUA_OK 0
|
||||
#endif
|
||||
#ifndef LUA_OPADD
|
||||
# define LUA_OPADD 0
|
||||
#endif
|
||||
#ifndef LUA_OPSUB
|
||||
# define LUA_OPSUB 1
|
||||
#endif
|
||||
#ifndef LUA_OPMUL
|
||||
# define LUA_OPMUL 2
|
||||
#endif
|
||||
#ifndef LUA_OPDIV
|
||||
# define LUA_OPDIV 3
|
||||
#endif
|
||||
#ifndef LUA_OPMOD
|
||||
# define LUA_OPMOD 4
|
||||
#endif
|
||||
#ifndef LUA_OPPOW
|
||||
# define LUA_OPPOW 5
|
||||
#endif
|
||||
#ifndef LUA_OPUNM
|
||||
# define LUA_OPUNM 6
|
||||
#endif
|
||||
#ifndef LUA_OPEQ
|
||||
# define LUA_OPEQ 0
|
||||
#endif
|
||||
#ifndef LUA_OPLT
|
||||
# define LUA_OPLT 1
|
||||
#endif
|
||||
#ifndef LUA_OPLE
|
||||
# define LUA_OPLE 2
|
||||
#endif
|
||||
|
||||
/* LuaJIT/Lua 5.1 does not have the updated
|
||||
* error codes for thread status/function returns (but some patched versions do)
|
||||
* define it only if it's not found
|
||||
*/
|
||||
#if !defined(LUA_ERRGCMM)
|
||||
/* Use + 2 because in some versions of Lua (Lua 5.1)
|
||||
* LUA_ERRFILE is defined as (LUA_ERRERR+1)
|
||||
* so we need to avoid it (LuaJIT might have something at this
|
||||
* integer value too)
|
||||
*/
|
||||
# define LUA_ERRGCMM (LUA_ERRERR + 2)
|
||||
#endif /* LUA_ERRGCMM define */
|
||||
|
||||
typedef size_t lua_Unsigned;
|
||||
|
||||
typedef struct luaL_Buffer_53 {
|
||||
luaL_Buffer b; /* make incorrect code crash! */
|
||||
char *ptr;
|
||||
size_t nelems;
|
||||
size_t capacity;
|
||||
lua_State *L2;
|
||||
} luaL_Buffer_53;
|
||||
#define luaL_Buffer luaL_Buffer_53
|
||||
|
||||
/* In PUC-Rio 5.1, userdata is a simple FILE*
|
||||
* In LuaJIT, it's a struct where the first member is a FILE*
|
||||
* We can't support the `closef` member
|
||||
*/
|
||||
typedef struct luaL_Stream {
|
||||
FILE *f;
|
||||
} luaL_Stream;
|
||||
|
||||
#define lua_absindex COMPAT53_CONCAT(COMPAT53_PREFIX, _absindex)
|
||||
COMPAT53_API int lua_absindex (lua_State *L, int i);
|
||||
|
||||
#define lua_arith COMPAT53_CONCAT(COMPAT53_PREFIX, _arith)
|
||||
COMPAT53_API void lua_arith (lua_State *L, int op);
|
||||
|
||||
#define lua_compare COMPAT53_CONCAT(COMPAT53_PREFIX, _compare)
|
||||
COMPAT53_API int lua_compare (lua_State *L, int idx1, int idx2, int op);
|
||||
|
||||
#define lua_copy COMPAT53_CONCAT(COMPAT53_PREFIX, _copy)
|
||||
COMPAT53_API void lua_copy (lua_State *L, int from, int to);
|
||||
|
||||
#define lua_getuservalue(L, i) \
|
||||
(lua_getfenv((L), (i)), lua_type((L), -1))
|
||||
#define lua_setuservalue(L, i) \
|
||||
(luaL_checktype((L), -1, LUA_TTABLE), lua_setfenv((L), (i)))
|
||||
|
||||
#define lua_len COMPAT53_CONCAT(COMPAT53_PREFIX, _len)
|
||||
COMPAT53_API void lua_len (lua_State *L, int i);
|
||||
|
||||
#define lua_pushstring(L, s) \
|
||||
(lua_pushstring((L), (s)), lua_tostring((L), -1))
|
||||
|
||||
#define lua_pushlstring(L, s, len) \
|
||||
((((len) == 0) ? lua_pushlstring((L), "", 0) : lua_pushlstring((L), (s), (len))), lua_tostring((L), -1))
|
||||
|
||||
#ifndef luaL_newlibtable
|
||||
# define luaL_newlibtable(L, l) \
|
||||
(lua_createtable((L), 0, sizeof((l))/sizeof(*(l))-1))
|
||||
#endif
|
||||
#ifndef luaL_newlib
|
||||
# define luaL_newlib(L, l) \
|
||||
(luaL_newlibtable((L), (l)), luaL_register((L), NULL, (l)))
|
||||
#endif
|
||||
|
||||
#define lua_pushglobaltable(L) \
|
||||
lua_pushvalue((L), LUA_GLOBALSINDEX)
|
||||
|
||||
#define lua_rawgetp COMPAT53_CONCAT(COMPAT53_PREFIX, _rawgetp)
|
||||
COMPAT53_API int lua_rawgetp (lua_State *L, int i, const void *p);
|
||||
|
||||
#define lua_rawsetp COMPAT53_CONCAT(COMPAT53_PREFIX, _rawsetp)
|
||||
COMPAT53_API void lua_rawsetp(lua_State *L, int i, const void *p);
|
||||
|
||||
#define lua_rawlen(L, i) lua_objlen((L), (i))
|
||||
|
||||
#define lua_tointeger(L, i) lua_tointegerx((L), (i), NULL)
|
||||
|
||||
#define lua_tonumberx COMPAT53_CONCAT(COMPAT53_PREFIX, _tonumberx)
|
||||
COMPAT53_API lua_Number lua_tonumberx (lua_State *L, int i, int *isnum);
|
||||
|
||||
#define luaL_checkversion COMPAT53_CONCAT(COMPAT53_PREFIX, L_checkversion)
|
||||
COMPAT53_API void luaL_checkversion (lua_State *L);
|
||||
|
||||
#define lua_load COMPAT53_CONCAT(COMPAT53_PREFIX, _load_53)
|
||||
COMPAT53_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char* source, const char* mode);
|
||||
|
||||
#define luaL_loadfilex COMPAT53_CONCAT(COMPAT53_PREFIX, L_loadfilex)
|
||||
COMPAT53_API int luaL_loadfilex (lua_State *L, const char *filename, const char *mode);
|
||||
|
||||
#define luaL_loadbufferx COMPAT53_CONCAT(COMPAT53_PREFIX, L_loadbufferx)
|
||||
COMPAT53_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t sz, const char *name, const char *mode);
|
||||
|
||||
#define luaL_checkstack COMPAT53_CONCAT(COMPAT53_PREFIX, L_checkstack_53)
|
||||
COMPAT53_API void luaL_checkstack (lua_State *L, int sp, const char *msg);
|
||||
|
||||
#define luaL_getsubtable COMPAT53_CONCAT(COMPAT53_PREFIX, L_getsubtable)
|
||||
COMPAT53_API int luaL_getsubtable (lua_State* L, int i, const char *name);
|
||||
|
||||
#define luaL_len COMPAT53_CONCAT(COMPAT53_PREFIX, L_len)
|
||||
COMPAT53_API lua_Integer luaL_len (lua_State *L, int i);
|
||||
|
||||
#define luaL_setfuncs COMPAT53_CONCAT(COMPAT53_PREFIX, L_setfuncs)
|
||||
COMPAT53_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup);
|
||||
|
||||
#define luaL_setmetatable COMPAT53_CONCAT(COMPAT53_PREFIX, L_setmetatable)
|
||||
COMPAT53_API void luaL_setmetatable (lua_State *L, const char *tname);
|
||||
|
||||
#define luaL_testudata COMPAT53_CONCAT(COMPAT53_PREFIX, L_testudata)
|
||||
COMPAT53_API void *luaL_testudata (lua_State *L, int i, const char *tname);
|
||||
|
||||
#define luaL_traceback COMPAT53_CONCAT(COMPAT53_PREFIX, L_traceback)
|
||||
COMPAT53_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg, int level);
|
||||
|
||||
#define luaL_fileresult COMPAT53_CONCAT(COMPAT53_PREFIX, L_fileresult)
|
||||
COMPAT53_API int luaL_fileresult (lua_State *L, int stat, const char *fname);
|
||||
|
||||
#define luaL_execresult COMPAT53_CONCAT(COMPAT53_PREFIX, L_execresult)
|
||||
COMPAT53_API int luaL_execresult (lua_State *L, int stat);
|
||||
|
||||
#define lua_callk(L, na, nr, ctx, cont) \
|
||||
((void)(ctx), (void)(cont), lua_call((L), (na), (nr)))
|
||||
#define lua_pcallk(L, na, nr, err, ctx, cont) \
|
||||
((void)(ctx), (void)(cont), lua_pcall((L), (na), (nr), (err)))
|
||||
|
||||
#define lua_resume(L, from, nargs) \
|
||||
((void)(from), lua_resume((L), (nargs)))
|
||||
|
||||
#define luaL_buffinit COMPAT53_CONCAT(COMPAT53_PREFIX, _buffinit_53)
|
||||
COMPAT53_API void luaL_buffinit (lua_State *L, luaL_Buffer_53 *B);
|
||||
|
||||
#define luaL_prepbuffsize COMPAT53_CONCAT(COMPAT53_PREFIX, _prepbufsize_53)
|
||||
COMPAT53_API char *luaL_prepbuffsize (luaL_Buffer_53 *B, size_t s);
|
||||
|
||||
#define luaL_addlstring COMPAT53_CONCAT(COMPAT53_PREFIX, _addlstring_53)
|
||||
COMPAT53_API void luaL_addlstring (luaL_Buffer_53 *B, const char *s, size_t l);
|
||||
|
||||
#define luaL_addvalue COMPAT53_CONCAT(COMPAT53_PREFIX, _addvalue_53)
|
||||
COMPAT53_API void luaL_addvalue (luaL_Buffer_53 *B);
|
||||
|
||||
#define luaL_pushresult COMPAT53_CONCAT(COMPAT53_PREFIX, _pushresult_53)
|
||||
COMPAT53_API void luaL_pushresult (luaL_Buffer_53 *B);
|
||||
|
||||
#undef luaL_buffinitsize
|
||||
#define luaL_buffinitsize(L, B, s) \
|
||||
(luaL_buffinit((L), (B)), luaL_prepbuffsize((B), (s)))
|
||||
|
||||
#undef luaL_prepbuffer
|
||||
#define luaL_prepbuffer(B) \
|
||||
luaL_prepbuffsize((B), LUAL_BUFFERSIZE)
|
||||
|
||||
#undef luaL_addchar
|
||||
#define luaL_addchar(B, c) \
|
||||
((void)((B)->nelems < (B)->capacity || luaL_prepbuffsize((B), 1)), \
|
||||
((B)->ptr[(B)->nelems++] = (c)))
|
||||
|
||||
#undef luaL_addsize
|
||||
#define luaL_addsize(B, s) \
|
||||
((B)->nelems += (s))
|
||||
|
||||
#undef luaL_addstring
|
||||
#define luaL_addstring(B, s) \
|
||||
luaL_addlstring((B), (s), strlen((s)))
|
||||
|
||||
#undef luaL_pushresultsize
|
||||
#define luaL_pushresultsize(B, s) \
|
||||
(luaL_addsize((B), (s)), luaL_pushresult((B)))
|
||||
|
||||
#if defined(LUA_COMPAT_APIINTCASTS)
|
||||
#define lua_pushunsigned(L, n) \
|
||||
lua_pushinteger((L), (lua_Integer)(n))
|
||||
#define lua_tounsignedx(L, i, is) \
|
||||
((lua_Unsigned)lua_tointegerx((L), (i), (is)))
|
||||
#define lua_tounsigned(L, i) \
|
||||
lua_tounsignedx((L), (i), NULL)
|
||||
#define luaL_checkunsigned(L, a) \
|
||||
((lua_Unsigned)luaL_checkinteger((L), (a)))
|
||||
#define luaL_optunsigned(L, a, d) \
|
||||
((lua_Unsigned)luaL_optinteger((L), (a), (lua_Integer)(d)))
|
||||
#endif
|
||||
|
||||
#endif /* Lua 5.1 only */
|
||||
|
||||
|
||||
|
||||
/* declarations for Lua 5.1 and 5.2 */
|
||||
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 502
|
||||
|
||||
typedef int lua_KContext;
|
||||
|
||||
typedef int (*lua_KFunction)(lua_State *L, int status, lua_KContext ctx);
|
||||
|
||||
#define lua_dump(L, w, d, s) \
|
||||
((void)(s), lua_dump((L), (w), (d)))
|
||||
|
||||
#define lua_getfield(L, i, k) \
|
||||
(lua_getfield((L), (i), (k)), lua_type((L), -1))
|
||||
|
||||
#define lua_gettable(L, i) \
|
||||
(lua_gettable((L), (i)), lua_type((L), -1))
|
||||
|
||||
#define lua_geti COMPAT53_CONCAT(COMPAT53_PREFIX, _geti)
|
||||
COMPAT53_API int lua_geti (lua_State *L, int index, lua_Integer i);
|
||||
|
||||
#define lua_getextraspace COMPAT53_CONCAT(COMPAT53_PREFIX, _getextraspace)
|
||||
COMPAT53_API void *lua_getextraspace (lua_State *L);
|
||||
|
||||
#define lua_isinteger COMPAT53_CONCAT(COMPAT53_PREFIX, _isinteger)
|
||||
COMPAT53_API int lua_isinteger (lua_State *L, int index);
|
||||
|
||||
#define lua_tointegerx COMPAT53_CONCAT(COMPAT53_PREFIX, _tointegerx_53)
|
||||
COMPAT53_API lua_Integer lua_tointegerx (lua_State *L, int i, int *isnum);
|
||||
|
||||
#define lua_numbertointeger(n, p) \
|
||||
((*(p) = (lua_Integer)(n)), 1)
|
||||
|
||||
#define lua_rawget(L, i) \
|
||||
(lua_rawget((L), (i)), lua_type((L), -1))
|
||||
|
||||
#define lua_rawgeti(L, i, n) \
|
||||
(lua_rawgeti((L), (i), (n)), lua_type((L), -1))
|
||||
|
||||
#define lua_rotate COMPAT53_CONCAT(COMPAT53_PREFIX, _rotate)
|
||||
COMPAT53_API void lua_rotate (lua_State *L, int idx, int n);
|
||||
|
||||
#define lua_seti COMPAT53_CONCAT(COMPAT53_PREFIX, _seti)
|
||||
COMPAT53_API void lua_seti (lua_State *L, int index, lua_Integer i);
|
||||
|
||||
#define lua_stringtonumber COMPAT53_CONCAT(COMPAT53_PREFIX, _stringtonumber)
|
||||
COMPAT53_API size_t lua_stringtonumber (lua_State *L, const char *s);
|
||||
|
||||
#define luaL_tolstring COMPAT53_CONCAT(COMPAT53_PREFIX, L_tolstring)
|
||||
COMPAT53_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len);
|
||||
|
||||
#define luaL_getmetafield(L, o, e) \
|
||||
(luaL_getmetafield((L), (o), (e)) ? lua_type((L), -1) : LUA_TNIL)
|
||||
|
||||
#define luaL_newmetatable(L, tn) \
|
||||
(luaL_newmetatable((L), (tn)) ? (lua_pushstring((L), (tn)), lua_setfield((L), -2, "__name"), 1) : 0)
|
||||
|
||||
#define luaL_requiref COMPAT53_CONCAT(COMPAT53_PREFIX, L_requiref_53)
|
||||
COMPAT53_API void luaL_requiref (lua_State *L, const char *modname,
|
||||
lua_CFunction openf, int glb );
|
||||
|
||||
#endif /* Lua 5.1 and Lua 5.2 */
|
||||
|
||||
|
||||
|
||||
/* declarations for Lua 5.2 */
|
||||
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 502
|
||||
|
||||
/* XXX not implemented:
|
||||
* lua_isyieldable
|
||||
* lua_arith (new operators)
|
||||
* lua_pushfstring (new formats)
|
||||
*/
|
||||
|
||||
#define lua_getglobal(L, n) \
|
||||
(lua_getglobal((L), (n)), lua_type((L), -1))
|
||||
|
||||
#define lua_getuservalue(L, i) \
|
||||
(lua_getuservalue((L), (i)), lua_type((L), -1))
|
||||
|
||||
#define lua_pushlstring(L, s, len) \
|
||||
(((len) == 0) ? lua_pushlstring((L), "", 0) : lua_pushlstring((L), (s), (len)))
|
||||
|
||||
#define lua_rawgetp(L, i, p) \
|
||||
(lua_rawgetp((L), (i), (p)), lua_type((L), -1))
|
||||
|
||||
#define LUA_KFUNCTION(_name) \
|
||||
static int (_name)(lua_State *L, int status, lua_KContext ctx); \
|
||||
static int (_name ## _52)(lua_State *L) { \
|
||||
lua_KContext ctx; \
|
||||
int status = lua_getctx(L, &ctx); \
|
||||
return (_name)(L, status, ctx); \
|
||||
} \
|
||||
static int (_name)(lua_State *L, int status, lua_KContext ctx)
|
||||
|
||||
#define lua_pcallk(L, na, nr, err, ctx, cont) \
|
||||
lua_pcallk((L), (na), (nr), (err), (ctx), cont ## _52)
|
||||
|
||||
#define lua_callk(L, na, nr, ctx, cont) \
|
||||
lua_callk((L), (na), (nr), (ctx), cont ## _52)
|
||||
|
||||
#define lua_yieldk(L, nr, ctx, cont) \
|
||||
lua_yieldk((L), (nr), (ctx), cont ## _52)
|
||||
|
||||
#ifdef lua_call
|
||||
# undef lua_call
|
||||
# define lua_call(L, na, nr) \
|
||||
(lua_callk)((L), (na), (nr), 0, NULL)
|
||||
#endif
|
||||
|
||||
#ifdef lua_pcall
|
||||
# undef lua_pcall
|
||||
# define lua_pcall(L, na, nr, err) \
|
||||
(lua_pcallk)((L), (na), (nr), (err), 0, NULL)
|
||||
#endif
|
||||
|
||||
#ifdef lua_yield
|
||||
# undef lua_yield
|
||||
# define lua_yield(L, nr) \
|
||||
(lua_yieldk)((L), (nr), 0, NULL)
|
||||
#endif
|
||||
|
||||
#endif /* Lua 5.2 only */
|
||||
|
||||
|
||||
|
||||
/* other Lua versions */
|
||||
#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 501 || LUA_VERSION_NUM > 504
|
||||
|
||||
# error "unsupported Lua version (i.e. not Lua 5.1, 5.2, 5.3, or 5.4)"
|
||||
|
||||
#endif /* other Lua versions except 5.1, 5.2, 5.3, and 5.4 */
|
||||
|
||||
|
||||
|
||||
/* helper macro for defining continuation functions (for every version
|
||||
* *except* Lua 5.2) */
|
||||
#ifndef LUA_KFUNCTION
|
||||
#define LUA_KFUNCTION(_name) \
|
||||
static int (_name)(lua_State *L, int status, lua_KContext ctx)
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(COMPAT53_INCLUDE_SOURCE)
|
||||
# include "compat-5.3.c"
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* COMPAT53_H_ */
|
||||
|
||||
@@ -0,0 +1,515 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2019-2021 A. Orlenko
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#include <lauxlib.h>
|
||||
#include <lua.h>
|
||||
|
||||
#include "compat-5.3.h"
|
||||
|
||||
size_t MLUA_WRAPPED_ERROR_SIZE = 0;
|
||||
size_t MLUA_WRAPPED_PANIC_SIZE = 0;
|
||||
|
||||
const void *MLUA_WRAPPED_ERROR_KEY = NULL;
|
||||
const void *MLUA_WRAPPED_PANIC_KEY = NULL;
|
||||
|
||||
extern void wrapped_error_traceback(lua_State *L, int error_idx,
|
||||
int traceback_idx);
|
||||
|
||||
extern int mlua_hook_proc(lua_State *L, lua_Debug *ar);
|
||||
|
||||
#define max(a, b) (a > b ? a : b)
|
||||
|
||||
// I believe luaL_traceback < 5.4 requires this much free stack to not error.
|
||||
// 5.4 uses luaL_Buffer
|
||||
const int LUA_TRACEBACK_STACK = 11;
|
||||
|
||||
typedef struct {
|
||||
const char *data;
|
||||
size_t len;
|
||||
} StringArg;
|
||||
|
||||
static void handle_wrapped_error(lua_State *L) {
|
||||
if (lua_checkstack(L, LUA_TRACEBACK_STACK) != 0) {
|
||||
luaL_traceback(L, L, NULL, 0);
|
||||
// Convert to CallbackError and attach traceback
|
||||
wrapped_error_traceback(L, -2, -1);
|
||||
lua_pop(L, 1);
|
||||
} else {
|
||||
// Convert to CallbackError with error message as a traceback
|
||||
wrapped_error_traceback(L, -1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// A wrapper around Rust function to protect from triggering longjmp in Rust.
|
||||
// Rust callback expected to return positive number of output values or
|
||||
// -1 in case of error, -2 in case of panic.
|
||||
static int lua_call_rust(lua_State *L) {
|
||||
int nargs = lua_gettop(L);
|
||||
|
||||
// We need one extra stack space to store preallocated memory, and at least 2
|
||||
// stack spaces overall for handling error metatables in rust fn
|
||||
int extra_stack = 1;
|
||||
if (nargs < 2) {
|
||||
extra_stack = 2 - nargs;
|
||||
}
|
||||
|
||||
luaL_checkstack(L, extra_stack,
|
||||
"not enough stack space for callback error handling");
|
||||
|
||||
// We cannot shadow rust errors with Lua ones, we pre-allocate enough memory
|
||||
// to store a wrapped error or panic *before* we proceed.
|
||||
lua_newuserdata(L, max(MLUA_WRAPPED_ERROR_SIZE, MLUA_WRAPPED_PANIC_SIZE));
|
||||
lua_rotate(L, 1, 1);
|
||||
|
||||
lua_CFunction rust_callback = lua_touserdata(L, lua_upvalueindex(1));
|
||||
|
||||
int ret = rust_callback(L);
|
||||
if (ret < 0) {
|
||||
if (ret == -1 /* WrappedError */) {
|
||||
handle_wrapped_error(L);
|
||||
}
|
||||
lua_error(L);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void lua_call_mlua_hook_proc(lua_State *L, lua_Debug *ar) {
|
||||
luaL_checkstack(L, 2, "not enough stack space for callback error handling");
|
||||
lua_newuserdata(L, max(MLUA_WRAPPED_ERROR_SIZE, MLUA_WRAPPED_PANIC_SIZE));
|
||||
lua_rotate(L, 1, 1);
|
||||
int ret = mlua_hook_proc(L, ar);
|
||||
if (ret < 0) {
|
||||
if (ret == -1 /* WrappedError */) {
|
||||
handle_wrapped_error(L);
|
||||
}
|
||||
lua_error(L);
|
||||
}
|
||||
}
|
||||
|
||||
static inline lua_Integer lua_popinteger(lua_State *L) {
|
||||
lua_Integer index = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return index;
|
||||
}
|
||||
|
||||
//
|
||||
// Common functions
|
||||
//
|
||||
|
||||
int lua_gc_s(lua_State *L) {
|
||||
int data = lua_popinteger(L);
|
||||
int what = lua_popinteger(L);
|
||||
int ret = lua_gc(L, what, data);
|
||||
lua_pushinteger(L, ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int luaL_ref_s(lua_State *L) {
|
||||
int ret = luaL_ref(L, -2);
|
||||
lua_pushinteger(L, ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_pushlstring_s(lua_State *L) {
|
||||
StringArg *s = lua_touserdata(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_pushlstring(L, s->data, s->len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_tolstring_s(lua_State *L) {
|
||||
void *len = lua_touserdata(L, -1);
|
||||
lua_pop(L, 1);
|
||||
const char *s = lua_tolstring(L, -1, len);
|
||||
lua_pushlightuserdata(L, (void *)s);
|
||||
return 2;
|
||||
}
|
||||
|
||||
int lua_newthread_s(lua_State *L) {
|
||||
lua_newthread(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_newuserdata_s(lua_State *L) {
|
||||
size_t size = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_newuserdata(L, size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_newwrappederror_s(lua_State *L) {
|
||||
lua_newuserdata(L, MLUA_WRAPPED_ERROR_SIZE);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_pushcclosure_s(lua_State *L) {
|
||||
int n = lua_gettop(L) - 1;
|
||||
lua_CFunction fn = lua_touserdata(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_pushcclosure(L, fn, n);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_pushrclosure_s(lua_State *L) {
|
||||
int n = lua_gettop(L);
|
||||
lua_pushcclosure(L, lua_call_rust, n);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int luaL_requiref_s(lua_State *L) {
|
||||
const char *modname = lua_touserdata(L, -3);
|
||||
lua_CFunction openf = lua_touserdata(L, -2);
|
||||
int glb = lua_tointeger(L, -1);
|
||||
lua_pop(L, 3);
|
||||
luaL_requiref(L, modname, openf, glb);
|
||||
return 1;
|
||||
}
|
||||
|
||||
//
|
||||
// Table functions
|
||||
//
|
||||
|
||||
int lua_newtable_s(lua_State *L) {
|
||||
lua_createtable(L, 0, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_createtable_s(lua_State *L) {
|
||||
int nrec = lua_popinteger(L);
|
||||
int narr = lua_popinteger(L);
|
||||
lua_createtable(L, narr, nrec);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_gettable_s(lua_State *L) {
|
||||
lua_gettable(L, -2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_settable_s(lua_State *L) {
|
||||
lua_settable(L, -3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lua_geti_s(lua_State *L) {
|
||||
lua_Integer index = lua_popinteger(L);
|
||||
lua_geti(L, -1, index);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_rawset_s(lua_State *L) {
|
||||
lua_rawset(L, -3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lua_rawseti_s(lua_State *L) {
|
||||
lua_Integer index = lua_popinteger(L);
|
||||
lua_rawseti(L, -2, index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lua_rawsetp_s(lua_State *L) {
|
||||
void *p = lua_touserdata(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_rawsetp(L, -2, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lua_rawsetfield_s(lua_State *L) {
|
||||
StringArg *s = lua_touserdata(L, -2);
|
||||
lua_pushlstring(L, s->data, s->len);
|
||||
lua_replace(L, -3);
|
||||
lua_rawset(L, -3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lua_rawinsert_s(lua_State *L) {
|
||||
lua_Integer index = lua_popinteger(L);
|
||||
lua_Integer size = lua_rawlen(L, -2);
|
||||
|
||||
for (lua_Integer i = size; i >= index; i--) {
|
||||
// table[i+1] = table[i]
|
||||
lua_rawgeti(L, -2, i);
|
||||
lua_rawseti(L, -3, i + 1);
|
||||
}
|
||||
lua_rawseti(L, -2, index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lua_rawremove_s(lua_State *L) {
|
||||
lua_Integer index = lua_popinteger(L);
|
||||
lua_Integer size = lua_rawlen(L, -1);
|
||||
|
||||
for (lua_Integer i = index; i < size; i++) {
|
||||
lua_rawgeti(L, -1, i + 1);
|
||||
lua_rawseti(L, -2, i);
|
||||
}
|
||||
lua_pushnil(L);
|
||||
lua_rawseti(L, -2, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int luaL_len_s(lua_State *L) {
|
||||
lua_pushinteger(L, luaL_len(L, -1));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_next_s(lua_State *L) {
|
||||
int ret = lua_next(L, -2);
|
||||
lua_pushinteger(L, ret);
|
||||
return ret == 0 ? 1 : 3;
|
||||
}
|
||||
|
||||
//
|
||||
// Moved from Rust to C
|
||||
//
|
||||
|
||||
// Wrapper to lookup in `field_getters` first, then `methods`, ending
|
||||
// original `__index`. Used only if `field_getters` or `methods` set.
|
||||
int meta_index_impl(lua_State *state) {
|
||||
// stack: self, key
|
||||
luaL_checkstack(state, 2, NULL);
|
||||
|
||||
// lookup in `field_getters` table
|
||||
if (lua_isnil(state, lua_upvalueindex(2)) == 0) {
|
||||
lua_pushvalue(state, -1); // `key` arg
|
||||
if (lua_rawget(state, lua_upvalueindex(2)) != LUA_TNIL) {
|
||||
lua_insert(state, -3); // move function
|
||||
lua_pop(state, 1); // remove `key`
|
||||
lua_call(state, 1, 1);
|
||||
return 1;
|
||||
}
|
||||
lua_pop(state, 1); // pop the nil value
|
||||
}
|
||||
// lookup in `methods` table
|
||||
if (lua_isnil(state, lua_upvalueindex(3)) == 0) {
|
||||
lua_pushvalue(state, -1); // `key` arg
|
||||
if (lua_rawget(state, lua_upvalueindex(3)) != LUA_TNIL) {
|
||||
lua_insert(state, -3);
|
||||
lua_pop(state, 2);
|
||||
return 1;
|
||||
}
|
||||
lua_pop(state, 1); // pop the nil value
|
||||
}
|
||||
|
||||
// lookup in `__index`
|
||||
lua_pushvalue(state, lua_upvalueindex(1));
|
||||
switch (lua_type(state, -1)) {
|
||||
case LUA_TNIL:
|
||||
lua_pop(state, 1); // pop the nil value
|
||||
const char *field = lua_tostring(state, -1);
|
||||
luaL_error(state, "attempt to get an unknown field '%s'", field);
|
||||
break;
|
||||
|
||||
case LUA_TTABLE:
|
||||
lua_insert(state, -2);
|
||||
lua_gettable(state, -2);
|
||||
break;
|
||||
|
||||
case LUA_TFUNCTION:
|
||||
lua_insert(state, -3);
|
||||
lua_call(state, 2, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Similar to `meta_index_impl`, checks `field_setters` table first, then
|
||||
// `__newindex` metamethod. Used only if `field_setters` set.
|
||||
int meta_newindex_impl(lua_State *state) {
|
||||
// stack: self, key, value
|
||||
luaL_checkstack(state, 2, NULL);
|
||||
|
||||
// lookup in `field_setters` table
|
||||
lua_pushvalue(state, -2); // `key` arg
|
||||
if (lua_rawget(state, lua_upvalueindex(2)) != LUA_TNIL) {
|
||||
lua_remove(state, -3); // remove `key`
|
||||
lua_insert(state, -3); // move function
|
||||
lua_call(state, 2, 0);
|
||||
return 0;
|
||||
}
|
||||
lua_pop(state, 1); // pop the nil value
|
||||
|
||||
// lookup in `__newindex`
|
||||
lua_pushvalue(state, lua_upvalueindex(1));
|
||||
switch (lua_type(state, -1)) {
|
||||
case LUA_TNIL:
|
||||
lua_pop(state, 1); // pop the nil value
|
||||
const char *field = lua_tostring(state, -2);
|
||||
luaL_error(state, "attempt to set an unknown field '%s'", field);
|
||||
break;
|
||||
|
||||
case LUA_TTABLE:
|
||||
lua_insert(state, -3);
|
||||
lua_settable(state, -3);
|
||||
break;
|
||||
|
||||
case LUA_TFUNCTION:
|
||||
lua_insert(state, -4);
|
||||
lua_call(state, 3, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// See Function::bind
|
||||
int bind_call_impl(lua_State *state) {
|
||||
int nargs = lua_gettop(state);
|
||||
int nbinds = lua_tointeger(state, lua_upvalueindex(2));
|
||||
luaL_checkstack(state, nbinds + 2, NULL);
|
||||
|
||||
lua_settop(state, nargs + nbinds + 1);
|
||||
lua_rotate(state, -(nargs + nbinds + 1), nbinds + 1);
|
||||
|
||||
lua_pushvalue(state, lua_upvalueindex(1));
|
||||
lua_replace(state, 1);
|
||||
|
||||
for (int i = 0; i < nbinds; i++) {
|
||||
lua_pushvalue(state, lua_upvalueindex(i + 3));
|
||||
lua_replace(state, i + 2);
|
||||
}
|
||||
|
||||
lua_call(state, nargs + nbinds, LUA_MULTRET);
|
||||
return lua_gettop(state);
|
||||
}
|
||||
|
||||
// Returns 1 if a value at index `index` is a special wrapped struct identified
|
||||
// by `key`
|
||||
int is_wrapped_struct(lua_State *state, int index, const void *key) {
|
||||
if (key == NULL) {
|
||||
// Not yet initialized?
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *ud = lua_touserdata(state, index);
|
||||
if (ud == NULL || lua_getmetatable(state, index) == 0) {
|
||||
return 0;
|
||||
}
|
||||
lua_rawgetp(state, LUA_REGISTRYINDEX, key);
|
||||
int res = lua_rawequal(state, -1, -2);
|
||||
lua_pop(state, 2);
|
||||
return res;
|
||||
}
|
||||
|
||||
// Takes an error at the top of the stack and converts Lua errors into a string
|
||||
// with attached traceback. If the error is a WrappedError or WrappedPanic, does
|
||||
// not modify it. This function does its best to avoid triggering another error
|
||||
// and shadowing previous rust errors.
|
||||
int error_traceback(lua_State *state) {
|
||||
if (lua_checkstack(state, 2) == 0) {
|
||||
// If we don't have enough stack space to even check the error type, do
|
||||
// nothing so we don't risk shadowing a rust panic.
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (MLUA_WRAPPED_PANIC_KEY == NULL ||
|
||||
is_wrapped_struct(state, -1, MLUA_WRAPPED_PANIC_KEY) ||
|
||||
is_wrapped_struct(state, -1, MLUA_WRAPPED_ERROR_KEY)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *s = luaL_tolstring(state, -1, NULL);
|
||||
if (lua_checkstack(state, LUA_TRACEBACK_STACK) != 0) {
|
||||
luaL_traceback(state, state, s, 1);
|
||||
lua_remove(state, -2);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int error_traceback_s(lua_State *L) {
|
||||
lua_State *L1 = lua_touserdata(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return error_traceback(L1);
|
||||
}
|
||||
|
||||
// A `pcall` implementation that does not allow Lua to catch Rust panics.
|
||||
// Instead, panics automatically resumed.
|
||||
int lua_nopanic_pcall(lua_State *state) {
|
||||
luaL_checkstack(state, 2, NULL);
|
||||
|
||||
int top = lua_gettop(state);
|
||||
if (top == 0) {
|
||||
lua_pushstring(state, "not enough arguments to pcall");
|
||||
lua_error(state);
|
||||
}
|
||||
|
||||
if (lua_pcall(state, top - 1, LUA_MULTRET, 0) == LUA_OK) {
|
||||
lua_pushboolean(state, 1);
|
||||
lua_insert(state, 1);
|
||||
return lua_gettop(state);
|
||||
}
|
||||
|
||||
if (is_wrapped_struct(state, -1, MLUA_WRAPPED_PANIC_KEY)) {
|
||||
lua_error(state);
|
||||
}
|
||||
lua_pushboolean(state, 0);
|
||||
lua_insert(state, -2);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// A `xpcall` implementation that does not allow Lua to catch Rust panics.
|
||||
// Instead, panics automatically resumed.
|
||||
|
||||
static int xpcall_msgh(lua_State *state) {
|
||||
luaL_checkstack(state, 2, NULL);
|
||||
if (is_wrapped_struct(state, -1, MLUA_WRAPPED_PANIC_KEY)) {
|
||||
return 1;
|
||||
}
|
||||
lua_pushvalue(state, lua_upvalueindex(1));
|
||||
lua_insert(state, 1);
|
||||
lua_call(state, lua_gettop(state) - 1, LUA_MULTRET);
|
||||
return lua_gettop(state);
|
||||
}
|
||||
|
||||
int lua_nopanic_xpcall(lua_State *state) {
|
||||
luaL_checkstack(state, 2, NULL);
|
||||
|
||||
int top = lua_gettop(state);
|
||||
if (top < 2) {
|
||||
lua_pushstring(state, "not enough arguments to xpcall");
|
||||
lua_error(state);
|
||||
}
|
||||
|
||||
lua_pushvalue(state, 2);
|
||||
lua_pushcclosure(state, xpcall_msgh, 1);
|
||||
lua_copy(state, 1, 2);
|
||||
lua_replace(state, 1);
|
||||
|
||||
if (lua_pcall(state, lua_gettop(state) - 2, LUA_MULTRET, 1) == LUA_OK) {
|
||||
lua_pushboolean(state, 1);
|
||||
lua_insert(state, 2);
|
||||
return lua_gettop(state) - 1;
|
||||
}
|
||||
|
||||
if (is_wrapped_struct(state, -1, MLUA_WRAPPED_PANIC_KEY)) {
|
||||
lua_error(state);
|
||||
}
|
||||
lua_pushboolean(state, 0);
|
||||
lua_insert(state, -2);
|
||||
return 2;
|
||||
}
|
||||
+11
-38
@@ -1,12 +1,10 @@
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::{ptr, slice};
|
||||
use std::slice;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::types::LuaRef;
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, error_traceback, pop_error, protect_lua_closure, StackGuard,
|
||||
};
|
||||
use crate::util::{assert_stack, check_stack, pop_error, StackGuard};
|
||||
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
@@ -67,7 +65,7 @@ impl<'lua> Function<'lua> {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, nargs + 3)?;
|
||||
|
||||
ffi::lua_pushcfunction(lua.state, error_traceback);
|
||||
ffi::lua_pushcfunction(lua.state, ffi::safe::error_traceback);
|
||||
let stack_start = ffi::lua_gettop(lua.state);
|
||||
lua.push_ref(&self.0);
|
||||
for arg in args {
|
||||
@@ -92,7 +90,7 @@ impl<'lua> Function<'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`].
|
||||
/// Internally it wraps the function to an [`AsyncThread`].
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
@@ -161,26 +159,6 @@ impl<'lua> Function<'lua> {
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn bind<A: ToLuaMulti<'lua>>(&self, args: A) -> Result<Function<'lua>> {
|
||||
unsafe extern "C" fn bind_call_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
let nargs = ffi::lua_gettop(state);
|
||||
let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(2)) as c_int;
|
||||
ffi::luaL_checkstack(state, nbinds + 2, ptr::null());
|
||||
|
||||
ffi::lua_settop(state, nargs + nbinds + 1);
|
||||
ffi::lua_rotate(state, -(nargs + nbinds + 1), nbinds + 1);
|
||||
|
||||
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
|
||||
ffi::lua_replace(state, 1);
|
||||
|
||||
for i in 0..nbinds {
|
||||
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(i + 3));
|
||||
ffi::lua_replace(state, i + 2);
|
||||
}
|
||||
|
||||
ffi::lua_call(state, nargs + nbinds, ffi::LUA_MULTRET);
|
||||
ffi::lua_gettop(state)
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
|
||||
let args = args.to_lua_multi(lua)?;
|
||||
@@ -193,15 +171,13 @@ impl<'lua> Function<'lua> {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, nargs + 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer);
|
||||
for arg in args {
|
||||
lua.push_value(arg)?;
|
||||
}
|
||||
|
||||
protect_lua_closure(lua.state, nargs + 2, 1, |state| {
|
||||
ffi::lua_pushcclosure(state, bind_call_impl, nargs + 2);
|
||||
})?;
|
||||
ffi::safe::lua_pushcclosure(lua.state, ffi::safe::bind_call_impl, nargs + 2)?;
|
||||
|
||||
Ok(Function(lua.pop_ref()))
|
||||
}
|
||||
@@ -211,7 +187,7 @@ impl<'lua> Function<'lua> {
|
||||
///
|
||||
/// If `strip` is true, the binary representation may not include all debug information
|
||||
/// about the function, to save space.
|
||||
pub fn dump(&self, strip: bool) -> Result<Vec<u8>> {
|
||||
pub fn dump(&self, strip: bool) -> Vec<u8> {
|
||||
unsafe extern "C" fn writer(
|
||||
_state: *mut ffi::lua_State,
|
||||
buf: *const c_void,
|
||||
@@ -229,18 +205,15 @@ impl<'lua> Function<'lua> {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 1);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let data_ptr = &mut data as *mut Vec<u8> as *mut c_void;
|
||||
let strip = if strip { 1 } else { 0 };
|
||||
ffi::lua_dump(
|
||||
lua.state,
|
||||
writer,
|
||||
&mut data as *mut Vec<u8> as *mut c_void,
|
||||
strip,
|
||||
);
|
||||
ffi::lua_dump(lua.state, writer, data_ptr, strip);
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
}
|
||||
|
||||
Ok(data)
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
-7
@@ -9,9 +9,9 @@ use crate::util::callback_error;
|
||||
/// Contains information about currently executing Lua code.
|
||||
///
|
||||
/// The `Debug` structure is provided as a parameter to the hook function set with
|
||||
/// [`Lua::set_hook`]. You may call the methods on this structure to retrieve information about the
|
||||
/// Lua code executing at the time that the hook function was called. Further information can be
|
||||
/// found in the [Lua 5.3 documentaton][lua_doc].
|
||||
/// [`Lua::set_hook`]. You may call the methods on this structure to retrieve information about the
|
||||
/// Lua code executing at the time that the hook function was called. Further information can be
|
||||
/// found in the [Lua 5.3 documentation][lua_doc].
|
||||
///
|
||||
/// [lua_doc]: https://www.lua.org/manual/5.3/manual.html#lua_Debug
|
||||
/// [`Lua::set_hook`]: struct.Lua.html#method.set_hook
|
||||
@@ -130,7 +130,7 @@ pub struct HookTriggers {
|
||||
pub on_returns: bool,
|
||||
/// Before executing a new line, or returning from a function call.
|
||||
pub every_line: bool,
|
||||
/// After a certain number of VM instructions have been executed. When set to `Some(count)`,
|
||||
/// After a certain number of VM instructions have been executed. When set to `Some(count)`,
|
||||
/// `count` is the number of VM instructions to execute before calling the hook.
|
||||
///
|
||||
/// # Performance
|
||||
@@ -165,7 +165,8 @@ impl HookTriggers {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe extern "C" fn hook_proc(state: *mut lua_State, ar: *mut lua_Debug) {
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn mlua_hook_proc(state: *mut lua_State, ar: *mut lua_Debug) -> c_int {
|
||||
callback_error(state, |_| {
|
||||
let debug = Debug {
|
||||
ar,
|
||||
@@ -182,8 +183,8 @@ pub(crate) unsafe extern "C" fn hook_proc(state: *mut lua_State, ar: *mut lua_De
|
||||
Err(_) => mlua_panic!("Lua should not allow hooks to be called within another hook"),
|
||||
}?;
|
||||
|
||||
Ok(())
|
||||
});
|
||||
Ok(0)
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn ptr_to_str<'a>(input: *const c_char) -> Option<&'a [u8]> {
|
||||
|
||||
+66
-11
@@ -23,6 +23,7 @@
|
||||
//!
|
||||
//! 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.
|
||||
//! Fields are supported using the [`UserDataFields`] API.
|
||||
//!
|
||||
//! # Serde support
|
||||
//!
|
||||
@@ -50,8 +51,8 @@
|
||||
//!
|
||||
//! [Lua programming language]: https://www.lua.org/
|
||||
//! [`Lua`]: struct.Lua.html
|
||||
//! [executing]: struct.Lua.html#method.exec
|
||||
//! [evaluating]: struct.Lua.html#method.eval
|
||||
//! [executing]: struct.Chunk.html#method.exec
|
||||
//! [evaluating]: struct.Chunk.html#method.eval
|
||||
//! [globals]: struct.Lua.html#method.globals
|
||||
//! [`ToLua`]: trait.ToLua.html
|
||||
//! [`FromLua`]: trait.FromLua.html
|
||||
@@ -59,6 +60,7 @@
|
||||
//! [`FromLuaMulti`]: trait.FromLuaMulti.html
|
||||
//! [`Function`]: struct.Function.html
|
||||
//! [`UserData`]: trait.UserData.html
|
||||
//! [`UserDataFields`]: trait.UserDataFields.html
|
||||
//! [`UserDataMethods`]: trait.UserDataMethods.html
|
||||
//! [`LuaSerdeExt`]: serde/trait.LuaSerdeExt.html
|
||||
//! [`Value`]: enum.Value.html
|
||||
@@ -70,7 +72,7 @@
|
||||
//! [`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
|
||||
|
||||
// mlua types in rustdoc of other crates get linked to here.
|
||||
#![doc(html_root_url = "https://docs.rs/mlua/0.5.2")]
|
||||
#![doc(html_root_url = "https://docs.rs/mlua/0.6.0-beta.2")]
|
||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||
// warnings at all.
|
||||
#![doc(test(attr(deny(warnings))))]
|
||||
@@ -96,12 +98,13 @@ mod userdata;
|
||||
mod util;
|
||||
mod value;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use crate::ffi::lua_State;
|
||||
|
||||
pub use crate::error::{Error, ExternalError, ExternalResult, Result};
|
||||
pub use crate::function::Function;
|
||||
pub use crate::hook::{Debug, DebugNames, DebugSource, DebugStack, HookTriggers};
|
||||
pub use crate::lua::{Chunk, ChunkMode, GCMode, Lua};
|
||||
pub use crate::lua::{AsChunk, Chunk, ChunkMode, GCMode, Lua, LuaOptions};
|
||||
pub use crate::multi::Variadic;
|
||||
pub use crate::scope::Scope;
|
||||
pub use crate::stdlib::StdLib;
|
||||
@@ -109,25 +112,77 @@ pub use crate::string::String;
|
||||
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::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods,
|
||||
};
|
||||
pub use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub use crate::thread::AsyncThread;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
pub use crate::serde::LuaSerdeExt;
|
||||
#[doc(inline)]
|
||||
pub use crate::serde::{ser::Options as SerializeOptions, LuaSerdeExt};
|
||||
|
||||
pub mod prelude;
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
pub mod serde;
|
||||
|
||||
// Re-export #[mlua_derive::lua_module].
|
||||
#[cfg(feature = "mlua_derive")]
|
||||
#[cfg(any(feature = "mlua_derive"))]
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use]
|
||||
extern crate mlua_derive;
|
||||
#[cfg(feature = "mlua_derive")]
|
||||
#[doc(hidden)]
|
||||
pub use mlua_derive::*;
|
||||
|
||||
/// Create a type that implements [`AsChunk`] and can capture Rust variables.
|
||||
///
|
||||
/// This macro allows to write Lua code directly in Rust code.
|
||||
///
|
||||
/// Rust variables can be referenced from Lua using `$` prefix, as shown in the example below.
|
||||
/// User Rust types needs to implement [`UserData`] or [`ToLua`] traits.
|
||||
///
|
||||
/// Captured variables are moved into the chunk.
|
||||
///
|
||||
/// ```
|
||||
/// use mlua::{Lua, Result, chunk};
|
||||
///
|
||||
/// fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// let name = "Rustacean";
|
||||
/// lua.load(chunk! {
|
||||
/// print("hello, " .. $name)
|
||||
/// }).exec()
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## Syntax issues
|
||||
///
|
||||
/// Since the Rust tokenizer will tokenize Lua code, this imposes some restrictions.
|
||||
/// The main thing to remember is:
|
||||
///
|
||||
/// - Use double quoted strings (`""`) instead of single quoted strings (`''`).
|
||||
///
|
||||
/// (Single quoted strings only work if they contain a single character, since in Rust,
|
||||
/// `'a'` is a character literal).
|
||||
///
|
||||
/// Other minor limitations:
|
||||
///
|
||||
/// - Certain escape codes in string literals.
|
||||
/// (Specifically: `\a`, `\b`, `\f`, `\v`, `\123` (octal escape codes), `\u`, and `\U`).
|
||||
///
|
||||
/// These are accepted: : `\\`, `\n`, `\t`, `\r`, `\xAB` (hex escape codes), and `\0`
|
||||
///
|
||||
/// - The `//` (floor division) operator is unusable, as its start a comment.
|
||||
///
|
||||
/// Everything else should work.
|
||||
///
|
||||
/// [`AsChunk`]: trait.AsChunk.html
|
||||
/// [`UserData`]: trait.UserData.html
|
||||
/// [`ToLua`]: trait.ToLua.html
|
||||
#[cfg(any(feature = "macros"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
|
||||
pub use mlua_derive::chunk;
|
||||
|
||||
#[cfg(any(feature = "module"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "module")))]
|
||||
pub use mlua_derive::lua_module;
|
||||
|
||||
+646
-376
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::wrong_self_convention)]
|
||||
|
||||
use std::iter::FromIterator;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
+6
-1
@@ -4,13 +4,18 @@ pub use crate::{
|
||||
AnyUserData as LuaAnyUserData, Chunk as LuaChunk, Error as LuaError,
|
||||
ExternalError as LuaExternalError, ExternalResult as LuaExternalResult, FromLua, FromLuaMulti,
|
||||
Function as LuaFunction, GCMode as LuaGCMode, Integer as LuaInteger,
|
||||
LightUserData as LuaLightUserData, Lua, MetaMethod as LuaMetaMethod,
|
||||
LightUserData as LuaLightUserData, Lua, LuaOptions, MetaMethod as LuaMetaMethod,
|
||||
MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber, RegistryKey as LuaRegistryKey,
|
||||
Result as LuaResult, String as LuaString, Table as LuaTable, TableExt as LuaTableExt,
|
||||
TablePairs as LuaTablePairs, TableSequence as LuaTableSequence, Thread as LuaThread,
|
||||
ThreadStatus as LuaThreadStatus, ToLua, ToLuaMulti, UserData as LuaUserData,
|
||||
UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable,
|
||||
UserDataMethods as LuaUserDataMethods, Value as LuaValue,
|
||||
};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub use crate::AsyncThread as LuaAsyncThread;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[doc(inline)]
|
||||
pub use crate::{LuaSerdeExt, SerializeOptions as LuaSerializeOptions};
|
||||
|
||||
+317
-122
@@ -2,8 +2,7 @@ use std::any::Any;
|
||||
use std::cell::{Cell, Ref, RefCell, RefMut};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
use std::rc::Rc;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::Serialize;
|
||||
@@ -12,13 +11,15 @@ use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::types::{Callback, LuaRef, MaybeSend, UserDataCell};
|
||||
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods, UserDataWrapped};
|
||||
use crate::util::{
|
||||
assert_stack, init_userdata_metatable, protect_lua_closure, push_string, push_userdata,
|
||||
take_userdata, StackGuard,
|
||||
use crate::types::{Callback, LuaRef, MaybeSend};
|
||||
use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
|
||||
};
|
||||
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti, Value};
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, get_userdata, init_userdata_metatable, push_userdata, take_userdata,
|
||||
StackGuard,
|
||||
};
|
||||
use crate::value::{FromLua, FromLuaMulti, MultiValue, ToLua, ToLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
@@ -33,13 +34,14 @@ use {
|
||||
/// See [`Lua::scope`] for more details.
|
||||
///
|
||||
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub struct Scope<'lua, 'scope> {
|
||||
lua: &'lua Lua,
|
||||
destructors: RefCell<Vec<(LuaRef<'lua>, fn(LuaRef<'lua>) -> Vec<Box<dyn Any>>)>>,
|
||||
destructors: RefCell<Vec<(LuaRef<'lua>, DestructorCallback<'lua>)>>,
|
||||
_scope_invariant: PhantomData<Cell<&'scope ()>>,
|
||||
}
|
||||
|
||||
type DestructorCallback<'lua> = Box<dyn Fn(LuaRef<'lua>) -> Vec<Box<dyn Any>> + 'lua>;
|
||||
|
||||
impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
pub(crate) fn new(lua: &'lua Lua) -> Scope<'lua, 'scope> {
|
||||
Scope {
|
||||
@@ -52,7 +54,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
/// 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.
|
||||
/// scope drop. See [`Lua::scope`] for more details.
|
||||
///
|
||||
/// [`Lua::create_function`]: struct.Lua.html#method.create_function
|
||||
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||
@@ -64,7 +66,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
{
|
||||
// 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
|
||||
// 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.
|
||||
//
|
||||
@@ -81,7 +83,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
/// 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.
|
||||
/// 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
|
||||
@@ -106,7 +108,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
/// Wraps a Rust async function or closure, creating a callable Lua function handle to it.
|
||||
///
|
||||
/// This is a version of [`Lua::create_async_function`] that creates a callback which expires on
|
||||
/// scope drop. See [`Lua::scope`] and [`Lua::async_scope`] for more details.
|
||||
/// scope drop. See [`Lua::scope`] and [`Lua::async_scope`] for more details.
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
@@ -149,7 +151,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
where
|
||||
T: 'static + UserData,
|
||||
{
|
||||
self.create_userdata_inner(UserDataWrapped::new(data))
|
||||
self.create_userdata_inner(UserDataCell::new(data))
|
||||
}
|
||||
|
||||
/// Create a Lua userdata object from a custom serializable userdata type.
|
||||
@@ -169,26 +171,43 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
where
|
||||
T: 'static + UserData + Serialize,
|
||||
{
|
||||
self.create_userdata_inner(UserDataWrapped::new_ser(data))
|
||||
self.create_userdata_inner(UserDataCell::new_ser(data))
|
||||
}
|
||||
|
||||
fn create_userdata_inner<T>(&self, data: UserDataWrapped<T>) -> Result<AnyUserData<'lua>>
|
||||
fn create_userdata_inner<T>(&self, data: UserDataCell<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;
|
||||
let ud = self.lua.make_userdata(data)?;
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
let newtable = self.lua.create_table()?;
|
||||
let destructor: DestructorCallback = Box::new(move |ud| {
|
||||
let state = ud.lua.state;
|
||||
let _sg = StackGuard::new(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.
|
||||
|
||||
ud.lua.push_ref(&ud);
|
||||
|
||||
// We know the destructor has not run yet because we hold a reference to the userdata.
|
||||
|
||||
// Clear uservalue
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
ffi::lua_pushnil(state);
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
ud.lua.push_ref(&newtable.0);
|
||||
ffi::lua_setuservalue(state, -2);
|
||||
|
||||
vec![Box::new(take_userdata::<UserDataCell<T>>(state))]
|
||||
}));
|
||||
Ok(u)
|
||||
});
|
||||
self.destructors
|
||||
.borrow_mut()
|
||||
.push((ud.0.clone(), destructor));
|
||||
|
||||
Ok(ud)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,10 +223,10 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
///
|
||||
/// 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
|
||||
/// '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
|
||||
/// `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.
|
||||
///
|
||||
@@ -219,51 +238,44 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
where
|
||||
T: 'scope + UserData,
|
||||
{
|
||||
let data = Rc::new(RefCell::new(UserDataWrapped::new(data)));
|
||||
let data = UserDataCell::new_arc(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
|
||||
// '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<UserDataCell<T>>,
|
||||
data: UserDataCell<T>,
|
||||
data_ptr: *mut c_void,
|
||||
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
|
||||
// `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::UserData(ud)) = value {
|
||||
unsafe {
|
||||
assert_stack(lua.state, 1);
|
||||
lua.push_ref(&ud.0);
|
||||
ffi::lua_getuservalue(lua.state, -1);
|
||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||
{
|
||||
ffi::lua_rawgeti(lua.state, -1, 1);
|
||||
ffi::lua_remove(lua.state, -2);
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 3)?;
|
||||
lua.push_userdata_ref(&ud.0)?;
|
||||
if get_userdata(lua.state, -1) == data_ptr {
|
||||
return Ok(());
|
||||
}
|
||||
return ffi::lua_touserdata(lua.state, -1)
|
||||
== check_data.as_ptr() as *mut c_void;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
};
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
};
|
||||
|
||||
match method {
|
||||
NonStaticMethod::Method(method) => {
|
||||
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
|
||||
if !check_ud_type(lua, args.pop_front()) {
|
||||
return Err(Error::UserDataTypeMismatch);
|
||||
}
|
||||
check_ud_type(lua, args.pop_front())?;
|
||||
let data = data
|
||||
.try_borrow()
|
||||
.map(|cell| Ref::map(cell, AsRef::as_ref))
|
||||
@@ -275,9 +287,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
NonStaticMethod::MethodMut(method) => {
|
||||
let method = RefCell::new(method);
|
||||
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
|
||||
if !check_ud_type(lua, args.pop_front()) {
|
||||
return Err(Error::UserDataTypeMismatch);
|
||||
}
|
||||
check_ud_type(lua, args.pop_front())?;
|
||||
let mut method = method
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
@@ -304,67 +314,133 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
}
|
||||
}
|
||||
|
||||
let mut ud_fields = NonStaticUserDataFields::default();
|
||||
let mut ud_methods = NonStaticUserDataMethods::default();
|
||||
T::add_fields(&mut ud_fields);
|
||||
T::add_methods(&mut ud_methods);
|
||||
|
||||
unsafe {
|
||||
let lua = self.lua;
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 6);
|
||||
check_stack(lua.state, 13)?;
|
||||
|
||||
push_userdata(lua.state, ())?;
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
ffi::lua_pushlightuserdata(lua.state, data.as_ptr() as *mut c_void);
|
||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||
protect_lua_closure(lua.state, 0, 1, |state| {
|
||||
// Lua 5.2/5.1 allows to store only table. Then we will wrap the value.
|
||||
ffi::lua_createtable(state, 1, 0);
|
||||
ffi::lua_pushlightuserdata(state, data.as_ptr() as *mut c_void);
|
||||
ffi::lua_rawseti(state, -2, 1);
|
||||
})?;
|
||||
ffi::lua_setuservalue(lua.state, -2);
|
||||
push_userdata(lua.state, data.clone())?;
|
||||
let data_ptr = ffi::lua_touserdata(lua.state, -1);
|
||||
|
||||
protect_lua_closure(lua.state, 0, 1, move |state| {
|
||||
ffi::lua_newtable(state);
|
||||
})?;
|
||||
// Prepare metatable, add meta methods first and then meta fields
|
||||
let meta_methods_nrec = ud_methods.meta_methods.len() + ud_fields.meta_fields.len() + 1;
|
||||
ffi::safe::lua_createtable(lua.state, 0, meta_methods_nrec as c_int)?;
|
||||
|
||||
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)?))?;
|
||||
let data = data.clone();
|
||||
lua.push_value(Value::Function(wrap_method(self, data, data_ptr, m)?))?;
|
||||
ffi::safe::lua_rawsetfield(lua.state, -2, k.validate()?.name())?;
|
||||
}
|
||||
for (k, f) in ud_fields.meta_fields {
|
||||
lua.push_value(f(mem::transmute(lua))?)?;
|
||||
ffi::safe::lua_rawsetfield(lua.state, -2, k.validate()?.name())?;
|
||||
}
|
||||
let metatable_index = ffi::lua_absindex(lua.state, -1);
|
||||
|
||||
protect_lua_closure(lua.state, 3, 1, |state| {
|
||||
ffi::lua_rawset(state, -3);
|
||||
})?;
|
||||
let mut field_getters_index = None;
|
||||
let field_getters_nrec = ud_fields.field_getters.len();
|
||||
if field_getters_nrec > 0 {
|
||||
ffi::safe::lua_createtable(lua.state, 0, field_getters_nrec as c_int)?;
|
||||
for (k, m) in ud_fields.field_getters {
|
||||
let data = data.clone();
|
||||
lua.push_value(Value::Function(wrap_method(self, data, data_ptr, m)?))?;
|
||||
ffi::safe::lua_rawsetfield(lua.state, -2, &k)?;
|
||||
}
|
||||
field_getters_index = Some(ffi::lua_absindex(lua.state, -1));
|
||||
}
|
||||
|
||||
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);
|
||||
})?;
|
||||
let mut field_setters_index = None;
|
||||
let field_setters_nrec = ud_fields.field_setters.len();
|
||||
if field_setters_nrec > 0 {
|
||||
ffi::safe::lua_createtable(lua.state, 0, field_setters_nrec as c_int)?;
|
||||
for (k, m) in ud_fields.field_setters {
|
||||
let data = data.clone();
|
||||
lua.push_value(Value::Function(wrap_method(self, data, data_ptr, m)?))?;
|
||||
ffi::safe::lua_rawsetfield(lua.state, -2, &k)?;
|
||||
}
|
||||
field_setters_index = Some(ffi::lua_absindex(lua.state, -1));
|
||||
}
|
||||
|
||||
let mut methods_index = None;
|
||||
let methods_nrec = ud_methods.methods.len();
|
||||
if methods_nrec > 0 {
|
||||
// Create table used for methods lookup
|
||||
ffi::safe::lua_createtable(lua.state, 0, methods_nrec as c_int)?;
|
||||
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);
|
||||
})?;
|
||||
let data = data.clone();
|
||||
lua.push_value(Value::Function(wrap_method(self, data, data_ptr, m)?))?;
|
||||
ffi::safe::lua_rawsetfield(lua.state, -2, &k)?;
|
||||
}
|
||||
methods_index = Some(ffi::lua_absindex(lua.state, -1));
|
||||
}
|
||||
|
||||
init_userdata_metatable::<()>(
|
||||
lua.state,
|
||||
metatable_index,
|
||||
field_getters_index,
|
||||
field_setters_index,
|
||||
methods_index,
|
||||
)?;
|
||||
|
||||
let count = field_getters_index.map(|_| 1).unwrap_or(0)
|
||||
+ field_setters_index.map(|_| 1).unwrap_or(0)
|
||||
+ methods_index.map(|_| 1).unwrap_or(0);
|
||||
ffi::lua_pop(lua.state, count);
|
||||
|
||||
let mt_id = ffi::lua_topointer(lua.state, -1);
|
||||
ffi::lua_setmetatable(lua.state, -2);
|
||||
let ud = AnyUserData(lua.pop_ref());
|
||||
lua.register_userdata_metatable(mt_id as isize);
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
let newtable = lua.create_table()?;
|
||||
let destructor: DestructorCallback = Box::new(move |ud| {
|
||||
let state = ud.lua.state;
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
|
||||
ud.lua.push_ref(&ud);
|
||||
|
||||
// We know the destructor has not run yet because we hold a reference to the userdata.
|
||||
|
||||
// Deregister metatable
|
||||
ffi::lua_getmetatable(state, -1);
|
||||
let mt_id = ffi::lua_topointer(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
ud.lua.deregister_userdata_metatable(mt_id as isize);
|
||||
|
||||
// Clear uservalue
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
ffi::lua_pushnil(state);
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
ud.lua.push_ref(&newtable.0);
|
||||
ffi::lua_setuservalue(state, -2);
|
||||
|
||||
// A hack to drop non-static `T`
|
||||
unsafe fn seal<T>(t: T) -> Box<dyn FnOnce() + 'static> {
|
||||
let f: Box<dyn FnOnce()> = Box::new(move || drop(t));
|
||||
mem::transmute(f)
|
||||
}
|
||||
|
||||
init_userdata_metatable::<()>(lua.state, -2, Some(-1))?;
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
}
|
||||
vec![Box::new(seal(take_userdata::<UserDataCell<T>>(state)))]
|
||||
});
|
||||
self.destructors
|
||||
.borrow_mut()
|
||||
.push((ud.0.clone(), destructor));
|
||||
|
||||
ffi::lua_setmetatable(lua.state, -2);
|
||||
|
||||
Ok(AnyUserData(lua.pop_ref()))
|
||||
Ok(ud)
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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,
|
||||
@@ -373,27 +449,31 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
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 destructor: DestructorCallback = Box::new(|f| {
|
||||
let state = f.lua.state;
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 3);
|
||||
|
||||
f.lua.push_ref(&f);
|
||||
|
||||
// We know the destructor has not run yet because we hold a reference to the callback.
|
||||
|
||||
ffi::lua_getupvalue(state, -1, 1);
|
||||
let ud1 = take_userdata::<Callback>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 1);
|
||||
|
||||
ffi::lua_getupvalue(state, -1, 2);
|
||||
let ud2 = take_userdata::<Lua>(state);
|
||||
let ud1 = take_userdata::<Callback>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 2);
|
||||
|
||||
ffi::lua_pop(state, 1);
|
||||
ffi::lua_getupvalue(state, -1, 3);
|
||||
let ud2 = take_userdata::<Lua>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 3);
|
||||
|
||||
vec![Box::new(ud1), Box::new(ud2)]
|
||||
}));
|
||||
});
|
||||
self.destructors
|
||||
.borrow_mut()
|
||||
.push((f.0.clone(), destructor));
|
||||
|
||||
Ok(f)
|
||||
}
|
||||
|
||||
@@ -405,10 +485,14 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
let f = mem::transmute::<AsyncCallback<'callback, 'scope>, AsyncCallback<'lua, 'static>>(f);
|
||||
let f = self.lua.create_async_callback(f)?;
|
||||
|
||||
let mut destructors = self.destructors.borrow_mut();
|
||||
destructors.push((f.0.clone(), |f| {
|
||||
// We need to pre-allocate strings to avoid failures in destructor.
|
||||
let get_poll_str = self.lua.create_string("get_poll")?;
|
||||
let poll_str = self.lua.create_string("poll")?;
|
||||
let destructor: DestructorCallback = Box::new(move |f| {
|
||||
let state = f.lua.state;
|
||||
assert_stack(state, 4);
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 5);
|
||||
|
||||
f.lua.push_ref(&f);
|
||||
|
||||
// We know the destructor has not run yet because we hold a reference to the callback.
|
||||
@@ -420,41 +504,44 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
ffi::lua_getfenv(state, -1);
|
||||
|
||||
// Second, get the `get_poll()` closure using the corresponding key
|
||||
ffi::lua_pushstring(state, cstr!("get_poll"));
|
||||
f.lua.push_ref(&get_poll_str.0);
|
||||
ffi::lua_rawget(state, -2);
|
||||
|
||||
// Destroy all upvalues
|
||||
ffi::lua_getupvalue(state, -1, 1);
|
||||
ffi::lua_getupvalue(state, -1, 2);
|
||||
let ud1 = take_userdata::<AsyncCallback>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 1);
|
||||
ffi::lua_setupvalue(state, -2, 2);
|
||||
|
||||
ffi::lua_getupvalue(state, -1, 2);
|
||||
ffi::lua_getupvalue(state, -1, 3);
|
||||
let ud2 = take_userdata::<Lua>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 2);
|
||||
ffi::lua_setupvalue(state, -2, 3);
|
||||
|
||||
ffi::lua_pop(state, 1);
|
||||
let mut data: Vec<Box<dyn Any>> = vec![Box::new(ud1), Box::new(ud2)];
|
||||
|
||||
// Finally, get polled future and destroy it
|
||||
ffi::lua_pushstring(state, cstr!("poll"));
|
||||
f.lua.push_ref(&poll_str.0);
|
||||
if ffi::lua_rawget(state, -2) == ffi::LUA_TFUNCTION {
|
||||
ffi::lua_getupvalue(state, -1, 1);
|
||||
ffi::lua_getupvalue(state, -1, 2);
|
||||
let ud3 = take_userdata::<LocalBoxFuture<Result<MultiValue>>>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 1);
|
||||
ffi::lua_setupvalue(state, -2, 2);
|
||||
data.push(Box::new(ud3));
|
||||
|
||||
ffi::lua_getupvalue(state, -1, 2);
|
||||
ffi::lua_getupvalue(state, -1, 3);
|
||||
let ud4 = take_userdata::<Lua>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 2);
|
||||
ffi::lua_setupvalue(state, -2, 3);
|
||||
data.push(Box::new(ud4));
|
||||
}
|
||||
|
||||
data
|
||||
}));
|
||||
});
|
||||
self.destructors
|
||||
.borrow_mut()
|
||||
.push((f.0.clone(), destructor));
|
||||
|
||||
Ok(f)
|
||||
}
|
||||
@@ -463,7 +550,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
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
|
||||
// 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
|
||||
@@ -590,59 +677,167 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||
mlua_panic!("asynchronous functions are not supported for non-static userdata")
|
||||
}
|
||||
|
||||
fn add_meta_method<A, R, M>(&mut self, meta: MetaMethod, method: M)
|
||||
fn add_meta_method<S, A, R, M>(&mut self, meta: S, method: M)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
|
||||
{
|
||||
self.meta_methods.push((
|
||||
meta,
|
||||
meta.into(),
|
||||
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)
|
||||
fn add_meta_method_mut<S, A, R, M>(&mut self, meta: S, mut method: M)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
|
||||
{
|
||||
self.meta_methods.push((
|
||||
meta,
|
||||
meta.into(),
|
||||
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)
|
||||
fn add_meta_function<S, A, R, F>(&mut self, meta: S, function: F)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
|
||||
{
|
||||
self.meta_methods.push((
|
||||
meta,
|
||||
meta.into(),
|
||||
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)
|
||||
fn add_meta_function_mut<S, A, R, F>(&mut self, meta: S, mut function: F)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
|
||||
{
|
||||
self.meta_methods.push((
|
||||
meta,
|
||||
meta.into(),
|
||||
NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
|
||||
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
})),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
struct NonStaticUserDataFields<'lua, T: UserData> {
|
||||
field_getters: Vec<(Vec<u8>, NonStaticMethod<'lua, T>)>,
|
||||
field_setters: Vec<(Vec<u8>, NonStaticMethod<'lua, T>)>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
meta_fields: Vec<(MetaMethod, Box<dyn Fn(&'lua Lua) -> Result<Value<'lua>>>)>,
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData> Default for NonStaticUserDataFields<'lua, T> {
|
||||
fn default() -> NonStaticUserDataFields<'lua, T> {
|
||||
NonStaticUserDataFields {
|
||||
field_getters: Vec::new(),
|
||||
field_setters: Vec::new(),
|
||||
meta_fields: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData> UserDataFields<'lua, T> for NonStaticUserDataFields<'lua, T> {
|
||||
fn add_field_method_get<S, R, M>(&mut self, name: &S, method: M)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
R: ToLua<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, &T) -> Result<R>,
|
||||
{
|
||||
self.field_getters.push((
|
||||
name.as_ref().to_vec(),
|
||||
NonStaticMethod::Method(Box::new(move |lua, ud, _| {
|
||||
method(lua, ud)?.to_lua_multi(lua)
|
||||
})),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_field_method_set<S, A, M>(&mut self, name: &S, mut method: M)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLua<'lua>,
|
||||
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<()>,
|
||||
{
|
||||
self.field_setters.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_field_function_get<S, R, F>(&mut self, name: &S, function: F)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
R: ToLua<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R>,
|
||||
{
|
||||
self.field_getters.push((
|
||||
name.as_ref().to_vec(),
|
||||
NonStaticMethod::Function(Box::new(move |lua, args| {
|
||||
function(lua, AnyUserData::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
})),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_field_function_set<S, A, F>(&mut self, name: &S, mut function: F)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLua<'lua>,
|
||||
F: 'static + MaybeSend + FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()>,
|
||||
{
|
||||
self.field_setters.push((
|
||||
name.as_ref().to_vec(),
|
||||
NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
|
||||
let (ud, val) = <_>::from_lua_multi(args, lua)?;
|
||||
function(lua, ud, val)?.to_lua_multi(lua)
|
||||
})),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_meta_field_with<S, R, F>(&mut self, meta: S, f: F)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua) -> Result<R>,
|
||||
R: ToLua<'lua>,
|
||||
{
|
||||
let meta = meta.into();
|
||||
self.meta_fields.push((
|
||||
meta.clone(),
|
||||
Box::new(move |lua| {
|
||||
let value = f(lua)?.to_lua(lua)?;
|
||||
if meta == MetaMethod::Index || meta == MetaMethod::NewIndex {
|
||||
match value {
|
||||
Value::Nil | Value::Table(_) | Value::Function(_) => {}
|
||||
_ => {
|
||||
return Err(Error::MetaMethodTypeError {
|
||||
method: meta.to_string(),
|
||||
type_name: value.type_name(),
|
||||
message: Some("expected nil, table or function".to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(value)
|
||||
}),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
+9
-1
@@ -7,7 +7,15 @@ use crate::table::{TablePairs, TableSequence};
|
||||
use crate::value::Value;
|
||||
|
||||
/// A struct for deserializing Lua values into Rust values.
|
||||
pub struct Deserializer<'lua>(pub Value<'lua>);
|
||||
#[derive(Debug)]
|
||||
pub struct Deserializer<'lua>(Value<'lua>);
|
||||
|
||||
impl<'lua> Deserializer<'lua> {
|
||||
/// Creates a new Lua Deserializer for the `Value`.
|
||||
pub fn new(value: Value<'lua>) -> Self {
|
||||
Deserializer(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
type Error = Error;
|
||||
|
||||
+66
-39
@@ -1,6 +1,6 @@
|
||||
//! (De)Serialization support using serde.
|
||||
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -9,9 +9,11 @@ use crate::error::Result;
|
||||
use crate::ffi;
|
||||
use crate::lua::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::util::{assert_stack, protect_lua, StackGuard};
|
||||
use crate::types::LightUserData;
|
||||
use crate::util::{assert_stack, check_stack, StackGuard};
|
||||
use crate::value::Value;
|
||||
|
||||
/// Trait for serializing/deserializing Lua values using Serde.
|
||||
pub trait LuaSerdeExt<'lua> {
|
||||
/// A special value (lightuserdata) to encode/decode optional (none) values.
|
||||
///
|
||||
@@ -25,7 +27,7 @@ pub trait LuaSerdeExt<'lua> {
|
||||
///
|
||||
/// fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// lua.globals().set("null", lua.null()?)?;
|
||||
/// lua.globals().set("null", lua.null())?;
|
||||
///
|
||||
/// let val = lua.load(r#"{a = null}"#).eval()?;
|
||||
/// let map: HashMap<String, Option<String>> = lua.from_value(val)?;
|
||||
@@ -34,7 +36,7 @@ pub trait LuaSerdeExt<'lua> {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn null(&'lua self) -> Result<Value<'lua>>;
|
||||
fn null(&'lua self) -> Value<'lua>;
|
||||
|
||||
/// A metatable attachable to a Lua table to systematically encode it as Array (instead of Map).
|
||||
/// As result, encoded Array will contain only sequence part of the table, with the same length
|
||||
@@ -50,7 +52,7 @@ pub trait LuaSerdeExt<'lua> {
|
||||
///
|
||||
/// fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// lua.globals().set("array_mt", lua.array_metatable()?)?;
|
||||
/// lua.globals().set("array_mt", lua.array_metatable())?;
|
||||
///
|
||||
/// // Encode as an empty array (no sequence part in the lua table)
|
||||
/// let val = lua.load("setmetatable({a = 5}, array_mt)").eval()?;
|
||||
@@ -65,7 +67,7 @@ pub trait LuaSerdeExt<'lua> {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn array_metatable(&'lua self) -> Result<Table<'lua>>;
|
||||
fn array_metatable(&'lua self) -> Table<'lua>;
|
||||
|
||||
/// Converts `T` into a `Value` instance.
|
||||
///
|
||||
@@ -100,6 +102,33 @@ pub trait LuaSerdeExt<'lua> {
|
||||
/// ```
|
||||
fn to_value<T: Serialize + ?Sized>(&'lua self, t: &T) -> Result<Value<'lua>>;
|
||||
|
||||
/// Converts `T` into a `Value` instance with options.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// [`Value`]: enum.Value.html
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use mlua::{Lua, Result, LuaSerdeExt, SerializeOptions};
|
||||
///
|
||||
/// fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// let v = vec![1, 2, 3];
|
||||
/// let options = SerializeOptions::new().set_array_metatable(false);
|
||||
/// lua.globals().set("v", lua.to_value_with(&v, options)?)?;
|
||||
///
|
||||
/// lua.load(r#"
|
||||
/// assert(#v == 3 and v[1] == 1 and v[2] == 2 and v[3] == 3)
|
||||
/// assert(getmetatable(v) == nil)
|
||||
/// "#).exec()
|
||||
/// }
|
||||
/// ```
|
||||
fn to_value_with<T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
|
||||
where
|
||||
T: Serialize + ?Sized;
|
||||
|
||||
/// Deserializes a `Value` into any serde deserializable object.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
@@ -132,31 +161,18 @@ pub trait LuaSerdeExt<'lua> {
|
||||
}
|
||||
|
||||
impl<'lua> LuaSerdeExt<'lua> for Lua {
|
||||
fn null(&'lua self) -> Result<Value<'lua>> {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
assert_stack(self.state, 3);
|
||||
|
||||
unsafe extern "C" fn push_null(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_pushlightuserdata(state, ptr::null_mut());
|
||||
1
|
||||
}
|
||||
protect_lua(self.state, 0, push_null)?;
|
||||
Ok(self.pop_value())
|
||||
}
|
||||
fn null(&'lua self) -> Value<'lua> {
|
||||
Value::LightUserData(LightUserData(ptr::null_mut()))
|
||||
}
|
||||
|
||||
fn array_metatable(&'lua self) -> Result<Table<'lua>> {
|
||||
fn array_metatable(&'lua self) -> Table<'lua> {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
assert_stack(self.state, 3);
|
||||
assert_stack(self.state, 1);
|
||||
|
||||
unsafe extern "C" fn get_array_mt(state: *mut ffi::lua_State) -> c_int {
|
||||
push_array_metatable(state);
|
||||
1
|
||||
}
|
||||
protect_lua(self.state, 0, get_array_mt)?;
|
||||
Ok(Table(self.pop_ref()))
|
||||
push_array_metatable(self.state);
|
||||
|
||||
Table(self.pop_ref())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,37 +180,48 @@ impl<'lua> LuaSerdeExt<'lua> for Lua {
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
t.serialize(ser::Serializer(self))
|
||||
t.serialize(ser::Serializer::new(self))
|
||||
}
|
||||
|
||||
fn to_value_with<T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
t.serialize(ser::Serializer::new_with_options(self, options))
|
||||
}
|
||||
|
||||
fn from_value<T>(&'lua self, value: Value<'lua>) -> Result<T>
|
||||
where
|
||||
T: Deserialize<'lua>,
|
||||
{
|
||||
T::deserialize(de::Deserializer(value))
|
||||
T::deserialize(de::Deserializer::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init_metatables(state: *mut ffi::lua_State) {
|
||||
ffi::lua_pushlightuserdata(
|
||||
state,
|
||||
&ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
|
||||
);
|
||||
ffi::lua_newtable(state);
|
||||
// Uses 6 stack spaces and calls checkstack.
|
||||
pub(crate) unsafe fn init_metatables(state: *mut ffi::lua_State) -> Result<()> {
|
||||
check_stack(state, 6)?;
|
||||
|
||||
ffi::safe::lua_createtable(state, 0, 1)?;
|
||||
|
||||
ffi::lua_pushstring(state, cstr!("__metatable"));
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
ffi::lua_rawset(state, -3);
|
||||
ffi::safe::lua_rawsetfield(state, -2, "__metatable")?;
|
||||
|
||||
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
||||
let array_metatable_key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
ffi::safe::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, array_metatable_key)
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn push_array_metatable(state: *mut ffi::lua_State) {
|
||||
let key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, key);
|
||||
let array_metatable_key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, array_metatable_key);
|
||||
}
|
||||
|
||||
static ARRAY_METATABLE_REGISTRY_KEY: u8 = 0;
|
||||
|
||||
pub mod de;
|
||||
pub mod ser;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use de::Deserializer;
|
||||
#[doc(inline)]
|
||||
pub use ser::Serializer;
|
||||
|
||||
+158
-46
@@ -9,17 +9,105 @@ use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
use crate::table::Table;
|
||||
use crate::types::Integer;
|
||||
use crate::util::{assert_stack, protect_lua, StackGuard};
|
||||
use crate::util::{check_stack, StackGuard};
|
||||
use crate::value::{ToLua, Value};
|
||||
|
||||
/// A struct for serializing Rust values into Lua values.
|
||||
pub struct Serializer<'lua>(pub &'lua Lua);
|
||||
#[derive(Debug)]
|
||||
pub struct Serializer<'lua> {
|
||||
lua: &'lua Lua,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
/// A struct with options to change default serializer behavior.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[non_exhaustive]
|
||||
pub struct Options {
|
||||
/// If true, sequence serialization to a Lua table will create table
|
||||
/// with the [`array_metatable`] attached.
|
||||
///
|
||||
/// Default: **true**
|
||||
///
|
||||
/// [`array_metatable`]: ../trait.LuaSerdeExt.html#tymethod.array_metatable
|
||||
pub set_array_metatable: bool,
|
||||
|
||||
/// If true, serialize `None` (part of `Option` type) to [`null`].
|
||||
/// Otherwise it will be set to Lua [`Nil`].
|
||||
///
|
||||
/// Default: **true**
|
||||
///
|
||||
/// [`null`]: ../trait.LuaSerdeExt.html#tymethod.null
|
||||
/// [`Nil`]: ../../enum.Value.html#variant.Nil
|
||||
pub serialize_none_to_null: bool,
|
||||
|
||||
/// If true, serialize `Unit` (type of `()` in Rust) and Unit structs to [`null`].
|
||||
/// Otherwise it will be set to Lua [`Nil`].
|
||||
///
|
||||
/// Default: **true**
|
||||
///
|
||||
/// [`null`]: ../trait.LuaSerdeExt.html#tymethod.null
|
||||
/// [`Nil`]: ../../enum.Value.html#variant.Nil
|
||||
pub serialize_unit_to_null: bool,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
fn default() -> Self {
|
||||
Options {
|
||||
set_array_metatable: true,
|
||||
serialize_none_to_null: true,
|
||||
serialize_unit_to_null: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Options {
|
||||
/// Retruns a new instance of `Options` with default parameters.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Sets [`set_array_metatable`] option.
|
||||
///
|
||||
/// [`set_array_metatable`]: #structfield.set_array_metatable
|
||||
pub fn set_array_metatable(mut self, enabled: bool) -> Self {
|
||||
self.set_array_metatable = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets [`serialize_none_to_null`] option.
|
||||
///
|
||||
/// [`serialize_none_to_null`]: #structfield.serialize_none_to_null
|
||||
pub fn serialize_none_to_null(mut self, enabled: bool) -> Self {
|
||||
self.serialize_none_to_null = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets [`serialize_unit_to_null`] option.
|
||||
///
|
||||
/// [`serialize_unit_to_null`]: #structfield.serialize_unit_to_null
|
||||
pub fn serialize_unit_to_null(mut self, enabled: bool) -> Self {
|
||||
self.serialize_unit_to_null = enabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Serializer<'lua> {
|
||||
/// Creates a new Lua Serializer with default options.
|
||||
pub fn new(lua: &'lua Lua) -> Self {
|
||||
Self::new_with_options(lua, Options::default())
|
||||
}
|
||||
|
||||
/// Creates a new Lua Serializer with custom options.
|
||||
pub fn new_with_options(lua: &'lua Lua, options: Options) -> Self {
|
||||
Serializer { lua, options }
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! lua_serialize_number {
|
||||
($name:ident, $t:ty) => {
|
||||
#[inline]
|
||||
fn $name(self, value: $t) -> Result<Value<'lua>> {
|
||||
value.to_lua(self.0)
|
||||
value.to_lua(self.lua)
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -62,35 +150,47 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
|
||||
#[inline]
|
||||
fn serialize_str(self, value: &str) -> Result<Value<'lua>> {
|
||||
self.0.create_string(value).map(Value::String)
|
||||
self.lua.create_string(value).map(Value::String)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_bytes(self, value: &[u8]) -> Result<Value<'lua>> {
|
||||
self.0.create_string(value).map(Value::String)
|
||||
self.lua.create_string(value).map(Value::String)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_none(self) -> Result<Value<'lua>> {
|
||||
self.0.null()
|
||||
if self.options.serialize_none_to_null {
|
||||
Ok(self.lua.null())
|
||||
} else {
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_some<T>(self, value: &T) -> Result<Value<'lua>>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_unit(self) -> Result<Value<'lua>> {
|
||||
self.0.null()
|
||||
if self.options.serialize_unit_to_null {
|
||||
Ok(self.lua.null())
|
||||
} else {
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_unit_struct(self, _name: &'static str) -> Result<Value<'lua>> {
|
||||
self.0.null()
|
||||
if self.options.serialize_unit_to_null {
|
||||
Ok(self.lua.null())
|
||||
} else {
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -106,7 +206,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
#[inline]
|
||||
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Value<'lua>>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
value.serialize(self)
|
||||
}
|
||||
@@ -120,11 +220,11 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
value: &T,
|
||||
) -> Result<Value<'lua>>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
let table = self.0.create_table()?;
|
||||
let variant = self.0.create_string(variant)?;
|
||||
let value = self.0.to_value(value)?;
|
||||
let table = self.lua.create_table()?;
|
||||
let variant = self.lua.create_string(variant)?;
|
||||
let value = self.lua.to_value_with(value, self.options)?;
|
||||
table.raw_set(variant, value)?;
|
||||
Ok(Value::Table(table))
|
||||
}
|
||||
@@ -132,9 +232,12 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
#[inline]
|
||||
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
|
||||
let len = len.unwrap_or(0) as c_int;
|
||||
let table = self.0.create_table_with_capacity(len, 0)?;
|
||||
table.set_metatable(Some(self.0.array_metatable()?));
|
||||
Ok(SerializeVec { table })
|
||||
let table = self.lua.create_table_with_capacity(len, 0)?;
|
||||
if self.options.set_array_metatable {
|
||||
table.set_metatable(Some(self.lua.array_metatable()));
|
||||
}
|
||||
let options = self.options;
|
||||
Ok(SerializeVec { table, options })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -159,9 +262,11 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleVariant> {
|
||||
let name = self.0.create_string(variant)?;
|
||||
let table = self.0.create_table()?;
|
||||
Ok(SerializeTupleVariant { name, table })
|
||||
Ok(SerializeTupleVariant {
|
||||
name: self.lua.create_string(variant)?,
|
||||
table: self.lua.create_table()?,
|
||||
options: self.options,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -169,7 +274,8 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
let len = len.unwrap_or(0) as c_int;
|
||||
Ok(SerializeMap {
|
||||
key: None,
|
||||
table: self.0.create_table_with_capacity(0, len)?,
|
||||
table: self.lua.create_table_with_capacity(0, len)?,
|
||||
options: self.options,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -186,14 +292,18 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
variant: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeStructVariant> {
|
||||
let name = self.0.create_string(variant)?;
|
||||
let table = self.0.create_table_with_capacity(0, len as c_int)?;
|
||||
Ok(SerializeStructVariant { name, table })
|
||||
Ok(SerializeStructVariant {
|
||||
name: self.lua.create_string(variant)?,
|
||||
table: self.lua.create_table_with_capacity(0, len as c_int)?,
|
||||
options: self.options,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeVec<'lua> {
|
||||
table: Table<'lua>,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
|
||||
@@ -202,24 +312,18 @@ impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
|
||||
|
||||
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
let value = lua.to_value(value)?;
|
||||
let value = lua.to_value_with(value, self.options)?;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 4);
|
||||
check_stack(lua.state, 6)?;
|
||||
|
||||
lua.push_ref(&self.table.0);
|
||||
lua.push_value(value)?;
|
||||
|
||||
unsafe extern "C" fn push_to_table(state: *mut ffi::lua_State) -> c_int {
|
||||
let len = ffi::lua_rawlen(state, -2) as Integer;
|
||||
ffi::lua_rawseti(state, -2, len + 1);
|
||||
1
|
||||
}
|
||||
|
||||
protect_lua(lua.state, 2, push_to_table)
|
||||
let len = ffi::lua_rawlen(lua.state, -2) as Integer;
|
||||
ffi::safe::lua_rawseti(lua.state, -2, len + 1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,7 +338,7 @@ impl<'lua> ser::SerializeTuple for SerializeVec<'lua> {
|
||||
|
||||
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
ser::SerializeSeq::serialize_element(self, value)
|
||||
}
|
||||
@@ -250,7 +354,7 @@ impl<'lua> ser::SerializeTupleStruct for SerializeVec<'lua> {
|
||||
|
||||
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
ser::SerializeSeq::serialize_element(self, value)
|
||||
}
|
||||
@@ -260,9 +364,11 @@ impl<'lua> ser::SerializeTupleStruct for SerializeVec<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeTupleVariant<'lua> {
|
||||
name: String<'lua>,
|
||||
table: Table<'lua>,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeTupleVariant for SerializeTupleVariant<'lua> {
|
||||
@@ -271,11 +377,12 @@ impl<'lua> ser::SerializeTupleVariant for SerializeTupleVariant<'lua> {
|
||||
|
||||
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
let idx = self.table.raw_len() + 1;
|
||||
self.table.raw_insert(idx, lua.to_value(value)?)
|
||||
self.table
|
||||
.raw_insert(idx, lua.to_value_with(value, self.options)?)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
@@ -286,9 +393,11 @@ impl<'lua> ser::SerializeTupleVariant for SerializeTupleVariant<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeMap<'lua> {
|
||||
table: Table<'lua>,
|
||||
key: Option<Value<'lua>>,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeMap for SerializeMap<'lua> {
|
||||
@@ -297,23 +406,23 @@ impl<'lua> ser::SerializeMap for SerializeMap<'lua> {
|
||||
|
||||
fn serialize_key<T>(&mut self, key: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
self.key = Some(lua.to_value(key)?);
|
||||
self.key = Some(lua.to_value_with(key, self.options)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_value<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
let key = mlua_expect!(
|
||||
self.key.take(),
|
||||
"serialize_value called before serialize_key"
|
||||
);
|
||||
let value = lua.to_value(value)?;
|
||||
let value = lua.to_value_with(value, self.options)?;
|
||||
self.table.raw_set(key, value)
|
||||
}
|
||||
|
||||
@@ -328,7 +437,7 @@ impl<'lua> ser::SerializeStruct for SerializeMap<'lua> {
|
||||
|
||||
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
ser::SerializeMap::serialize_key(self, key)?;
|
||||
ser::SerializeMap::serialize_value(self, value)
|
||||
@@ -339,9 +448,11 @@ impl<'lua> ser::SerializeStruct for SerializeMap<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeStructVariant<'lua> {
|
||||
name: String<'lua>,
|
||||
table: Table<'lua>,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeStructVariant for SerializeStructVariant<'lua> {
|
||||
@@ -350,10 +461,11 @@ impl<'lua> ser::SerializeStructVariant for SerializeStructVariant<'lua> {
|
||||
|
||||
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
self.table.raw_set(key, lua.to_value(value)?)?;
|
||||
self.table
|
||||
.raw_set(key, lua.to_value_with(value, self.options)?)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -39,17 +39,17 @@ impl StdLib {
|
||||
#[cfg(any(feature = "luajit", doc))]
|
||||
pub const JIT: StdLib = StdLib(1 << 9);
|
||||
|
||||
/// (unsafe) [`ffi`](http://luajit.org/ext_ffi.html) library
|
||||
/// (**unsafe**) [`ffi`](http://luajit.org/ext_ffi.html) library
|
||||
///
|
||||
/// Requires `feature = "luajit"`
|
||||
#[cfg(any(feature = "luajit", doc))]
|
||||
pub const FFI: StdLib = StdLib(1 << 30);
|
||||
/// (unsafe) [`debug`](https://www.lua.org/manual/5.3/manual.html#6.10) library
|
||||
/// (**unsafe**) [`debug`](https://www.lua.org/manual/5.3/manual.html#6.10) library
|
||||
pub const DEBUG: StdLib = StdLib(1 << 31);
|
||||
|
||||
/// No libraries
|
||||
pub const NONE: StdLib = StdLib(0);
|
||||
/// (unsafe) All standard libraries
|
||||
/// (**unsafe**) All standard libraries
|
||||
pub const ALL: StdLib = StdLib(u32::MAX);
|
||||
/// The safe subset of the standard libraries
|
||||
pub const ALL_SAFE: StdLib = StdLib((1 << 30) - 1);
|
||||
|
||||
+77
-115
@@ -1,5 +1,4 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
@@ -11,7 +10,7 @@ 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::util::{assert_stack, check_stack, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, Nil, ToLua, ToLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
@@ -60,19 +59,15 @@ impl<'lua> Table<'lua> {
|
||||
let lua = self.0.lua;
|
||||
let key = key.to_lua(lua)?;
|
||||
let value = value.to_lua(lua)?;
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 6);
|
||||
check_stack(lua.state, 6)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
lua.push_value(value)?;
|
||||
|
||||
unsafe extern "C" fn set_table(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_settable(state, -3);
|
||||
1
|
||||
}
|
||||
protect_lua(lua.state, 3, set_table)
|
||||
ffi::safe::lua_settable(lua.state, -3)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,18 +98,15 @@ impl<'lua> Table<'lua> {
|
||||
pub fn get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
|
||||
let lua = self.0.lua;
|
||||
let key = key.to_lua(lua)?;
|
||||
|
||||
let value = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 5);
|
||||
check_stack(lua.state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
ffi::safe::lua_gettable(lua.state, -2)?;
|
||||
|
||||
unsafe extern "C" fn get_table(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_gettable(state, -2);
|
||||
1
|
||||
}
|
||||
protect_lua(lua.state, 2, get_table)?;
|
||||
lua.pop_value()
|
||||
};
|
||||
V::from_lua(value, lua)
|
||||
@@ -127,19 +119,13 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 5);
|
||||
check_stack(lua.state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
ffi::safe::lua_gettable(lua.state, -2)?;
|
||||
|
||||
unsafe extern "C" fn get_table(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_gettable(state, -2);
|
||||
1
|
||||
}
|
||||
protect_lua(lua.state, 2, get_table)?;
|
||||
|
||||
let has = ffi::lua_isnil(lua.state, -1) == 0;
|
||||
Ok(has)
|
||||
Ok(ffi::lua_isnil(lua.state, -1) == 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,19 +193,12 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 6);
|
||||
check_stack(lua.state, 6)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
lua.push_value(value)?;
|
||||
|
||||
unsafe extern "C" fn raw_set(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_rawset(state, -3);
|
||||
0
|
||||
}
|
||||
protect_lua(lua.state, 3, raw_set)?;
|
||||
|
||||
Ok(())
|
||||
ffi::safe::lua_rawset(lua.state, -3)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,13 +206,15 @@ impl<'lua> Table<'lua> {
|
||||
pub fn raw_get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
|
||||
let lua = self.0.lua;
|
||||
let key = key.to_lua(lua)?;
|
||||
|
||||
let value = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 3);
|
||||
check_stack(lua.state, 3)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
ffi::lua_rawget(lua.state, -2);
|
||||
|
||||
lua.pop_value()
|
||||
};
|
||||
V::from_lua(value, lua)
|
||||
@@ -251,29 +232,21 @@ impl<'lua> Table<'lua> {
|
||||
let value = value.to_lua(lua)?;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 6);
|
||||
check_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);
|
||||
})
|
||||
ffi::safe::lua_rawinsert(lua.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,
|
||||
/// and erases element `table[key]`. The complexity is O(n) in the worst case,
|
||||
/// where n is the table length.
|
||||
///
|
||||
/// For othey key types this is equivalent to setting `table[key] = nil`.
|
||||
/// For other 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)?;
|
||||
@@ -285,18 +258,10 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 6);
|
||||
check_stack(lua.state, 5)?;
|
||||
|
||||
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);
|
||||
})
|
||||
ffi::safe::lua_rawremove(lua.state, -1, idx)
|
||||
}
|
||||
}
|
||||
_ => self.raw_set(key, Nil),
|
||||
@@ -312,9 +277,10 @@ impl<'lua> Table<'lua> {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 4);
|
||||
check_stack(lua.state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
protect_lua_closure(lua.state, 1, 0, |state| ffi::luaL_len(state, -1))
|
||||
ffi::safe::luaL_len(lua.state, -1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,9 +290,9 @@ impl<'lua> Table<'lua> {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 1);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let len = ffi::lua_rawlen(lua.state, -1);
|
||||
len as Integer
|
||||
ffi::lua_rawlen(lua.state, -1) as Integer
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,13 +303,13 @@ impl<'lua> Table<'lua> {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 1);
|
||||
assert_stack(lua.state, 2);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
if ffi::lua_getmetatable(lua.state, -1) == 0 {
|
||||
None
|
||||
} else {
|
||||
let table = Table(lua.pop_ref());
|
||||
Some(table)
|
||||
Some(Table(lua.pop_ref()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -356,7 +322,8 @@ impl<'lua> Table<'lua> {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 1);
|
||||
assert_stack(lua.state, 2);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
if let Some(metatable) = metatable {
|
||||
lua.push_ref(&metatable.0);
|
||||
@@ -403,7 +370,7 @@ impl<'lua> Table<'lua> {
|
||||
pub fn pairs<K: FromLua<'lua>, V: FromLua<'lua>>(self) -> TablePairs<'lua, K, V> {
|
||||
TablePairs {
|
||||
table: self.0,
|
||||
next_key: Some(Nil),
|
||||
key: Some(Nil),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
@@ -411,7 +378,7 @@ impl<'lua> Table<'lua> {
|
||||
/// Consume this table and return an iterator over all values in the sequence part of the table.
|
||||
///
|
||||
/// The iterator will yield all values `t[1]`, `t[2]`, and so on, until a `nil` value is
|
||||
/// encountered. This mirrors the behaviour of Lua's `ipairs` function and will invoke the
|
||||
/// encountered. This mirrors the behavior of Lua's `ipairs` function and will invoke the
|
||||
/// `__index` metamethod according to the usual rules. However, the deprecated `__ipairs`
|
||||
/// metatable will not be called.
|
||||
///
|
||||
@@ -474,9 +441,12 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) fn raw_sequence_values_by_len<V: FromLua<'lua>>(self) -> TableSequence<'lua, V> {
|
||||
let len = self.raw_len();
|
||||
#[cfg(any(feature = "async", feature = "serialize"))]
|
||||
pub(crate) fn raw_sequence_values_by_len<V: FromLua<'lua>>(
|
||||
self,
|
||||
len: Option<Integer>,
|
||||
) -> TableSequence<'lua, V> {
|
||||
let len = len.unwrap_or_else(|| self.raw_len());
|
||||
TableSequence {
|
||||
table: self.0,
|
||||
index: Some(1),
|
||||
@@ -492,6 +462,7 @@ impl<'lua> Table<'lua> {
|
||||
unsafe {
|
||||
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 false;
|
||||
@@ -641,7 +612,7 @@ impl<'lua> Serialize for Table<'lua> {
|
||||
let len = self.raw_len() as usize;
|
||||
if len > 0 || self.is_array() {
|
||||
let mut seq = serializer.serialize_seq(Some(len))?;
|
||||
for v in self.clone().raw_sequence_values_by_len::<Value>() {
|
||||
for v in self.clone().raw_sequence_values_by_len::<Value>(None) {
|
||||
let v = v.map_err(serde::ser::Error::custom)?;
|
||||
seq.serialize_element(&v)?;
|
||||
}
|
||||
@@ -664,7 +635,7 @@ impl<'lua> Serialize for Table<'lua> {
|
||||
/// [`Table::pairs`]: struct.Table.html#method.pairs
|
||||
pub struct TablePairs<'lua, K, V> {
|
||||
table: LuaRef<'lua>,
|
||||
next_key: Option<Value<'lua>>,
|
||||
key: Option<Value<'lua>>,
|
||||
_phantom: PhantomData<(K, V)>,
|
||||
}
|
||||
|
||||
@@ -676,41 +647,34 @@ where
|
||||
type Item = Result<(K, V)>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(next_key) = self.next_key.take() {
|
||||
if let Some(prev_key) = self.key.take() {
|
||||
let lua = self.table.lua;
|
||||
|
||||
let res = (|| {
|
||||
let res = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 6);
|
||||
let res = (|| unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 5)?;
|
||||
|
||||
lua.push_ref(&self.table);
|
||||
lua.push_value(next_key)?;
|
||||
lua.push_ref(&self.table);
|
||||
lua.push_value(prev_key)?;
|
||||
|
||||
let next = protect_lua_closure(lua.state, 2, ffi::LUA_MULTRET, |state| {
|
||||
ffi::lua_next(state, -2) != 0
|
||||
})?;
|
||||
if next {
|
||||
ffi::lua_pushvalue(lua.state, -2);
|
||||
let key = lua.pop_value();
|
||||
let value = lua.pop_value();
|
||||
self.next_key = Some(lua.pop_value());
|
||||
|
||||
Some((key, value))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
Ok(if let Some((key, value)) = res {
|
||||
Some((K::from_lua(key, lua)?, V::from_lua(value, lua)?))
|
||||
if ffi::safe::lua_next(lua.state, -2)? != 0 {
|
||||
let value = lua.pop_value();
|
||||
let key = lua.pop_value();
|
||||
Ok(Some((
|
||||
key.clone(),
|
||||
K::from_lua(key, lua)?,
|
||||
V::from_lua(value, lua)?,
|
||||
)))
|
||||
} else {
|
||||
None
|
||||
})
|
||||
Ok(None)
|
||||
}
|
||||
})();
|
||||
|
||||
match res {
|
||||
Ok(Some((key, value))) => Some(Ok((key, value))),
|
||||
Ok(Some((key, ret_key, value))) => {
|
||||
self.key = Some(key);
|
||||
Some(Ok((ret_key, value)))
|
||||
}
|
||||
Ok(None) => None,
|
||||
Err(e) => Some(Err(e)),
|
||||
}
|
||||
@@ -743,31 +707,29 @@ where
|
||||
if let Some(index) = self.index.take() {
|
||||
let lua = self.table.lua;
|
||||
|
||||
let res = unsafe {
|
||||
let res = (|| unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 5);
|
||||
check_stack(lua.state, 1 + if self.raw { 0 } else { 4 })?;
|
||||
|
||||
lua.push_ref(&self.table);
|
||||
let lua_geti = if self.raw {
|
||||
ffi::lua_rawgeti
|
||||
let res = if self.raw {
|
||||
ffi::lua_rawgeti(lua.state, -1, index)
|
||||
} else {
|
||||
ffi::lua_geti
|
||||
ffi::safe::lua_geti(lua.state, -1, index)?
|
||||
};
|
||||
match protect_lua_closure(lua.state, 1, 1, |state| lua_geti(state, -1, index)) {
|
||||
Ok(ffi::LUA_TNIL) if index > self.len.unwrap_or(0) => None,
|
||||
Ok(_) => {
|
||||
let value = lua.pop_value();
|
||||
self.index = Some(index + 1);
|
||||
Some(Ok(value))
|
||||
}
|
||||
Err(err) => Some(Err(err)),
|
||||
match res {
|
||||
ffi::LUA_TNIL if index > self.len.unwrap_or(0) => Ok(None),
|
||||
_ => Ok(Some((index, lua.pop_value()))),
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
match res {
|
||||
Some(Ok(r)) => Some(V::from_lua(r, lua)),
|
||||
Some(Err(err)) => Some(Err(err)),
|
||||
None => None,
|
||||
Ok(Some((index, r))) => {
|
||||
self.index = Some(index + 1);
|
||||
Some(V::from_lua(r, lua))
|
||||
}
|
||||
Ok(None) => None,
|
||||
Err(err) => Some(Err(err)),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
|
||||
+72
-51
@@ -1,25 +1,27 @@
|
||||
use std::cmp;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::types::LuaRef;
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, error_traceback, pop_error, protect_lua_closure, StackGuard,
|
||||
};
|
||||
use crate::util::{assert_stack, check_stack, pop_error, StackGuard};
|
||||
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
|
||||
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored"), doc))]
|
||||
use crate::function::Function;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::{
|
||||
error::ExternalError,
|
||||
lua::{AsyncPollPending, Lua, WAKER_REGISTRY_KEY},
|
||||
util::{get_gc_userdata, push_gc_userdata},
|
||||
lua::{ASYNC_POLL_PENDING, WAKER_REGISTRY_KEY},
|
||||
util::get_gc_userdata,
|
||||
value::Value,
|
||||
},
|
||||
futures_core::{future::Future, stream::Stream},
|
||||
std::{
|
||||
cell::RefCell,
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
os::raw::c_void,
|
||||
pin::Pin,
|
||||
task::{Context, Poll, Waker},
|
||||
@@ -109,24 +111,21 @@ impl<'lua> Thread<'lua> {
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
let args = args.to_lua_multi(lua)?;
|
||||
let nargs = args.len() as c_int;
|
||||
let results = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 3);
|
||||
check_stack(lua.state, cmp::min(nargs + 1, 3))?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let thread_state = ffi::lua_tothread(lua.state, -1);
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
|
||||
let status = ffi::lua_status(thread_state);
|
||||
if status != ffi::LUA_YIELD && ffi::lua_gettop(thread_state) == 0 {
|
||||
return Err(Error::CoroutineInactive);
|
||||
}
|
||||
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
|
||||
let nargs = args.len() as c_int;
|
||||
check_stack(lua.state, nargs)?;
|
||||
check_stack(thread_state, nargs + 1)?;
|
||||
|
||||
check_stack(thread_state, nargs)?;
|
||||
for arg in args {
|
||||
lua.push_value(arg)?;
|
||||
}
|
||||
@@ -136,17 +135,14 @@ impl<'lua> Thread<'lua> {
|
||||
|
||||
let ret = ffi::lua_resume(thread_state, lua.state, nargs, &mut nresults as *mut c_int);
|
||||
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
|
||||
protect_lua_closure(lua.state, 0, 0, |_| {
|
||||
error_traceback(thread_state);
|
||||
0
|
||||
})?;
|
||||
ffi::safe::error_traceback2(lua.state, thread_state)?;
|
||||
return Err(pop_error(thread_state, ret));
|
||||
}
|
||||
|
||||
let mut results = MultiValue::new();
|
||||
check_stack(lua.state, nresults + 2)?; // 2 is extra for `lua.pop_value()` below
|
||||
ffi::lua_xmove(thread_state, lua.state, nresults);
|
||||
|
||||
assert_stack(lua.state, 2);
|
||||
for _ in 0..nresults {
|
||||
results.push_front(lua.pop_value());
|
||||
}
|
||||
@@ -177,6 +173,43 @@ impl<'lua> Thread<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Resets a thread
|
||||
///
|
||||
/// In [Lua 5.4]: cleans its call stack and closes all pending to-be-closed variables.
|
||||
/// Returns a error in case of either the original error that stopped the thread or errors
|
||||
/// in closing methods.
|
||||
///
|
||||
/// In [LuaJIT]: resets to the initial state of a newly created Lua thread.
|
||||
/// Lua threads in arbitrary states (like yielded or errored) can be reset properly.
|
||||
///
|
||||
/// Sets a Lua function for the thread afterwards.
|
||||
///
|
||||
/// Requires `feature = "lua54"` OR `feature = "luajit,vendored"`
|
||||
///
|
||||
/// [Lua 5.4]: https://www.lua.org/manual/5.4/manual.html#lua_resetthread
|
||||
/// [LuaJIT]: https://github.com/openresty/luajit2#lua_resetthread
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored"), doc))]
|
||||
pub fn reset(&self, func: Function<'lua>) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 2)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let thread_state = ffi::lua_tothread(lua.state, -1);
|
||||
|
||||
let ret = ffi::lua_resetthread(lua.state, thread_state);
|
||||
if ret != ffi::LUA_OK {
|
||||
return Err(pop_error(thread_state, ret));
|
||||
}
|
||||
|
||||
lua.push_ref(&func.0);
|
||||
ffi::lua_xmove(lua.state, thread_state, 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts Thread to an AsyncThread which implements Future and Stream traits.
|
||||
///
|
||||
/// `args` are passed as arguments to the thread function for first call.
|
||||
@@ -262,7 +295,7 @@ where
|
||||
self.thread.resume(())?
|
||||
};
|
||||
|
||||
if is_poll_pending(lua, &ret) {
|
||||
if is_poll_pending(&ret) {
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
@@ -283,7 +316,7 @@ where
|
||||
|
||||
match self.thread.status() {
|
||||
ThreadStatus::Resumable => {}
|
||||
_ => return Poll::Ready(Err("Thread already finished".to_lua_err())),
|
||||
_ => return Poll::Ready(Err(Error::CoroutineInactive)),
|
||||
};
|
||||
|
||||
let _wg = WakerGuard::new(lua.state, cx.waker().clone());
|
||||
@@ -293,7 +326,7 @@ where
|
||||
self.thread.resume(())?
|
||||
};
|
||||
|
||||
if is_poll_pending(lua, &ret) {
|
||||
if is_poll_pending(&ret) {
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
@@ -308,44 +341,31 @@ where
|
||||
}
|
||||
|
||||
#[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;
|
||||
fn is_poll_pending(val: &MultiValue) -> bool {
|
||||
match val.iter().enumerate().last() {
|
||||
Some((1, Value::LightUserData(ud))) => {
|
||||
ud.0 == &ASYNC_POLL_PENDING as *const u8 as *mut c_void
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
struct WakerGuard(*mut ffi::lua_State);
|
||||
struct WakerGuard(*mut ffi::lua_State, Option<Waker>);
|
||||
|
||||
#[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);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
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);
|
||||
let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, waker_key);
|
||||
let waker_slot = get_gc_userdata::<Option<Waker>>(state, -1).as_mut();
|
||||
let old = mlua_expect!(waker_slot, "Waker is destroyed").replace(waker);
|
||||
|
||||
Ok(WakerGuard(state))
|
||||
Ok(WakerGuard(state, old))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -353,14 +373,15 @@ impl WakerGuard {
|
||||
#[cfg(feature = "async")]
|
||||
impl Drop for WakerGuard {
|
||||
fn drop(&mut self) {
|
||||
let state = self.0;
|
||||
unsafe {
|
||||
let state = self.0;
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
assert_stack(state, 3);
|
||||
|
||||
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);
|
||||
let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, waker_key);
|
||||
let waker_slot = get_gc_userdata::<Option<Waker>>(state, -1).as_mut();
|
||||
mem::swap(mlua_expect!(waker_slot, "Waker is destroyed"), &mut self.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-4
@@ -10,7 +10,6 @@ use crate::error::Result;
|
||||
use crate::ffi;
|
||||
use crate::hook::Debug;
|
||||
use crate::lua::Lua;
|
||||
use crate::userdata::UserDataWrapped;
|
||||
use crate::util::{assert_stack, StackGuard};
|
||||
use crate::value::MultiValue;
|
||||
|
||||
@@ -32,8 +31,6 @@ pub(crate) type AsyncCallback<'lua, 'a> =
|
||||
|
||||
pub(crate) type HookCallback = Arc<RefCell<dyn FnMut(&Lua, Debug) -> Result<()>>>;
|
||||
|
||||
pub(crate) type UserDataCell<T> = RefCell<UserDataWrapped<T>>;
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
pub trait MaybeSend: Send {}
|
||||
#[cfg(feature = "send")]
|
||||
@@ -46,7 +43,7 @@ impl<T> MaybeSend for T {}
|
||||
|
||||
/// An auto generated key into the Lua registry.
|
||||
///
|
||||
/// This is a handle to a value stored inside the Lua registry. It is not automatically
|
||||
/// 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`].
|
||||
///
|
||||
|
||||
+413
-131
@@ -1,4 +1,9 @@
|
||||
use std::cell::{Ref, RefMut};
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::Deref;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use std::future::Future;
|
||||
@@ -13,10 +18,13 @@ use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::types::{LuaRef, MaybeSend, UserDataCell};
|
||||
use crate::util::{assert_stack, get_destructed_userdata_metatable, get_userdata, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti, Value};
|
||||
use crate::table::{Table, TablePairs};
|
||||
use crate::types::{LuaRef, MaybeSend};
|
||||
use crate::util::{check_stack, get_destructed_userdata_metatable, get_userdata, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti};
|
||||
|
||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||
use crate::value::Value;
|
||||
|
||||
/// Kinds of metamethods that can be overridden.
|
||||
///
|
||||
@@ -24,7 +32,7 @@ use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti, Value};
|
||||
/// generally no need to do so: [`UserData`] implementors can instead just implement `Drop`.
|
||||
///
|
||||
/// [`UserData`]: trait.UserData.html
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MetaMethod {
|
||||
/// The `+` operator.
|
||||
Add,
|
||||
@@ -105,58 +113,151 @@ pub enum MetaMethod {
|
||||
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#3.3.8
|
||||
#[cfg(any(feature = "lua54", doc))]
|
||||
Close,
|
||||
/// A custom metamethod.
|
||||
///
|
||||
/// Must not be in the protected list: `__gc`, `__metatable`, `__mlua*`.
|
||||
Custom(StdString),
|
||||
}
|
||||
|
||||
impl PartialEq for MetaMethod {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.name() == other.name()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for MetaMethod {}
|
||||
|
||||
impl Hash for MetaMethod {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.name().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for MetaMethod {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(fmt, "{}", self.name())
|
||||
}
|
||||
}
|
||||
|
||||
impl MetaMethod {
|
||||
pub(crate) fn name(self) -> &'static [u8] {
|
||||
/// Returns Lua metamethod name, usually prefixed by two underscores.
|
||||
pub fn name(&self) -> &str {
|
||||
match self {
|
||||
MetaMethod::Add => b"__add",
|
||||
MetaMethod::Sub => b"__sub",
|
||||
MetaMethod::Mul => b"__mul",
|
||||
MetaMethod::Div => b"__div",
|
||||
MetaMethod::Mod => b"__mod",
|
||||
MetaMethod::Pow => b"__pow",
|
||||
MetaMethod::Unm => b"__unm",
|
||||
MetaMethod::Add => "__add",
|
||||
MetaMethod::Sub => "__sub",
|
||||
MetaMethod::Mul => "__mul",
|
||||
MetaMethod::Div => "__div",
|
||||
MetaMethod::Mod => "__mod",
|
||||
MetaMethod::Pow => "__pow",
|
||||
MetaMethod::Unm => "__unm",
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
MetaMethod::IDiv => b"__idiv",
|
||||
MetaMethod::IDiv => "__idiv",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
MetaMethod::BAnd => b"__band",
|
||||
MetaMethod::BAnd => "__band",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
MetaMethod::BOr => b"__bor",
|
||||
MetaMethod::BOr => "__bor",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
MetaMethod::BXor => b"__bxor",
|
||||
MetaMethod::BXor => "__bxor",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
MetaMethod::BNot => b"__bnot",
|
||||
MetaMethod::BNot => "__bnot",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
MetaMethod::Shl => b"__shl",
|
||||
MetaMethod::Shl => "__shl",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
MetaMethod::Shr => b"__shr",
|
||||
MetaMethod::Shr => "__shr",
|
||||
|
||||
MetaMethod::Concat => b"__concat",
|
||||
MetaMethod::Len => b"__len",
|
||||
MetaMethod::Eq => b"__eq",
|
||||
MetaMethod::Lt => b"__lt",
|
||||
MetaMethod::Le => b"__le",
|
||||
MetaMethod::Index => b"__index",
|
||||
MetaMethod::NewIndex => b"__newindex",
|
||||
MetaMethod::Call => b"__call",
|
||||
MetaMethod::ToString => b"__tostring",
|
||||
MetaMethod::Concat => "__concat",
|
||||
MetaMethod::Len => "__len",
|
||||
MetaMethod::Eq => "__eq",
|
||||
MetaMethod::Lt => "__lt",
|
||||
MetaMethod::Le => "__le",
|
||||
MetaMethod::Index => "__index",
|
||||
MetaMethod::NewIndex => "__newindex",
|
||||
MetaMethod::Call => "__call",
|
||||
MetaMethod::ToString => "__tostring",
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
MetaMethod::Pairs => b"__pairs",
|
||||
MetaMethod::Pairs => "__pairs",
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
MetaMethod::Close => b"__close",
|
||||
MetaMethod::Close => "__close",
|
||||
|
||||
MetaMethod::Custom(ref name) => name,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn validate(self) -> Result<Self> {
|
||||
match self {
|
||||
MetaMethod::Custom(name) if name == "__gc" => Err(Error::MetaMethodRestricted(name)),
|
||||
MetaMethod::Custom(name) if name == "__metatable" => {
|
||||
Err(Error::MetaMethodRestricted(name))
|
||||
}
|
||||
MetaMethod::Custom(name) if name.starts_with("__mlua") => {
|
||||
Err(Error::MetaMethodRestricted(name))
|
||||
}
|
||||
_ => Ok(self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StdString> for MetaMethod {
|
||||
fn from(name: StdString) -> Self {
|
||||
match name.as_str() {
|
||||
"__add" => MetaMethod::Add,
|
||||
"__sub" => MetaMethod::Sub,
|
||||
"__mul" => MetaMethod::Mul,
|
||||
"__div" => MetaMethod::Div,
|
||||
"__mod" => MetaMethod::Mod,
|
||||
"__pow" => MetaMethod::Pow,
|
||||
"__unm" => MetaMethod::Unm,
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__idiv" => MetaMethod::IDiv,
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__band" => MetaMethod::BAnd,
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__bor" => MetaMethod::BOr,
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__bxor" => MetaMethod::BXor,
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__bnot" => MetaMethod::BNot,
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__shl" => MetaMethod::Shl,
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__shr" => MetaMethod::Shr,
|
||||
|
||||
"__concat" => MetaMethod::Concat,
|
||||
"__len" => MetaMethod::Len,
|
||||
"__eq" => MetaMethod::Eq,
|
||||
"__lt" => MetaMethod::Lt,
|
||||
"__le" => MetaMethod::Le,
|
||||
"__index" => MetaMethod::Index,
|
||||
"__newindex" => MetaMethod::NewIndex,
|
||||
"__call" => MetaMethod::Call,
|
||||
"__tostring" => MetaMethod::ToString,
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
"__pairs" => MetaMethod::Pairs,
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
"__close" => MetaMethod::Close,
|
||||
|
||||
_ => MetaMethod::Custom(name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for MetaMethod {
|
||||
fn from(name: &str) -> Self {
|
||||
MetaMethod::from(name.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
/// Method registry for [`UserData`] implementors.
|
||||
///
|
||||
/// [`UserData`]: trait.UserData.html
|
||||
pub trait UserDataMethods<'lua, T: UserData> {
|
||||
/// Add a method which accepts a `&T` as the first parameter.
|
||||
/// Add a regular method which accepts a `&T` as the first parameter.
|
||||
///
|
||||
/// Regular methods are implemented by overriding the `__index` metamethod and returning the
|
||||
/// accessed method. This allows them to be used with the expected `userdata:method()` syntax.
|
||||
@@ -165,7 +266,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
/// be used as a fall-back if no regular method is found.
|
||||
fn add_method<S, A, R, M>(&mut self, name: &S, method: M)
|
||||
where
|
||||
S: ?Sized + AsRef<[u8]>,
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>;
|
||||
@@ -177,7 +278,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
/// [`add_method`]: #method.add_method
|
||||
fn add_method_mut<S, A, R, M>(&mut self, name: &S, method: M)
|
||||
where
|
||||
S: ?Sized + AsRef<[u8]>,
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>;
|
||||
@@ -195,24 +296,25 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
fn add_async_method<S, A, R, M, MR>(&mut self, name: &S, method: M)
|
||||
where
|
||||
T: Clone,
|
||||
S: ?Sized + AsRef<[u8]>,
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
|
||||
MR: 'lua + Future<Output = Result<R>>;
|
||||
|
||||
/// Add a regular method as a function which accepts generic arguments, the first argument will
|
||||
/// be a `UserData` of type T if the method is called with Lua method syntax:
|
||||
/// be a [`AnyUserData`] of type `T` if the method is called with Lua method syntax:
|
||||
/// `my_userdata:my_method(arg1, arg2)`, or it is passed in as the first argument:
|
||||
/// `my_userdata.my_method(my_userdata, arg1, arg2)`.
|
||||
///
|
||||
/// Prefer to use [`add_method`] or [`add_method_mut`] as they are easier to use.
|
||||
///
|
||||
/// [`AnyUserData`]: struct.AnyUserData.html
|
||||
/// [`add_method`]: #method.add_method
|
||||
/// [`add_method_mut`]: #method.add_method_mut
|
||||
fn add_function<S, A, R, F>(&mut self, name: &S, function: F)
|
||||
where
|
||||
S: ?Sized + AsRef<[u8]>,
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>;
|
||||
@@ -224,7 +326,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
/// [`add_function`]: #method.add_function
|
||||
fn add_function_mut<S, A, R, F>(&mut self, name: &S, function: F)
|
||||
where
|
||||
S: ?Sized + AsRef<[u8]>,
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>;
|
||||
@@ -242,7 +344,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
fn add_async_function<S, A, R, F, FR>(&mut self, name: &S, function: F)
|
||||
where
|
||||
T: Clone,
|
||||
S: ?Sized + AsRef<[u8]>,
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
|
||||
@@ -256,8 +358,9 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
||||
///
|
||||
/// [`add_meta_function`]: #method.add_meta_function
|
||||
fn add_meta_method<A, R, M>(&mut self, meta: MetaMethod, method: M)
|
||||
fn add_meta_method<S, A, R, M>(&mut self, meta: S, method: M)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>;
|
||||
@@ -270,8 +373,9 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
||||
///
|
||||
/// [`add_meta_function`]: #method.add_meta_function
|
||||
fn add_meta_method_mut<A, R, M>(&mut self, meta: MetaMethod, method: M)
|
||||
fn add_meta_method_mut<S, A, R, M>(&mut self, meta: S, method: M)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>;
|
||||
@@ -281,8 +385,9 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
/// Metamethods for binary operators can be triggered if either the left or right argument to
|
||||
/// the binary operator has a metatable, so the first argument here is not necessarily a
|
||||
/// userdata of type `T`.
|
||||
fn add_meta_function<A, R, F>(&mut self, meta: MetaMethod, function: F)
|
||||
fn add_meta_function<S, A, R, F>(&mut self, meta: S, function: F)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>;
|
||||
@@ -292,13 +397,85 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
/// This is a version of [`add_meta_function`] that accepts a FnMut argument.
|
||||
///
|
||||
/// [`add_meta_function`]: #method.add_meta_function
|
||||
fn add_meta_function_mut<A, R, F>(&mut self, meta: MetaMethod, function: F)
|
||||
fn add_meta_function_mut<S, A, R, F>(&mut self, meta: S, function: F)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>;
|
||||
}
|
||||
|
||||
/// Field registry for [`UserData`] implementors.
|
||||
///
|
||||
/// [`UserData`]: trait.UserData.html
|
||||
pub trait UserDataFields<'lua, T: UserData> {
|
||||
/// Add a regular field getter as a method which accepts a `&T` as the parameter.
|
||||
///
|
||||
/// Regular field getters are implemented by overriding the `__index` metamethod and returning the
|
||||
/// accessed field. This allows them to be used with the expected `userdata.field` syntax.
|
||||
///
|
||||
/// If `add_meta_method` is used to set the `__index` metamethod, the `__index` metamethod will
|
||||
/// be used as a fall-back if no regular field or method are found.
|
||||
fn add_field_method_get<S, R, M>(&mut self, name: &S, method: M)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
R: ToLua<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, &T) -> Result<R>;
|
||||
|
||||
/// Add a regular field setter as a method which accepts a `&mut T` as the first parameter.
|
||||
///
|
||||
/// Regular field setters are implemented by overriding the `__newindex` metamethod and setting the
|
||||
/// accessed field. This allows them to be used with the expected `userdata.field = value` syntax.
|
||||
///
|
||||
/// If `add_meta_method` is used to set the `__newindex` metamethod, the `__newindex` metamethod will
|
||||
/// be used as a fall-back if no regular field is found.
|
||||
fn add_field_method_set<S, A, M>(&mut self, name: &S, method: M)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLua<'lua>,
|
||||
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<()>;
|
||||
|
||||
/// Add a regular field getter as a function which accepts a generic [`AnyUserData`] of type `T`
|
||||
/// argument.
|
||||
///
|
||||
/// Prefer to use [`add_field_method_get`] as it is easier to use.
|
||||
///
|
||||
/// [`AnyUserData`]: struct.AnyUserData.html
|
||||
/// [`add_field_method_get`]: #method.add_field_method_get
|
||||
fn add_field_function_get<S, R, F>(&mut self, name: &S, function: F)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
R: ToLua<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R>;
|
||||
|
||||
/// Add a regular field setter as a function which accepts a generic [`AnyUserData`] of type `T`
|
||||
/// first argument.
|
||||
///
|
||||
/// Prefer to use [`add_field_method_set`] as it is easier to use.
|
||||
///
|
||||
/// [`AnyUserData`]: struct.AnyUserData.html
|
||||
/// [`add_field_method_set`]: #method.add_field_method_set
|
||||
fn add_field_function_set<S, A, F>(&mut self, name: &S, function: F)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLua<'lua>,
|
||||
F: 'static + MaybeSend + FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()>;
|
||||
|
||||
/// Add a metamethod value computed from `f`.
|
||||
///
|
||||
/// This will initialize the metamethod value from `f` on `UserData` creation.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// `mlua` will trigger an error on an attempt to define a protected metamethod,
|
||||
/// like `__gc` or `__metatable`.
|
||||
fn add_meta_field_with<S, R, F>(&mut self, meta: S, f: F)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua) -> Result<R>,
|
||||
R: ToLua<'lua>;
|
||||
}
|
||||
|
||||
/// Trait for custom userdata types.
|
||||
///
|
||||
/// By implementing this trait, a struct becomes eligible for use inside Lua code. Implementations
|
||||
@@ -322,21 +499,21 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Custom methods and operators can be provided by implementing `add_methods` (refer to
|
||||
/// [`UserDataMethods`] for more information):
|
||||
/// Custom fields, methods and operators can be provided by implementing `add_fields` or `add_methods`
|
||||
/// (refer to [`UserDataFields`] and [`UserDataMethods`] for more information):
|
||||
///
|
||||
/// ```
|
||||
/// # use mlua::{Lua, MetaMethod, Result, UserData, UserDataMethods};
|
||||
/// # use mlua::{Lua, MetaMethod, Result, UserData, UserDataFields, UserDataMethods};
|
||||
/// # fn main() -> Result<()> {
|
||||
/// # let lua = Lua::new();
|
||||
/// struct MyUserData(i32);
|
||||
///
|
||||
/// impl UserData for MyUserData {
|
||||
/// fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
/// methods.add_method("get", |_, this, _: ()| {
|
||||
/// Ok(this.0)
|
||||
/// });
|
||||
/// fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
/// fields.add_field_method_get("val", |_, this| Ok(this.0));
|
||||
/// }
|
||||
///
|
||||
/// fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
/// methods.add_method_mut("add", |_, this, value: i32| {
|
||||
/// this.0 += value;
|
||||
/// Ok(())
|
||||
@@ -351,9 +528,9 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
/// lua.globals().set("myobject", MyUserData(123))?;
|
||||
///
|
||||
/// lua.load(r#"
|
||||
/// assert(myobject:get() == 123)
|
||||
/// assert(myobject.val == 123)
|
||||
/// myobject:add(7)
|
||||
/// assert(myobject:get() == 130)
|
||||
/// assert(myobject.val == 130)
|
||||
/// assert(myobject + 10 == 140)
|
||||
/// "#).exec()?;
|
||||
/// # Ok(())
|
||||
@@ -362,12 +539,72 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
///
|
||||
/// [`ToLua`]: trait.ToLua.html
|
||||
/// [`FromLua`]: trait.FromLua.html
|
||||
/// [`UserDataFields`]: trait.UserDataFields.html
|
||||
/// [`UserDataMethods`]: trait.UserDataMethods.html
|
||||
pub trait UserData: Sized {
|
||||
/// Adds custom fields specific to this userdata.
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(_fields: &mut F) {}
|
||||
|
||||
/// Adds custom methods and operators specific to this userdata.
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {}
|
||||
}
|
||||
|
||||
// Wraps UserData in a way to always implement `serde::Serialize` trait.
|
||||
pub(crate) enum UserDataCell<T> {
|
||||
Arc(Arc<RefCell<UserDataWrapped<T>>>),
|
||||
Plain(RefCell<UserDataWrapped<T>>),
|
||||
}
|
||||
|
||||
impl<T> UserDataCell<T> {
|
||||
pub(crate) fn new(data: T) -> Self {
|
||||
UserDataCell::Plain(RefCell::new(UserDataWrapped {
|
||||
data: Box::into_raw(Box::new(data)),
|
||||
#[cfg(feature = "serialize")]
|
||||
ser: Box::into_raw(Box::new(UserDataSerializeError)),
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn new_arc(data: T) -> Self {
|
||||
UserDataCell::Arc(Arc::new(RefCell::new(UserDataWrapped {
|
||||
data: Box::into_raw(Box::new(data)),
|
||||
#[cfg(feature = "serialize")]
|
||||
ser: Box::into_raw(Box::new(UserDataSerializeError)),
|
||||
})))
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) fn new_ser(data: T) -> Self
|
||||
where
|
||||
T: 'static + Serialize,
|
||||
{
|
||||
let data_raw = Box::into_raw(Box::new(data));
|
||||
UserDataCell::Plain(RefCell::new(UserDataWrapped {
|
||||
data: data_raw,
|
||||
ser: data_raw,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for UserDataCell<T> {
|
||||
type Target = RefCell<UserDataWrapped<T>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
UserDataCell::Arc(t) => &*t,
|
||||
UserDataCell::Plain(t) => &*t,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for UserDataCell<T> {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
UserDataCell::Arc(t) => UserDataCell::Arc(t.clone()),
|
||||
UserDataCell::Plain(_) => mlua_panic!("cannot clone non-arc userdata"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct UserDataWrapped<T> {
|
||||
pub(crate) data: *mut T,
|
||||
#[cfg(feature = "serialize")]
|
||||
@@ -386,28 +623,6 @@ impl<T> Drop for UserDataWrapped<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> UserDataWrapped<T> {
|
||||
pub(crate) fn new(data: T) -> Self {
|
||||
UserDataWrapped {
|
||||
data: Box::into_raw(Box::new(data)),
|
||||
#[cfg(feature = "serialize")]
|
||||
ser: Box::into_raw(Box::new(UserDataSerializeError)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) fn new_ser(data: T) -> Self
|
||||
where
|
||||
T: 'static + Serialize,
|
||||
{
|
||||
let data_raw = Box::into_raw(Box::new(data));
|
||||
UserDataWrapped {
|
||||
data: data_raw,
|
||||
ser: data_raw,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<T> for UserDataWrapped<T> {
|
||||
fn as_ref(&self) -> &T {
|
||||
unsafe { &*self.data }
|
||||
@@ -500,19 +715,21 @@ impl<'lua> AnyUserData<'lua> {
|
||||
let lua = self.0.lua;
|
||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||
let v = {
|
||||
// Lua 5.2/5.1 allows to store only a table. Then we will wrap the value.
|
||||
let t = lua.create_table()?;
|
||||
// Lua <= 5.2 allows to store only a table. Then we will wrap the value.
|
||||
let t = lua.create_table_with_capacity(1, 0)?;
|
||||
t.raw_set(1, v)?;
|
||||
crate::Value::Table(t)
|
||||
Value::Table(t)
|
||||
};
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
let v = v.to_lua(lua)?;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 2);
|
||||
lua.push_ref(&self.0);
|
||||
check_stack(lua.state, 3)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
lua.push_value(v)?;
|
||||
ffi::lua_setuservalue(lua.state, -2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -526,57 +743,54 @@ impl<'lua> AnyUserData<'lua> {
|
||||
let lua = self.0.lua;
|
||||
let res = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 3);
|
||||
lua.push_ref(&self.0);
|
||||
check_stack(lua.state, 3)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
ffi::lua_getuservalue(lua.state, -1);
|
||||
lua.pop_value()
|
||||
};
|
||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||
return crate::Table::from_lua(res, lua)?.get(1);
|
||||
return match <Option<Table>>::from_lua(res, lua)? {
|
||||
Some(t) => t.get(1),
|
||||
None => V::from_lua(Value::Nil, lua),
|
||||
};
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
V::from_lua(res, lua)
|
||||
}
|
||||
|
||||
/// Checks for a metamethod in this `AnyUserData`
|
||||
pub fn has_metamethod(&self, method: MetaMethod) -> Result<bool> {
|
||||
match self.get_metatable() {
|
||||
Ok(mt) => {
|
||||
let name = self.0.lua.create_string(method.name())?;
|
||||
if let Value::Nil = mt.raw_get(name)? {
|
||||
Ok(false)
|
||||
} else {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
Err(Error::UserDataTypeMismatch) => Ok(false),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
/// Returns a metatable of this `UserData`.
|
||||
///
|
||||
/// Returned [`UserDataMetatable`] object wraps the original metatable and
|
||||
/// provides safe access to its methods.
|
||||
///
|
||||
/// For `T: UserData + 'static` returned metatable is shared among all instances of type `T`.
|
||||
///
|
||||
/// [`UserDataMetatable`]: struct.UserDataMetatable.html
|
||||
pub fn get_metatable(&self) -> Result<UserDataMetatable<'lua>> {
|
||||
self.get_raw_metatable().map(UserDataMetatable)
|
||||
}
|
||||
|
||||
fn get_metatable(&self) -> Result<Table<'lua>> {
|
||||
fn get_raw_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);
|
||||
}
|
||||
check_stack(lua.state, 3)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
ffi::lua_getmetatable(lua.state, -1); // Checked that non-empty on the previous call
|
||||
Ok(Table(lua.pop_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
|
||||
let other = other.as_ref();
|
||||
// Uses lua_rawequal() under the hood
|
||||
if self == other {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
let mt = self.get_metatable()?;
|
||||
if mt != other.get_metatable()? {
|
||||
let mt = self.get_raw_metatable()?;
|
||||
if mt != other.get_raw_metatable()? {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
@@ -594,34 +808,28 @@ impl<'lua> AnyUserData<'lua> {
|
||||
T: 'static + UserData,
|
||||
F: FnOnce(&'a UserDataCell<T>) -> Result<R>,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let lua = self.0.lua;
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 3);
|
||||
check_stack(lua.state, 3)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
|
||||
if ffi::lua_getmetatable(lua.state, -1) == 0 {
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
} else {
|
||||
ffi::lua_rawgeti(
|
||||
lua.state,
|
||||
ffi::LUA_REGISTRYINDEX,
|
||||
lua.userdata_metatable::<T>()? as ffi::lua_Integer,
|
||||
);
|
||||
return Err(Error::UserDataTypeMismatch);
|
||||
}
|
||||
lua.push_userdata_metatable::<T>()?;
|
||||
|
||||
if ffi::lua_rawequal(lua.state, -1, -2) == 0 {
|
||||
// Maybe UserData destructed?
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
get_destructed_userdata_metatable(lua.state);
|
||||
if ffi::lua_rawequal(lua.state, -1, -2) == 1 {
|
||||
Err(Error::UserDataDestructed)
|
||||
} else {
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
}
|
||||
if ffi::lua_rawequal(lua.state, -1, -2) == 0 {
|
||||
// Maybe UserData destructed?
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
get_destructed_userdata_metatable(lua.state);
|
||||
if ffi::lua_rawequal(lua.state, -1, -2) == 1 {
|
||||
Err(Error::UserDataDestructed)
|
||||
} else {
|
||||
func(&*get_userdata::<UserDataCell<T>>(lua.state, -3))
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
}
|
||||
} else {
|
||||
func(&*get_userdata::<UserDataCell<T>>(lua.state, -3))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -640,23 +848,97 @@ impl<'lua> AsRef<AnyUserData<'lua>> for AnyUserData<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle to a `UserData` metatable.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UserDataMetatable<'lua>(pub(crate) Table<'lua>);
|
||||
|
||||
impl<'lua> UserDataMetatable<'lua> {
|
||||
/// Gets the value associated to `key` from the metatable.
|
||||
///
|
||||
/// If no value is associated to `key`, returns the `Nil` value.
|
||||
/// Access to restricted metamethods such as `__gc` or `__metatable` will cause an error.
|
||||
pub fn get<K: Into<MetaMethod>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
|
||||
self.0.raw_get(key.into().validate()?.name())
|
||||
}
|
||||
|
||||
/// Sets a key-value pair in the metatable.
|
||||
///
|
||||
/// If the value is `Nil`, this will effectively remove the `key`.
|
||||
/// Access to restricted metamethods such as `__gc` or `__metatable` will cause an error.
|
||||
/// Setting `__index` or `__newindex` metamethods is also restricted because their values are cached
|
||||
/// for `mlua` internal usage.
|
||||
pub fn set<K: Into<MetaMethod>, V: ToLua<'lua>>(&self, key: K, value: V) -> Result<()> {
|
||||
let key = key.into().validate()?;
|
||||
// `__index` and `__newindex` cannot be changed in runtime, because values are cached
|
||||
if key == MetaMethod::Index || key == MetaMethod::NewIndex {
|
||||
return Err(Error::MetaMethodRestricted(key.to_string()));
|
||||
}
|
||||
self.0.raw_set(key.name(), value)
|
||||
}
|
||||
|
||||
/// Checks whether the metatable contains a non-nil value for `key`.
|
||||
pub fn contains<K: Into<MetaMethod>>(&self, key: K) -> Result<bool> {
|
||||
self.0.contains_key(key.into().validate()?.name())
|
||||
}
|
||||
|
||||
/// Consumes this metatable and returns an iterator over the pairs of the metatable.
|
||||
///
|
||||
/// The pairs are wrapped in a [`Result`], since they are lazily converted to `V` type.
|
||||
///
|
||||
/// [`Result`]: type.Result.html
|
||||
pub fn pairs<V: FromLua<'lua>>(self) -> UserDataMetatablePairs<'lua, V> {
|
||||
UserDataMetatablePairs(self.0.pairs())
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the pairs of a [`UserData`] metatable.
|
||||
///
|
||||
/// It skips restricted metamethods, such as `__gc` or `__metatable`.
|
||||
///
|
||||
/// This struct is created by the [`UserDataMetatable::pairs`] method.
|
||||
///
|
||||
/// [`UserData`]: trait.UserData.html
|
||||
/// [`UserDataMetatable::pairs`]: struct.UserDataMetatable.html#method.pairs
|
||||
pub struct UserDataMetatablePairs<'lua, V>(TablePairs<'lua, StdString, V>);
|
||||
|
||||
impl<'lua, V> Iterator for UserDataMetatablePairs<'lua, V>
|
||||
where
|
||||
V: FromLua<'lua>,
|
||||
{
|
||||
type Item = Result<(MetaMethod, V)>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
match self.0.next()? {
|
||||
Ok((key, value)) => {
|
||||
// Skip restricted metamethods
|
||||
if let Ok(metamethod) = MetaMethod::from(key).validate() {
|
||||
break Some(Ok((metamethod, value)));
|
||||
}
|
||||
}
|
||||
Err(e) => break Some(Err(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'lua> Serialize for AnyUserData<'lua> {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let f = || unsafe {
|
||||
let res = (|| unsafe {
|
||||
let lua = self.0.lua;
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 2);
|
||||
check_stack(lua.state, 3)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
let ud = &*get_userdata::<UserDataCell<()>>(lua.state, -1);
|
||||
(*ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?.ser)
|
||||
.serialize(serializer)
|
||||
.map_err(|err| Error::SerializeError(err.to_string()))
|
||||
};
|
||||
f().map_err(ser::Error::custom)
|
||||
})();
|
||||
res.map_err(ser::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
+271
-390
@@ -1,24 +1,26 @@
|
||||
use std::any::{Any, TypeId};
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt::Write;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{mem, ptr, slice};
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static METATABLE_CACHE: Lazy<Mutex<HashMap<TypeId, u8>>> = Lazy::new(|| {
|
||||
// The capacity must(!) be greater than number of stored keys
|
||||
static ref METATABLE_CACHE: Mutex<HashMap<TypeId, u8>> = Mutex::new(HashMap::with_capacity(32));
|
||||
}
|
||||
Mutex::new(HashMap::with_capacity(32))
|
||||
});
|
||||
|
||||
// Checks that Lua has enough free stack space for future stack operations. On failure, this will
|
||||
// Checks that Lua has enough free stack space for future stack operations. On failure, this will
|
||||
// panic with an internal error message.
|
||||
pub unsafe fn assert_stack(state: *mut ffi::lua_State, amount: c_int) {
|
||||
// TODO: This should only be triggered when there is a logic error in `mlua`. In the future,
|
||||
// TODO: This should only be triggered when there is a logic error in `mlua`. In the future,
|
||||
// when there is a way to be confident about stack safety and test it, this could be enabled
|
||||
// only when `cfg!(debug_assertions)` is true.
|
||||
mlua_assert!(
|
||||
@@ -39,16 +41,27 @@ pub unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) -> Result<(
|
||||
pub struct StackGuard {
|
||||
state: *mut ffi::lua_State,
|
||||
top: c_int,
|
||||
extra: c_int,
|
||||
}
|
||||
|
||||
impl StackGuard {
|
||||
// Creates a StackGuard instance with wa record of the stack size, and on Drop will check the
|
||||
// stack size and drop any extra elements. If the stack size at the end is *smaller* than at
|
||||
// Creates a StackGuard instance with record of the stack size, and on Drop will check the
|
||||
// stack size and drop any extra elements. If the stack size at the end is *smaller* than at
|
||||
// the beginning, this is considered a fatal logic error and will result in a panic.
|
||||
pub unsafe fn new(state: *mut ffi::lua_State) -> StackGuard {
|
||||
StackGuard {
|
||||
state,
|
||||
top: ffi::lua_gettop(state),
|
||||
extra: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Similar to `new`, but checks and keeps `extra` elements from top of the stack on Drop.
|
||||
pub unsafe fn new_extra(state: *mut ffi::lua_State, extra: c_int) -> StackGuard {
|
||||
StackGuard {
|
||||
state,
|
||||
top: ffi::lua_gettop(state),
|
||||
extra,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,11 +70,14 @@ impl Drop for StackGuard {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let top = ffi::lua_gettop(self.state);
|
||||
if top < self.top {
|
||||
if top < self.top + self.extra {
|
||||
mlua_panic!("{} too many stack values popped", self.top - top)
|
||||
}
|
||||
if top > self.top {
|
||||
ffi::lua_settop(self.state, self.top);
|
||||
if top > self.top + self.extra {
|
||||
if self.extra > 0 {
|
||||
ffi::lua_rotate(self.state, self.top + 1, self.extra);
|
||||
}
|
||||
ffi::lua_settop(self.state, self.top + self.extra);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,17 +85,17 @@ impl Drop for StackGuard {
|
||||
|
||||
// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
|
||||
// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
|
||||
// limited lua stack. `nargs` is the same as the the parameter to `lua_pcall`, and `nresults` is
|
||||
// always LUA_MULTRET. Internally uses 2 extra stack spaces, and does not call checkstack.
|
||||
// limited lua stack. `nargs` is the same as the the parameter to `lua_pcall`, and `nresults` is
|
||||
// always LUA_MULTRET. Internally uses 2 extra stack spaces, and does not call checkstack.
|
||||
// Provided function must *never* panic.
|
||||
pub unsafe fn protect_lua(
|
||||
state: *mut ffi::lua_State,
|
||||
nargs: c_int,
|
||||
f: unsafe extern "C" fn(*mut ffi::lua_State) -> c_int,
|
||||
f: unsafe extern "C" fn(*mut ffi::lua_State) -> c_int, // Must be "C-unwind" after stabilizing
|
||||
) -> Result<()> {
|
||||
let stack_start = ffi::lua_gettop(state) - nargs;
|
||||
|
||||
ffi::lua_pushcfunction(state, error_traceback);
|
||||
ffi::lua_pushcfunction(state, ffi::safe::error_traceback);
|
||||
ffi::lua_pushcfunction(state, f);
|
||||
if nargs > 0 {
|
||||
ffi::lua_rotate(state, stack_start + 1, 2);
|
||||
@@ -95,84 +111,12 @@ pub unsafe fn protect_lua(
|
||||
}
|
||||
}
|
||||
|
||||
// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
|
||||
// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
|
||||
// limited lua stack. `nargs` and `nresults` are similar to the parameters of `lua_pcall`, but the
|
||||
// given function return type is not the return value count, instead the inner function return
|
||||
// values are assumed to match the `nresults` param. Internally uses 3 extra stack spaces, and does
|
||||
// not call checkstack. Provided function must *not* panic, and since it will generally be
|
||||
// lonjmping, should not contain any values that implement Drop.
|
||||
pub unsafe fn protect_lua_closure<F, R>(
|
||||
state: *mut ffi::lua_State,
|
||||
nargs: c_int,
|
||||
nresults: c_int,
|
||||
f: F,
|
||||
) -> Result<R>
|
||||
where
|
||||
F: Fn(*mut ffi::lua_State) -> R,
|
||||
R: Copy,
|
||||
{
|
||||
union URes<R: Copy> {
|
||||
uninit: (),
|
||||
init: R,
|
||||
}
|
||||
|
||||
struct Params<F, R: Copy> {
|
||||
function: F,
|
||||
result: URes<R>,
|
||||
nresults: c_int,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn do_call<F, R>(state: *mut ffi::lua_State) -> c_int
|
||||
where
|
||||
R: Copy,
|
||||
F: Fn(*mut ffi::lua_State) -> R,
|
||||
{
|
||||
let params = ffi::lua_touserdata(state, -1) as *mut Params<F, R>;
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
(*params).result.init = ((*params).function)(state);
|
||||
|
||||
if (*params).nresults == ffi::LUA_MULTRET {
|
||||
ffi::lua_gettop(state)
|
||||
} else {
|
||||
(*params).nresults
|
||||
}
|
||||
}
|
||||
|
||||
let stack_start = ffi::lua_gettop(state) - nargs;
|
||||
|
||||
ffi::lua_pushcfunction(state, error_traceback);
|
||||
ffi::lua_pushcfunction(state, do_call::<F, R>);
|
||||
if nargs > 0 {
|
||||
ffi::lua_rotate(state, stack_start + 1, 2);
|
||||
}
|
||||
|
||||
let mut params = Params {
|
||||
function: f,
|
||||
result: URes { uninit: () },
|
||||
nresults,
|
||||
};
|
||||
|
||||
ffi::lua_pushlightuserdata(state, &mut params as *mut Params<F, R> as *mut c_void);
|
||||
let ret = ffi::lua_pcall(state, nargs + 1, nresults, stack_start + 1);
|
||||
ffi::lua_remove(state, stack_start + 1);
|
||||
|
||||
if ret == ffi::LUA_OK {
|
||||
// LUA_OK is only returned when the do_call function has completed successfully, so
|
||||
// params.result is definitely initialized.
|
||||
Ok(params.result.init)
|
||||
} else {
|
||||
Err(pop_error(state, ret))
|
||||
}
|
||||
}
|
||||
|
||||
// Pops an error off of the stack and returns it. The specific behavior depends on the type of the
|
||||
// Pops an error off of the stack and returns it. The specific behavior depends on the type of the
|
||||
// error at the top of the stack:
|
||||
// 1) If the error is actually a WrappedPanic, this will continue the panic.
|
||||
// 2) If the error on the top of the stack is actually a WrappedError, just returns it.
|
||||
// 3) Otherwise, interprets the error as the appropriate lua error.
|
||||
// Uses 2 stack spaces, does not call lua_checkstack.
|
||||
// Uses 2 stack spaces, does not call checkstack.
|
||||
pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
|
||||
mlua_debug_assert!(
|
||||
err_code != ffi::LUA_OK && err_code != ffi::LUA_YIELD,
|
||||
@@ -186,10 +130,10 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
|
||||
if let Some(p) = (*panic).0.take() {
|
||||
resume_unwind(p);
|
||||
} else {
|
||||
mlua_panic!("error during panic handling, panic was resumed twice")
|
||||
Error::PreviouslyResumedPanic
|
||||
}
|
||||
} else {
|
||||
let err_string = to_string(state, -1).into_owned();
|
||||
let err_string = to_string(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
match err_code {
|
||||
@@ -206,7 +150,7 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
|
||||
ffi::LUA_ERRERR => {
|
||||
// This error is raised when the error handler raises an error too many times
|
||||
// recursively, and continuing to trigger the error handler would cause a stack
|
||||
// overflow. It is not very useful to differentiate between this and "ordinary"
|
||||
// overflow. It is not very useful to differentiate between this and "ordinary"
|
||||
// runtime errors, so we handle them the same way.
|
||||
Error::RuntimeError(err_string)
|
||||
}
|
||||
@@ -218,22 +162,9 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
|
||||
}
|
||||
}
|
||||
|
||||
// Internally uses 4 stack spaces, does not call checkstack
|
||||
pub unsafe fn push_string<S: ?Sized + AsRef<[u8]>>(
|
||||
state: *mut ffi::lua_State,
|
||||
s: &S,
|
||||
) -> Result<()> {
|
||||
protect_lua_closure(state, 0, 1, |state| {
|
||||
let s = s.as_ref();
|
||||
ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len());
|
||||
})
|
||||
}
|
||||
|
||||
// Internally uses 4 stack spaces, does not call checkstack
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
pub unsafe fn push_userdata<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
|
||||
})?;
|
||||
let ud = ffi::safe::lua_newuserdata(state, mem::size_of::<T>())? as *mut T;
|
||||
ptr::write(ud, t);
|
||||
Ok(())
|
||||
}
|
||||
@@ -245,34 +176,26 @@ pub unsafe fn get_userdata<T>(state: *mut ffi::lua_State, index: c_int) -> *mut
|
||||
}
|
||||
|
||||
// Pops the userdata off of the top of the stack and returns it to rust, invalidating the lua
|
||||
// userdata and gives it the special "destructed" userdata metatable. Userdata must not have been
|
||||
// previously invalidated, and this method does not check for this. Uses 1 extra stack space and
|
||||
// does not call checkstack
|
||||
// userdata and gives it the special "destructed" userdata metatable. Userdata must not have been
|
||||
// previously invalidated, and this method does not check for this.
|
||||
// Uses 1 extra stack space and does not call checkstack.
|
||||
pub unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
|
||||
// We set the metatable of userdata on __gc to a special table with no __gc method and with
|
||||
// metamethods that trigger an error on access. We do this so that it will not be double
|
||||
// metamethods that trigger an error on access. We do this so that it will not be double
|
||||
// dropped, and also so that it cannot be used or identified as any particular userdata type
|
||||
// after the first call to __gc.
|
||||
get_destructed_userdata_metatable(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
let ud = ffi::lua_touserdata(state, -1) as *mut T;
|
||||
mlua_debug_assert!(!ud.is_null(), "userdata pointer is null");
|
||||
let ud = get_userdata(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
ptr::read(ud)
|
||||
}
|
||||
|
||||
// Pushes the userdata and attaches a metatable with __gc method
|
||||
// Internally uses 5 stack spaces, does not call checkstack
|
||||
// Pushes the userdata and attaches a metatable with __gc method.
|
||||
// Internally uses 3 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);
|
||||
push_userdata(state, t)?;
|
||||
get_gc_metatable_for::<T>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
Ok(())
|
||||
}
|
||||
@@ -284,83 +207,74 @@ pub unsafe fn get_gc_userdata<T: Any>(state: *mut ffi::lua_State, index: c_int)
|
||||
return ptr::null_mut();
|
||||
}
|
||||
get_gc_metatable_for::<T>(state);
|
||||
let res = ffi::lua_rawequal(state, -1, -2) != 0;
|
||||
let res = ffi::lua_rawequal(state, -1, -2);
|
||||
ffi::lua_pop(state, 2);
|
||||
if !res {
|
||||
if res == 0 {
|
||||
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.
|
||||
// The function also, if given a `members` table index, will set up an __index metamethod to return
|
||||
// the appropriate member on __index. Additionally, if there is already an __index entry on the
|
||||
// given metatable, instead of simply overwriting the __index, instead the created __index method
|
||||
// will capture the previous one, and use it as a fallback only if the given key is not found in the
|
||||
// provided members table. Internally uses 6 stack spaces and does not call checkstack.
|
||||
// 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.
|
||||
// The function also, if given a `field_getters` or `methods` tables, will create an `__index` metamethod
|
||||
// (capturing previous one) to lookup in `field_getters` first, then `methods` and falling back to the
|
||||
// captured `__index` if no matches found.
|
||||
// The same is also applicable for `__newindex` metamethod and `field_setters` table.
|
||||
// Internally uses 9 stack spaces and does not call checkstack.
|
||||
pub unsafe fn init_userdata_metatable<T>(
|
||||
state: *mut ffi::lua_State,
|
||||
metatable: c_int,
|
||||
members: Option<c_int>,
|
||||
field_getters: Option<c_int>,
|
||||
field_setters: Option<c_int>,
|
||||
methods: Option<c_int>,
|
||||
) -> Result<()> {
|
||||
// Used if both an __index metamethod is set and regular methods, checks methods table
|
||||
// first, then __index metamethod.
|
||||
unsafe extern "C" fn meta_index_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
ffi::lua_gettable(state, ffi::lua_upvalueindex(2));
|
||||
if ffi::lua_isnil(state, -1) == 0 {
|
||||
ffi::lua_insert(state, -3);
|
||||
ffi::lua_pop(state, 2);
|
||||
1
|
||||
} else {
|
||||
ffi::lua_pop(state, 1);
|
||||
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
|
||||
ffi::lua_insert(state, -3);
|
||||
ffi::lua_call(state, 2, 1);
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
let members = members.map(|i| ffi::lua_absindex(state, i));
|
||||
ffi::lua_pushvalue(state, metatable);
|
||||
|
||||
if let Some(members) = members {
|
||||
push_string(state, "__index")?;
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
if field_getters.is_some() || methods.is_some() {
|
||||
ffi::safe::lua_pushstring(state, "__index")?;
|
||||
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
let index_type = ffi::lua_rawget(state, -3);
|
||||
if index_type == ffi::LUA_TNIL {
|
||||
ffi::lua_pop(state, 1);
|
||||
ffi::lua_pushvalue(state, members);
|
||||
} else if index_type == ffi::LUA_TFUNCTION {
|
||||
ffi::lua_pushvalue(state, members);
|
||||
protect_lua_closure(state, 2, 1, |state| {
|
||||
ffi::lua_pushcclosure(state, meta_index_impl, 2);
|
||||
})?;
|
||||
} else {
|
||||
mlua_panic!("improper __index type {}", index_type);
|
||||
match index_type {
|
||||
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
|
||||
for &idx in &[field_getters, methods] {
|
||||
if let Some(idx) = idx {
|
||||
ffi::lua_pushvalue(state, idx);
|
||||
} else {
|
||||
ffi::lua_pushnil(state);
|
||||
}
|
||||
}
|
||||
ffi::safe::lua_pushcclosure(state, ffi::safe::meta_index_impl, 3)?;
|
||||
}
|
||||
_ => mlua_panic!("improper __index type {}", index_type),
|
||||
}
|
||||
|
||||
protect_lua_closure(state, 3, 1, |state| {
|
||||
ffi::lua_rawset(state, -3);
|
||||
})?;
|
||||
ffi::safe::lua_rawset(state, -3)?;
|
||||
}
|
||||
|
||||
push_string(state, "__gc")?;
|
||||
ffi::lua_pushcfunction(state, userdata_destructor::<T>);
|
||||
protect_lua_closure(state, 3, 1, |state| {
|
||||
ffi::lua_rawset(state, -3);
|
||||
})?;
|
||||
if let Some(field_setters) = field_setters {
|
||||
ffi::safe::lua_pushstring(state, "__newindex")?;
|
||||
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
let newindex_type = ffi::lua_rawget(state, -3);
|
||||
match newindex_type {
|
||||
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
|
||||
ffi::lua_pushvalue(state, field_setters);
|
||||
ffi::safe::lua_pushcclosure(state, ffi::safe::meta_newindex_impl, 2)?;
|
||||
}
|
||||
_ => mlua_panic!("improper __newindex type {}", newindex_type),
|
||||
}
|
||||
|
||||
ffi::safe::lua_rawset(state, -3)?;
|
||||
}
|
||||
|
||||
ffi::safe::lua_pushrclosure(state, userdata_destructor::<T>, 0)?;
|
||||
ffi::safe::lua_rawsetfield(state, -2, "__gc")?;
|
||||
|
||||
push_string(state, "__metatable")?;
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
protect_lua_closure(state, 3, 1, |state| {
|
||||
ffi::lua_rawset(state, -3);
|
||||
})?;
|
||||
ffi::safe::lua_rawsetfield(state, -2, "__metatable")?;
|
||||
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
@@ -377,38 +291,20 @@ pub unsafe extern "C" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c
|
||||
|
||||
// In the context of a lua callback, this will call the given function and if the given function
|
||||
// returns an error, *or if the given function panics*, this will result in a call to lua_error (a
|
||||
// longjmp). The error or panic is wrapped in such a way that when calling pop_error back on
|
||||
// the rust side, it will resume the panic.
|
||||
// longjmp) by a C shim. The error or panic is wrapped in such a way that when calling pop_error back
|
||||
// on the rust side, it will resume the panic (or when popping a panic value from the stack).
|
||||
//
|
||||
// This function assumes the structure of the stack at the beginning of a callback, that the only
|
||||
// elements on the stack are the arguments to the callback.
|
||||
//
|
||||
// This function uses some of the bottom of the stack for error handling, the given callback will be
|
||||
// given the number of arguments available as an argument, and should return the number of returns
|
||||
// as normal, but cannot assume that the arguments available start at 0.
|
||||
pub unsafe fn callback_error<R, F>(state: *mut ffi::lua_State, f: F) -> R
|
||||
// as normal, but cannot assume that the arguments available start at 1.
|
||||
pub unsafe fn callback_error<F>(state: *mut ffi::lua_State, f: F) -> c_int
|
||||
where
|
||||
F: FnOnce(c_int) -> Result<R>,
|
||||
F: FnOnce(c_int) -> Result<c_int>,
|
||||
{
|
||||
let nargs = ffi::lua_gettop(state);
|
||||
|
||||
// We need one extra stack space to store preallocated memory, and at least 3 stack spaces
|
||||
// overall for handling error metatables
|
||||
let extra_stack = if nargs < 3 { 3 - nargs } else { 1 };
|
||||
ffi::luaL_checkstack(
|
||||
state,
|
||||
extra_stack,
|
||||
cstr!("not enough stack space for callback error handling"),
|
||||
);
|
||||
|
||||
// We cannot shadow rust errors with Lua ones, we pre-allocate enough memory to store a wrapped
|
||||
// error or panic *before* we proceed.
|
||||
let ud = ffi::lua_newuserdata(
|
||||
state,
|
||||
mem::size_of::<WrappedError>().max(mem::size_of::<WrappedPanic>()),
|
||||
);
|
||||
ffi::lua_rotate(state, 1, 1);
|
||||
|
||||
let nargs = ffi::lua_gettop(state) - 1;
|
||||
match catch_unwind(AssertUnwindSafe(|| f(nargs))) {
|
||||
Ok(Ok(r)) => {
|
||||
ffi::lua_remove(state, 1);
|
||||
@@ -416,70 +312,45 @@ where
|
||||
}
|
||||
Ok(Err(err)) => {
|
||||
ffi::lua_settop(state, 1);
|
||||
ptr::write(ud as *mut WrappedError, WrappedError(err));
|
||||
let error_ud = ffi::lua_touserdata(state, 1);
|
||||
ptr::write(error_ud as *mut WrappedError, WrappedError(err));
|
||||
get_gc_metatable_for::<WrappedError>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
ffi::lua_error(state)
|
||||
-1
|
||||
}
|
||||
Err(p) => {
|
||||
ffi::lua_settop(state, 1);
|
||||
ptr::write(ud as *mut WrappedPanic, WrappedPanic(Some(p)));
|
||||
let error_ud = ffi::lua_touserdata(state, 1);
|
||||
ptr::write(error_ud as *mut WrappedPanic, WrappedPanic(Some(p)));
|
||||
get_gc_metatable_for::<WrappedPanic>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
ffi::lua_error(state)
|
||||
-2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Takes an error at the top of the stack, and if it is a WrappedError, converts it to an
|
||||
// Error::CallbackError with a traceback, if it is some lua type, prints the error along with a
|
||||
// traceback, and if it is a WrappedPanic, does not modify it. This function does its best to avoid
|
||||
// triggering another error and shadowing previous rust errors, but it may trigger Lua errors that
|
||||
// shadow rust errors under certain memory conditions. This function ensures that such behavior
|
||||
// will *never* occur with a rust panic, however.
|
||||
pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
|
||||
// I believe luaL_traceback requires this much free stack to not error.
|
||||
const LUA_TRACEBACK_STACK: c_int = 11;
|
||||
|
||||
if ffi::lua_checkstack(state, 2) == 0 {
|
||||
// If we don't have enough stack space to even check the error type, do nothing so we don't
|
||||
// risk shadowing a rust panic.
|
||||
} else if let Some(error) = get_wrapped_error(state, -1).as_ref() {
|
||||
// lua_newuserdata and luaL_traceback may error, but nothing that implements Drop should be
|
||||
// on the rust stack at this time.
|
||||
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedError>()) as *mut WrappedError;
|
||||
let traceback = if ffi::lua_checkstack(state, LUA_TRACEBACK_STACK) != 0 {
|
||||
ffi::luaL_traceback(state, state, ptr::null(), 0);
|
||||
|
||||
let traceback = to_string(state, -1).into_owned();
|
||||
ffi::lua_pop(state, 1);
|
||||
traceback
|
||||
} else {
|
||||
"<not enough stack space for traceback>".to_owned()
|
||||
};
|
||||
|
||||
let error = error.clone();
|
||||
ffi::lua_remove(state, -2);
|
||||
|
||||
ptr::write(
|
||||
ud,
|
||||
WrappedError(Error::CallbackError {
|
||||
traceback,
|
||||
cause: Arc::new(error),
|
||||
}),
|
||||
);
|
||||
get_gc_metatable_for::<WrappedError>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
} else if get_gc_userdata::<WrappedPanic>(state, -1).is_null()
|
||||
&& ffi::lua_checkstack(state, LUA_TRACEBACK_STACK) != 0
|
||||
{
|
||||
let s = ffi::luaL_tolstring(state, -1, ptr::null_mut());
|
||||
ffi::luaL_traceback(state, state, s, 0);
|
||||
ffi::lua_remove(state, -2);
|
||||
}
|
||||
1
|
||||
// A part of the C shim for errors handling.
|
||||
// Receives indexes of error and traceback (optional) in the stack.
|
||||
// Converts error into a `CallbackError` and attaches the traceback provided.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wrapped_error_traceback(
|
||||
state: *mut ffi::lua_State,
|
||||
error_idx: c_int,
|
||||
traceback_idx: c_int,
|
||||
) {
|
||||
let wrapped_error = mlua_expect!(
|
||||
get_gc_userdata::<WrappedError>(state, error_idx).as_mut(),
|
||||
"cannot get <WrappedError>"
|
||||
);
|
||||
let traceback = match traceback_idx {
|
||||
0 => "<not enough stack space for traceback>".to_string(),
|
||||
_ => to_string(state, traceback_idx),
|
||||
};
|
||||
let cause = Arc::new(wrapped_error.0.clone());
|
||||
wrapped_error.0 = Error::CallbackError { traceback, cause };
|
||||
}
|
||||
|
||||
// Returns Lua main thread for Lua >= 5.2 or checks that the passed thread is main for Lua 5.1.
|
||||
// Does not call lua_checkstack, uses 1 stack space.
|
||||
pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua_State> {
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
@@ -502,14 +373,19 @@ pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua
|
||||
}
|
||||
}
|
||||
|
||||
// Pushes a WrappedError to the top of the stack. Uses two stack spaces and does not call
|
||||
// lua_checkstack.
|
||||
// Pushes a WrappedError to the top of the stack.
|
||||
// Uses 2 stack spaces and does not call checkstack.
|
||||
pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) -> Result<()> {
|
||||
push_gc_userdata::<WrappedError>(state, WrappedError(err))
|
||||
let error_ud = ffi::safe::lua_newwrappederror(state)? as *mut WrappedError;
|
||||
ptr::write(error_ud, WrappedError(err));
|
||||
get_gc_metatable_for::<WrappedError>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Checks if the value at the given index is a WrappedError, and if it is returns a pointer to it,
|
||||
// otherwise returns null. Uses 2 stack spaces and does not call lua_checkstack.
|
||||
// otherwise returns null.
|
||||
// Uses 2 stack spaces and does not call checkstack.
|
||||
pub unsafe fn get_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> *const Error {
|
||||
let ud = get_gc_userdata::<WrappedError>(state, index);
|
||||
if ud.is_null() {
|
||||
@@ -518,13 +394,15 @@ pub unsafe fn get_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> *co
|
||||
&(*ud).0
|
||||
}
|
||||
|
||||
// Initialize the internal (with __gc) metatable for a type T
|
||||
// Initialize the internal (with __gc method) metatable for a type T.
|
||||
// Uses 6 stack spaces and calls checkstack.
|
||||
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>();
|
||||
customize_fn: Option<fn(*mut ffi::lua_State) -> Result<()>>,
|
||||
) -> Result<*const u8> {
|
||||
check_stack(state, 6)?;
|
||||
|
||||
let type_id = TypeId::of::<T>();
|
||||
let ref_addr = {
|
||||
let mut mt_cache = mlua_expect!(METATABLE_CACHE.lock(), "cannot lock metatable cache");
|
||||
mlua_assert!(
|
||||
@@ -535,21 +413,21 @@ pub unsafe fn init_gc_metatable_for<T: Any>(
|
||||
&mt_cache[&type_id] as *const u8
|
||||
};
|
||||
|
||||
ffi::lua_newtable(state);
|
||||
ffi::safe::lua_createtable(state, 0, 3)?;
|
||||
|
||||
ffi::lua_pushstring(state, cstr!("__gc"));
|
||||
ffi::lua_pushcfunction(state, userdata_destructor::<T>);
|
||||
ffi::lua_rawset(state, -3);
|
||||
ffi::safe::lua_pushrclosure(state, userdata_destructor::<T>, 0)?;
|
||||
ffi::safe::lua_rawsetfield(state, -2, "__gc")?;
|
||||
|
||||
ffi::lua_pushstring(state, cstr!("__metatable"));
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
ffi::lua_rawset(state, -3);
|
||||
ffi::safe::lua_rawsetfield(state, -2, "__metatable")?;
|
||||
|
||||
if let Some(f) = customize_fn {
|
||||
f(state)
|
||||
f(state)?;
|
||||
}
|
||||
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *mut c_void);
|
||||
ffi::safe::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *mut c_void)?;
|
||||
|
||||
Ok(ref_addr)
|
||||
}
|
||||
|
||||
pub unsafe fn get_gc_metatable_for<T: Any>(state: *mut ffi::lua_State) {
|
||||
@@ -558,24 +436,23 @@ pub unsafe fn get_gc_metatable_for<T: Any>(state: *mut ffi::lua_State) {
|
||||
let mt_cache = mlua_expect!(METATABLE_CACHE.lock(), "cannot lock metatable cache");
|
||||
mlua_expect!(mt_cache.get(&type_id), "gc metatable does not exist") as *const u8
|
||||
};
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *mut c_void);
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *const c_void);
|
||||
}
|
||||
|
||||
// Initialize the error, panic, and destructed userdata metatables.
|
||||
pub unsafe fn init_error_registry(state: *mut ffi::lua_State) {
|
||||
assert_stack(state, 8);
|
||||
// Returns address of WrappedError and WrappedPanic metatables in Lua registry.
|
||||
pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(*const u8, *const u8)> {
|
||||
check_stack(state, 7)?;
|
||||
|
||||
// Create error and panic metatables
|
||||
|
||||
unsafe extern "C" fn error_tostring(state: *mut ffi::lua_State) -> c_int {
|
||||
let err_buf = callback_error(state, |_| {
|
||||
callback_error(state, |_| {
|
||||
check_stack(state, 3)?;
|
||||
if let Some(error) = get_wrapped_error(state, -1).as_ref() {
|
||||
ffi::lua_pushlightuserdata(
|
||||
state,
|
||||
&ERROR_PRINT_BUFFER_KEY as *const u8 as *mut c_void,
|
||||
);
|
||||
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
|
||||
|
||||
let err_buf = if let Some(error) = get_wrapped_error(state, -1).as_ref() {
|
||||
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
|
||||
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
|
||||
ffi::lua_pop(state, 2);
|
||||
|
||||
@@ -584,183 +461,187 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) {
|
||||
// be possible to make this consume arbitrary amounts of memory (for example, some
|
||||
// kind of recursive error structure?)
|
||||
let _ = write!(&mut (*err_buf), "{}", error);
|
||||
// Find first two sources that caused the error
|
||||
let mut source1 = error.source();
|
||||
let mut source0 = source1.and_then(|s| s.source());
|
||||
while let Some(source) = source0.and_then(|s| s.source()) {
|
||||
source1 = source0;
|
||||
source0 = Some(source);
|
||||
}
|
||||
match (source1, source0) {
|
||||
(_, Some(error0)) if error0.to_string().contains("\nstack traceback:\n") => {
|
||||
let _ = write!(&mut (*err_buf), "\ncaused by: {}", error0);
|
||||
}
|
||||
(Some(error1), Some(error0)) => {
|
||||
let _ = write!(&mut (*err_buf), "\ncaused by: {}", error0);
|
||||
let s = error1.to_string();
|
||||
if let Some(traceback) = s.splitn(2, "\nstack traceback:\n").nth(1) {
|
||||
let _ = write!(&mut (*err_buf), "\nstack traceback:\n{}", traceback);
|
||||
}
|
||||
}
|
||||
(Some(error1), None) => {
|
||||
let _ = write!(&mut (*err_buf), "\ncaused by: {}", error1);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(err_buf)
|
||||
} else if let Some(panic) = get_gc_userdata::<WrappedPanic>(state, -1).as_ref() {
|
||||
if let Some(ref p) = (*panic).0 {
|
||||
ffi::lua_pushlightuserdata(
|
||||
state,
|
||||
&ERROR_PRINT_BUFFER_KEY as *const u8 as *mut c_void,
|
||||
);
|
||||
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
|
||||
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
|
||||
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
|
||||
(*err_buf).clear();
|
||||
ffi::lua_pop(state, 2);
|
||||
|
||||
let error = if let Some(x) = p.downcast_ref::<&str>() {
|
||||
x.to_string()
|
||||
} else if let Some(x) = p.downcast_ref::<String>() {
|
||||
x.to_string()
|
||||
if let Some(msg) = p.downcast_ref::<&str>() {
|
||||
let _ = write!(&mut (*err_buf), "{}", msg);
|
||||
} else if let Some(msg) = p.downcast_ref::<String>() {
|
||||
let _ = write!(&mut (*err_buf), "{}", msg);
|
||||
} else {
|
||||
"panic".to_string()
|
||||
let _ = write!(&mut (*err_buf), "<panic>");
|
||||
};
|
||||
|
||||
(*err_buf).clear();
|
||||
let _ = write!(&mut (*err_buf), "{}", error);
|
||||
Ok(err_buf)
|
||||
} else {
|
||||
mlua_panic!("error during panic handling, panic was resumed")
|
||||
Err(Error::PreviouslyResumedPanic)
|
||||
}
|
||||
} else {
|
||||
// I'm not sure whether this is possible to trigger without bugs in mlua?
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
}
|
||||
});
|
||||
}?;
|
||||
|
||||
ffi::lua_pushlstring(
|
||||
state,
|
||||
(*err_buf).as_ptr() as *const c_char,
|
||||
(*err_buf).len(),
|
||||
);
|
||||
(*err_buf).clear();
|
||||
1
|
||||
ffi::safe::lua_pushstring(state, &*err_buf)?;
|
||||
(*err_buf).clear();
|
||||
|
||||
Ok(1)
|
||||
})
|
||||
}
|
||||
|
||||
init_gc_metatable_for::<WrappedError>(
|
||||
let wrapped_error_key = init_gc_metatable_for::<WrappedError>(
|
||||
state,
|
||||
Some(|state| {
|
||||
ffi::lua_pushstring(state, cstr!("__tostring"));
|
||||
ffi::lua_pushcfunction(state, error_tostring);
|
||||
ffi::lua_rawset(state, -3);
|
||||
ffi::safe::lua_pushrclosure(state, error_tostring, 0)?;
|
||||
ffi::safe::lua_rawsetfield(state, -2, "__tostring")
|
||||
}),
|
||||
);
|
||||
)?;
|
||||
|
||||
init_gc_metatable_for::<WrappedPanic>(
|
||||
let wrapped_panic_key = init_gc_metatable_for::<WrappedPanic>(
|
||||
state,
|
||||
Some(|state| {
|
||||
ffi::lua_pushstring(state, cstr!("__tostring"));
|
||||
ffi::lua_pushcfunction(state, error_tostring);
|
||||
ffi::lua_rawset(state, -3);
|
||||
ffi::safe::lua_pushrclosure(state, error_tostring, 0)?;
|
||||
ffi::safe::lua_rawsetfield(state, -2, "__tostring")
|
||||
}),
|
||||
);
|
||||
)?;
|
||||
|
||||
// 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_gc_metatable_for::<WrappedError>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
ffi::lua_error(state)
|
||||
callback_error(state, |_| {
|
||||
check_stack(state, 2)?;
|
||||
let error_ud = ffi::safe::lua_newwrappederror(state)? as *mut WrappedError;
|
||||
ptr::write(error_ud, WrappedError(Error::CallbackDestructed));
|
||||
get_gc_metatable_for::<WrappedError>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
Ok(-1) // to trigger lua_error
|
||||
})
|
||||
}
|
||||
|
||||
ffi::lua_pushlightuserdata(
|
||||
state,
|
||||
&DESTRUCTED_USERDATA_METATABLE as *const u8 as *mut c_void,
|
||||
);
|
||||
ffi::lua_newtable(state);
|
||||
|
||||
ffi::safe::lua_createtable(state, 0, 26)?;
|
||||
ffi::safe::lua_pushrclosure(state, destructed_error, 0)?;
|
||||
for &method in &[
|
||||
cstr!("__add"),
|
||||
cstr!("__sub"),
|
||||
cstr!("__mul"),
|
||||
cstr!("__div"),
|
||||
cstr!("__mod"),
|
||||
cstr!("__pow"),
|
||||
cstr!("__unm"),
|
||||
"__add",
|
||||
"__sub",
|
||||
"__mul",
|
||||
"__div",
|
||||
"__mod",
|
||||
"__pow",
|
||||
"__unm",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
cstr!("__idiv"),
|
||||
"__idiv",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
cstr!("__band"),
|
||||
"__band",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
cstr!("__bor"),
|
||||
"__bor",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
cstr!("__bxor"),
|
||||
"__bxor",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
cstr!("__bnot"),
|
||||
"__bnot",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
cstr!("__shl"),
|
||||
"__shl",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
cstr!("__shr"),
|
||||
cstr!("__concat"),
|
||||
cstr!("__len"),
|
||||
cstr!("__eq"),
|
||||
cstr!("__lt"),
|
||||
cstr!("__le"),
|
||||
cstr!("__index"),
|
||||
cstr!("__newindex"),
|
||||
cstr!("__call"),
|
||||
cstr!("__tostring"),
|
||||
"__shr",
|
||||
"__concat",
|
||||
"__len",
|
||||
"__eq",
|
||||
"__lt",
|
||||
"__le",
|
||||
"__index",
|
||||
"__newindex",
|
||||
"__call",
|
||||
"__tostring",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
cstr!("__pairs"),
|
||||
"__pairs",
|
||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||
cstr!("__ipairs"),
|
||||
"__ipairs",
|
||||
#[cfg(feature = "lua54")]
|
||||
cstr!("__close"),
|
||||
"__close",
|
||||
] {
|
||||
ffi::lua_pushstring(state, method);
|
||||
ffi::lua_pushcfunction(state, destructed_error);
|
||||
ffi::lua_rawset(state, -3);
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
ffi::safe::lua_rawsetfield(state, -3, method)?;
|
||||
}
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
||||
let destructed_metatable_key = &DESTRUCTED_USERDATA_METATABLE as *const u8 as *const c_void;
|
||||
ffi::safe::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, destructed_metatable_key)?;
|
||||
|
||||
// Create error print buffer
|
||||
init_gc_metatable_for::<String>(state, None)?;
|
||||
push_gc_userdata(state, String::new())?;
|
||||
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
|
||||
ffi::safe::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key)?;
|
||||
|
||||
ffi::lua_pushlightuserdata(state, &ERROR_PRINT_BUFFER_KEY as *const u8 as *mut c_void);
|
||||
|
||||
let ud = ffi::lua_newuserdata(state, mem::size_of::<String>()) as *mut String;
|
||||
ptr::write(ud, String::new());
|
||||
|
||||
ffi::lua_newtable(state);
|
||||
ffi::lua_pushstring(state, cstr!("__gc"));
|
||||
ffi::lua_pushcfunction(state, userdata_destructor::<String>);
|
||||
ffi::lua_rawset(state, -3);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
|
||||
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
||||
Ok((wrapped_error_key, wrapped_panic_key))
|
||||
}
|
||||
|
||||
struct WrappedError(pub Error);
|
||||
struct WrappedPanic(pub Option<Box<dyn Any + Send + 'static>>);
|
||||
pub(crate) struct WrappedError(pub Error);
|
||||
pub(crate) struct WrappedPanic(pub Option<Box<dyn Any + Send + 'static>>);
|
||||
|
||||
// Converts the given lua value to a string in a reasonable format without causing a Lua error or
|
||||
// panicking.
|
||||
unsafe fn to_string<'a>(state: *mut ffi::lua_State, index: c_int) -> Cow<'a, str> {
|
||||
unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> String {
|
||||
match ffi::lua_type(state, index) {
|
||||
ffi::LUA_TNONE => "<none>".into(),
|
||||
ffi::LUA_TNIL => "<nil>".into(),
|
||||
ffi::LUA_TBOOLEAN => (ffi::lua_toboolean(state, index) != 1).to_string().into(),
|
||||
ffi::LUA_TNONE => "<none>".to_string(),
|
||||
ffi::LUA_TNIL => "<nil>".to_string(),
|
||||
ffi::LUA_TBOOLEAN => (ffi::lua_toboolean(state, index) != 1).to_string(),
|
||||
ffi::LUA_TLIGHTUSERDATA => {
|
||||
format!("<lightuserdata {:?}>", ffi::lua_topointer(state, index)).into()
|
||||
format!("<lightuserdata {:?}>", ffi::lua_topointer(state, index))
|
||||
}
|
||||
ffi::LUA_TNUMBER => {
|
||||
let mut isint = 0;
|
||||
let i = ffi::lua_tointegerx(state, -1, &mut isint);
|
||||
if isint == 0 {
|
||||
ffi::lua_tonumber(state, index).to_string().into()
|
||||
ffi::lua_tonumber(state, index).to_string()
|
||||
} else {
|
||||
i.to_string().into()
|
||||
i.to_string()
|
||||
}
|
||||
}
|
||||
ffi::LUA_TSTRING => {
|
||||
let mut size = 0;
|
||||
// This will not trigger a 'm' error, because the reference is guaranteed to be of
|
||||
// string type
|
||||
let data = ffi::lua_tolstring(state, index, &mut size);
|
||||
String::from_utf8_lossy(slice::from_raw_parts(data as *const u8, size))
|
||||
String::from_utf8_lossy(slice::from_raw_parts(data as *const u8, size)).into_owned()
|
||||
}
|
||||
ffi::LUA_TTABLE => format!("<table {:?}>", ffi::lua_topointer(state, index)).into(),
|
||||
ffi::LUA_TFUNCTION => format!("<function {:?}>", ffi::lua_topointer(state, index)).into(),
|
||||
ffi::LUA_TUSERDATA => format!("<userdata {:?}>", ffi::lua_topointer(state, index)).into(),
|
||||
ffi::LUA_TTHREAD => format!("<thread {:?}>", ffi::lua_topointer(state, index)).into(),
|
||||
_ => "<unknown>".into(),
|
||||
ffi::LUA_TTABLE => format!("<table {:?}>", ffi::lua_topointer(state, index)),
|
||||
ffi::LUA_TFUNCTION => format!("<function {:?}>", ffi::lua_topointer(state, index)),
|
||||
ffi::LUA_TUSERDATA => format!("<userdata {:?}>", ffi::lua_topointer(state, index)),
|
||||
ffi::LUA_TTHREAD => format!("<thread {:?}>", ffi::lua_topointer(state, index)),
|
||||
_ => "<unknown>".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) {
|
||||
ffi::lua_pushlightuserdata(
|
||||
state,
|
||||
&DESTRUCTED_USERDATA_METATABLE as *const u8 as *mut c_void,
|
||||
);
|
||||
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
|
||||
let key = &DESTRUCTED_USERDATA_METATABLE as *const u8 as *const c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, key);
|
||||
}
|
||||
|
||||
static DESTRUCTED_USERDATA_METATABLE: u8 = 0;
|
||||
|
||||
+6
-8
@@ -8,7 +8,6 @@ use {
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
@@ -17,10 +16,9 @@ use crate::thread::Thread;
|
||||
use crate::types::{Integer, LightUserData, Number};
|
||||
use crate::userdata::AnyUserData;
|
||||
|
||||
/// A dynamically typed Lua value. The `String`, `Table`, `Function`, `Thread`, and `UserData`
|
||||
/// variants contain handle types into the internal Lua state. It is a logic error to mix handle
|
||||
/// types between separate `Lua` instances, or between a parent `Lua` instance and one received as a
|
||||
/// parameter in a Rust callback, and doing so will result in a panic.
|
||||
/// A dynamically typed Lua value. The `String`, `Table`, `Function`, `Thread`, and `UserData`
|
||||
/// variants contain handle types into the internal Lua state. It is a logic error to mix handle
|
||||
/// types between separate `Lua` instances, and doing so will result in a panic.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Value<'lua> {
|
||||
/// The Lua value `nil`.
|
||||
@@ -48,7 +46,7 @@ pub enum Value<'lua> {
|
||||
/// Reference to a userdata object that holds a custom type which implements `UserData`.
|
||||
/// Special builtin userdata types will be represented as other `Value` variants.
|
||||
UserData(AnyUserData<'lua>),
|
||||
/// `Error` is a special builtin userdata type. When received from Lua it is implicitly cloned.
|
||||
/// `Error` is a special builtin userdata type. When received from Lua it is implicitly cloned.
|
||||
Error(Error),
|
||||
}
|
||||
pub use self::Value::Nil;
|
||||
@@ -96,8 +94,8 @@ impl<'lua> PartialEq for Value<'lua> {
|
||||
(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::Integer(a), Value::Number(b)) => *a as Number == *b,
|
||||
(Value::Number(a), Value::Integer(b)) => *a == *b as 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,
|
||||
|
||||
+18
-11
@@ -1,15 +1,4 @@
|
||||
#![cfg(feature = "async")]
|
||||
#![cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
feature(link_args)
|
||||
)]
|
||||
|
||||
#[cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
@@ -136,6 +125,24 @@ async fn test_async_handle_yield() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_multi_return_nil() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
lua.globals().set(
|
||||
"func",
|
||||
lua.create_async_function(|_, _: ()| async { Ok((Option::<String>::None, "error")) })?,
|
||||
)?;
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
local ok, err = func()
|
||||
assert(err == "error")
|
||||
"#,
|
||||
)
|
||||
.exec_async()
|
||||
.await
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_return_async_closure() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
@@ -1,15 +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",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use bstr::{BStr, BString};
|
||||
use mlua::{Lua, Result};
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
use std::ffi::CString;
|
||||
|
||||
use maplit::{btreemap, btreeset, hashmap, hashset};
|
||||
use mlua::{Lua, Result};
|
||||
|
||||
#[test]
|
||||
fn test_conv_vec() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let v = vec![1, 2, 3];
|
||||
lua.globals().set("v", v.clone())?;
|
||||
let v2: Vec<i32> = lua.globals().get("v")?;
|
||||
assert!(v == v2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conv_hashmap() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let map = hashmap! {"hello".to_string() => "world".to_string()};
|
||||
lua.globals().set("map", map.clone())?;
|
||||
let map2: HashMap<String, String> = lua.globals().get("map")?;
|
||||
assert!(map == map2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conv_hashset() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let set = hashset! {"hello".to_string(), "world".to_string()};
|
||||
lua.globals().set("set", set.clone())?;
|
||||
let set2: HashSet<String> = lua.globals().get("set")?;
|
||||
assert!(set == set2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conv_btreemap() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let map = btreemap! {"hello".to_string() => "world".to_string()};
|
||||
lua.globals().set("map", map.clone())?;
|
||||
let map2: BTreeMap<String, String> = lua.globals().get("map")?;
|
||||
assert!(map == map2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conv_btreeset() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let set = btreeset! {"hello".to_string(), "world".to_string()};
|
||||
lua.globals().set("set", set.clone())?;
|
||||
let set2: BTreeSet<String> = lua.globals().get("set")?;
|
||||
assert!(set == set2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conv_cstring() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let s = CString::new(b"hello".to_vec()).unwrap();
|
||||
lua.globals().set("s", s.clone())?;
|
||||
let s2: CString = lua.globals().get("s")?;
|
||||
assert!(s == s2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conv_cow() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let s = Cow::from("hello");
|
||||
lua.globals().set("s", s.clone())?;
|
||||
let s2: String = lua.globals().get("s")?;
|
||||
assert!(s == s2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conv_boxed_str() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let s = String::from("hello").into_boxed_str();
|
||||
lua.globals().set("s", s.clone())?;
|
||||
let s2: Box<str> = lua.globals().get("s")?;
|
||||
assert!(s == s2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conv_boxed_slice() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let v = vec![1, 2, 3].into_boxed_slice();
|
||||
lua.globals().set("v", v.clone())?;
|
||||
let v2: Box<[i32]> = lua.globals().get("v")?;
|
||||
assert!(v == v2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
+1
-13
@@ -1,15 +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",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use mlua::{Function, Lua, Result, String};
|
||||
|
||||
#[test]
|
||||
@@ -95,7 +83,7 @@ fn test_dump() -> Result<()> {
|
||||
let concat_lua = lua
|
||||
.load(r#"function(arg1, arg2) return arg1 .. arg2 end"#)
|
||||
.eval::<Function>()?;
|
||||
let concat = lua.load(&concat_lua.dump(false)?).into_function()?;
|
||||
let concat = lua.load(&concat_lua.dump(false)).into_function()?;
|
||||
|
||||
assert_eq!(concat.call::<_, String>(("foo", "bar"))?, "foobar");
|
||||
|
||||
|
||||
@@ -1,15 +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",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::ops::Deref;
|
||||
use std::str;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#![cfg(feature = "macros")]
|
||||
|
||||
use mlua::{chunk, Lua, Result};
|
||||
|
||||
#[test]
|
||||
fn test_chunk_macro() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let name = "Rustacean";
|
||||
let table = vec![1];
|
||||
|
||||
let data = lua.create_table()?;
|
||||
data.raw_set("num", 1)?;
|
||||
|
||||
lua.globals().set("g", 123)?;
|
||||
|
||||
lua.load(chunk! {
|
||||
assert($name == "Rustacean")
|
||||
assert($table[1] == 1)
|
||||
assert($data.num == 1)
|
||||
assert(g == 123)
|
||||
s = 321
|
||||
})
|
||||
.exec()?;
|
||||
|
||||
assert_eq!(lua.globals().get::<_, i32>("s")?, 321);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,15 +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",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use mlua::{Lua, Result, UserData};
|
||||
|
||||
+162
-44
@@ -1,19 +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",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use mlua::{Error, Function, Lua, MetaMethod, Result, String, UserData, UserDataMethods};
|
||||
use mlua::{
|
||||
AnyUserData, Error, Function, Lua, MetaMethod, Result, String, UserData, UserDataFields,
|
||||
UserDataMethods,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn scope_func() -> Result<()> {
|
||||
@@ -35,42 +27,16 @@ fn scope_func() -> Result<()> {
|
||||
assert_eq!(Rc::strong_count(&rc), 1);
|
||||
|
||||
match lua.globals().get::<_, Function>("bad")?.call::<_, ()>(()) {
|
||||
Err(Error::CallbackError { .. }) => {}
|
||||
Err(Error::CallbackError { ref cause, .. }) => match *cause.as_ref() {
|
||||
Error::CallbackDestructed => {}
|
||||
ref err => panic!("wrong error type {:?}", err),
|
||||
},
|
||||
r => panic!("improper return for destructed function: {:?}", r),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scope_drop() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
struct MyUserdata(Rc<()>);
|
||||
impl UserData for MyUserdata {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("method", |_, _, ()| Ok(()));
|
||||
}
|
||||
}
|
||||
|
||||
let rc = Rc::new(());
|
||||
|
||||
lua.scope(|scope| {
|
||||
lua.globals()
|
||||
.set("test", scope.create_userdata(MyUserdata(rc.clone()))?)?;
|
||||
assert_eq!(Rc::strong_count(&rc), 2);
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(Rc::strong_count(&rc), 1);
|
||||
|
||||
match lua.load("test:method()").exec() {
|
||||
Err(Error::CallbackError { .. }) => {}
|
||||
r => panic!("improper return for destructed userdata: {:?}", r),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scope_capture() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -90,7 +56,7 @@ fn scope_capture() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn outer_lua_access() -> Result<()> {
|
||||
fn scope_outer_lua_access() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let table = lua.create_table()?;
|
||||
@@ -104,6 +70,41 @@ fn outer_lua_access() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scope_userdata_fields() -> Result<()> {
|
||||
struct MyUserData<'a>(&'a Cell<i64>);
|
||||
|
||||
impl<'a> UserData for MyUserData<'a> {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("val", |_, data| Ok(data.0.get()));
|
||||
fields.add_field_method_set("val", |_, data, val| {
|
||||
data.0.set(val);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
|
||||
let i = Cell::new(42);
|
||||
let f: Function = lua
|
||||
.load(
|
||||
r#"
|
||||
function(u)
|
||||
assert(u.val == 42)
|
||||
u.val = 44
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.eval()?;
|
||||
|
||||
lua.scope(|scope| f.call::<_, ()>(scope.create_nonstatic_userdata(MyUserData(&i))?))?;
|
||||
|
||||
assert_eq!(i.get(), 44);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scope_userdata_methods() -> Result<()> {
|
||||
struct MyUserData<'a>(&'a Cell<i64>);
|
||||
@@ -238,3 +239,120 @@ fn scope_userdata_mismatch() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scope_userdata_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(()));
|
||||
}
|
||||
}
|
||||
|
||||
struct MyUserDataArc(Arc<()>);
|
||||
|
||||
impl UserData for MyUserDataArc {}
|
||||
|
||||
let rc = Rc::new(());
|
||||
let arc = Arc::new(());
|
||||
lua.scope(|scope| {
|
||||
let ud = scope.create_userdata(MyUserData(rc.clone()))?;
|
||||
ud.set_user_value(MyUserDataArc(arc.clone()))?;
|
||||
lua.globals().set("ud", ud)?;
|
||||
assert_eq!(Rc::strong_count(&rc), 2);
|
||||
assert_eq!(Arc::strong_count(&arc), 2);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Rc::strong_count(&rc), 1);
|
||||
assert_eq!(Arc::strong_count(&arc), 1);
|
||||
|
||||
match lua.load("ud:method()").exec() {
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
Error::CallbackDestructed => {}
|
||||
err => panic!("expected CallbackDestructed, got {:?}", err),
|
||||
},
|
||||
r => panic!("improper return for destructed userdata: {:?}", r),
|
||||
};
|
||||
|
||||
let ud = lua.globals().get::<_, AnyUserData>("ud")?;
|
||||
match ud.borrow::<MyUserData>() {
|
||||
Ok(_) => panic!("succesfull borrow for destructed userdata"),
|
||||
Err(Error::UserDataDestructed) => {}
|
||||
Err(err) => panic!("improper borrow error for destructed userdata: {:?}", err),
|
||||
}
|
||||
|
||||
match ud.get_metatable() {
|
||||
Ok(_) => panic!("successful metatable retrieval of destructed userdata"),
|
||||
Err(Error::UserDataDestructed) => {}
|
||||
Err(err) => panic!(
|
||||
"improper metatable error for destructed userdata: {:?}",
|
||||
err
|
||||
),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scope_nonstatic_userdata_drop() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
struct MyUserData<'a>(&'a Cell<i64>, Arc<()>);
|
||||
|
||||
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(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
struct MyUserDataArc(Arc<()>);
|
||||
|
||||
impl UserData for MyUserDataArc {}
|
||||
|
||||
let i = Cell::new(1);
|
||||
let arc = Arc::new(());
|
||||
lua.scope(|scope| {
|
||||
let ud = scope.create_nonstatic_userdata(MyUserData(&i, arc.clone()))?;
|
||||
ud.set_user_value(MyUserDataArc(arc.clone()))?;
|
||||
lua.globals().set("ud", ud)?;
|
||||
lua.load("ud:inc()").exec()?;
|
||||
assert_eq!(Arc::strong_count(&arc), 3);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&arc), 1);
|
||||
|
||||
match lua.load("ud:inc()").exec() {
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
Error::CallbackDestructed => {}
|
||||
err => panic!("expected CallbackDestructed, got {:?}", err),
|
||||
},
|
||||
r => panic!("improper return for destructed userdata: {:?}", r),
|
||||
};
|
||||
|
||||
let ud = lua.globals().get::<_, AnyUserData>("ud")?;
|
||||
match ud.borrow::<MyUserData>() {
|
||||
Ok(_) => panic!("succesfull borrow for destructed userdata"),
|
||||
Err(Error::UserDataDestructed) => {}
|
||||
Err(err) => panic!("improper borrow error for destructed userdata: {:?}", err),
|
||||
}
|
||||
match ud.get_metatable() {
|
||||
Ok(_) => panic!("successful metatable retrieval of destructed userdata"),
|
||||
Err(Error::UserDataDestructed) => {}
|
||||
Err(err) => panic!(
|
||||
"improper metatable error for destructed userdata: {:?}",
|
||||
err
|
||||
),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+95
-21
@@ -1,17 +1,8 @@
|
||||
#![cfg(feature = "serialize")]
|
||||
#![cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
feature(link_args)
|
||||
)]
|
||||
|
||||
#[cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
use std::collections::HashMap;
|
||||
|
||||
use mlua::{Error, Lua, LuaSerdeExt, Result as LuaResult, UserData, Value};
|
||||
use mlua::{Error, Lua, LuaSerdeExt, Result as LuaResult, SerializeOptions, UserData, Value};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[test]
|
||||
@@ -19,17 +10,17 @@ fn test_serialize() -> Result<(), Box<dyn std::error::Error>> {
|
||||
#[derive(Serialize)]
|
||||
struct MyUserData(i64, String);
|
||||
|
||||
impl UserData for MyUserData {};
|
||||
impl UserData for MyUserData {}
|
||||
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
let ud = lua.create_ser_userdata(MyUserData(123, "test userdata".into()))?;
|
||||
globals.set("ud", ud)?;
|
||||
globals.set("null", lua.null()?)?;
|
||||
globals.set("null", lua.null())?;
|
||||
|
||||
let empty_array = lua.create_table()?;
|
||||
empty_array.set_metatable(Some(lua.array_metatable()?));
|
||||
empty_array.set_metatable(Some(lua.array_metatable()));
|
||||
globals.set("empty_array", empty_array)?;
|
||||
|
||||
let val = lua
|
||||
@@ -81,7 +72,7 @@ fn test_serialize_in_scope() -> LuaResult<()> {
|
||||
#[derive(Serialize, Clone)]
|
||||
struct MyUserData(i64, String);
|
||||
|
||||
impl UserData for MyUserData {};
|
||||
impl UserData for MyUserData {}
|
||||
|
||||
let lua = Lua::new();
|
||||
lua.scope(|scope| {
|
||||
@@ -104,6 +95,19 @@ fn test_serialize_in_scope() -> LuaResult<()> {
|
||||
Err(e) => panic!("expected destructed error, got {}", e),
|
||||
}
|
||||
|
||||
struct MyUserDataRef<'a>(&'a ());
|
||||
|
||||
impl<'a> UserData for MyUserDataRef<'a> {}
|
||||
|
||||
lua.scope(|scope| {
|
||||
let ud = scope.create_nonstatic_userdata(MyUserDataRef(&()))?;
|
||||
match serde_json::to_value(&ud) {
|
||||
Ok(v) => panic!("expected serialization error, got {}", v),
|
||||
Err(serde_json::Error { .. }) => {}
|
||||
};
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -112,7 +116,7 @@ fn test_serialize_failure() -> Result<(), Box<dyn std::error::Error>> {
|
||||
#[derive(Serialize)]
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {};
|
||||
impl UserData for MyUserData {}
|
||||
|
||||
let lua = Lua::new();
|
||||
|
||||
@@ -141,14 +145,14 @@ fn test_serialize_failure() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn test_to_value_struct() -> LuaResult<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("null", lua.null()?)?;
|
||||
globals.set("null", lua.null())?;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Test {
|
||||
name: String,
|
||||
key: i64,
|
||||
data: Option<bool>,
|
||||
};
|
||||
}
|
||||
|
||||
let test = Test {
|
||||
name: "alex".to_string(),
|
||||
@@ -171,14 +175,14 @@ fn test_to_value_struct() -> LuaResult<()> {
|
||||
fn test_to_value_enum() -> LuaResult<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("null", lua.null()?)?;
|
||||
globals.set("null", lua.null())?;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Test {
|
||||
name: String,
|
||||
key: i64,
|
||||
data: Option<bool>,
|
||||
};
|
||||
}
|
||||
|
||||
let test = Test {
|
||||
name: "alex".to_string(),
|
||||
@@ -228,6 +232,76 @@ fn test_to_value_enum() -> LuaResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_value_with_options() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("null", lua.null())?;
|
||||
|
||||
// set_array_metatable
|
||||
let data = lua.to_value_with(
|
||||
&Vec::<i32>::new(),
|
||||
SerializeOptions::new().set_array_metatable(false),
|
||||
)?;
|
||||
globals.set("data", data)?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(type(data) == "table" and #data == 0)
|
||||
assert(getmetatable(data) == nil)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct UnitStruct;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct MyData {
|
||||
map: HashMap<&'static str, Option<i32>>,
|
||||
unit: (),
|
||||
unitstruct: UnitStruct,
|
||||
}
|
||||
|
||||
// serialize_none_to_null
|
||||
let mut map = HashMap::new();
|
||||
map.insert("key", None);
|
||||
let mydata = MyData {
|
||||
map,
|
||||
unit: (),
|
||||
unitstruct: UnitStruct,
|
||||
};
|
||||
let data2 = lua.to_value_with(
|
||||
&mydata,
|
||||
SerializeOptions::new().serialize_none_to_null(false),
|
||||
)?;
|
||||
globals.set("data2", data2)?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(data2.map.key == nil)
|
||||
assert(data2.unit == null)
|
||||
assert(data2.unitstruct == null)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
// serialize_unit_to_null
|
||||
let data3 = lua.to_value_with(
|
||||
&mydata,
|
||||
SerializeOptions::new().serialize_unit_to_null(false),
|
||||
)?;
|
||||
globals.set("data3", data3)?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(data3.map.key == null)
|
||||
assert(data3.unit == nil)
|
||||
assert(data3.unitstruct == nil)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value_struct() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let lua = Lua::new();
|
||||
@@ -303,7 +377,7 @@ fn test_from_value_enum() -> Result<(), Box<dyn std::error::Error>> {
|
||||
#[test]
|
||||
fn test_from_value_enum_untagged() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let lua = Lua::new();
|
||||
lua.globals().set("null", lua.null()?)?;
|
||||
lua.globals().set("null", lua.null())?;
|
||||
|
||||
#[derive(Deserialize, PartialEq, Debug)]
|
||||
#[serde(untagged)]
|
||||
|
||||
@@ -1,15 +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",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use mlua::{Lua, Result, String};
|
||||
|
||||
@@ -1,15 +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",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use mlua::{Lua, Nil, Result, Table, TableExt, Value};
|
||||
|
||||
#[test]
|
||||
|
||||
+152
-66
@@ -1,23 +1,12 @@
|
||||
#![cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
feature(link_args)
|
||||
)]
|
||||
|
||||
#[cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use std::iter::FromIterator;
|
||||
use std::panic::catch_unwind;
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
use std::{error, f32, f64, fmt};
|
||||
|
||||
use mlua::{
|
||||
ChunkMode, Error, ExternalError, Function, Lua, Nil, Result, StdLib, String, Table, UserData,
|
||||
Value, Variadic,
|
||||
ChunkMode, Error, ExternalError, Function, Lua, LuaOptions, Nil, Result, StdLib, String, Table,
|
||||
UserData, Value, Variadic,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -35,7 +24,7 @@ fn test_safety() -> Result<()> {
|
||||
assert!(lua.load(r#"require "debug""#).exec().is_ok());
|
||||
drop(lua);
|
||||
|
||||
match Lua::new_with(StdLib::DEBUG) {
|
||||
match Lua::new_with(StdLib::DEBUG, LuaOptions::default()) {
|
||||
Err(Error::SafetyError(_)) => {}
|
||||
Err(e) => panic!("expected SafetyError, got {:?}", e),
|
||||
Ok(_) => panic!("expected SafetyError, got new Lua state"),
|
||||
@@ -64,7 +53,7 @@ fn test_safety() -> Result<()> {
|
||||
Ok(_) => panic!("expected SafetyError, got no error"),
|
||||
}
|
||||
|
||||
let bytecode = lua.load("return 1 + 1").into_function()?.dump(true)?;
|
||||
let bytecode = lua.load("return 1 + 1").into_function()?.dump(true);
|
||||
match lua.load(&bytecode).exec() {
|
||||
Err(Error::SafetyError(msg)) => {
|
||||
assert!(msg.contains("binary chunks are disabled in safe mode"))
|
||||
@@ -75,7 +64,7 @@ fn test_safety() -> Result<()> {
|
||||
drop(lua);
|
||||
|
||||
// Test safety rules after dynamically loading `package` library
|
||||
let lua = Lua::new_with(StdLib::NONE)?;
|
||||
let lua = Lua::new_with(StdLib::NONE, LuaOptions::default())?;
|
||||
assert!(lua.globals().get::<_, Option<Value>>("require")?.is_none());
|
||||
lua.load_from_std_lib(StdLib::PACKAGE)?;
|
||||
match lua.load(r#"package.loadlib()"#).exec() {
|
||||
@@ -174,7 +163,7 @@ fn test_load_mode() -> Result<()> {
|
||||
Err(e) => panic!("expected SyntaxError, got {:?}", e),
|
||||
};
|
||||
|
||||
let bytecode = lua.load("return 1 + 1").into_function()?.dump(true)?;
|
||||
let bytecode = lua.load("return 1 + 1").into_function()?.dump(true);
|
||||
assert_eq!(lua.load(&bytecode).eval::<i32>()?, 2);
|
||||
assert_eq!(
|
||||
lua.load(&bytecode)
|
||||
@@ -233,6 +222,7 @@ fn test_coercion() -> Result<()> {
|
||||
int = 123
|
||||
str = "123"
|
||||
num = 123.0
|
||||
func = function() end
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
@@ -241,6 +231,7 @@ fn test_coercion() -> Result<()> {
|
||||
assert_eq!(globals.get::<_, String>("int")?, "123");
|
||||
assert_eq!(globals.get::<_, i32>("str")?, 123);
|
||||
assert_eq!(globals.get::<_, i32>("num")?, 123);
|
||||
assert!(globals.get::<_, String>("func").is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -384,62 +375,139 @@ fn test_error() -> Result<()> {
|
||||
|
||||
assert!(understand_recursion.call::<_, ()>(()).is_err());
|
||||
|
||||
match catch_unwind(|| -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_panic() -> Result<()> {
|
||||
fn make_lua(options: LuaOptions) -> Result<Lua> {
|
||||
let lua = Lua::new_with(StdLib::ALL_SAFE, options)?;
|
||||
let rust_panic_function =
|
||||
lua.create_function(|_, msg: Option<StdString>| -> Result<()> {
|
||||
if let Some(msg) = msg {
|
||||
panic!("{}", msg)
|
||||
}
|
||||
panic!("rust panic")
|
||||
})?;
|
||||
lua.globals()
|
||||
.set("rust_panic_function", rust_panic_function)?;
|
||||
Ok(lua)
|
||||
}
|
||||
|
||||
// Test triggerting Lua error passing Rust panic (must be resumed)
|
||||
{
|
||||
let lua = make_lua(LuaOptions::default())?;
|
||||
|
||||
match catch_unwind(AssertUnwindSafe(|| -> Result<()> {
|
||||
lua.load(
|
||||
r#"
|
||||
_, err = pcall(rust_panic_function)
|
||||
error(err)
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
})) {
|
||||
Ok(Ok(_)) => panic!("no panic was detected"),
|
||||
Ok(Err(e)) => panic!("error during panic test {:?}", e),
|
||||
Err(p) => assert!(*p.downcast::<&str>().unwrap() == "rust panic"),
|
||||
};
|
||||
|
||||
// Trigger same panic again
|
||||
match lua.load("error(err)").exec() {
|
||||
Ok(_) => panic!("no error was detected"),
|
||||
Err(Error::PreviouslyResumedPanic) => {}
|
||||
Err(e) => panic!("expected PreviouslyResumedPanic, got {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
// Test returning Rust panic (must be resumed)
|
||||
{
|
||||
let lua = make_lua(LuaOptions::default())?;
|
||||
match catch_unwind(AssertUnwindSafe(|| -> Result<()> {
|
||||
let _catched_panic = lua
|
||||
.load(
|
||||
r#"
|
||||
-- Set global
|
||||
_, err = pcall(rust_panic_function)
|
||||
return err
|
||||
"#,
|
||||
)
|
||||
.eval::<Value>()?;
|
||||
Ok(())
|
||||
})) {
|
||||
Ok(_) => panic!("no panic was detected"),
|
||||
Err(_) => {}
|
||||
};
|
||||
|
||||
assert!(lua.globals().get::<_, Value>("err")? == Value::Nil);
|
||||
match lua.load("tostring(err)").exec() {
|
||||
Ok(_) => panic!("no error was detected"),
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
Error::PreviouslyResumedPanic => {}
|
||||
e => panic!("expected PreviouslyResumedPanic, got {:?}", e),
|
||||
},
|
||||
Err(e) => panic!("expected CallbackError, got {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
// Test representing rust panic as a string
|
||||
match catch_unwind(|| -> Result<()> {
|
||||
let lua = make_lua(LuaOptions::default())?;
|
||||
lua.load(
|
||||
r#"
|
||||
function rust_panic()
|
||||
local _, err = pcall(function () rust_panic_function() end)
|
||||
if err ~= nil then
|
||||
error(err)
|
||||
end
|
||||
end
|
||||
local _, err = pcall(rust_panic_function)
|
||||
error(tostring(err))
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
let rust_panic_function =
|
||||
lua.create_function(|_, ()| -> Result<()> { panic!("test_panic") })?;
|
||||
globals.set("rust_panic_function", rust_panic_function)?;
|
||||
|
||||
let rust_panic = globals.get::<_, Function>("rust_panic")?;
|
||||
|
||||
rust_panic.call::<_, ()>(())
|
||||
}) {
|
||||
Ok(Ok(_)) => panic!("no panic was detected"),
|
||||
Ok(Err(e)) => panic!("error during panic test {:?}", e),
|
||||
Err(p) => assert!(*p.downcast::<&str>().unwrap() == "test_panic"),
|
||||
};
|
||||
|
||||
match catch_unwind(|| -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
function rust_panic()
|
||||
local _, err = pcall(function () rust_panic_function() end)
|
||||
if err ~= nil then
|
||||
error(tostring(err))
|
||||
end
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
let rust_panic_function =
|
||||
lua.create_function(|_, ()| -> Result<()> { panic!("test_panic") })?;
|
||||
globals.set("rust_panic_function", rust_panic_function)?;
|
||||
|
||||
let rust_panic = globals.get::<_, Function>("rust_panic")?;
|
||||
|
||||
rust_panic.call::<_, ()>(())
|
||||
.exec()
|
||||
}) {
|
||||
Ok(Ok(_)) => panic!("no error was detected"),
|
||||
Ok(Err(Error::RuntimeError(_))) => {}
|
||||
Ok(Err(e)) => panic!("unexpected error during panic test {:?}", e),
|
||||
Ok(Err(e)) => panic!("expected RuntimeError, got {:?}", e),
|
||||
Err(_) => panic!("panic was detected"),
|
||||
};
|
||||
}
|
||||
|
||||
// Test disabling `catch_rust_panics` option / pcall correctness
|
||||
match catch_unwind(|| -> Result<()> {
|
||||
let lua = make_lua(LuaOptions::new().catch_rust_panics(false))?;
|
||||
lua.load(
|
||||
r#"
|
||||
local ok, err = pcall(function(msg) error(msg) end, "hello")
|
||||
assert(not ok and err:find("hello") ~= nil)
|
||||
|
||||
ok, err = pcall(rust_panic_function, "rust panic from lua")
|
||||
-- Nothing to return, panic should be automatically resumed
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
}) {
|
||||
Ok(r) => panic!("no panic was detected: {:?}", r),
|
||||
Err(p) => assert!(*p.downcast::<StdString>().unwrap() == "rust panic from lua"),
|
||||
}
|
||||
|
||||
// Test enabling `catch_rust_panics` option / xpcall correctness
|
||||
match catch_unwind(|| -> Result<()> {
|
||||
let lua = make_lua(LuaOptions::new().catch_rust_panics(false))?;
|
||||
lua.load(
|
||||
r#"
|
||||
local msgh_ok = false
|
||||
local msgh = function(err)
|
||||
msgh_ok = err ~= nil and err:find("hello") ~= nil
|
||||
return err
|
||||
end
|
||||
local ok, err = xpcall(function(msg) error(msg) end, msgh, "hello")
|
||||
assert(not ok and err:find("hello") ~= nil)
|
||||
assert(msgh_ok)
|
||||
|
||||
ok, err = xpcall(rust_panic_function, msgh, "rust panic from lua")
|
||||
-- Nothing to return, panic should be automatically resumed
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
}) {
|
||||
Ok(r) => panic!("no panic was detected: {:?}", r),
|
||||
Err(p) => assert!(*p.downcast::<StdString>().unwrap() == "rust panic from lua"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -818,6 +886,24 @@ fn too_many_binds() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ref_stack_exhaustion() {
|
||||
match catch_unwind(AssertUnwindSafe(|| -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let mut vals = Vec::new();
|
||||
for _ in 0..1000000 {
|
||||
vals.push(lua.create_table()?);
|
||||
}
|
||||
Ok(())
|
||||
})) {
|
||||
Ok(_) => panic!("no panic was detected"),
|
||||
Err(p) => assert!(p
|
||||
.downcast::<StdString>()
|
||||
.unwrap()
|
||||
.starts_with("cannot create a Lua reference, out of auxiliary stack space")),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn large_args() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
+43
-12
@@ -1,15 +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",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use std::panic::catch_unwind;
|
||||
|
||||
use mlua::{Error, Function, Lua, Result, Thread, ThreadStatus};
|
||||
@@ -105,6 +93,49 @@ fn test_thread() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored")))]
|
||||
#[test]
|
||||
fn test_thread_reset() -> Result<()> {
|
||||
use mlua::{AnyUserData, UserData};
|
||||
use std::sync::Arc;
|
||||
|
||||
let lua = Lua::new();
|
||||
|
||||
struct MyUserData(Arc<()>);
|
||||
impl UserData for MyUserData {}
|
||||
|
||||
let arc = Arc::new(());
|
||||
|
||||
let func: Function = lua.load(r#"function(ud) coroutine.yield(ud) end"#).eval()?;
|
||||
let thread = lua.create_thread(func.clone())?;
|
||||
|
||||
for _ in 0..2 {
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
let _ = thread.resume::<_, AnyUserData>(MyUserData(arc.clone()))?;
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
assert_eq!(Arc::strong_count(&arc), 2);
|
||||
thread.resume::<_, ()>(())?;
|
||||
assert_eq!(thread.status(), ThreadStatus::Unresumable);
|
||||
thread.reset(func.clone())?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&arc), 1);
|
||||
}
|
||||
|
||||
// Check for errors (Lua 5.4 only)
|
||||
#[cfg(feature = "lua54")]
|
||||
{
|
||||
let func: Function = lua.load(r#"function(ud) error("test error") end"#).eval()?;
|
||||
let thread = lua.create_thread(func.clone())?;
|
||||
let _ = thread.resume::<_, AnyUserData>(MyUserData(arc.clone()));
|
||||
assert_eq!(thread.status(), ThreadStatus::Error);
|
||||
assert_eq!(Arc::strong_count(&arc), 2);
|
||||
assert!(thread.reset(func.clone()).is_err());
|
||||
assert_eq!(thread.status(), ThreadStatus::Error);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coroutine_from_closure() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
@@ -1,15 +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",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use mlua::{Function, LightUserData, Lua, Result};
|
||||
|
||||
+127
-23
@@ -1,23 +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",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
||||
|
||||
use mlua::{
|
||||
AnyUserData, ExternalError, Function, Lua, MetaMethod, Result, String, UserData,
|
||||
UserDataMethods, Value,
|
||||
AnyUserData, Error, ExternalError, Function, Lua, MetaMethod, Nil, Result, String, UserData,
|
||||
UserDataFields, UserDataMethods, Value,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -25,8 +13,8 @@ fn test_user_data() -> Result<()> {
|
||||
struct UserData1(i64);
|
||||
struct UserData2(Box<i64>);
|
||||
|
||||
impl UserData for UserData1 {};
|
||||
impl UserData for UserData2 {};
|
||||
impl UserData for UserData1 {}
|
||||
impl UserData for UserData2 {}
|
||||
|
||||
let lua = Lua::new();
|
||||
let userdata1 = lua.create_userdata(UserData1(1))?;
|
||||
@@ -167,10 +155,10 @@ fn test_metamethods() -> Result<()> {
|
||||
assert!(userdata2.equals(userdata3)?);
|
||||
|
||||
let userdata1: AnyUserData = globals.get("userdata1")?;
|
||||
assert!(userdata1.has_metamethod(MetaMethod::Add)?);
|
||||
assert!(userdata1.has_metamethod(MetaMethod::Sub)?);
|
||||
assert!(userdata1.has_metamethod(MetaMethod::Index)?);
|
||||
assert!(!userdata1.has_metamethod(MetaMethod::Pow)?);
|
||||
assert!(userdata1.get_metatable()?.contains(MetaMethod::Add)?);
|
||||
assert!(userdata1.get_metatable()?.contains(MetaMethod::Sub)?);
|
||||
assert!(userdata1.get_metatable()?.contains(MetaMethod::Index)?);
|
||||
assert!(!userdata1.get_metatable()?.contains(MetaMethod::Pow)?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -262,7 +250,7 @@ fn test_gc_userdata() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detroys_userdata() -> Result<()> {
|
||||
fn test_destroy_userdata() -> Result<()> {
|
||||
struct MyUserdata(Arc<()>);
|
||||
|
||||
impl UserData for MyUserdata {}
|
||||
@@ -284,7 +272,7 @@ fn detroys_userdata() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn user_value() -> Result<()> {
|
||||
fn test_user_value() -> Result<()> {
|
||||
struct MyUserData;
|
||||
|
||||
impl UserData for MyUserData {}
|
||||
@@ -307,7 +295,7 @@ fn test_functions() -> Result<()> {
|
||||
methods.add_function("get_value", |_, ud: AnyUserData| {
|
||||
Ok(ud.borrow::<MyUserData>()?.0)
|
||||
});
|
||||
methods.add_function("set_value", |_, (ud, value): (AnyUserData, i64)| {
|
||||
methods.add_function_mut("set_value", |_, (ud, value): (AnyUserData, i64)| {
|
||||
ud.borrow_mut::<MyUserData>()?.0 = value;
|
||||
Ok(())
|
||||
});
|
||||
@@ -347,3 +335,119 @@ fn test_functions() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fields() -> Result<()> {
|
||||
#[derive(Copy, Clone)]
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("val", |_, data| Ok(data.0));
|
||||
fields.add_field_method_set("val", |_, data, val| {
|
||||
data.0 = val;
|
||||
Ok(())
|
||||
});
|
||||
|
||||
// Use userdata "uservalue" storage
|
||||
fields.add_field_function_get("uval", |_, ud| ud.get_user_value::<Option<String>>());
|
||||
fields
|
||||
.add_field_function_set("uval", |_, ud, s| ud.set_user_value::<Option<String>>(s));
|
||||
|
||||
fields.add_meta_field_with(MetaMethod::Index, |lua| {
|
||||
let index = lua.create_table()?;
|
||||
index.set("f", 321)?;
|
||||
Ok(index)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("ud", MyUserData(7))?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(ud.val == 7)
|
||||
ud.val = 10
|
||||
assert(ud.val == 10)
|
||||
|
||||
assert(ud.uval == nil)
|
||||
ud.uval = "hello"
|
||||
assert(ud.uval == "hello")
|
||||
|
||||
assert(ud.f == 321)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_metatable() -> Result<()> {
|
||||
#[derive(Copy, Clone)]
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_meta_field_with("__type_name", |_| Ok("MyUserData"));
|
||||
}
|
||||
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_function("my_type_name", |_, data: AnyUserData| {
|
||||
let metatable = data.get_metatable()?;
|
||||
metatable.get::<_, String>("__type_name")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("ud", MyUserData(7))?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(ud:my_type_name() == "MyUserData")
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
let ud: AnyUserData = globals.get("ud")?;
|
||||
let metatable = ud.get_metatable()?;
|
||||
|
||||
match metatable.get::<_, Value>("__gc") {
|
||||
Ok(_) => panic!("expected MetaMethodRestricted, got no error"),
|
||||
Err(Error::MetaMethodRestricted(_)) => {}
|
||||
Err(e) => panic!("expected MetaMethodRestricted, got {:?}", e),
|
||||
}
|
||||
|
||||
match metatable.set(MetaMethod::Index, Nil) {
|
||||
Ok(_) => panic!("expected MetaMethodRestricted, got no error"),
|
||||
Err(Error::MetaMethodRestricted(_)) => {}
|
||||
Err(e) => panic!("expected MetaMethodRestricted, got {:?}", e),
|
||||
}
|
||||
|
||||
let mut methods = metatable
|
||||
.pairs()
|
||||
.into_iter()
|
||||
.map(|kv: Result<(_, Value)>| Ok(kv?.0))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
methods.sort_by_cached_key(|k| k.name().to_owned());
|
||||
assert_eq!(methods, vec![MetaMethod::Index, "__type_name".into()]);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct MyUserData2(i64);
|
||||
|
||||
impl UserData for MyUserData2 {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_meta_field_with("__index", |_| Ok(1));
|
||||
}
|
||||
}
|
||||
|
||||
match lua.create_userdata(MyUserData2(1)) {
|
||||
Ok(_) => panic!("expected MetaMethodTypeError, got no error"),
|
||||
Err(Error::MetaMethodTypeError { .. }) => {}
|
||||
Err(e) => panic!("expected MetaMethodTypeError, got {:?}", e),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,15 +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",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use mlua::{Lua, Result, Value};
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user