Compare commits

..

12 Commits

Author SHA1 Message Date
Alex Orlenko 15e353a7f8 v0.8.9 2023-05-16 23:02:19 +01:00
Alex Orlenko 765117c2bb Update tarpaulin settings 2023-05-16 23:02:17 +01:00
Alex Orlenko 573d71345f Don't set html_root_url (it's not recommended) 2023-05-16 22:55:29 +01:00
Alex Orlenko 72de17bf47 Allow deserializing Lua null into unit(()) or unit struct. See #264 2023-05-16 22:53:37 +01:00
Alex Orlenko 5a96e80266 Use lua_closethread instead of lua_resetthread in vendored mode (introduced in Lua 5.4.6) 2023-05-16 22:50:46 +01:00
Alex Orlenko bfdb4087b8 Update minimal (vendored) Lua 5.4 to 5.4.6 2023-05-16 22:49:49 +01:00
Alex Orlenko eb84284824 Fix ref_stack_exhaustion test (Lua 5.4.6) 2023-05-16 22:12:36 +01:00
Alex Orlenko 34679e105d v0.8.8 2023-03-05 17:50:53 +00:00
Alex Orlenko bc194981fc Optimize userdata methods call when __index and fields_getters are nil 2023-03-05 14:43:12 +00:00
Alex Orlenko c9715aa5d9 Fix potential deadlock when trying to reuse dropped RegistryKey.
If no free registry id found, we call protect_lua! macro while keeping mutex guard to the unref list.
Protected calls can trigger garbage collection and if RegistryKey is placed in userdata being collected, this can lead to deadlock.
The solution is drop mutex guard as soon as possible.
Also this commit includes optimization in creating reference in Lua registry.
2023-03-05 14:39:22 +00:00
Alex Orlenko c108dc8213 Force protected mode for long enough strings 2023-03-05 14:35:15 +00:00
Alex Orlenko e86ef9d755 v0.8.7 2023-01-04 16:15:23 +00:00
114 changed files with 4489 additions and 9648 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
- name: Generate coverage report
run: |
cargo tarpaulin --out xml --tests --exclude-files benches/* --exclude-files mlua-sys/src/*/*
cargo tarpaulin --out xml --tests --exclude-files benches/* --exclude-files src/ffi/*/*
- name: Upload report to codecov.io
uses: codecov/codecov-action@v3
+18 -20
View File
@@ -9,7 +9,7 @@ jobs:
matrix:
os: [ubuntu-22.04, macos-latest, windows-latest]
rust: [stable]
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
lua: [lua54, lua53, lua52, lua51, luajit, luau]
include:
- os: ubuntu-22.04
target: x86_64-unknown-linux-gnu
@@ -27,7 +27,7 @@ jobs:
- name: Build ${{ matrix.lua }} vendored
run: |
cargo build --features "${{ matrix.lua }},vendored"
cargo build --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable"
cargo build --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
shell: bash
- name: Build ${{ matrix.lua }} pkg-config
if: ${{ matrix.os == 'ubuntu-22.04' }}
@@ -50,7 +50,7 @@ jobs:
toolchain: stable
target: aarch64-apple-darwin
- name: Cross-compile
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable"
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
build_aarch64_cross_ubuntu:
name: Cross-compile to aarch64-unknown-linux-gnu
@@ -71,7 +71,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 }},vendored,async,send,serialize,macros,parking_lot,unstable"
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
shell: bash
build_armv7_cross_ubuntu:
@@ -93,7 +93,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 }},vendored,async,send,serialize,macros,parking_lot,unstable"
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
shell: bash
test:
@@ -104,7 +104,7 @@ jobs:
matrix:
os: [ubuntu-22.04, macos-latest, windows-latest]
rust: [stable, nightly]
lua: [lua54, lua53, lua52, lua51, luajit, luajit52, luau, luau-jit, luau-vector4]
lua: [lua54, lua53, lua52, lua51, luajit, luajit52, luau]
include:
- os: ubuntu-22.04
target: x86_64-unknown-linux-gnu
@@ -118,18 +118,17 @@ jobs:
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
- uses: Swatinem/rust-cache@v1
- name: Run ${{ matrix.lua }} tests
run: |
cargo test --features "${{ matrix.lua }},vendored"
cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,parking_lot,unstable"
shell: bash
- name: Run compile tests (macos lua54)
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua54' }}
run: |
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" -- --ignored
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable" -- --ignored
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot" -- --ignored
shell: bash
test_with_sanitizer:
@@ -140,7 +139,7 @@ jobs:
matrix:
os: [ubuntu-22.04]
rust: [nightly]
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
lua: [lua54, lua53, lua52, lua51, luajit, luau]
include:
- os: ubuntu-22.04
target: x86_64-unknown-linux-gnu
@@ -150,13 +149,12 @@ jobs:
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
- uses: Swatinem/rust-cache@v1
- name: Run ${{ matrix.lua }} tests with address sanitizer
run: |
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
RUSTFLAGS="-Z sanitizer=address" \
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
shell: bash
env:
RUSTFLAGS: -Z sanitizer=address
test_modules:
name: Test modules
@@ -178,7 +176,7 @@ jobs:
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
- uses: Swatinem/rust-cache@v1
- name: Run ${{ matrix.lua }} module tests
run: |
(cd tests/module && cargo build --release --features "${{ matrix.lua }}")
@@ -218,18 +216,18 @@ jobs:
- run: cargo fmt -- --check
clippy:
name: Clippy
name: Clippy check
runs-on: ubuntu-22.04
strategy:
matrix:
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
lua: [lua54, lua53, lua52, lua51, luajit, luau]
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: nightly
components: clippy
- uses: giraffate/clippy-action@v1
- uses: actions-rs/clippy-check@v1
with:
reporter: 'github-pr-review'
clippy_flags: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable"
token: ${{ secrets.GITHUB_TOKEN }}
args: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
-1
View File
@@ -4,4 +4,3 @@ Cargo.lock
.vscode/
.DS_Store
.stignore
-107
View File
@@ -1,110 +1,3 @@
## v0.9.0
Changes since v0.9.0-rc.3
- Improved non-static (scoped) userdata support
- Added `Scope::create_any_userdata()` method
- Added `Lua::set_vector_metatable()` method (`unstable` feature flag)
- Added `OwnedThread` type (`unstable` feature flag)
- Minimal Luau updated to 0.590
- Added new option `sort_keys` to `DeserializeOptions` (`Lua::from_value()` method)
- Changed `Table::raw_len()` output type to `usize`
- Helper functions for `Value` (eg: `Value::as_number()`/`Value::as_string`/etc)
- Performance improvements
## v0.9.0-rc.3
- Minimal Luau updated to 0.588
## v0.9.0-rc.2
- Added `#[derive(FromLua)]` macro to opt-in into `FromLua<T> where T: 'static + Clone` (userdata type).
- Support vendored module mode for windows (raw-dylib linking, Rust 1.71+)
- `module` and `vendored` features are now mutually exclusive
- Use `C-unwind` ABI (Rust 1.71+)
- Changed `AsChunk` trait to support capturing wrapped Lua types
## v0.9.0-rc.1
- `UserDataMethods::add_async_method()` takes `&T` instead of cloning `T`
- Implemented `PartialEq<[T]>` for tables
- Added Luau 4-dimensional vectors support (`luau-vector4` feature)
- `Table::sequence_values()` iterator no longer uses any metamethods (`Table::raw_sequence_values()` is deprecated)
- Added `Table:is_empty()` function that checks both hash and array parts
- Refactored Debug interface
- Re-exported `ffi` (`mlua-sys`) crate for easier writing of unsafe code
- Refactored Lua 5.4 warnings interface
- Take `&str` as function name in `TableExt` and `AnyUserDataExt` traits
- Added module attribule `skip_memory_check` to improve performance
- Added `AnyUserData::wrap()` to provide more easy way of creating _any_ userdata in Lua
## v0.9.0-beta.3
- Added `OwnedAnyUserData::take()`
- Switch to `DeserializeOwned`
- Overwrite error context when called multiple times
- New feature flag `luau-jit` to enable (experimental) Luau codegen backend
- Set `__name` field in userdata metatable
- Added `Value::to_string()` method similar to `luaL_tolstring`
- Lua 5.4.6
- Application data container now allows to mutably and immutably borrow different types at the same time
- Performance optimizations
- Support getting and setting environment for Lua functions.
- Added `UserDataFields::add_field()` method to add static fields to UserData
Breaking changes:
- Require environment to be a `Table` instead of `Value` in Chunks.
- `AsChunk::env()` renamed to `AsChunk::environment()`
## v0.9.0-beta.2
New features:
- Added `Thread::set_hook()` function to set hook on threads
- Added pretty print to the Debug formatting to Lua `Value` and `Table`
- ffi layer moved to `mlua-sys` crate
- Added OwnedString (unstable)
Breaking changes:
- Refactor `HookTriggers` (make it const)
## v0.9.0-beta.1
New features:
- Owned Lua types (unstable feature flag)
- New functions `Function::wrap`/`Function::wrap_mut`/`Function::wrap_async`
- `Lua::register_userdata_type()` to register a custom userdata types (without requiring `UserData` trait)
- `Lua::create_any_userdata()`
- Added `create_userdata_ref`/`create_userdata_ref_mut` for scopes
- Added `AnyUserDataExt` trait with auxiliary functions for `AnyUserData`
- Added `UserDataRef` and `UserDataRefMut` type wrapped that implement `FromLua`
- Improved error handling:
* Improved error reporting when calling Rust functions from Lua.
* Added `Error::BadArgument` to help identify bad argument position or name
* Added `ErrorContext` extension trait to attach additional context to `Error`
Breaking changes:
- Refactored `AsChunk` trait
- `ToLua`/`ToLuaMulti` renamed to `IntoLua`/`IntoLuaMulti`
- Renamed `to_lua_err` to `into_lua_err`
- Removed `FromLua` impl for `T: UserData+Clone`
- Removed `Lua::async_scope`
- Added `&Lua` arg to Luau interrupt callback
Other:
- Better Debug for String
- Allow deserializing values from serializable UserData using `Lua::from_value()` method
- Added `Table::clear()` method
- Added `Error::downcast_ref()` method
- Support setting memory limit for Lua 5.1/JIT/Luau
- Support setting module name in `#[lua_module(name = "...")]` macro
- Minor fixes and improvements
## v0.8.10
- Update to Luau 0.590 (luau0-src to 0.7.x)
- Fix loading luau code starting with \t
- Pin lua-src and luajit-src versions
## v0.8.9
- Update minimal (vendored) Lua 5.4 to 5.4.6
+29 -26
View File
@@ -1,8 +1,7 @@
[package]
name = "mlua"
version = "0.9.0" # remember to update mlua_derive
version = "0.8.9" # remember to update mlua_derive
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
rust-version = "1.71"
edition = "2021"
repository = "https://github.com/khvzak/mlua"
documentation = "https://docs.rs/mlua"
@@ -10,66 +9,70 @@ readme = "README.md"
keywords = ["lua", "luajit", "luau", "async", "scripting"]
categories = ["api-bindings", "asynchronous"]
license = "MIT"
links = "lua"
build = "build/main.rs"
description = """
High level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox Luau
with async/await features and support of writing native Lua modules in Rust.
"""
[package.metadata.docs.rs]
features = ["lua54", "vendored", "async", "send", "serialize", "macros", "parking_lot", "unstable"]
features = ["lua54", "vendored", "async", "send", "serialize", "macros", "parking_lot"]
rustdoc-args = ["--cfg", "docsrs"]
[workspace]
members = [
"mlua_derive",
"mlua-sys",
]
[features]
lua54 = ["ffi/lua54"]
lua53 = ["ffi/lua53"]
lua52 = ["ffi/lua52"]
lua51 = ["ffi/lua51"]
luajit = ["ffi/luajit"]
luajit52 = ["luajit", "ffi/luajit52"]
luau = ["ffi/luau"]
luau-jit = ["luau", "ffi/luau-codegen"]
luau-vector4 = ["luau", "ffi/luau-vector4"]
vendored = ["ffi/vendored"]
module = ["mlua_derive", "ffi/module"]
async = ["futures-util"]
lua54 = []
lua53 = []
lua52 = []
lua51 = []
luajit = []
luajit52 = ["luajit"]
luau = ["luau0-src"]
vendored = ["lua-src", "luajit-src"]
module = ["mlua_derive"]
async = ["futures-core", "futures-task", "futures-util"]
send = []
serialize = ["serde", "erased-serde", "serde-value"]
serialize = ["serde", "erased-serde"]
macros = ["mlua_derive/macros"]
unstable = []
[dependencies]
mlua_derive = { version = "=0.9.0", optional = true, path = "mlua_derive" }
bstr = { version = "1.0", features = ["std"], default_features = false }
mlua_derive = { version = "=0.8.0", optional = true, path = "mlua_derive" }
bstr = { version = "0.2", features = ["std"], default_features = false }
once_cell = { version = "1.0" }
num-traits = { version = "0.2.14" }
rustc-hash = "1.0"
futures-util = { version = "0.3", optional = true, default-features = false, features = ["std"] }
futures-core = { version = "0.3.5", optional = true }
futures-task = { version = "0.3.5", optional = true }
futures-util = { version = "0.3.5", optional = true }
serde = { version = "1.0", optional = true }
erased-serde = { version = "0.3", optional = true }
serde-value = { version = "0.7", optional = true }
parking_lot = { version = "0.12", optional = true }
ffi = { package = "mlua-sys", version = "0.3.2", path = "mlua-sys" }
[build-dependencies]
cc = { version = "1.0" }
pkg-config = { version = "0.3.17" }
lua-src = { version = ">= 546.0.0, < 550.0.0", optional = true }
luajit-src = { version = ">= 210.4.0, < 220.0.0", optional = true }
luau0-src = { version = "0.5.0", optional = true }
[dev-dependencies]
rustyline = "12.0"
criterion = { version = "0.5", features = ["async_tokio"] }
rustyline = "10.0"
criterion = { version = "0.3.4", features = ["html_reports", "async_tokio"] }
trybuild = "1.0"
futures = "0.3.5"
hyper = { version = "0.14", features = ["client", "server"] }
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1.0", features = ["full"] }
futures-timer = "3.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
maplit = "1.0"
tempfile = "3"
static_assertions = "1.0"
[[bench]]
name = "benchmark"
+11 -16
View File
@@ -7,9 +7,9 @@
[crates.io]: https://crates.io/crates/mlua
[API Documentation]: https://docs.rs/mlua/badge.svg
[docs.rs]: https://docs.rs/mlua
[Coverage Status]: https://codecov.io/gh/khvzak/mlua/branch/master/graph/badge.svg?token=99339FS1CG
[Coverage Status]: https://codecov.io/gh/khvzak/mlua/branch/v0.8/graph/badge.svg?token=99339FS1CG
[codecov.io]: https://codecov.io/gh/khvzak/mlua
[MSRV]: https://img.shields.io/badge/rust-1.71+-brightgreen.svg?&logo=rust
[MSRV]: https://img.shields.io/badge/rust-1.56+-brightgreen.svg?&logo=rust
[Guided Tour] | [Benchmarks] | [FAQ]
@@ -17,10 +17,6 @@
[Benchmarks]: https://github.com/khvzak/script-bench-rs
[FAQ]: FAQ.md
> **Note**
>
> See v0.9 [release notes](https://github.com/khvzak/mlua/blob/master/docs/release_notes/v0.9.md).
`mlua` is bindings to [Lua](https://www.lua.org) programming language for Rust with a goal to provide
_safe_ (as far as it's possible), high level, easy to use, practical and flexible API.
@@ -45,8 +41,6 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
* `luajit`: activate [LuaJIT] support
* `luajit52`: activate [LuaJIT] support with partial compatibility with Lua 5.2
* `luau`: activate [Luau] support (auto vendored mode)
* `luau-jit`: activate [Luau] support with JIT backend.
* `luau-vector4`: activate [Luau] support with 4-dimensional vector.
* `vendored`: build static Lua(JIT) library from sources during `mlua` compilation using [lua-src] or [luajit-src] crates
* `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])
@@ -54,7 +48,6 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
* `serialize`: add serialization and deserialization support to `mlua` types using [serde] framework
* `macros`: enable procedural macros (such as `chunk!`)
* `parking_lot`: support UserData types wrapped in [parking_lot]'s primitives (`Arc<Mutex>` and `Arc<RwLock>`)
* `unstable`: enable **unstable** features. The public API of these features may break between releases.
[5.4]: https://www.lua.org/manual/5.4/manual.html
[5.3]: https://www.lua.org/manual/5.3/manual.html
@@ -98,12 +91,12 @@ You have to enable one of the features: `lua54`, `lua53`, `lua52`, `lua51`, `lua
By default `mlua` uses `pkg-config` tool to find lua includes and libraries for the chosen Lua version.
In most cases it works as desired, although sometimes could be more preferable to use a custom lua library.
To achieve this, mlua supports `LUA_LIB`, `LUA_LIB_NAME` and `LUA_LINK` environment variables.
To achieve this, mlua supports `LUA_INC`, `LUA_LIB`, `LUA_LIB_NAME` and `LUA_LINK` environment variables.
`LUA_LINK` is optional and may be `dylib` (a dynamic library) or `static` (a static library, `.a` archive).
An example how to use them:
``` sh
my_project $ LUA_LIB=$HOME/tmp/lua-5.2.4/src LUA_LIB_NAME=lua LUA_LINK=static cargo build
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 auxiliary crates [lua-src](https://crates.io/crates/lua-src) and
@@ -117,7 +110,7 @@ Add to `Cargo.toml` :
``` toml
[dependencies]
mlua = { version = "0.9.0", features = ["lua54", "vendored"] }
mlua = { version = "0.8", features = ["lua54", "vendored"] }
```
`main.rs`
@@ -152,7 +145,7 @@ Add to `Cargo.toml` :
crate-type = ["cdylib"]
[dependencies]
mlua = { version = "0.9.0", features = ["lua54", "vendored", "module"] }
mlua = { version = "0.8", features = ["lua54", "vendored", "module"] }
```
`lib.rs` :
@@ -197,11 +190,13 @@ rustflags = [
]
```
On Linux you can build modules normally with `cargo build --release`.
Vendored and non-vendored builds are supported for these OS.
On Windows the target module will be linked with `lua5x.dll` library (depending on your feature flags).
Your main application should provide this library.
On Windows `vendored` mode for modules is not supported since you need to link to a Lua dll.
Easiest way is to use either MinGW64 (as part of [MSYS2](https://github.com/msys2/msys2) package) with `pkg-config` or
MSVC with `LUA_INC` / `LUA_LIB` / `LUA_LIB_NAME` environment variables.
Module builds don't require Lua lib or headers to be installed on the system.
More details about compiling and linking Lua modules can be found on the [Building Modules](http://lua-users.org/wiki/BuildingModules) page.
### Publishing to luarocks.org
+6 -34
View File
@@ -59,31 +59,6 @@ fn create_string_table(c: &mut Criterion) {
});
}
fn table_get_set(c: &mut Criterion) {
let lua = Lua::new();
let table = lua.create_table().unwrap();
c.bench_function("table raw_get and raw_set [10]", |b| {
b.iter_batched(
|| {
collect_gc_twice(&lua);
table.clear().unwrap();
},
|_| {
for (i, &s) in ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
.iter()
.enumerate()
{
table.raw_set(s, i).unwrap();
assert_eq!(table.raw_get::<_, usize>(s).unwrap(), i);
}
},
BatchSize::SmallInput,
);
});
}
fn create_function(c: &mut Criterion) {
let lua = Lua::new();
@@ -145,7 +120,7 @@ fn call_sum_callback(c: &mut Criterion) {
}
fn call_async_sum_callback(c: &mut Criterion) {
let options = LuaOptions::new().thread_pool_size(1024);
let options = LuaOptions::new().thread_cache_size(1024);
let lua = Lua::new_with(LuaStdLib::ALL_SAFE, options).unwrap();
let callback = lua
.create_async_function(|_, (a, b, c): (i64, i64, i64)| async move {
@@ -289,19 +264,17 @@ fn call_userdata_method(c: &mut Criterion) {
}
fn call_async_userdata_method(c: &mut Criterion) {
struct UserData(String);
#[derive(Clone, Copy)]
struct UserData(i64);
impl LuaUserData for UserData {
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("method", |_, this, ()| async move { Ok(this.0.clone()) });
methods.add_async_method("method", |_, this, ()| async move { Ok(this.0) });
}
}
let options = LuaOptions::new().thread_pool_size(1024);
let options = LuaOptions::new().thread_cache_size(1024);
let lua = Lua::new_with(LuaStdLib::ALL_SAFE, options).unwrap();
lua.globals()
.set("userdata", UserData("hello".to_string()))
.unwrap();
lua.globals().set("userdata", UserData(10)).unwrap();
c.bench_function("call async [userdata method] 10", |b| {
let rt = Runtime::new().unwrap();
@@ -330,7 +303,6 @@ criterion_group! {
create_table,
create_array,
create_string_table,
table_get_set,
create_function,
call_lua_function,
call_sum_callback,
+5
View File
@@ -0,0 +1,5 @@
use std::path::PathBuf;
pub fn probe_lua() -> Option<PathBuf> {
None
}
+93
View File
@@ -0,0 +1,93 @@
#![allow(dead_code)]
use std::env;
use std::ops::Bound;
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() -> Option<PathBuf> {
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");
println!("cargo:rerun-if-env-changed=LUA_LIB_NAME");
println!("cargo:rerun-if-env-changed=LUA_LINK");
let need_lua_lib = cfg!(any(not(feature = "module"), target_os = "windows"));
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);
}
return Some(PathBuf::from(include_dir));
}
// Find using `pkg-config`
#[cfg(feature = "lua54")]
let (incl_bound, excl_bound, alt_probe, ver) = ("5.4", "5.5", "lua5.4", "5.4");
#[cfg(feature = "lua53")]
let (incl_bound, excl_bound, alt_probe, ver) = ("5.3", "5.4", "lua5.3", "5.3");
#[cfg(feature = "lua52")]
let (incl_bound, excl_bound, alt_probe, ver) = ("5.2", "5.3", "lua5.2", "5.2");
#[cfg(feature = "lua51")]
let (incl_bound, excl_bound, alt_probe, ver) = ("5.1", "5.2", "lua5.1", "5.1");
#[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "lua51"
))]
{
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included(incl_bound), Bound::Excluded(excl_bound)))
.cargo_metadata(need_lua_lib)
.probe("lua");
if lua.is_err() {
lua = pkg_config::Config::new()
.cargo_metadata(need_lua_lib)
.probe(alt_probe);
}
lua.unwrap_or_else(|_| panic!("cannot find Lua {} using `pkg-config`", ver))
.include_paths
.get(0)
.cloned()
}
#[cfg(feature = "luajit")]
{
let lua = pkg_config::Config::new()
.range_version((Bound::Included("2.0.4"), Bound::Unbounded))
.cargo_metadata(need_lua_lib)
.probe("luajit");
lua.expect("cannot find LuaJIT using `pkg-config`")
.include_paths
.get(0)
.cloned()
}
}
@@ -1,28 +1,28 @@
#![allow(dead_code)]
pub fn probe_lua() {
use std::path::PathBuf;
pub fn probe_lua() -> Option<PathBuf> {
#[cfg(feature = "lua54")]
let artifacts = lua_src::Build::new().build(lua_src::Lua54);
#[cfg(feature = "lua53")]
let artifacts = lua_src::Build::new().build(lua_src::Lua53);
#[cfg(feature = "lua52")]
let artifacts = lua_src::Build::new().build(lua_src::Lua52);
#[cfg(feature = "lua51")]
let artifacts = lua_src::Build::new().build(lua_src::Lua51);
#[cfg(feature = "luajit")]
let artifacts = luajit_src::Build::new()
.lua52compat(cfg!(feature = "luajit52"))
.build();
let artifacts = {
let mut builder = luajit_src::Build::new();
if cfg!(feature = "luajit52") {
builder.lua52compat(true);
}
builder.build()
};
#[cfg(feature = "luau")]
let artifacts = luau0_src::Build::new()
.enable_codegen(cfg!(feature = "luau-codegen"))
.set_vector_size(if cfg!(feature = "luau-vector4") { 4 } else { 3 })
.build();
let artifacts = luau0_src::Build::new().build();
artifacts.print_cargo_metadata();
Some(artifacts.include_dir().to_owned())
}
+115
View File
@@ -0,0 +1,115 @@
#[cfg_attr(
any(
feature = "luau",
all(
feature = "vendored",
any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit"
)
)
),
path = "find_vendored.rs"
)]
#[cfg_attr(
all(
not(feature = "vendored"),
any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit"
)
),
path = "find_normal.rs"
)]
#[cfg_attr(
not(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit",
feature = "luau"
)),
path = "find_dummy.rs"
)]
mod find;
fn main() {
#[cfg(not(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit",
feature = "luau"
)))]
compile_error!(
"You must enable one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
);
#[cfg(all(
feature = "lua54",
any(
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit",
feature = "luau"
)
))]
compile_error!(
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
);
#[cfg(all(
feature = "lua53",
any(
feature = "lua52",
feature = "lua51",
feature = "luajit",
feature = "luau"
)
))]
compile_error!(
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
);
#[cfg(all(
feature = "lua52",
any(feature = "lua51", feature = "luajit", feature = "luau")
))]
compile_error!(
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
);
#[cfg(all(feature = "lua51", any(feature = "luajit", feature = "luau")))]
compile_error!(
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
);
#[cfg(all(feature = "luajit", feature = "luau"))]
compile_error!(
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
);
// We don't support "vendored module" mode on windows
#[cfg(all(feature = "vendored", feature = "module", target_os = "windows"))]
compile_error!(
"Vendored (static) builds are not supported for modules on Windows.\n"
+ "Please, use `pkg-config` or custom mode to link to a Lua dll."
);
#[cfg(all(feature = "luau", feature = "module"))]
compile_error!("Luau does not support module mode");
#[cfg(any(not(feature = "module"), target_os = "windows"))]
find::probe_lua();
println!("cargo:rerun-if-changed=build");
}
-361
View File
@@ -1,361 +0,0 @@
## mlua v0.9 release notes
The v0.9 version of mlua is a major release that includes a number of API changes and improvements. This release is a stepping stone towards the v1.0.
This document highlights the most important changes. For a full list of changes, see the [CHANGELOG].
[CHANGELOG]: https://github.com/khvzak/mlua/blob/master/CHANGELOG.md
### New features
#### 1. New Any UserData API
This is a long awaited feature that allows to register in Lua foreign types that cannot implement `UserData` trait because of the Rust orphan rules.
Now you can register any type that implements [`Any`] trait as a userdata type.
Consider the following example:
```rust
lua.register_userdata_type::<std::string::String>(|reg| {
reg.add_method("len", |_, this, ()| Ok(this.len()));
reg.add_method_mut("push", |_, this, s: String| {
this.push_str(&s);
Ok(())
});
reg.add_meta_method(MetaMethod::ToString, |lua, this, ()| lua.create_string(this));
})?;
let s = lua.create_any_userdata("hello".to_string())?;
lua.load(chunk! {
print("s:len() is " .. $s:len())
$s:push(" world")
// Prints: hello, world
print($s)
})
.exec()?;
```
In this example we registered [`std::string::String`] as a userdata type with a set of methods and then created an instance of this type in Lua.
It's _not_ required to register a type before using the `Lua::create_any_userdata()` method, instead an empty metatable will be created for you.
You can also register the same type multiple times with different methods. Any previously created instances will share the old metatable, while new instances will have the new one.
The new set of API is called `any_userdata` because it allows to register types that implements [`Any`] trait.
[`std::string::String`]: https://doc.rust-lang.org/stable/std/string/struct.String.html
[`Any`]: https://doc.rust-lang.org/stable/std/any/trait.Any.html
#### 2. Scope support for the new any userdata types
When you need to create non-static userdata instances in Lua, the usual way is use `Lua::scope()` helper to make them scoped. When out of scope, any scoped objects will be automatically
dropped. The only downside of this approach is that every new instance will have a new metatable. This is not very fast if you need to create a lot of instances.
With the new Any UserData API, you can place non-static references `&T` where `T: 'static` into a scope and they will share a single static metatable.
```rust
lua.register_userdata_type::<std::string::String>(|reg| {
reg.add_method_mut("replace", |_, this, (pat, to): (String, String)| {
*this = this.replace(&pat, &to);
Ok(())
});
reg.add_meta_method(MetaMethod::ToString, |lua, this, ()| lua.create_string(this));
})?;
let mut s = "hello, world".to_string();
lua.scope(|scope| {
// This userdata instance holds only a mutable reference to our string
let ud = scope.create_any_userdata_ref_mut(&mut s)?;
lua.load(chunk! {
$ud:replace("world", "user")
})
.exec()
})?;
// Prints: hello, user!
println!("{s}!");
```
#### 3. Owned types (`unstable`)
One of the common questions was how to embed a Lua type into Rust struct to use it later. It was non-trivial to do because of the `'lua` lifetime attached to every Lua value.
In v0.9 mlua introduces "owned" types `OwnedTable`/`OwnedFunction`/`OwnedString`/`OwnedAnyUserData`/ `OwnedThread`that are `'static` (no lifetime attached).
```rust
let lua = Lua::new();
struct MyStruct {
table: OwnedTable,
func: OwnedFunction,
}
let my_struct = MyStruct {
table: lua.globals().into_owned(),
func: lua
.create_function(|_, t: Table| Ok(format!("{t:#?}")))?
.into_owned(),
};
// It's safe to drop Lua!
drop(lua);
let result = my_struct.func.call::<_, String>(my_struct.table)?;
println!("{result}");
```
Prior to v0.9, it was possible to do by creating a reference to the Lua value in registry using `Lua::create_registry_value()`
and retrieving value later using `Lua::registry_value()` method.
All owned handles hold a *strong* reference to the current Lua instance.
Be warned, if you place them into a Lua type (eg. `UserData` or a Rust callback), it is *very easy*
to accidentally cause reference cycles that would prevent destroying Lua instance.
Please note this functionality is available under the `unstable` feature flag and not available when the `send` feature is enabled.
#### New ffi module
In v0.9 release the internal `ffi` module has been moved into the new [`mlua-sys`] crate and became available for public use.
This crate provides unified Lua FFI API (targeting Lua 5.4) using a (limited) compatibility layer for older versions.
mlua re-exports the `ffi` module aliasing the `mlua-sys` crate and provides (unsafe) functionality to work with raw Lua state:
```rust
unsafe {
unsafe extern "C-unwind" fn lua_add(state: *mut mlua::lua_State) -> i32 {
let a = mlua::ffi::luaL_checkinteger(state, 1);
let b = mlua::ffi::luaL_checkinteger(state, 2);
mlua::ffi::lua_pushinteger(state, a + b);
1
}
let add = lua.create_c_function(lua_add)?;
assert_eq!(add.call::<_, i32>((2, 3))?, 5);
}
```
[`mlua-sys`]: https://crates.io/crates/mlua-sys
#### Luau JIT support
mlua brings support for the new [Luau] JIT backend under the `luau-jit` feature flag.
It will automatically trigger JIT compilation for new Lua chunks. To disable it, just call `lua.enable_jit(false)` before loading Lua code
(but any previously compiled chunks will remain JIT-compiled).
[Luau]: https://luau-lang.org
### Improvements
#### 1. Better error reporting
When calling a Rust function from Lua and passing wrong arguments, previous mlua versions reported a error message without any context or reference to the particular argument.
In v0.9 it reports a error message with the argument index and expected type:
```rust
let func = lua.create_function(|_, _a: i32| Ok(()))?;
lua.load(chunk! {
local ok, err = pcall($func, "not a number")
// Prints: bad argument #1: error converting Lua string to i32 (expected number or string coercible to number)
print(err)
})
.exec()?;
```
Similar changes have been made for userdata functions and methods:
```rust
lua.register_userdata_type::<&'static str>(|reg| {
reg.add_method("len", |_, this, ()| Ok(this.len()));
})?;
let s = lua.create_any_userdata("hello")?;
lua.load(chunk! {
local ok, err = pcall($s.len, 123)
// Prints: bad argument `self` to `&str.len`: error converting Lua integer to userdata
print(err)
})
.exec()?;
```
#### 2. Error context
Similar to the [`anyhow`] Error type, now it's possible to attach context to Lua errors:
```rust
let read = lua.create_function(|lua, path: String| {
let bytes = std::fs::read(&path)
.into_lua_err()
.context(format!("Failed to open `{path}`"))?;
Ok(lua.create_string(bytes))
})?;
lua.load(chunk! {
local ok, err = pcall($read, "/nonexistent")
/// Prints:
/// Failed to open /nonexistent
/// No such file or directory (os error 2)
/// stack traceback:
/// ...
print(err)
})
.exec()?;
```
[`anyhow`]: https://crates.io/crates/anyhow
#### 4. New methods `Function::wrap`/`AnyUserData::wrap`
Sometimes it's useful to have `IntoLua` trait implementation for a Rust function or type `T: Any` without needing to call `Lua::create_function()`/`Lua::create_any_userdata()` methods.
Since v0.9 you can call the new methods `Function::wrap()`/`AnyUserData::wrap()` that allows to do this. They return an abstract type that `impl IntoLua`:
```rust
lua.globals().set("print_rust", Function::wrap(|_, s: String| Ok(println!("{}", s))))?;
lua.globals().set("rust_ud", AnyUserData::wrap("hello"))?;
```
In addition there are also `Function::wrap_mut()`/`Function::wrap_async()` methods that allow to wrap mutable and async functions respectively.
For a `T: 'UserData + 'static` the `IntoLua` trait is still always implemented.
#### `UserDataRef` and `UserDataRefMut` type wrappers
The new wrappers `UserDataRef` and `UserDataRefMut` are receivers for userdata type `T` and borrow underlying instance for the lifetime of the wrapper.
```rust
lua.globals()
.set("ud", AnyUserData::wrap("hello".to_string()))?;
let mut ud_mut: UserDataRefMut<String> = lua.globals().get("ud")?;
ud_mut.push_str(", Rust");
drop(ud_mut);
let ud_ref: UserDataRef<String> = lua.globals().get("ud")?;
// Prints: hello, Rust
println!("{}", *ud_ref);
```
In the previous mlua versions the same functionality can be achieved by receiving `AnyUserData` and calling `AnyUserData::borrow()`/`AnyUserData::borrow_mut()` methods.
The new wrappers are identical to Rust [`Ref`]/[`RefMut`] types.
[`Ref`]: https://doc.rust-lang.org/std/cell/struct.Ref.html
[`RefMut`]: https://doc.rust-lang.org/std/cell/struct.RefMut.html
#### New `AnyUserDataExt` trait
Similar to the `TableExt` trait, the `AnyUserDataExt` provides a set of extra methods for the `AnyUserData` type.
1) `AnyUserDataExt::get()/set()` to get/set a value by key from the userdata, assuming it has `__index` metamethod.
2) `AnyUserDataExt::call()` to call the userdata as a function assuming it has `__call` metamethod.
3) `AnyUserData::call_method(name, ...)` to call the userdata method, assuming it has `__index` metamethod and the associated function.
#### Pretty formatting Lua values
`mlua::Value` implements a new format `:#?` that allows to (recursively) pretty print Lua values:
```rust
println!("{:#?}", lua.globals());
```
Prints:
```
{
["_G"] = table: 0x7fa2d0706260,
["_VERSION"] = "Lua 5.4",
["assert"] = function: 0x10451d11d,
["collectgarbage"] = function: 0x10451d198,
["coroutine"] = {
["close"] = function: 0x10451e28f,
...
},
["dofile"] = function: 0x10451d37c,
...
}
```
In addition a new method `Value::to_string()` has been added to convert `Value` to a string (using `__tostring` metamethod if available).
#### Environment for Lua functions
Any Lua functions have an associated environment table that is used to resolve global variables. By default it sets to a Lua globals table.
In the new release it's possible to get or update a function environment using `Function::environment()` or `Function::set_environment()` methods respectively.
```rust
let f = lua.load("return a").into_function()?;
assert_eq!(f.environment(), Some(lua.globals()));
lua.globals().set("a", 1)?;
assert_eq!(f.call::<_, i32>(())?, 1);
f.set_environment(lua.create_table_from([("a", "hello")])?)?;
assert_eq!(f.call::<_, mlua::String>(())?, "hello");
```
#### Performance optimizations
The new mlua version has a number of performance improvements. Please check the [benchmarks results] to see how mlua compares to rlua and rhai.
[benchmarks results]: https://github.com/khvzak/script-bench-rs
### Changes in `module` mode
#### New attributes
The `lua_module` macro now support the following attributes:
- `name=...` - sets name of the module (defaults to the name of the function).
Eg.:
```rust
#[mlua::lua_module(name = "alt_module")]
fn my_module(lua: &Lua) -> LuaResult<LuaTable> {
lua.create_table()
}
```
Under the hood a new function `luaopen_alt_module` will be created for the Lua module loader.
- `skip_memory_check` - skip memory allocation checks for some operations.
In module mode, mlua runs in unknown environment and cannot say are there any memory limits or not. As result, some operations that require memory allocation runs in
protected mode. Setting this attribute will improve performance of such operations with risk of having uncaught exceptions and memory leaks.
#### Improved Windows target
In previous mlua versions, building a Lua module for Windows requires having Lua development libraries installed on the system.
In contrast, on Linux and macOS, modules can be built without any external dependencies using the `-undefined=dynamic_lookup` linker flag.
With Rust 1.71+ it's now possible to lift this restriction for Windows as well. You can build modules normally and they will be linked with
`lua54.dll`/`lua53.dll`/`lua52.dll`/`lua51.dll` depending on the enabled Lua version.
You still need to have the dll although, linked to application where the module will be loaded.
### Breaking changes
1) `ToLua`/`ToLuaMulti` traits have been renamed to `IntoLua`/`IntoLuaMulti` respectively (with the methods called `into_lua`/`into_lua_multi`).
The main reason for this change is following the Rust self [convention](https://rust-lang.github.io/rust-clippy/master/index.html#/wrong_self_convention).
2) Removed `FromLua` implementation for `T: UserData + Clone`.
During the usage of mlua, it was found that this implementation is not very useful and prevents custom `FromLua` implementations for `T: UserData`.
It should be a developer decision to opt-in `FromLua` for their `T` if needed rather than having enabled it unconditionally.
To opt-in `FromLua` for `T: Clone` you can use a simple `#[derive(FromLua)]` macro (requires `feature = "macros"`):
```rust
#[derive(Clone, Copy, mlua::FromLua)]
struct MyUserData(i32);
```
`T` is not required to implement `UserData` because of the new relaxed restrictions on userdata types.
+7 -6
View File
@@ -3,15 +3,16 @@ use std::collections::HashMap;
use hyper::body::{Body as HyperBody, HttpBody as _};
use hyper::Client as HyperClient;
use mlua::{chunk, ExternalResult, Lua, Result, UserData, UserDataMethods};
use mlua::{chunk, AnyUserData, ExternalResult, Lua, Result, UserData, UserDataMethods};
struct BodyReader(HyperBody);
impl UserData for BodyReader {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method_mut("read", |lua, reader, ()| async move {
methods.add_async_function("read", |lua, reader: AnyUserData| async move {
let mut reader = reader.borrow_mut::<Self>()?;
if let Some(bytes) = reader.0.data().await {
let bytes = bytes.into_lua_err()?;
let bytes = bytes.to_lua_err()?;
return Some(lua.create_string(&bytes)).transpose();
}
Ok(None)
@@ -25,8 +26,8 @@ async fn main() -> Result<()> {
let fetch_url = lua.create_async_function(|lua, uri: String| async move {
let client = HyperClient::new();
let uri = uri.parse().into_lua_err()?;
let resp = client.get(uri).await.into_lua_err()?;
let uri = uri.parse().to_lua_err()?;
let resp = client.get(uri).await.to_lua_err()?;
let lua_resp = lua.create_table()?;
lua_resp.set("status", resp.status().as_u16())?;
@@ -36,7 +37,7 @@ async fn main() -> Result<()> {
headers
.entry(key.as_str())
.or_insert(Vec::new())
.push(value.to_str().into_lua_err()?);
.push(value.to_str().to_lua_err()?);
}
lua_resp.set("headers", headers)?;
+2 -2
View File
@@ -10,8 +10,8 @@ async fn main() -> Result<()> {
let resp = reqwest::get(&uri)
.await
.and_then(|resp| resp.error_for_status())
.into_lua_err()?;
let json = resp.json::<serde_json::Value>().await.into_lua_err()?;
.to_lua_err()?;
let json = resp.json::<serde_json::Value>().await.to_lua_err()?;
lua.to_value(&json)
})?;
+23 -12
View File
@@ -6,7 +6,9 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener, TcpStream};
use tokio::task;
use mlua::{chunk, Function, Lua, RegistryKey, String as LuaString, UserData, UserDataMethods};
use mlua::{
chunk, AnyUserData, Function, Lua, RegistryKey, String as LuaString, UserData, UserDataMethods,
};
struct LuaTcpStream(TcpStream);
@@ -16,19 +18,28 @@ impl UserData for LuaTcpStream {
Ok(this.0.peer_addr()?.to_string())
});
methods.add_async_method_mut("read", |lua, this, size| async move {
let mut buf = vec![0; size];
let n = this.0.read(&mut buf).await?;
buf.truncate(n);
lua.create_string(&buf)
});
methods.add_async_function(
"read",
|lua, (this, size): (AnyUserData, usize)| async move {
let mut this = this.borrow_mut::<Self>()?;
let mut buf = vec![0; size];
let n = this.0.read(&mut buf).await?;
buf.truncate(n);
lua.create_string(&buf)
},
);
methods.add_async_method_mut("write", |_, this, data: LuaString| async move {
let n = this.0.write(&data.as_bytes()).await?;
Ok(n)
});
methods.add_async_function(
"write",
|_, (this, data): (AnyUserData, LuaString)| async move {
let mut this = this.borrow_mut::<Self>()?;
let n = this.0.write(&data.as_bytes()).await?;
Ok(n)
},
);
methods.add_async_method_mut("close", |_, this, ()| async move {
methods.add_async_function("close", |_, this: AnyUserData| async move {
let mut this = this.borrow_mut::<Self>()?;
this.0.shutdown().await?;
Ok(())
});
+2 -14
View File
@@ -1,9 +1,7 @@
use std::f32;
use std::iter::FromIterator;
use mlua::{
chunk, FromLua, Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Value, Variadic,
};
use mlua::{chunk, Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Variadic};
fn main() -> Result<()> {
// You can create a new Lua state with `Lua::new()`. This loads the default Lua std library
@@ -32,7 +30,7 @@ fn main() -> Result<()> {
global = 'foo'..'bar'
"#,
)
.set_name("example code")
.set_name("example code")?
.exec()?;
assert_eq!(globals.get::<_, String>("global")?, "foobar");
@@ -153,16 +151,6 @@ fn main() -> Result<()> {
#[derive(Copy, Clone)]
struct Vec2(f32, f32);
// We can implement `FromLua` trait for our `Vec2` to return a copy
impl<'lua> FromLua<'lua> for Vec2 {
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match value {
Value::UserData(ud) => Ok(*ud.borrow::<Self>()?),
_ => unreachable!(),
}
}
}
impl UserData for Vec2 {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("magnitude", |_, vec, ()| {
+1 -1
View File
@@ -2,7 +2,7 @@
name = "rust_module"
version = "0.0.0"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2021"
edition = "2018"
[lib]
crate-type = ["cdylib"]
+4 -4
View File
@@ -1,11 +1,11 @@
//! This example shows a simple read-evaluate-print-loop (REPL).
use mlua::{Error, Lua, MultiValue};
use rustyline::DefaultEditor;
use rustyline::Editor;
fn main() {
let lua = Lua::new();
let mut editor = DefaultEditor::new().expect("Failed to create editor");
let mut editor = Editor::<()>::new().expect("Failed to make rustyline editor");
loop {
let mut prompt = "> ";
@@ -19,12 +19,12 @@ fn main() {
match lua.load(&line).eval::<MultiValue>() {
Ok(values) => {
editor.add_history_entry(line).unwrap();
editor.add_history_entry(line);
println!(
"{}",
values
.iter()
.map(|value| format!("{:#?}", value))
.map(|value| format!("{:?}", value))
.collect::<Vec<_>>()
.join("\t")
);
-43
View File
@@ -1,43 +0,0 @@
[package]
name = "mlua-sys"
version = "0.3.2"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
rust-version = "1.71"
edition = "2021"
repository = "https://github.com/khvzak/mlua"
documentation = "https://docs.rs/mlua-sys"
readme = "README.md"
categories = ["external-ffi-bindings"]
license = "MIT"
links = "lua"
build = "build/main.rs"
description = """
Low level (FFI) bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox Luau
"""
[package.metadata.docs.rs]
features = ["lua54", "vendored"]
rustdoc-args = ["--cfg", "docsrs"]
[features]
lua54 = []
lua53 = []
lua52 = []
lua51 = []
luajit = []
luajit52 = ["luajit"]
luau = ["luau0-src"]
luau-codegen = ["luau"]
luau-vector4 = ["luau"]
vendored = ["lua-src", "luajit-src"]
module = []
[dependencies]
[build-dependencies]
cc = "1.0"
cfg-if = "1.0"
pkg-config = "0.3.17"
lua-src = { version = ">= 546.0.0, < 546.1.0", optional = true }
luajit-src = { version = ">= 210.4.6, < 210.5.0", optional = true }
luau0-src = { version = "0.7.0", optional = true }
-8
View File
@@ -1,8 +0,0 @@
# mlua-sys
Low level (FFI) bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox [Luau].
Intended to be consumed by the [mlua] crate.
[Luau]: https://github.com/Roblox/luau
[mlua]: https://crates.io/crates/mlua
-52
View File
@@ -1,52 +0,0 @@
#![allow(dead_code)]
use std::env;
use std::ops::Bound;
pub fn probe_lua() {
let lib_dir = env::var("LUA_LIB").unwrap_or_default();
let lua_lib = env::var("LUA_LIB_NAME").unwrap_or_default();
println!("cargo:rerun-if-env-changed=LUA_LIB");
println!("cargo:rerun-if-env-changed=LUA_LIB_NAME");
println!("cargo:rerun-if-env-changed=LUA_LINK");
if !lua_lib.is_empty() {
if !lib_dir.is_empty() {
println!("cargo:rustc-link-search=native={lib_dir}");
}
let mut link_lib = "";
if env::var("LUA_LINK").as_deref() == Ok("static") {
link_lib = "static=";
};
println!("cargo:rustc-link-lib={link_lib}{lua_lib}");
return;
}
// Find using `pkg-config`
#[cfg(feature = "lua54")]
let (incl_bound, excl_bound, alt_probe, ver) = ("5.4", "5.5", Some("lua5.4"), "5.4");
#[cfg(feature = "lua53")]
let (incl_bound, excl_bound, alt_probe, ver) = ("5.3", "5.4", Some("lua5.3"), "5.3");
#[cfg(feature = "lua52")]
let (incl_bound, excl_bound, alt_probe, ver) = ("5.2", "5.3", Some("lua5.2"), "5.2");
#[cfg(feature = "lua51")]
let (incl_bound, excl_bound, alt_probe, ver) = ("5.1", "5.2", Some("lua5.1"), "5.1");
#[cfg(feature = "luajit")]
let (incl_bound, excl_bound, alt_probe, ver) = ("2.0.4", "2.2", None, "JIT");
#[rustfmt::skip]
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included(incl_bound), Bound::Excluded(excl_bound)))
.cargo_metadata(true)
.probe(if cfg!(feature = "luajit") { "luajit" } else { "lua" });
if lua.is_err() && alt_probe.is_some() {
lua = pkg_config::Config::new()
.cargo_metadata(true)
.probe(alt_probe.unwrap());
}
lua.unwrap_or_else(|err| panic!("cannot find Lua{ver} using `pkg-config`: {err}"));
}
-19
View File
@@ -1,19 +0,0 @@
cfg_if::cfg_if! {
if #[cfg(all(feature = "lua54", not(any(feature = "lua53", feature = "lua52", feature = "lua51", feature = "luajit", feature = "luau"))))] {
include!("main_inner.rs");
} else if #[cfg(all(feature = "lua53", not(any(feature = "lua54", feature = "lua52", feature = "lua51", feature = "luajit", feature = "luau"))))] {
include!("main_inner.rs");
} else if #[cfg(all(feature = "lua52", not(any(feature = "lua54", feature = "lua53", feature = "lua51", feature = "luajit", feature = "luau"))))] {
include!("main_inner.rs");
} else if #[cfg(all(feature = "lua51", not(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit", feature = "luau"))))] {
include!("main_inner.rs");
} else if #[cfg(all(feature = "luajit", not(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "lua51", feature = "luau"))))] {
include!("main_inner.rs");
} else if #[cfg(all(feature = "luau", not(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "lua51", feature = "luajit"))))] {
include!("main_inner.rs");
} else {
fn main() {
compile_error!("You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau");
}
}
}
-33
View File
@@ -1,33 +0,0 @@
cfg_if::cfg_if! {
if #[cfg(any(feature = "luau", feature = "vendored"))] {
#[path = "find_vendored.rs"]
mod find;
} else {
#[path = "find_normal.rs"]
mod find;
}
}
fn main() {
#[cfg(all(feature = "luau", feature = "module"))]
compile_error!("Luau does not support `module` mode");
#[cfg(all(feature = "module", feature = "vendored"))]
compile_error!("`vendored` and `module` features are mutually exclusive");
println!("cargo:rerun-if-changed=build");
#[cfg(windows)]
if cfg!(feature = "module") {
if !std::env::var("LUA_LIB_NAME").unwrap_or_default().is_empty() {
// Don't use raw-dylib linking
find::probe_lua();
return;
}
println!("cargo:rustc-cfg=raw_dylib");
}
#[cfg(not(feature = "module"))]
find::probe_lua();
}
-11
View File
@@ -1,11 +0,0 @@
//! Contains definitions from `luacodegen.h`.
use std::os::raw::c_int;
use super::lua::lua_State;
extern "C-unwind" {
pub fn luau_codegen_supported() -> c_int;
pub fn luau_codegen_create(state: *mut lua_State);
pub fn luau_codegen_compile(state: *mut lua_State, idx: c_int);
}
-7
View File
@@ -1,7 +0,0 @@
#[allow(unused_macros)]
macro_rules! cstr {
($s:expr) => {
concat!($s, "\0") as *const str as *const [::std::os::raw::c_char]
as *const ::std::os::raw::c_char
};
}
+4 -4
View File
@@ -1,8 +1,8 @@
[package]
name = "mlua_derive"
version = "0.9.0"
version = "0.8.0"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2021"
edition = "2018"
description = "Procedural macros for the mlua crate."
repository = "https://github.com/khvzak/mlua"
keywords = ["lua", "mlua"]
@@ -18,7 +18,7 @@ macros = ["proc-macro-error", "itertools", "regex", "once_cell"]
quote = "1.0"
proc-macro2 = { version = "1.0", features = ["span-locations"] }
proc-macro-error = { version = "1.0", optional = true }
syn = { version = "2.0", features = ["full"] }
itertools = { version = "0.11", optional = true }
syn = { version = "1.0", features = ["full"] }
itertools = { version = "0.10", optional = true }
regex = { version = "1.4", optional = true }
once_cell = { version = "1.0", optional = true }
-32
View File
@@ -1,32 +0,0 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
pub fn from_lua(input: TokenStream) -> TokenStream {
let DeriveInput {
ident, generics, ..
} = parse_macro_input!(input as DeriveInput);
let where_clause = match &generics.where_clause {
Some(where_clause) => quote! { #where_clause, Self: 'static + Clone },
None => quote! { where Self: 'static + Clone },
};
let ident_str = ident.to_string();
quote! {
impl #generics ::mlua::FromLua<'_> for #ident #generics #where_clause {
#[inline]
fn from_lua(value: ::mlua::Value<'_>, lua: &'_ ::mlua::Lua) -> ::mlua::Result<Self> {
match value {
::mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
_ => Err(::mlua::Error::FromLuaConversionError {
from: value.type_name(),
to: #ident_str,
message: None,
}),
}
}
}
}
.into()
}
+33 -69
View File
@@ -1,8 +1,7 @@
use proc_macro::TokenStream;
use proc_macro2::{Ident, Span};
use quote::quote;
use syn::meta::ParseNestedMeta;
use syn::{parse_macro_input, ItemFn, LitStr, Result};
use syn::{parse_macro_input, AttributeArgs, Error, ItemFn};
#[cfg(feature = "macros")]
use {
@@ -10,48 +9,19 @@ use {
proc_macro_error::proc_macro_error,
};
#[derive(Default)]
struct ModuleAttributes {
name: Option<Ident>,
skip_memory_check: bool,
}
impl ModuleAttributes {
fn parse(&mut self, meta: ParseNestedMeta) -> Result<()> {
if meta.path.is_ident("name") {
match meta.value() {
Ok(value) => {
self.name = Some(value.parse::<LitStr>()?.parse()?);
}
Err(_) => {
return Err(meta.error("`name` attribute must have a value"));
}
}
} else if meta.path.is_ident("skip_memory_check") {
if meta.value().is_ok() {
return Err(meta.error("`skip_memory_check` attribute have no values"));
}
self.skip_memory_check = true;
} else {
return Err(meta.error("unsupported module attribute"));
}
Ok(())
}
}
#[proc_macro_attribute]
pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
let mut args = ModuleAttributes::default();
if !attr.is_empty() {
let args_parser = syn::meta::parser(|meta| args.parse(meta));
parse_macro_input!(attr with args_parser);
let args = parse_macro_input!(attr as AttributeArgs);
let func = parse_macro_input!(item as ItemFn);
if !args.is_empty() {
let err = Error::new(Span::call_site(), "the macro does not support arguments")
.to_compile_error();
return err.into();
}
let func = parse_macro_input!(item as ItemFn);
let func_name = &func.sig.ident;
let module_name = args.name.unwrap_or_else(|| func_name.clone());
let ext_entrypoint_name = Ident::new(&format!("luaopen_{module_name}"), Span::call_site());
let skip_memory_check = args.skip_memory_check;
let func_name = func.sig.ident.clone();
let ext_entrypoint_name = Ident::new(&format!("luaopen_{}", func_name), Span::call_site());
let wrapped = quote! {
::mlua::require_module_feature!();
@@ -59,10 +29,10 @@ pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
#func
#[no_mangle]
unsafe extern "C-unwind" fn #ext_entrypoint_name(state: *mut ::mlua::lua_State) -> ::std::os::raw::c_int {
let lua = ::mlua::Lua::init_from_ptr(state);
lua.skip_memory_check(#skip_memory_check);
lua.entrypoint1(#func_name)
unsafe extern "C" fn #ext_entrypoint_name(state: *mut ::mlua::lua_State) -> ::std::os::raw::c_int {
::mlua::Lua::init_from_ptr(state)
.entrypoint1(#func_name)
.expect("cannot initialize module")
}
};
@@ -91,22 +61,30 @@ pub fn chunk(input: TokenStream) -> TokenStream {
});
let wrapped_code = quote! {{
use ::mlua::{AsChunk, ChunkMode, Lua, Result, Table};
use ::mlua::{AsChunk, ChunkMode, Lua, Result, Value};
use ::std::borrow::Cow;
use ::std::cell::Cell;
use ::std::io::Result as IoResult;
use ::std::marker::PhantomData;
use ::std::sync::Mutex;
struct InnerChunk<'lua, F: FnOnce(&'lua Lua) -> Result<Table<'lua>>>(Cell<Option<F>>, PhantomData<&'lua ()>);
fn annotate<'a, F: FnOnce(&'a Lua) -> Result<Value<'a>>>(f: F) -> F { f }
impl<'lua, F> AsChunk<'lua, 'static> for InnerChunk<'lua, 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<Table<'lua>>,
F: FnOnce(&'lua Lua) -> Result<Value<'lua>>,
{
fn environment(&self, lua: &'lua Lua) -> Result<Option<Table<'lua>>> {
fn source(&self) -> IoResult<Cow<[u8]>> {
Ok(Cow::Borrowed((#source).as_bytes()))
}
fn env(&self, lua: &'lua Lua) -> Result<Option<Value<'lua>>> {
if #caps_len > 0 {
if let Some(make_env) = self.0.take() {
return make_env(lua).map(Some);
if let Ok(mut make_env) = self.0.lock() {
if let Some(make_env) = make_env.take() {
return make_env(lua).map(Some);
}
}
}
Ok(None)
@@ -115,15 +93,9 @@ pub fn chunk(input: TokenStream) -> TokenStream {
fn mode(&self) -> Option<ChunkMode> {
Some(ChunkMode::Text)
}
fn source(self) -> IoResult<Cow<'static, [u8]>> {
Ok(Cow::Borrowed((#source).as_bytes()))
}
}
fn annotate<'a, F: FnOnce(&'a Lua) -> Result<Table<'a>>>(f: F) -> F { f }
let make_env = annotate(move |lua: &Lua| -> Result<Table> {
let make_env = annotate(move |lua: &Lua| -> Result<Value> {
let globals = lua.globals();
let env = lua.create_table()?;
let meta = lua.create_table()?;
@@ -134,24 +106,16 @@ pub fn chunk(input: TokenStream) -> TokenStream {
#(#caps)*
env.set_metatable(Some(meta));
Ok(env)
Ok(Value::Table(env))
});
InnerChunk(Cell::new(Some(make_env)), PhantomData)
&InnerChunk(Mutex::new(Some(make_env)), PhantomData)
}};
wrapped_code.into()
}
#[cfg(feature = "macros")]
#[proc_macro_derive(FromLua)]
pub fn from_lua(input: TokenStream) -> TokenStream {
from_lua::from_lua(input)
}
#[cfg(feature = "macros")]
mod chunk;
#[cfg(feature = "macros")]
mod from_lua;
#[cfg(feature = "macros")]
mod token;
+1 -1
View File
@@ -59,7 +59,7 @@ fn parse_pos(span: &Span) -> Option<(usize, usize)> {
static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"bytes\(([0-9]+)\.\.([0-9]+)\)").unwrap());
match RE.captures(&format!("{span:?}")) {
match RE.captures(&format!("{:?}", span)) {
Some(caps) => match (caps.get(1), caps.get(2)) {
(Some(start), Some(end)) => Some((
match start.as_str().parse() {
+93 -110
View File
@@ -5,17 +5,23 @@ use std::io::Result as IoResult;
use std::path::{Path, PathBuf};
use std::string::String as StdString;
use crate::error::{Error, ErrorContext, Result};
use crate::error::{Error, Result};
use crate::ffi;
use crate::function::Function;
use crate::lua::Lua;
use crate::table::Table;
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti};
use crate::value::{FromLuaMulti, ToLua, ToLuaMulti, Value};
#[cfg(feature = "async")]
use {futures_core::future::LocalBoxFuture, futures_util::future};
/// Trait for types [loadable by Lua] and convertible to a [`Chunk`]
///
/// [loadable by Lua]: https://www.lua.org/manual/5.4/manual.html#3.3.2
/// [`Chunk`]: crate::Chunk
pub trait AsChunk<'lua, 'a> {
pub trait AsChunk<'lua> {
/// Returns chunk data (can be text or binary)
fn source(&self) -> IoResult<Cow<[u8]>>;
/// Returns optional chunk name
fn name(&self) -> Option<StdString> {
None
@@ -24,8 +30,7 @@ pub trait AsChunk<'lua, 'a> {
/// Returns optional chunk [environment]
///
/// [environment]: https://www.lua.org/manual/5.4/manual.html#2.2
fn environment(&self, lua: &'lua Lua) -> Result<Option<Table<'lua>>> {
let _lua = lua; // suppress warning
fn env(&self, _lua: &'lua Lua) -> Result<Option<Value<'lua>>> {
Ok(None)
}
@@ -33,64 +38,49 @@ pub trait AsChunk<'lua, 'a> {
fn mode(&self) -> Option<ChunkMode> {
None
}
/// Returns chunk data (can be text or binary)
fn source(self) -> IoResult<Cow<'a, [u8]>>;
}
impl<'a> AsChunk<'_, 'a> for &'a str {
fn source(self) -> IoResult<Cow<'a, [u8]>> {
impl<'lua> AsChunk<'lua> for str {
fn source(&self) -> IoResult<Cow<[u8]>> {
Ok(Cow::Borrowed(self.as_ref()))
}
}
impl AsChunk<'_, 'static> for StdString {
fn source(self) -> IoResult<Cow<'static, [u8]>> {
Ok(Cow::Owned(self.into_bytes()))
impl<'lua> AsChunk<'lua> for StdString {
fn source(&self) -> IoResult<Cow<[u8]>> {
Ok(Cow::Borrowed(self.as_ref()))
}
}
impl<'a> AsChunk<'_, 'a> for &'a StdString {
fn source(self) -> IoResult<Cow<'a, [u8]>> {
Ok(Cow::Borrowed(self.as_bytes()))
}
}
impl<'a> AsChunk<'_, 'a> for &'a [u8] {
fn source(self) -> IoResult<Cow<'a, [u8]>> {
impl<'lua> AsChunk<'lua> for [u8] {
fn source(&self) -> IoResult<Cow<[u8]>> {
Ok(Cow::Borrowed(self))
}
}
impl AsChunk<'_, 'static> for Vec<u8> {
fn source(self) -> IoResult<Cow<'static, [u8]>> {
Ok(Cow::Owned(self))
impl<'lua> AsChunk<'lua> for Vec<u8> {
fn source(&self) -> IoResult<Cow<[u8]>> {
Ok(Cow::Borrowed(self))
}
}
impl<'a> AsChunk<'_, 'a> for &'a Vec<u8> {
fn source(self) -> IoResult<Cow<'a, [u8]>> {
Ok(Cow::Borrowed(self.as_ref()))
impl<'lua> AsChunk<'lua> for Path {
fn source(&self) -> IoResult<Cow<[u8]>> {
std::fs::read(self).map(Cow::Owned)
}
}
impl AsChunk<'_, 'static> for &Path {
fn name(&self) -> Option<StdString> {
Some(format!("@{}", self.display()))
}
fn source(self) -> IoResult<Cow<'static, [u8]>> {
std::fs::read(self).map(Cow::Owned)
}
}
impl AsChunk<'_, 'static> for PathBuf {
fn name(&self) -> Option<StdString> {
Some(format!("@{}", self.display()))
impl<'lua> AsChunk<'lua> for PathBuf {
fn source(&self) -> IoResult<Cow<[u8]>> {
std::fs::read(self).map(Cow::Owned)
}
fn source(self) -> IoResult<Cow<'static, [u8]>> {
std::fs::read(self).map(Cow::Owned)
fn name(&self) -> Option<StdString> {
Some(format!("@{}", self.display()))
}
}
@@ -100,10 +90,10 @@ impl AsChunk<'_, 'static> for PathBuf {
#[must_use = "`Chunk`s do nothing unless one of `exec`, `eval`, `call`, or `into_function` are called on them"]
pub struct Chunk<'lua, 'a> {
pub(crate) lua: &'lua Lua,
pub(crate) name: StdString,
pub(crate) env: Result<Option<Table<'lua>>>,
pub(crate) mode: Option<ChunkMode>,
pub(crate) source: IoResult<Cow<'a, [u8]>>,
pub(crate) name: Option<StdString>,
pub(crate) env: Result<Option<Value<'lua>>>,
pub(crate) mode: Option<ChunkMode>,
#[cfg(feature = "luau")]
pub(crate) compiler: Option<Compiler>,
}
@@ -125,21 +115,12 @@ pub struct Compiler {
coverage_level: u8,
vector_lib: Option<String>,
vector_ctor: Option<String>,
vector_type: Option<String>,
mutable_globals: Vec<String>,
}
#[cfg(any(feature = "luau", doc))]
impl Default for Compiler {
fn default() -> Self {
Self::new()
}
}
#[cfg(any(feature = "luau", doc))]
impl Compiler {
/// Creates Luau compiler instance with default options
pub const fn new() -> Self {
// Defaults are taken from luacode.h
Compiler {
optimization_level: 1,
@@ -147,10 +128,17 @@ impl Compiler {
coverage_level: 0,
vector_lib: None,
vector_ctor: None,
vector_type: None,
mutable_globals: Vec::new(),
}
}
}
#[cfg(any(feature = "luau", doc))]
impl Compiler {
/// Creates Luau compiler instance with default options
pub fn new() -> Self {
Compiler::default()
}
/// Sets Luau compiler optimization level.
///
@@ -158,8 +146,7 @@ impl Compiler {
/// * 0 - no optimization
/// * 1 - baseline optimization level that doesn't prevent debuggability (default)
/// * 2 - includes optimizations that harm debuggability such as inlining
#[must_use]
pub const fn set_optimization_level(mut self, level: u8) -> Self {
pub fn set_optimization_level(mut self, level: u8) -> Self {
self.optimization_level = level;
self
}
@@ -170,8 +157,7 @@ impl Compiler {
/// * 0 - no debugging support
/// * 1 - line info & function names only; sufficient for backtraces (default)
/// * 2 - full debug info with local & upvalue names; necessary for debugger
#[must_use]
pub const fn set_debug_level(mut self, level: u8) -> Self {
pub fn set_debug_level(mut self, level: u8) -> Self {
self.debug_level = level;
self
}
@@ -182,37 +168,26 @@ impl Compiler {
/// * 0 - no code coverage support (default)
/// * 1 - statement coverage
/// * 2 - statement and expression coverage (verbose)
#[must_use]
pub const fn set_coverage_level(mut self, level: u8) -> Self {
pub fn set_coverage_level(mut self, level: u8) -> Self {
self.coverage_level = level;
self
}
#[doc(hidden)]
#[must_use]
pub fn set_vector_lib(mut self, lib: impl Into<String>) -> Self {
self.vector_lib = Some(lib.into());
pub fn set_vector_lib(mut self, lib: Option<String>) -> Self {
self.vector_lib = lib;
self
}
#[doc(hidden)]
#[must_use]
pub fn set_vector_ctor(mut self, ctor: impl Into<String>) -> Self {
self.vector_ctor = Some(ctor.into());
self
}
#[doc(hidden)]
#[must_use]
pub fn set_vector_type(mut self, r#type: impl Into<String>) -> Self {
self.vector_type = Some(r#type.into());
pub fn set_vector_ctor(mut self, ctor: Option<String>) -> Self {
self.vector_ctor = ctor;
self
}
/// Sets a list of globals that are mutable.
///
/// It disables the import optimization for fields accessed through these.
#[must_use]
pub fn set_mutable_globals(mut self, globals: Vec<String>) -> Self {
self.mutable_globals = globals;
self
@@ -229,9 +204,6 @@ impl Compiler {
let vector_ctor = self.vector_ctor.clone();
let vector_ctor = vector_ctor.and_then(|ctor| CString::new(ctor).ok());
let vector_ctor = vector_ctor.as_ref();
let vector_type = self.vector_type.clone();
let vector_type = vector_type.and_then(|t| CString::new(t).ok());
let vector_type = vector_type.as_ref();
let mutable_globals = self
.mutable_globals
@@ -243,10 +215,10 @@ impl Compiler {
.iter()
.map(|s| s.as_ptr())
.collect::<Vec<_>>();
let mut mutable_globals_ptr = ptr::null();
let mut mutable_globals_ptr = ptr::null_mut();
if !mutable_globals.is_empty() {
mutable_globals.push(ptr::null());
mutable_globals_ptr = mutable_globals.as_ptr();
mutable_globals_ptr = mutable_globals.as_mut_ptr();
}
unsafe {
@@ -256,7 +228,6 @@ impl Compiler {
coverageLevel: self.coverage_level as c_int,
vectorLib: vector_lib.map_or(ptr::null(), |s| s.as_ptr()),
vectorCtor: vector_ctor.map_or(ptr::null(), |s| s.as_ptr()),
vectorType: vector_type.map_or(ptr::null(), |s| s.as_ptr()),
mutableGlobals: mutable_globals_ptr,
};
ffi::luau_compile(source.as_ref(), options)
@@ -266,14 +237,16 @@ impl Compiler {
impl<'lua, 'a> Chunk<'lua, 'a> {
/// Sets the name of this chunk, which results in more informative error traces.
pub fn set_name(mut self, name: impl Into<String>) -> Self {
self.name = name.into();
self
pub fn set_name(mut self, name: impl AsRef<str>) -> Result<Self> {
self.name = Some(name.as_ref().to_string());
// Do extra validation
let _ = self.convert_name()?;
Ok(self)
}
/// Sets the environment of the loaded chunk to the given value.
/// Sets the first upvalue (`_ENV`) of the loaded chunk to the given value.
///
/// In Lua >=5.2 main chunks always have exactly one upvalue, and this upvalue is used as the `_ENV`
/// Lua main chunks always have exactly one upvalue, and this upvalue is used as the `_ENV`
/// variable inside the chunk. By default this value is set to the global environment.
///
/// Calling this method changes the `_ENV` upvalue to the value provided, and variables inside
@@ -282,12 +255,10 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
/// All global variables (including the standard library!) are looked up in `_ENV`, so it may be
/// necessary to populate the environment in order for scripts using custom environments to be
/// useful.
pub fn set_environment<V: IntoLua<'lua>>(mut self, env: V) -> Self {
self.env = env
.into_lua(self.lua)
.and_then(|val| self.lua.unpack(val))
.context("bad environment value");
self
pub fn set_environment<V: ToLua<'lua>>(mut self, env: V) -> Result<Self> {
// Prefer to propagate errors here and wrap to `Ok`
self.env = Ok(Some(env.to_lua(self.lua)?));
Ok(self)
}
/// Sets whether the chunk is text or binary (autodetected by default).
@@ -328,8 +299,11 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
/// [`exec`]: #method.exec
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub async fn exec_async(self) -> Result<()> {
self.call_async(()).await
pub fn exec_async<'fut>(self) -> LocalBoxFuture<'fut, Result<()>>
where
'lua: 'fut,
{
self.call_async(())
}
/// Evaluate the chunk as either an expression or block.
@@ -360,23 +334,24 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
/// [`eval`]: #method.eval
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub async fn eval_async<R>(self) -> Result<R>
pub fn eval_async<'fut, R>(self) -> LocalBoxFuture<'fut, Result<R>>
where
R: FromLuaMulti<'lua> + 'lua,
'lua: 'fut,
R: FromLuaMulti<'lua> + 'fut,
{
if self.detect_mode() == ChunkMode::Binary {
self.call_async(()).await
self.call_async(())
} else if let Ok(function) = self.to_expression() {
function.call_async(()).await
function.call_async(())
} else {
self.call_async(()).await
self.call_async(())
}
}
/// Load the chunk function and call it with the given arguments.
///
/// This is equivalent to `into_function` and calling the resulting function.
pub fn call<A: IntoLuaMulti<'lua>, R: FromLuaMulti<'lua>>(self, args: A) -> Result<R> {
pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(self, args: A) -> Result<R> {
self.into_function()?.call(args)
}
@@ -389,12 +364,16 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
/// [`call`]: #method.call
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub async fn call_async<A, R>(self, args: A) -> Result<R>
pub fn call_async<'fut, A, R>(self, args: A) -> LocalBoxFuture<'fut, Result<R>>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua,
'lua: 'fut,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
{
self.into_function()?.call_async(args).await
match self.into_function() {
Ok(func) => func.call_async(args),
Err(e) => Box::pin(future::err(e)),
}
}
/// Load this chunk into a regular `Function`.
@@ -408,9 +387,9 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
self.compile();
}
let name = Self::convert_name(self.name)?;
let name = self.convert_name()?;
self.lua
.load_chunk(Some(&name), self.env?, self.mode, self.source?.as_ref())
.load_chunk(self.source?.as_ref(), name.as_deref(), self.env?, self.mode)
}
/// Compiles the chunk and changes mode to binary.
@@ -429,7 +408,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
self.mode = Some(ChunkMode::Binary);
}
#[cfg(not(feature = "luau"))]
if let Ok(func) = self.lua.load_chunk(None, None, None, source.as_ref()) {
if let Ok(func) = self.lua.load_chunk(source.as_ref(), None, None, None) {
let data = func.dump(false);
self.source = Ok(Cow::Owned(data));
self.mode = Some(ChunkMode::Binary);
@@ -469,7 +448,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
} else {
let mut cache = ChunksCache(HashMap::new());
cache.0.insert(text_source, binary_source.as_ref().to_vec());
let _ = self.lua.try_set_app_data(cache);
self.lua.set_app_data(cache);
}
}
}
@@ -481,7 +460,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
fn to_expression(&self) -> Result<Function<'lua>> {
// We assume that mode is Text
let source = self.source.as_ref();
let source = source.map_err(Error::runtime)?;
let source = source.map_err(|err| Error::RuntimeError(err.to_string()))?;
let source = Self::expression_source(source);
// We don't need to compile source if no compiler options set
#[cfg(feature = "luau")]
@@ -491,9 +470,9 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
.map(|c| c.compile(&source))
.unwrap_or(source);
let name = Self::convert_name(self.name.clone())?;
let name = self.convert_name()?;
self.lua
.load_chunk(Some(&name), self.env.clone()?, None, &source)
.load_chunk(&source, name.as_deref(), self.env.clone()?, None)
}
fn detect_mode(&self) -> ChunkMode {
@@ -514,8 +493,12 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
}
}
fn convert_name(name: String) -> Result<CString> {
CString::new(name).map_err(|err| Error::runtime(format!("invalid name: {err}")))
fn convert_name(&self) -> Result<Option<CString>> {
self.name
.clone()
.map(CString::new)
.transpose()
.map_err(|err| Error::RuntimeError(format!("invalid name: {err}")))
}
fn expression_source(source: &[u8]) -> Vec<u8> {
+99 -218
View File
@@ -1,11 +1,11 @@
#![allow(clippy::wrong_self_convention)]
use std::borrow::Cow;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::convert::TryInto;
use std::ffi::{CStr, CString};
use std::hash::{BuildHasher, Hash};
use std::os::raw::c_int;
use std::string::String as StdString;
use std::{slice, str};
use bstr::{BStr, BString};
use num_traits::cast;
@@ -17,15 +17,12 @@ use crate::string::String;
use crate::table::Table;
use crate::thread::Thread;
use crate::types::{LightUserData, MaybeSend};
use crate::userdata::{AnyUserData, UserData, UserDataRef, UserDataRefMut};
use crate::value::{FromLua, IntoLua, Nil, Value};
use crate::userdata::{AnyUserData, UserData};
use crate::value::{FromLua, Nil, ToLua, Value};
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
use crate::{function::OwnedFunction, table::OwnedTable, userdata::OwnedAnyUserData};
impl<'lua> IntoLua<'lua> for Value<'lua> {
impl<'lua> ToLua<'lua> for Value<'lua> {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(self)
}
}
@@ -37,9 +34,9 @@ impl<'lua> FromLua<'lua> for Value<'lua> {
}
}
impl<'lua> IntoLua<'lua> for String<'lua> {
impl<'lua> ToLua<'lua> for String<'lua> {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(self))
}
}
@@ -51,15 +48,15 @@ impl<'lua> FromLua<'lua> for String<'lua> {
lua.coerce_string(value)?
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: "string",
to: "String",
message: Some("expected string or number".to_string()),
})
}
}
impl<'lua> IntoLua<'lua> for Table<'lua> {
impl<'lua> ToLua<'lua> for Table<'lua> {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(self))
}
}
@@ -78,27 +75,9 @@ impl<'lua> FromLua<'lua> for Table<'lua> {
}
}
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
impl<'lua> IntoLua<'lua> for OwnedTable {
impl<'lua> ToLua<'lua> for Function<'lua> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(Table(lua.adopt_owned_ref(self.0))))
}
}
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
impl<'lua> FromLua<'lua> for OwnedTable {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<OwnedTable> {
Table::from_lua(value, lua).map(|s| s.into_owned())
}
}
impl<'lua> IntoLua<'lua> for Function<'lua> {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Function(self))
}
}
@@ -117,27 +96,9 @@ impl<'lua> FromLua<'lua> for Function<'lua> {
}
}
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
impl<'lua> IntoLua<'lua> for OwnedFunction {
impl<'lua> ToLua<'lua> for Thread<'lua> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Function(Function(lua.adopt_owned_ref(self.0))))
}
}
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
impl<'lua> FromLua<'lua> for OwnedFunction {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<OwnedFunction> {
Function::from_lua(value, lua).map(|s| s.into_owned())
}
}
impl<'lua> IntoLua<'lua> for Thread<'lua> {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Thread(self))
}
}
@@ -156,9 +117,9 @@ impl<'lua> FromLua<'lua> for Thread<'lua> {
}
}
impl<'lua> IntoLua<'lua> for AnyUserData<'lua> {
impl<'lua> ToLua<'lua> for AnyUserData<'lua> {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::UserData(self))
}
}
@@ -177,48 +138,30 @@ impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
}
}
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
impl<'lua> IntoLua<'lua> for OwnedAnyUserData {
impl<'lua, T: 'static + MaybeSend + UserData> ToLua<'lua> for T {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::UserData(AnyUserData(lua.adopt_owned_ref(self.0))))
}
}
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
impl<'lua> FromLua<'lua> for OwnedAnyUserData {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<OwnedAnyUserData> {
AnyUserData::from_lua(value, lua).map(|s| s.into_owned())
}
}
impl<'lua, T: UserData + MaybeSend + 'static> IntoLua<'lua> for T {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::UserData(lua.create_userdata(self)?))
}
}
impl<'lua, T: 'static> FromLua<'lua> for UserDataRef<'lua, T> {
impl<'lua, T: 'static + UserData + Clone> FromLua<'lua> for T {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
Self::from_value(value)
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<T> {
match value {
Value::UserData(ud) => Ok(ud.borrow::<T>()?.clone()),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "userdata",
message: None,
}),
}
}
}
impl<'lua, T: 'static> FromLua<'lua> for UserDataRefMut<'lua, T> {
impl<'lua> ToLua<'lua> for Error {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
Self::from_value(value)
}
}
impl<'lua> IntoLua<'lua> for Error {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Error(self))
}
}
@@ -228,7 +171,7 @@ impl<'lua> FromLua<'lua> for Error {
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Error> {
match value {
Value::Error(err) => Ok(err),
val => Ok(Error::runtime(
val => Ok(Error::RuntimeError(
lua.coerce_string(val)?
.and_then(|s| Some(s.to_str().ok()?.to_owned()))
.unwrap_or_else(|| "<unprintable error>".to_owned()),
@@ -237,9 +180,9 @@ impl<'lua> FromLua<'lua> for Error {
}
}
impl<'lua> IntoLua<'lua> for bool {
impl<'lua> ToLua<'lua> for bool {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Boolean(self))
}
}
@@ -255,9 +198,9 @@ impl<'lua> FromLua<'lua> for bool {
}
}
impl<'lua> IntoLua<'lua> for LightUserData {
impl<'lua> ToLua<'lua> for LightUserData {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::LightUserData(self))
}
}
@@ -276,39 +219,11 @@ impl<'lua> FromLua<'lua> for LightUserData {
}
}
#[cfg(feature = "luau")]
impl<'lua> IntoLua<'lua> for crate::types::Vector {
impl<'lua> ToLua<'lua> for StdString {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Vector(self))
}
}
#[cfg(feature = "luau")]
impl<'lua> FromLua<'lua> for crate::types::Vector {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match value {
Value::Vector(v) => Ok(v),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "vector",
message: None,
}),
}
}
}
impl<'lua> IntoLua<'lua> for StdString {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(&self)?))
}
#[inline]
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
push_bytes_into_stack(self, lua)
}
}
impl<'lua> FromLua<'lua> for StdString {
@@ -325,49 +240,25 @@ impl<'lua> FromLua<'lua> for StdString {
.to_str()?
.to_owned())
}
#[inline]
unsafe fn from_stack(idx: c_int, lua: &'lua Lua) -> Result<Self> {
let state = lua.state();
if ffi::lua_type(state, idx) == ffi::LUA_TSTRING {
let mut size = 0;
let data = ffi::lua_tolstring(state, idx, &mut size);
let bytes = slice::from_raw_parts(data as *const u8, size);
return str::from_utf8(bytes).map(|s| s.to_owned()).map_err(|e| {
Error::FromLuaConversionError {
from: "string",
to: "String",
message: Some(e.to_string()),
}
});
}
// Fallback to default
Self::from_lua(lua.stack_value(idx), lua)
}
}
impl<'lua> IntoLua<'lua> for &str {
impl<'lua> ToLua<'lua> for &str {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self)?))
}
#[inline]
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
push_bytes_into_stack(self, lua)
}
}
impl<'lua> IntoLua<'lua> for Cow<'_, str> {
impl<'lua> ToLua<'lua> for Cow<'_, str> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self.as_bytes())?))
}
}
impl<'lua> IntoLua<'lua> for Box<str> {
impl<'lua> ToLua<'lua> for Box<str> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(&*self)?))
}
}
@@ -389,9 +280,9 @@ impl<'lua> FromLua<'lua> for Box<str> {
}
}
impl<'lua> IntoLua<'lua> for CString {
impl<'lua> ToLua<'lua> for CString {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self.as_bytes())?))
}
}
@@ -419,23 +310,23 @@ impl<'lua> FromLua<'lua> for CString {
}
}
impl<'lua> IntoLua<'lua> for &CStr {
impl<'lua> ToLua<'lua> for &CStr {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self.to_bytes())?))
}
}
impl<'lua> IntoLua<'lua> for Cow<'_, CStr> {
impl<'lua> ToLua<'lua> for Cow<'_, CStr> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self.to_bytes())?))
}
}
impl<'lua> IntoLua<'lua> for BString {
impl<'lua> ToLua<'lua> for BString {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(&self)?))
}
}
@@ -448,7 +339,7 @@ impl<'lua> FromLua<'lua> for BString {
lua.coerce_string(value)?
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: "BString",
to: "String",
message: Some("expected string or number".to_string()),
})?
.as_bytes()
@@ -457,33 +348,18 @@ impl<'lua> FromLua<'lua> for BString {
}
}
impl<'lua> IntoLua<'lua> for &BStr {
impl<'lua> ToLua<'lua> for &BStr {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self)?))
}
}
#[inline]
unsafe fn push_bytes_into_stack<'lua, T>(this: T, lua: &'lua Lua) -> Result<()>
where
T: IntoLua<'lua> + AsRef<[u8]>,
{
let bytes = this.as_ref();
if lua.unlikely_memory_error() && bytes.len() < (1 << 30) {
// Fast path: push directly into the Lua stack.
ffi::lua_pushlstring(lua.state(), bytes.as_ptr() as *const _, bytes.len());
return Ok(());
}
// Fallback to default
lua.push_value(T::into_lua(this, lua)?)
}
macro_rules! lua_convert_int {
($x:ty) => {
impl<'lua> IntoLua<'lua> for $x {
impl<'lua> ToLua<'lua> for $x {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
cast(self)
.map(Value::Integer)
.or_else(|| cast(self).map(Value::Number))
@@ -544,9 +420,9 @@ lua_convert_int!(usize);
macro_rules! lua_convert_float {
($x:ty) => {
impl<'lua> IntoLua<'lua> for $x {
impl<'lua> ToLua<'lua> for $x {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
cast(self)
.ok_or_else(|| Error::ToLuaConversionError {
from: stringify!($x),
@@ -582,24 +458,24 @@ macro_rules! lua_convert_float {
lua_convert_float!(f32);
lua_convert_float!(f64);
impl<'lua, T> IntoLua<'lua> for &[T]
impl<'lua, T> ToLua<'lua> for &[T]
where
T: IntoLua<'lua> + Clone,
T: Clone + ToLua<'lua>,
{
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(
lua.create_sequence_from(self.iter().cloned())?,
))
}
}
impl<'lua, T, const N: usize> IntoLua<'lua> for [T; N]
impl<'lua, T, const N: usize> ToLua<'lua> for [T; N]
where
T: IntoLua<'lua>,
T: ToLua<'lua>,
{
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_sequence_from(self)?))
}
}
@@ -612,22 +488,21 @@ where
fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self> {
match value {
#[cfg(feature = "luau")]
#[rustfmt::skip]
Value::Vector(v) if N == crate::types::Vector::SIZE => unsafe {
use std::{mem, ptr};
let mut arr: [mem::MaybeUninit<T>; N] = mem::MaybeUninit::uninit().assume_init();
ptr::write(arr[0].as_mut_ptr() , T::from_lua(Value::Number(v.x() as _), _lua)?);
ptr::write(arr[1].as_mut_ptr(), T::from_lua(Value::Number(v.y() as _), _lua)?);
ptr::write(arr[2].as_mut_ptr(), T::from_lua(Value::Number(v.z() as _), _lua)?);
#[cfg(feature = "luau-vector4")]
ptr::write(arr[3].as_mut_ptr(), T::from_lua(Value::Number(v.w() as _), _lua)?);
Ok(mem::transmute_copy(&arr))
},
Value::Vector(x, y, z) if N == 3 => Ok(mlua_expect!(
vec![
T::from_lua(Value::Number(x as _), _lua)?,
T::from_lua(Value::Number(y as _), _lua)?,
T::from_lua(Value::Number(z as _), _lua)?,
]
.try_into()
.map_err(|_| ()),
"cannot convert vector to array"
)),
Value::Table(table) => {
let vec = table.sequence_values().collect::<Result<Vec<_>>>()?;
vec.try_into()
.map_err(|vec: Vec<T>| Error::FromLuaConversionError {
from: "table",
from: "Table",
to: "Array",
message: Some(format!("expected table of length {}, got {}", N, vec.len())),
})
@@ -641,9 +516,9 @@ where
}
}
impl<'lua, T: IntoLua<'lua>> IntoLua<'lua> for Box<[T]> {
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Box<[T]> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_sequence_from(self.into_vec())?))
}
}
@@ -655,9 +530,9 @@ impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Box<[T]> {
}
}
impl<'lua, T: IntoLua<'lua>> IntoLua<'lua> for Vec<T> {
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Vec<T> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_sequence_from(self)?))
}
}
@@ -666,6 +541,12 @@ impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Vec<T> {
#[inline]
fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self> {
match value {
#[cfg(feature = "luau")]
Value::Vector(x, y, z) => Ok(vec![
T::from_lua(Value::Number(x as _), _lua)?,
T::from_lua(Value::Number(y as _), _lua)?,
T::from_lua(Value::Number(z as _), _lua)?,
]),
Value::Table(table) => table.sequence_values().collect(),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
@@ -676,11 +557,11 @@ impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Vec<T> {
}
}
impl<'lua, K: Eq + Hash + IntoLua<'lua>, V: IntoLua<'lua>, S: BuildHasher> IntoLua<'lua>
impl<'lua, K: Eq + Hash + ToLua<'lua>, V: ToLua<'lua>, S: BuildHasher> ToLua<'lua>
for HashMap<K, V, S>
{
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_table_from(self)?))
}
}
@@ -702,9 +583,9 @@ impl<'lua, K: Eq + Hash + FromLua<'lua>, V: FromLua<'lua>, S: BuildHasher + Defa
}
}
impl<'lua, K: Ord + IntoLua<'lua>, V: IntoLua<'lua>> IntoLua<'lua> for BTreeMap<K, V> {
impl<'lua, K: Ord + ToLua<'lua>, V: ToLua<'lua>> ToLua<'lua> for BTreeMap<K, V> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_table_from(self)?))
}
}
@@ -724,9 +605,9 @@ impl<'lua, K: Ord + FromLua<'lua>, V: FromLua<'lua>> FromLua<'lua> for BTreeMap<
}
}
impl<'lua, T: Eq + Hash + IntoLua<'lua>, S: BuildHasher> IntoLua<'lua> for HashSet<T, S> {
impl<'lua, T: Eq + Hash + ToLua<'lua>, S: BuildHasher> ToLua<'lua> for HashSet<T, S> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_table_from(
self.into_iter().map(|val| (val, true)),
)?))
@@ -737,7 +618,7 @@ impl<'lua, T: Eq + Hash + FromLua<'lua>, S: BuildHasher + Default> FromLua<'lua>
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match value {
Value::Table(table) if table.raw_len() > 0 => table.sequence_values().collect(),
Value::Table(table) if table.len()? > 0 => table.sequence_values().collect(),
Value::Table(table) => table
.pairs::<T, Value<'lua>>()
.map(|res| res.map(|(k, _)| k))
@@ -751,9 +632,9 @@ impl<'lua, T: Eq + Hash + FromLua<'lua>, S: BuildHasher + Default> FromLua<'lua>
}
}
impl<'lua, T: Ord + IntoLua<'lua>> IntoLua<'lua> for BTreeSet<T> {
impl<'lua, T: Ord + ToLua<'lua>> ToLua<'lua> for BTreeSet<T> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_table_from(
self.into_iter().map(|val| (val, true)),
)?))
@@ -764,7 +645,7 @@ impl<'lua, T: Ord + FromLua<'lua>> FromLua<'lua> for BTreeSet<T> {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match value {
Value::Table(table) if table.raw_len() > 0 => table.sequence_values().collect(),
Value::Table(table) if table.len()? > 0 => table.sequence_values().collect(),
Value::Table(table) => table
.pairs::<T, Value<'lua>>()
.map(|res| res.map(|(k, _)| k))
@@ -778,11 +659,11 @@ impl<'lua, T: Ord + FromLua<'lua>> FromLua<'lua> for BTreeSet<T> {
}
}
impl<'lua, T: IntoLua<'lua>> IntoLua<'lua> for Option<T> {
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Option<T> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
match self {
Some(val) => val.into_lua(lua),
Some(val) => val.to_lua(lua),
None => Ok(Nil),
}
}
+41 -166
View File
@@ -1,3 +1,5 @@
#![allow(clippy::wrong_self_convention)]
use std::error::Error as StdError;
use std::fmt;
use std::io::Error as IoError;
@@ -7,8 +9,6 @@ use std::str::Utf8Error;
use std::string::String as StdString;
use std::sync::Arc;
use crate::private::Sealed;
/// Error type returned by `mlua` methods.
#[derive(Debug, Clone)]
#[non_exhaustive]
@@ -47,6 +47,11 @@ pub enum Error {
/// This error can only happen when Lua state was not created by us and does not have the
/// custom allocator attached.
MemoryLimitNotAvailable,
/// Main thread is not available.
///
/// This error can only happen in Lua5.1/LuaJIT module mode, when module loaded within a coroutine.
/// These Lua versions does not have `LUA_RIDX_MAINTHREAD` registry key.
MainThreadNotAvailable,
/// A mutable callback has triggered Lua code that has called the same mutable callback again.
///
/// This is an error because a mutable callback can only be borrowed mutably once.
@@ -64,22 +69,8 @@ pub enum Error {
/// called with a huge number of arguments, or a rust callback returns a huge number of return
/// values.
StackError,
/// Too many arguments to `Function::bind`.
/// Too many arguments to `Function::bind`
BindError,
/// Bad argument received from Lua (usually when calling a function).
///
/// This error can help to identify the argument that caused the error
/// (which is stored in the corresponding field).
BadArgument {
/// Function that was called.
to: Option<StdString>,
/// Argument position (usually starts from 1).
pos: usize,
/// Argument name.
name: Option<StdString>,
/// Underlying error returned when converting argument to a Lua value.
cause: Arc<Error>,
},
/// A Rust value could not be converted to a Lua value.
ToLuaConversionError {
/// Name of the Rust type that could not be converted.
@@ -125,7 +116,7 @@ pub enum Error {
///
/// [`AnyUserData`]: crate::AnyUserData
UserDataDestructed,
/// An [`AnyUserData`] immutable borrow failed.
/// An [`AnyUserData`] immutable borrow failed because it is already borrowed mutably.
///
/// This error can occur when a method on a [`UserData`] type calls back into Lua, which then
/// tries to call a method on the same [`UserData`] type. Consider restructuring your API to
@@ -134,7 +125,7 @@ pub enum Error {
/// [`AnyUserData`]: crate::AnyUserData
/// [`UserData`]: crate::UserData
UserDataBorrowError,
/// An [`AnyUserData`] mutable borrow failed.
/// An [`AnyUserData`] mutable borrow failed because it is already borrowed.
///
/// This error can occur when a method on a [`UserData`] type calls back into Lua, which then
/// tries to call a method on the same [`UserData`] type. Consider restructuring your API to
@@ -151,11 +142,8 @@ pub enum Error {
///
/// [`MetaMethod`]: crate::MetaMethod
MetaMethodTypeError {
/// Name of the metamethod.
method: StdString,
/// Passed value type.
type_name: &'static str,
/// A string containing more detailed error information.
message: Option<StdString>,
},
/// A [`RegistryKey`] produced from a different Lua state was used.
@@ -190,13 +178,6 @@ pub enum Error {
/// error. The Rust code that originally invoked the Lua code then receives a `CallbackError`,
/// from which the original error (and a stack traceback) can be recovered.
ExternalError(Arc<dyn StdError + Send + Sync>),
/// An error with additional context.
WithContext {
/// A string containing additional context.
context: StdString,
/// Underlying error.
cause: Arc<Error>,
},
}
/// A specialized `Result` type used by `mlua`'s API.
@@ -206,21 +187,24 @@ pub type Result<T> = StdResult<T, Error>;
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::SyntaxError { ref message, .. } => write!(fmt, "syntax error: {message}"),
Error::RuntimeError(ref msg) => write!(fmt, "runtime error: {msg}"),
Error::SyntaxError { ref message, .. } => write!(fmt, "syntax error: {}", message),
Error::RuntimeError(ref msg) => write!(fmt, "runtime error: {}", msg),
Error::MemoryError(ref msg) => {
write!(fmt, "memory error: {msg}")
write!(fmt, "memory error: {}", msg)
}
#[cfg(any(feature = "lua53", feature = "lua52"))]
Error::GarbageCollectorError(ref msg) => {
write!(fmt, "garbage collector error: {msg}")
write!(fmt, "garbage collector error: {}", msg)
}
Error::SafetyError(ref msg) => {
write!(fmt, "safety error: {msg}")
write!(fmt, "safety error: {}", msg)
},
Error::MemoryLimitNotAvailable => {
write!(fmt, "setting memory limit is not available")
}
Error::MainThreadNotAvailable => {
write!(fmt, "main thread is not available in Lua 5.1")
}
Error::RecursiveMutCallback => write!(fmt, "mutable callback called recursively"),
Error::CallbackDestructed => write!(
fmt,
@@ -234,55 +218,44 @@ impl fmt::Display for Error {
fmt,
"too many arguments to Function::bind"
),
Error::BadArgument { ref to, pos, ref name, ref cause } => {
if let Some(name) = name {
write!(fmt, "bad argument `{name}`")?;
} else {
write!(fmt, "bad argument #{pos}")?;
}
if let Some(to) = to {
write!(fmt, " to `{to}`")?;
}
write!(fmt, ": {cause}")
},
Error::ToLuaConversionError { from, to, ref message } => {
write!(fmt, "error converting {from} to Lua {to}")?;
write!(fmt, "error converting {} to Lua {}", from, to)?;
match *message {
None => Ok(()),
Some(ref message) => write!(fmt, " ({message})"),
Some(ref message) => write!(fmt, " ({})", message),
}
}
Error::FromLuaConversionError { from, to, ref message } => {
write!(fmt, "error converting Lua {from} to {to}")?;
write!(fmt, "error converting Lua {} to {}", from, to)?;
match *message {
None => Ok(()),
Some(ref message) => write!(fmt, " ({message})"),
Some(ref message) => write!(fmt, " ({})", message),
}
}
Error::CoroutineInactive => write!(fmt, "cannot resume inactive coroutine"),
Error::UserDataTypeMismatch => write!(fmt, "userdata is not expected type"),
Error::UserDataDestructed => write!(fmt, "userdata has been destructed"),
Error::UserDataBorrowError => write!(fmt, "error borrowing userdata"),
Error::UserDataBorrowMutError => write!(fmt, "error mutably borrowing userdata"),
Error::MetaMethodRestricted(ref method) => write!(fmt, "metamethod {method} is restricted"),
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 {method} has unsupported type {type_name}")?;
write!(fmt, "metamethod {} has unsupported type {}", method, type_name)?;
match *message {
None => Ok(()),
Some(ref message) => write!(fmt, " ({message})"),
Some(ref message) => write!(fmt, " ({})", message),
}
}
Error::MismatchedRegistryKey => {
write!(fmt, "RegistryKey used from different Lua state")
}
Error::CallbackError { ref cause, ref traceback } => {
writeln!(fmt, "callback error")?;
// Trace errors down to the root
let (mut cause, mut full_traceback) = (cause, None);
while let Error::CallbackError { cause: ref cause2, traceback: ref traceback2 } = **cause {
cause = cause2;
full_traceback = Some(traceback2);
}
writeln!(fmt, "{cause}")?;
if let Some(full_traceback) = full_traceback {
let traceback = traceback.trim_start_matches("stack traceback:");
let traceback = traceback.trim_start().trim_end();
@@ -296,24 +269,20 @@ impl fmt::Display for Error {
} else {
writeln!(fmt, "{}", traceback.trim_end())?;
}
Ok(())
write!(fmt, "caused by: {}", cause)
}
Error::PreviouslyResumedPanic => {
write!(fmt, "previously resumed panic returned again")
}
#[cfg(feature = "serialize")]
Error::SerializeError(ref err) => {
write!(fmt, "serialize error: {err}")
write!(fmt, "serialize error: {}", err)
},
#[cfg(feature = "serialize")]
Error::DeserializeError(ref err) => {
write!(fmt, "deserialize error: {err}")
write!(fmt, "deserialize error: {}", err)
},
Error::ExternalError(ref err) => write!(fmt, "{err}"),
Error::WithContext { ref context, ref cause } => {
writeln!(fmt, "{context}")?;
write!(fmt, "{cause}")
}
Error::ExternalError(ref err) => write!(fmt, "{}", err),
}
}
}
@@ -327,147 +296,53 @@ impl StdError for Error {
// Given that we include source to fmt::Display implementation for `CallbackError`, this call returns nothing.
Error::CallbackError { .. } => None,
Error::ExternalError(ref err) => err.source(),
Error::WithContext { ref cause, .. } => match cause.as_ref() {
Error::ExternalError(err) => err.source(),
_ => None,
},
_ => None,
}
}
}
impl Error {
/// Creates a new `RuntimeError` with the given message.
#[inline]
pub fn runtime<S: fmt::Display>(message: S) -> Self {
Error::RuntimeError(message.to_string())
}
/// Wraps an external error object.
#[inline]
pub fn external<T: Into<Box<dyn StdError + Send + Sync>>>(err: T) -> Self {
pub fn external<T: Into<Box<dyn StdError + Send + Sync>>>(err: T) -> Error {
Error::ExternalError(err.into().into())
}
/// Attempts to downcast the external error object to a concrete type by reference.
pub fn downcast_ref<T>(&self) -> Option<&T>
where
T: StdError + 'static,
{
match self {
Error::ExternalError(err) => err.downcast_ref(),
Error::WithContext { cause, .. } => match cause.as_ref() {
Error::ExternalError(err) => err.downcast_ref(),
_ => None,
},
_ => None,
}
}
pub(crate) fn bad_self_argument(to: &str, cause: Error) -> Self {
Error::BadArgument {
to: Some(to.to_string()),
pos: 1,
name: Some("self".to_string()),
cause: Arc::new(cause),
}
}
pub(crate) fn from_lua_conversion<'a>(
from: &'static str,
to: &'static str,
message: impl Into<Option<&'a str>>,
) -> Self {
Error::FromLuaConversionError {
from,
to,
message: message.into().map(|s| s.into()),
}
}
}
/// Trait for converting [`std::error::Error`] into Lua [`Error`].
pub trait ExternalError {
fn into_lua_err(self) -> Error;
fn to_lua_err(self) -> Error;
}
impl<E: Into<Box<dyn StdError + Send + Sync>>> ExternalError for E {
fn into_lua_err(self) -> Error {
fn to_lua_err(self) -> Error {
Error::external(self)
}
}
/// Trait for converting [`std::result::Result`] into Lua [`Result`].
pub trait ExternalResult<T> {
fn into_lua_err(self) -> Result<T>;
fn to_lua_err(self) -> Result<T>;
}
impl<T, E> ExternalResult<T> for StdResult<T, E>
where
E: ExternalError,
{
fn into_lua_err(self) -> Result<T> {
self.map_err(|e| e.into_lua_err())
fn to_lua_err(self) -> Result<T> {
self.map_err(|e| e.to_lua_err())
}
}
/// Provides the `context` method for [`Error`] and `Result<T, Error>`.
pub trait ErrorContext: Sealed {
/// Wraps the error value with additional context.
fn context<C: fmt::Display>(self, context: C) -> Self;
/// Wrap the error value with additional context that is evaluated lazily
/// only once an error does occur.
fn with_context<C: fmt::Display>(self, f: impl FnOnce(&Error) -> C) -> Self;
}
impl ErrorContext for Error {
fn context<C: fmt::Display>(self, context: C) -> Self {
let context = context.to_string();
match self {
Error::WithContext { cause, .. } => Error::WithContext { context, cause },
_ => Error::WithContext {
context,
cause: Arc::new(self),
},
}
}
fn with_context<C: fmt::Display>(self, f: impl FnOnce(&Error) -> C) -> Self {
let context = f(&self).to_string();
match self {
Error::WithContext { cause, .. } => Error::WithContext { context, cause },
_ => Error::WithContext {
context,
cause: Arc::new(self),
},
}
}
}
impl<T> ErrorContext for StdResult<T, Error> {
fn context<C: fmt::Display>(self, context: C) -> Self {
self.map_err(|err| err.context(context))
}
fn with_context<C: fmt::Display>(self, f: impl FnOnce(&Error) -> C) -> Self {
self.map_err(|err| err.with_context(f))
}
}
impl From<AddrParseError> for Error {
impl std::convert::From<AddrParseError> for Error {
fn from(err: AddrParseError) -> Self {
Error::external(err)
}
}
impl From<IoError> for Error {
impl std::convert::From<IoError> for Error {
fn from(err: IoError) -> Self {
Error::external(err)
}
}
impl From<Utf8Error> for Error {
impl std::convert::From<Utf8Error> for Error {
fn from(err: Utf8Error) -> Self {
Error::external(err)
}
@@ -486,10 +486,10 @@ pub unsafe fn luaL_traceback(
lua_concat(L, lua_gettop(L) - top);
}
pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize) -> *const c_char {
idx = lua_absindex(L, idx);
pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char {
if luaL_callmeta(L, idx, cstr!("__tostring")) == 0 {
match lua_type(L, idx) {
let t = lua_type(L, idx);
match t {
LUA_TNIL => {
lua_pushliteral(L, "nil");
}
@@ -503,7 +503,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize)
lua_pushliteral(L, "true");
}
}
t => {
_ => {
let tt = luaL_getmetafield(L, idx, cstr!("__name"));
let name = if tt == LUA_TSTRING {
lua_tostring(L, -1)
@@ -512,7 +512,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize)
};
lua_pushfstring(L, cstr!("%s: %p"), name, lua_topointer(L, idx));
if tt != LUA_TNIL {
lua_replace(L, -2); // remove '__name'
lua_replace(L, -2);
}
}
};
@@ -14,8 +14,7 @@ pub struct luaL_Reg {
pub func: lua_CFunction,
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn luaL_register(L: *mut lua_State, libname: *const c_char, l: *const luaL_Reg);
#[link_name = "luaL_getmetafield"]
pub fn luaL_getmetafield_(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
@@ -57,8 +56,7 @@ extern "C-unwind" {
pub const LUA_NOREF: c_int = -2;
pub const LUA_REFNIL: c_int = -1;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
@@ -73,23 +73,23 @@ pub type lua_Integer = i32;
pub type lua_Integer = i64;
/// Type for native C functions that can be passed to Lua.
pub type lua_CFunction = unsafe extern "C-unwind" fn(L: *mut lua_State) -> c_int;
pub type lua_CFunction = unsafe extern "C" fn(L: *mut lua_State) -> c_int;
// Type for functions that read/write blocks when loading/dumping Lua chunks
#[rustfmt::skip]
pub type lua_Reader =
unsafe extern "C-unwind" fn(L: *mut lua_State, ud: *mut c_void, sz: *mut usize) -> *const c_char;
#[rustfmt::skip]
unsafe extern "C" fn(L: *mut lua_State, ud: *mut c_void, sz: *mut usize) -> *const c_char;
pub type lua_Writer =
unsafe extern "C-unwind" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
unsafe extern "C" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
/// Type for memory-allocation functions
#[rustfmt::skip]
pub type lua_Alloc =
unsafe extern "C-unwind" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
pub type lua_Alloc = unsafe extern "C" fn(
ud: *mut c_void,
ptr: *mut c_void,
osize: usize,
nsize: usize,
) -> *mut c_void;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
//
// State manipulation
//
@@ -97,6 +97,9 @@ extern "C-unwind" {
pub fn lua_close(L: *mut lua_State);
pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State;
#[cfg(all(feature = "luajit", feature = "vendored"))]
pub fn lua_resetthread(L: *mut lua_State, th: *mut lua_State);
pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction;
//
@@ -218,16 +221,14 @@ pub const LUA_GCSTEP: c_int = 5;
pub const LUA_GCSETPAUSE: c_int = 6;
pub const LUA_GCSETSTEPMUL: c_int = 7;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn lua_gc(L: *mut lua_State, what: c_int, data: c_int) -> c_int;
}
//
// Miscellaneous functions
//
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn lua_error(L: *mut lua_State) -> !;
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_concat(L: *mut lua_State, n: c_int);
@@ -350,10 +351,9 @@ pub const LUA_MASKLINE: c_int = 1 << (LUA_HOOKLINE as usize);
pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize);
/// Type for functions to be called on debug events.
pub type lua_Hook = unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug);
pub type lua_Hook = unsafe extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug);
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn lua_getstack(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int;
pub fn lua_getinfo(L: *mut lua_State, what: *const c_char, ar: *mut lua_Debug) -> c_int;
pub fn lua_getlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
@@ -20,8 +20,7 @@ pub const LUA_JITLIBNAME: &str = "jit";
#[cfg(feature = "luajit")]
pub const LUA_FFILIBNAME: &str = "ffi";
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn luaopen_base(L: *mut lua_State) -> c_int;
pub fn luaopen_table(L: *mut lua_State) -> c_int;
pub fn luaopen_io(L: *mut lua_State) -> c_int;
@@ -205,10 +205,10 @@ pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_in
}
}
pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize) -> *const c_char {
idx = lua_absindex(L, idx);
pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char {
if luaL_callmeta(L, idx, cstr!("__tostring")) == 0 {
match lua_type(L, idx) {
let t = lua_type(L, idx);
match t {
LUA_TNIL => {
lua_pushliteral(L, "nil");
}
@@ -222,7 +222,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize)
lua_pushliteral(L, "true");
}
}
t => {
_ => {
let tt = luaL_getmetafield(L, idx, cstr!("__name"));
let name = if tt == LUA_TSTRING {
lua_tostring(L, -1)
@@ -231,7 +231,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize)
};
lua_pushfstring(L, cstr!("%s: %p"), name, lua_topointer(L, idx));
if tt != LUA_TNIL {
lua_replace(L, -2); // remove '__name'
lua_replace(L, -2);
}
}
};
@@ -14,8 +14,7 @@ pub struct luaL_Reg {
pub func: lua_CFunction,
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number);
#[link_name = "luaL_getmetafield"]
@@ -66,8 +65,7 @@ extern "C-unwind" {
pub const LUA_NOREF: c_int = -2;
pub const LUA_REFNIL: c_int = -1;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
@@ -80,8 +78,7 @@ pub unsafe fn luaL_loadfile(L: *mut lua_State, f: *const c_char) -> c_int {
luaL_loadfilex(L, f, ptr::null())
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn luaL_loadbufferx(
L: *mut lua_State,
buff: *const c_char,
@@ -78,23 +78,23 @@ pub type lua_Integer = i64;
pub type lua_Unsigned = c_uint;
/// Type for native C functions that can be passed to Lua
pub type lua_CFunction = unsafe extern "C-unwind" fn(L: *mut lua_State) -> c_int;
pub type lua_CFunction = unsafe extern "C" fn(L: *mut lua_State) -> c_int;
// Type for functions that read/write blocks when loading/dumping Lua chunks
#[rustfmt::skip]
pub type lua_Reader =
unsafe extern "C-unwind" fn(L: *mut lua_State, ud: *mut c_void, sz: *mut usize) -> *const c_char;
#[rustfmt::skip]
unsafe extern "C" fn(L: *mut lua_State, ud: *mut c_void, sz: *mut usize) -> *const c_char;
pub type lua_Writer =
unsafe extern "C-unwind" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
unsafe extern "C" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
/// Type for memory-allocation functions
#[rustfmt::skip]
pub type lua_Alloc =
unsafe extern "C-unwind" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
pub type lua_Alloc = unsafe extern "C" fn(
ud: *mut c_void,
ptr: *mut c_void,
osize: usize,
nsize: usize,
) -> *mut c_void;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
//
// State manipulation
//
@@ -155,19 +155,20 @@ pub const LUA_OPMOD: c_int = 4;
pub const LUA_OPPOW: c_int = 5;
pub const LUA_OPUNM: c_int = 6;
extern "C" {
pub fn lua_arith(L: *mut lua_State, op: c_int);
}
pub const LUA_OPEQ: c_int = 0;
pub const LUA_OPLT: c_int = 1;
pub const LUA_OPLE: c_int = 2;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn lua_arith(L: *mut lua_State, op: c_int);
extern "C" {
pub fn lua_rawequal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
pub fn lua_compare(L: *mut lua_State, idx1: c_int, idx2: c_int, op: c_int) -> c_int;
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
//
// Push functions (C -> stack)
//
@@ -261,8 +262,7 @@ pub unsafe fn lua_pcall(L: *mut lua_State, n: c_int, r: c_int, f: c_int) -> c_in
lua_pcallk(L, n, r, f, 0, None)
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
//
// Coroutine functions
//
@@ -298,13 +298,11 @@ pub const LUA_GCISRUNNING: c_int = 9;
pub const LUA_GCGEN: c_int = 10;
pub const LUA_GCINC: c_int = 11;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn lua_gc(L: *mut lua_State, what: c_int, data: c_int) -> c_int;
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
//
// Miscellaneous functions
//
@@ -439,10 +437,9 @@ pub const LUA_MASKLINE: c_int = 1 << (LUA_HOOKLINE as usize);
pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize);
/// Type for functions to be called on debug events.
pub type lua_Hook = unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug);
pub type lua_Hook = unsafe extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug);
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn lua_getstack(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int;
pub fn lua_getinfo(L: *mut lua_State, what: *const c_char, ar: *mut lua_Debug) -> c_int;
pub fn lua_getlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
@@ -14,8 +14,7 @@ pub const LUA_MATHLIBNAME: &str = "math";
pub const LUA_DBLIBNAME: &str = "debug";
pub const LUA_LOADLIBNAME: &str = "package";
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn luaopen_base(L: *mut lua_State) -> c_int;
pub fn luaopen_coroutine(L: *mut lua_State) -> c_int;
pub fn luaopen_table(L: *mut lua_State) -> c_int;
@@ -1,4 +1,4 @@
//! MLua compatibility layer for Lua 5.3
//! MLua compatibility layer for Lua 5.2
use std::os::raw::c_int;
@@ -20,14 +20,12 @@ pub struct luaL_Reg {
pub func: lua_CFunction,
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number, sz: usize);
pub fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
pub fn luaL_callmeta(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
#[link_name = "luaL_tolstring"]
pub fn luaL_tolstring_(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
pub fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
pub fn luaL_argerror(L: *mut lua_State, arg: c_int, extramsg: *const c_char) -> c_int;
pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char;
pub fn luaL_optlstring(
@@ -68,8 +66,7 @@ extern "C-unwind" {
pub const LUA_NOREF: c_int = -2;
pub const LUA_REFNIL: c_int = -1;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
@@ -82,8 +79,7 @@ pub unsafe fn luaL_loadfile(L: *mut lua_State, f: *const c_char) -> c_int {
luaL_loadfilex(L, f, ptr::null())
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn luaL_loadbufferx(
L: *mut lua_State,
buff: *const c_char,
@@ -171,11 +167,6 @@ pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) {
lua::lua_getfield(L, lua::LUA_REGISTRYINDEX, n);
}
#[inline(always)]
pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char {
luaL_tolstring_(L, lua::lua_absindex(L, idx), len)
}
// luaL_opt would be implemented here but it is undocumented, so it's omitted
#[inline(always)]
@@ -82,27 +82,27 @@ pub type lua_Unsigned = u64;
pub type lua_KContext = isize;
/// Type for native C functions that can be passed to Lua
pub type lua_CFunction = unsafe extern "C-unwind" fn(L: *mut lua_State) -> c_int;
pub type lua_CFunction = unsafe extern "C" fn(L: *mut lua_State) -> c_int;
/// Type for continuation functions
pub type lua_KFunction =
unsafe extern "C-unwind" fn(L: *mut lua_State, status: c_int, ctx: lua_KContext) -> c_int;
unsafe extern "C" fn(L: *mut lua_State, status: c_int, ctx: lua_KContext) -> c_int;
// Type for functions that read/write blocks when loading/dumping Lua chunks
#[rustfmt::skip]
pub type lua_Reader =
unsafe extern "C-unwind" fn(L: *mut lua_State, ud: *mut c_void, sz: *mut usize) -> *const c_char;
#[rustfmt::skip]
unsafe extern "C" fn(L: *mut lua_State, ud: *mut c_void, sz: *mut usize) -> *const c_char;
pub type lua_Writer =
unsafe extern "C-unwind" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
unsafe extern "C" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
/// Type for memory-allocation functions
#[rustfmt::skip]
pub type lua_Alloc =
unsafe extern "C-unwind" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
pub type lua_Alloc = unsafe extern "C" fn(
ud: *mut c_void,
ptr: *mut c_void,
osize: usize,
nsize: usize,
) -> *mut c_void;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
//
// State manipulation
//
@@ -167,19 +167,20 @@ pub const LUA_OPSHR: c_int = 11;
pub const LUA_OPUNM: c_int = 12;
pub const LUA_OPBNOT: c_int = 13;
extern "C" {
pub fn lua_arith(L: *mut lua_State, op: c_int);
}
pub const LUA_OPEQ: c_int = 0;
pub const LUA_OPLT: c_int = 1;
pub const LUA_OPLE: c_int = 2;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn lua_arith(L: *mut lua_State, op: c_int);
extern "C" {
pub fn lua_rawequal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
pub fn lua_compare(L: *mut lua_State, idx1: c_int, idx2: c_int, op: c_int) -> c_int;
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
//
// Push functions (C -> stack)
//
@@ -269,8 +270,7 @@ pub unsafe fn lua_pcall(L: *mut lua_State, n: c_int, r: c_int, f: c_int) -> c_in
lua_pcallk(L, n, r, f, 0, None)
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
//
// Coroutine functions
//
@@ -304,13 +304,11 @@ pub const LUA_GCSETPAUSE: c_int = 6;
pub const LUA_GCSETSTEPMUL: c_int = 7;
pub const LUA_GCISRUNNING: c_int = 9;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn lua_gc(L: *mut lua_State, what: c_int, data: c_int) -> c_int;
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
//
// Miscellaneous functions
//
@@ -463,10 +461,9 @@ pub const LUA_MASKLINE: c_int = 1 << (LUA_HOOKLINE as usize);
pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize);
/// Type for functions to be called on debug events.
pub type lua_Hook = unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug);
pub type lua_Hook = unsafe extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug);
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn lua_getstack(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int;
pub fn lua_getinfo(L: *mut lua_State, what: *const c_char, ar: *mut lua_Debug) -> c_int;
pub fn lua_getlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
@@ -15,8 +15,7 @@ pub const LUA_MATHLIBNAME: &str = "math";
pub const LUA_DBLIBNAME: &str = "debug";
pub const LUA_LOADLIBNAME: &str = "package";
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn luaopen_base(L: *mut lua_State) -> c_int;
pub fn luaopen_coroutine(L: *mut lua_State) -> c_int;
pub fn luaopen_table(L: *mut lua_State) -> c_int;
@@ -20,8 +20,7 @@ pub struct luaL_Reg {
pub func: lua_CFunction,
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number, sz: usize);
pub fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
@@ -67,8 +66,7 @@ extern "C-unwind" {
pub const LUA_NOREF: c_int = -2;
pub const LUA_REFNIL: c_int = -1;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
@@ -81,8 +79,7 @@ pub unsafe fn luaL_loadfile(L: *mut lua_State, f: *const c_char) -> c_int {
luaL_loadfilex(L, f, ptr::null())
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn luaL_loadbufferx(
L: *mut lua_State,
buff: *const c_char,
@@ -81,31 +81,31 @@ pub type lua_Unsigned = u64;
pub type lua_KContext = isize;
/// Type for native C functions that can be passed to Lua
pub type lua_CFunction = unsafe extern "C-unwind" fn(L: *mut lua_State) -> c_int;
pub type lua_CFunction = unsafe extern "C" fn(L: *mut lua_State) -> c_int;
/// Type for continuation functions
pub type lua_KFunction =
unsafe extern "C-unwind" fn(L: *mut lua_State, status: c_int, ctx: lua_KContext) -> c_int;
unsafe extern "C" fn(L: *mut lua_State, status: c_int, ctx: lua_KContext) -> c_int;
// Type for functions that read/write blocks when loading/dumping Lua chunks
#[rustfmt::skip]
pub type lua_Reader =
unsafe extern "C-unwind" fn(L: *mut lua_State, ud: *mut c_void, sz: *mut usize) -> *const c_char;
#[rustfmt::skip]
unsafe extern "C" fn(L: *mut lua_State, ud: *mut c_void, sz: *mut usize) -> *const c_char;
pub type lua_Writer =
unsafe extern "C-unwind" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
unsafe extern "C" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
/// Type for memory-allocation functions
#[rustfmt::skip]
pub type lua_Alloc =
unsafe extern "C-unwind" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
pub type lua_Alloc = unsafe extern "C" fn(
ud: *mut c_void,
ptr: *mut c_void,
osize: usize,
nsize: usize,
) -> *mut c_void;
/// Type for warning functions
pub type lua_WarnFunction =
unsafe extern "C-unwind" fn(ud: *mut c_void, msg: *const c_char, tocont: c_int);
unsafe extern "C" fn(ud: *mut c_void, msg: *const c_char, tocont: c_int);
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
//
// State manipulation
//
@@ -174,19 +174,20 @@ pub const LUA_OPSHR: c_int = 11;
pub const LUA_OPUNM: c_int = 12;
pub const LUA_OPBNOT: c_int = 13;
extern "C" {
pub fn lua_arith(L: *mut lua_State, op: c_int);
}
pub const LUA_OPEQ: c_int = 0;
pub const LUA_OPLT: c_int = 1;
pub const LUA_OPLE: c_int = 2;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn lua_arith(L: *mut lua_State, op: c_int);
extern "C" {
pub fn lua_rawequal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
pub fn lua_compare(L: *mut lua_State, idx1: c_int, idx2: c_int, op: c_int) -> c_int;
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
//
// Push functions (C -> stack)
//
@@ -276,8 +277,7 @@ pub unsafe fn lua_pcall(L: *mut lua_State, n: c_int, r: c_int, f: c_int) -> c_in
lua_pcallk(L, n, r, f, 0, None)
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
//
// Coroutine functions
//
@@ -305,8 +305,7 @@ pub unsafe fn lua_yield(L: *mut lua_State, n: c_int) -> c_int {
//
// Warning-related functions
//
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn lua_setwarnf(L: *mut lua_State, f: Option<lua_WarnFunction>, ud: *mut c_void);
pub fn lua_warning(L: *mut lua_State, msg: *const c_char, tocont: c_int);
}
@@ -326,13 +325,11 @@ pub const LUA_GCISRUNNING: c_int = 9;
pub const LUA_GCGEN: c_int = 10;
pub const LUA_GCINC: c_int = 11;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn lua_gc(L: *mut lua_State, what: c_int, ...) -> c_int;
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
//
// Miscellaneous functions
//
@@ -503,10 +500,9 @@ pub const LUA_MASKLINE: c_int = 1 << (LUA_HOOKLINE as usize);
pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize);
/// Type for functions to be called on debug events.
pub type lua_Hook = unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug);
pub type lua_Hook = unsafe extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug);
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn lua_getstack(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int;
pub fn lua_getinfo(L: *mut lua_State, what: *const c_char, ar: *mut lua_Debug) -> c_int;
pub fn lua_getlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
@@ -14,8 +14,7 @@ pub const LUA_MATHLIBNAME: &str = "math";
pub const LUA_DBLIBNAME: &str = "debug";
pub const LUA_LOADLIBNAME: &str = "package";
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
extern "C" {
pub fn luaopen_base(L: *mut lua_State) -> c_int;
pub fn luaopen_coroutine(L: *mut lua_State) -> c_int;
pub fn luaopen_table(L: *mut lua_State) -> c_int;
@@ -323,7 +323,7 @@ pub unsafe fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char)
pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_int {
if luaL_newmetatable_(L, tname) != 0 {
lua_pushstring(L, tname);
lua_setfield(L, -2, cstr!("__type"));
lua_setfield(L, -2, cstr!("__name"));
1
} else {
0
@@ -341,7 +341,7 @@ pub unsafe fn luaL_loadbufferx(
fn free(p: *mut c_void);
}
let chunk_is_text = size == 0 || (*data as u8) >= b'\t';
let chunk_is_text = size == 0 || (*data as u8) >= b'\n';
if !mode.is_null() {
let modeb = CStr::from_ptr(mode).to_bytes();
if !chunk_is_text && !modeb.contains(&b'b') {
@@ -436,10 +436,10 @@ pub unsafe fn luaL_traceback(
lua_concat(L, lua_gettop(L) - top);
}
pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize) -> *const c_char {
idx = lua_absindex(L, idx);
pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char {
if luaL_callmeta(L, idx, cstr!("__tostring")) == 0 {
match lua_type(L, idx) {
let t = lua_type(L, idx);
match t {
LUA_TNIL => {
lua_pushliteral(L, "nil");
}
@@ -453,8 +453,8 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize)
lua_pushliteral(L, "true");
}
}
t => {
let tt = luaL_getmetafield(L, idx, cstr!("__type"));
_ => {
let tt = luaL_getmetafield(L, idx, cstr!("__name"));
let name = if tt == LUA_TSTRING {
lua_tostring(L, -1)
} else {
@@ -462,7 +462,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize)
};
lua_pushfstring(L, cstr!("%s: %p"), name, lua_topointer(L, idx));
if tt != LUA_TNIL {
lua_replace(L, -2); // remove '__type'
lua_replace(L, -2);
}
}
};
@@ -13,7 +13,7 @@ pub struct luaL_Reg {
pub func: lua_CFunction,
}
extern "C-unwind" {
extern "C" {
pub fn luaL_register(L: *mut lua_State, libname: *const c_char, l: *const luaL_Reg);
#[link_name = "luaL_getmetafield"]
pub fn luaL_getmetafield_(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
@@ -69,27 +69,21 @@ pub type lua_Integer = c_int;
pub type lua_Unsigned = c_uint;
/// Type for native C functions that can be passed to Lua.
pub type lua_CFunction = unsafe extern "C-unwind" fn(L: *mut lua_State) -> c_int;
pub type lua_Continuation = unsafe extern "C-unwind" fn(L: *mut lua_State, status: c_int) -> c_int;
pub type lua_CFunction = unsafe extern "C" fn(L: *mut lua_State) -> c_int;
pub type lua_Continuation = unsafe extern "C" fn(L: *mut lua_State, status: c_int) -> c_int;
/// Type for userdata destructor functions.
pub type lua_Udestructor = unsafe extern "C-unwind" fn(*mut c_void);
pub type lua_Destructor = unsafe extern "C-unwind" fn(L: *mut lua_State, *mut c_void);
pub type lua_Udestructor = unsafe extern "C" fn(*mut c_void);
/// Type for memory-allocation functions.
pub type lua_Alloc = unsafe extern "C-unwind" fn(
pub type lua_Alloc = unsafe extern "C" fn(
ud: *mut c_void,
ptr: *mut c_void,
osize: usize,
nsize: usize,
) -> *mut c_void;
/// Returns Luau release version (eg. `0.xxx`).
pub const fn luau_version() -> Option<&'static str> {
option_env!("LUAU_VERSION")
}
extern "C-unwind" {
extern "C" {
//
// State manipulation
//
@@ -156,10 +150,7 @@ extern "C-unwind" {
pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number);
pub fn lua_pushinteger(L: *mut lua_State, n: lua_Integer);
pub fn lua_pushunsigned(L: *mut lua_State, n: lua_Unsigned);
#[cfg(not(feature = "luau-vector4"))]
pub fn lua_pushvector(L: *mut lua_State, x: c_float, y: c_float, z: c_float);
#[cfg(feature = "luau-vector4")]
pub fn lua_pushvector(L: *mut lua_State, x: c_float, y: c_float, z: c_float, w: c_float);
#[link_name = "lua_pushlstring"]
pub fn lua_pushlstring_(L: *mut lua_State, s: *const c_char, l: usize);
#[link_name = "lua_pushstring"]
@@ -251,14 +242,14 @@ pub const LUA_GCSETGOAL: c_int = 7;
pub const LUA_GCSETSTEPMUL: c_int = 8;
pub const LUA_GCSETSTEPSIZE: c_int = 9;
extern "C-unwind" {
extern "C" {
pub fn lua_gc(L: *mut lua_State, what: c_int, data: c_int) -> c_int;
}
//
// Memory statistics
//
extern "C-unwind" {
extern "C" {
pub fn lua_setmemcat(L: *mut lua_State, category: c_int);
pub fn lua_totalbytes(L: *mut lua_State, category: c_int) -> usize;
}
@@ -266,7 +257,7 @@ extern "C-unwind" {
//
// Miscellaneous functions
//
extern "C-unwind" {
extern "C" {
pub fn lua_error(L: *mut lua_State) -> !;
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_rawiter(L: *mut lua_State, idx: c_int, iter: c_int) -> c_int;
@@ -274,8 +265,11 @@ extern "C-unwind" {
// TODO: lua_encodepointer
pub fn lua_clock() -> c_double;
pub fn lua_setuserdatatag(L: *mut lua_State, idx: c_int, tag: c_int);
pub fn lua_setuserdatadtor(L: *mut lua_State, tag: c_int, dtor: Option<lua_Destructor>);
pub fn lua_getuserdatadtor(L: *mut lua_State, tag: c_int) -> Option<lua_Destructor>;
pub fn lua_setuserdatadtor(
L: *mut lua_State,
tag: c_int,
dtor: Option<unsafe extern "C" fn(*mut lua_State, *mut c_void)>,
);
pub fn lua_clonefunction(L: *mut lua_State, idx: c_int);
pub fn lua_cleartable(L: *mut lua_State, idx: c_int);
}
@@ -286,7 +280,7 @@ extern "C-unwind" {
pub const LUA_NOREF: c_int = -1;
pub const LUA_REFNIL: c_int = 0;
extern "C-unwind" {
extern "C" {
pub fn lua_ref(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_unref(L: *mut lua_State, r#ref: c_int);
}
@@ -423,9 +417,9 @@ pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
const LUA_IDSIZE: usize = 256;
/// Type for functions to be called on debug events.
pub type lua_Hook = unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug);
pub type lua_Hook = unsafe extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug);
pub type lua_Coverage = unsafe extern "C-unwind" fn(
pub type lua_Coverage = unsafe extern "C" fn(
context: *mut c_void,
function: *const c_char,
linedefined: c_int,
@@ -434,7 +428,7 @@ pub type lua_Coverage = unsafe extern "C-unwind" fn(
size: usize,
);
extern "C-unwind" {
extern "C" {
pub fn lua_stackdepth(L: *mut lua_State) -> c_int;
pub fn lua_getinfo(
L: *mut lua_State,
@@ -492,23 +486,23 @@ pub struct lua_Callbacks {
pub userdata: *mut c_void,
/// gets called at safepoints (loop back edges, call/ret, gc) if set
pub interrupt: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, gc: c_int)>,
pub interrupt: Option<unsafe extern "C" fn(L: *mut lua_State, gc: c_int)>,
/// gets called when an unprotected error is raised (if longjmp is used)
pub panic: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, errcode: c_int)>,
pub panic: Option<unsafe extern "C" fn(L: *mut lua_State, errcode: c_int)>,
/// gets called when L is created (LP == parent) or destroyed (LP == NULL)
pub userthread: Option<unsafe extern "C-unwind" fn(LP: *mut lua_State, L: *mut lua_State)>,
pub userthread: Option<unsafe extern "C" fn(LP: *mut lua_State, L: *mut lua_State)>,
/// gets called when a string is created; returned atom can be retrieved via tostringatom
pub useratom: Option<unsafe extern "C-unwind" fn(s: *const c_char, l: usize) -> i16>,
pub useratom: Option<unsafe extern "C" fn(s: *const c_char, l: usize) -> i16>,
/// gets called when BREAK instruction is encountered
pub debugbreak: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug)>,
pub debugbreak: Option<unsafe extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug)>,
/// gets called after each instruction in single step mode
pub debugstep: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug)>,
pub debugstep: Option<unsafe extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug)>,
/// gets called when thread execution is interrupted by break in another thread
pub debuginterrupt: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug)>,
pub debuginterrupt: Option<unsafe extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug)>,
/// gets called when protected call results in an error
pub debugprotectederror: Option<unsafe extern "C-unwind" fn(L: *mut lua_State)>,
pub debugprotectederror: Option<unsafe extern "C" fn(L: *mut lua_State)>,
}
extern "C" {
@@ -10,11 +10,10 @@ pub struct lua_CompileOptions {
pub coverageLevel: c_int,
pub vectorLib: *const c_char,
pub vectorCtor: *const c_char,
pub vectorType: *const c_char,
pub mutableGlobals: *const *const c_char,
pub mutableGlobals: *mut *const c_char,
}
extern "C-unwind" {
extern "C" {
#[link_name = "luau_compile"]
pub fn luau_compile_(
source: *const c_char,
@@ -22,9 +21,7 @@ extern "C-unwind" {
options: *mut lua_CompileOptions,
outsize: *mut usize,
) -> *mut c_char;
}
extern "C" {
fn free(p: *mut c_void);
}
@@ -13,7 +13,7 @@ pub const LUA_UTF8LIBNAME: &str = "utf8";
pub const LUA_MATHLIBNAME: &str = "math";
pub const LUA_DBLIBNAME: &str = "debug";
extern "C-unwind" {
extern "C" {
pub fn luaopen_base(L: *mut lua_State) -> c_int;
pub fn luaopen_coroutine(L: *mut lua_State) -> c_int;
pub fn luaopen_table(L: *mut lua_State) -> c_int;
@@ -4,12 +4,10 @@ pub use compat::*;
pub use lauxlib::*;
pub use lua::*;
pub use luacode::*;
pub use luacodegen::*;
pub use lualib::*;
pub mod compat;
pub mod lauxlib;
pub mod lua;
pub mod luacode;
pub mod luacodegen;
pub mod lualib;
+38 -35
View File
@@ -1,48 +1,44 @@
//! Low level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox Luau.
//! Low level bindings to Lua 5.4/5.3/5.2/5.1 including LuaJIT.
#![allow(non_camel_case_types, non_snake_case, dead_code)]
#![allow(clippy::missing_safety_doc)]
#![doc(test(attr(deny(warnings))))]
#![cfg_attr(docsrs, feature(doc_cfg))]
use std::os::raw::c_int;
#[cfg(any(feature = "lua54", doc))]
#[cfg(feature = "lua54")]
pub use lua54::*;
#[cfg(any(feature = "lua53", doc))]
#[cfg(feature = "lua53")]
pub use lua53::*;
#[cfg(any(feature = "lua52", doc))]
#[cfg(feature = "lua52")]
pub use lua52::*;
#[cfg(any(feature = "lua51", feature = "luajit", doc))]
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub use lua51::*;
#[cfg(any(feature = "luau", doc))]
#[cfg(feature = "luau")]
pub use luau::*;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[doc(hidden)]
pub const LUA_MAX_UPVALUES: c_int = 255;
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[doc(hidden)]
#[cfg(any(feature = "lua51", all(feature = "luajit", not(feature = "vendored"))))]
pub const LUA_MAX_UPVALUES: c_int = 60;
#[cfg(all(feature = "luajit", feature = "vendored"))]
pub const LUA_MAX_UPVALUES: c_int = 120;
#[cfg(feature = "luau")]
#[doc(hidden)]
pub const LUA_MAX_UPVALUES: c_int = 200;
// I believe `luaL_traceback` < 5.4 requires this much free stack to not error.
// 5.4 uses `luaL_Buffer`
#[doc(hidden)]
pub const LUA_TRACEBACK_STACK: c_int = 11;
// The minimum alignment guaranteed by the architecture. This value is used to
// add fast paths for low alignment values.
// Copied from https://github.com/rust-lang/rust/blob/master/library/std/src/sys/common/alloc.rs
#[cfg(any(
#[cfg(all(any(
target_arch = "x86",
target_arch = "arm",
target_arch = "mips",
@@ -54,10 +50,9 @@ pub const LUA_TRACEBACK_STACK: c_int = 11;
target_arch = "hexagon",
all(target_arch = "riscv32", not(target_os = "espidf")),
all(target_arch = "xtensa", not(target_os = "espidf")),
))]
#[doc(hidden)]
)))]
pub const SYS_MIN_ALIGN: usize = 8;
#[cfg(any(
#[cfg(all(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "mips64",
@@ -65,36 +60,44 @@ pub const SYS_MIN_ALIGN: usize = 8;
target_arch = "sparc64",
target_arch = "riscv64",
target_arch = "wasm64",
))]
#[doc(hidden)]
)))]
pub const SYS_MIN_ALIGN: usize = 16;
// The allocator on the esp-idf platform guarentees 4 byte alignment.
#[cfg(any(
#[cfg(all(any(
all(target_arch = "riscv32", target_os = "espidf"),
all(target_arch = "xtensa", target_os = "espidf"),
))]
#[doc(hidden)]
)))]
pub const SYS_MIN_ALIGN: usize = 4;
#[macro_use]
mod macros;
// Hack to avoid stripping a few unused Lua symbols that could be imported
// by C modules in unsafe mode
#[cfg(not(feature = "luau"))]
pub(crate) fn keep_lua_symbols() {
let mut symbols: Vec<*const extern "C" fn()> = Vec::new();
symbols.push(lua_atpanic as _);
symbols.push(lua_isuserdata as _);
symbols.push(lua_tocfunction as _);
symbols.push(luaL_loadstring as _);
symbols.push(luaL_openlibs as _);
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
{
symbols.push(lua_getglobal as _);
symbols.push(lua_setglobal as _);
symbols.push(luaL_setfuncs as _);
}
}
#[cfg(any(feature = "lua54", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
#[cfg(feature = "lua54")]
pub mod lua54;
#[cfg(any(feature = "lua53", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "lua53")))]
#[cfg(feature = "lua53")]
pub mod lua53;
#[cfg(any(feature = "lua52", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "lua52")))]
#[cfg(feature = "lua52")]
pub mod lua52;
#[cfg(any(feature = "lua51", feature = "luajit", doc))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua51", feature = "luajit"))))]
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub mod lua51;
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[cfg(feature = "luau")]
pub mod luau;
+84 -328
View File
@@ -1,74 +1,33 @@
use std::cell::RefCell;
use std::mem;
use std::os::raw::{c_int, c_void};
use std::ptr;
use std::slice;
use crate::error::{Error, Result};
use crate::lua::Lua;
use crate::memory::MemoryState;
use crate::table::Table;
use crate::types::{Callback, LuaRef, MaybeSend};
use crate::ffi;
use crate::types::LuaRef;
use crate::util::{
assert_stack, check_stack, error_traceback, linenumber_to_usize, pop_error, ptr_to_lossy_str,
ptr_to_str, StackGuard,
assert_stack, check_stack, error_traceback, pop_error, ptr_to_cstr_bytes, StackGuard,
};
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti, Value};
use crate::value::{FromLuaMulti, ToLuaMulti};
#[cfg(feature = "async")]
use {
crate::types::AsyncCallback,
futures_util::future::{self, Future},
};
use {futures_core::future::LocalBoxFuture, futures_util::future};
/// Handle to an internal Lua function.
#[derive(Clone, Debug)]
pub struct Function<'lua>(pub(crate) LuaRef<'lua>);
/// Owned handle to an internal Lua function.
///
/// The owned handle holds a *strong* reference to the current Lua instance.
/// Be warned, if you place it into a Lua type (eg. [`UserData`] or a Rust callback), it is *very easy*
/// to accidentally cause reference cycles that would prevent destroying Lua instance.
///
/// [`UserData`]: crate::UserData
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
#[derive(Clone, Debug)]
pub struct OwnedFunction(pub(crate) crate::types::LuaOwnedRef);
#[cfg(feature = "unstable")]
impl OwnedFunction {
/// Get borrowed handle to the underlying Lua function.
#[cfg_attr(feature = "send", allow(unused))]
pub const fn to_ref(&self) -> Function {
Function(self.0.to_ref())
}
}
/// Contains information about a function.
///
/// Please refer to the [`Lua Debug Interface`] for more information.
///
/// [`Lua Debug Interface`]: https://www.lua.org/manual/5.4/manual.html#4.7
#[derive(Clone, Debug)]
pub struct FunctionInfo {
/// A (reasonable) name of the function (`None` if the name cannot be found).
pub name: Option<String>,
/// Explains the `name` field (can be `global`/`local`/`method`/`field`/`upvalue`/etc).
///
/// Always `None` for Luau.
pub name_what: Option<&'static str>,
/// A string `Lua` if the function is a Lua function, `C` if it is a C function, `main` if it is the main part of a chunk.
pub what: &'static str,
/// Source of the chunk that created the function.
pub source: Option<String>,
/// A "printable" version of `source`, to be used in error messages.
pub short_src: Option<String>,
/// The line number where the definition of the function starts.
pub line_defined: Option<usize>,
/// The line number where the definition of the function ends (not set by Luau).
pub last_line_defined: Option<usize>,
pub name: Option<Vec<u8>>,
pub name_what: Option<Vec<u8>>,
pub what: Option<Vec<u8>>,
pub source: Option<Vec<u8>>,
pub short_src: Option<Vec<u8>>,
pub line_defined: i32,
#[cfg(not(feature = "luau"))]
pub last_line_defined: i32,
}
/// Luau function coverage snapshot.
@@ -76,7 +35,7 @@ pub struct FunctionInfo {
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CoverageInfo {
pub function: Option<String>,
pub function: Option<std::string::String>,
pub line_defined: i32,
pub depth: i32,
pub hits: Vec<i32>,
@@ -123,28 +82,36 @@ impl<'lua> Function<'lua> {
/// # Ok(())
/// # }
/// ```
pub fn call<A: IntoLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> Result<R> {
pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> Result<R> {
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 2)?;
// Push error handler
MemoryState::relax_limit_with(state, || ffi::lua_pushcfunction(state, error_traceback));
let stack_start = ffi::lua_gettop(state);
// Push function and the arguments
let mut args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int;
let results = unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, nargs + 3)?;
ffi::lua_pushcfunction(lua.state, error_traceback);
let stack_start = ffi::lua_gettop(lua.state);
lua.push_ref(&self.0);
let nargs = args.push_into_stack_multi(lua)?;
// Call the function
let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start);
if ret != ffi::LUA_OK {
return Err(pop_error(state, ret));
for arg in args.drain_all() {
lua.push_value(arg)?;
}
// Get the results
let nresults = ffi::lua_gettop(state) - stack_start;
R::from_stack_multi(nresults, lua)
}
let ret = ffi::lua_pcall(lua.state, nargs, ffi::LUA_MULTRET, stack_start);
if ret != ffi::LUA_OK {
return Err(pop_error(lua.state, ret));
}
let nresults = ffi::lua_gettop(lua.state) - stack_start;
let mut results = args; // Reuse MultiValue container
assert_stack(lua.state, 2);
for _ in 0..nresults {
results.push_front(lua.pop_value());
}
ffi::lua_pop(lua.state, 1);
results
};
R::from_lua_multi(results, lua)
}
/// Returns a future that, when polled, calls `self`, passing `args` as function arguments,
@@ -158,13 +125,14 @@ impl<'lua> Function<'lua> {
///
/// ```
/// use std::time::Duration;
/// use futures_timer::Delay;
/// # use mlua::{Lua, Result};
/// # #[tokio::main]
/// # async fn main() -> Result<()> {
/// # let lua = Lua::new();
///
/// let sleep = lua.create_async_function(move |_lua, n: u64| async move {
/// tokio::time::sleep(Duration::from_millis(n)).await;
/// Delay::new(Duration::from_millis(n)).await;
/// Ok(())
/// })?;
///
@@ -177,18 +145,21 @@ impl<'lua> Function<'lua> {
/// [`AsyncThread`]: crate::AsyncThread
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn call_async<A, R>(&self, args: A) -> impl Future<Output = Result<R>> + 'lua
pub fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua,
'lua: 'fut,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
{
let lua = self.0.lua;
let thread_res = lua.create_recycled_thread(self).map(|th| {
let mut th = th.into_async(args);
th.set_recyclable(true);
th
});
async move { thread_res?.await }
match lua.create_recycled_thread(self.clone()) {
Ok(t) => {
let mut t = t.into_async(args);
t.set_recyclable(true);
Box::pin(t)
}
Err(e) => Box::pin(future::err(e)),
}
}
/// Returns a function that, when called, calls `self`, passing `args` as the first set of
@@ -218,8 +189,8 @@ impl<'lua> Function<'lua> {
/// # Ok(())
/// # }
/// ```
pub fn bind<A: IntoLuaMulti<'lua>>(&self, args: A) -> Result<Function<'lua>> {
unsafe extern "C-unwind" fn args_wrapper_impl(state: *mut ffi::lua_State) -> c_int {
pub fn bind<A: ToLuaMulti<'lua>>(&self, args: A) -> Result<Function<'lua>> {
unsafe extern "C" fn args_wrapper_impl(state: *mut ffi::lua_State) -> c_int {
let nargs = ffi::lua_gettop(state);
let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(1)) as c_int;
ffi::luaL_checkstack(state, nbinds, ptr::null());
@@ -235,9 +206,8 @@ impl<'lua> Function<'lua> {
}
let lua = self.0.lua;
let state = lua.state();
let args = args.into_lua_multi(lua)?;
let args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int;
if nargs == 0 {
@@ -249,14 +219,14 @@ impl<'lua> Function<'lua> {
}
let args_wrapper = unsafe {
let _sg = StackGuard::new(state);
check_stack(state, nargs + 3)?;
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, nargs + 3)?;
ffi::lua_pushinteger(state, nargs as ffi::lua_Integer);
ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer);
for arg in args {
lua.push_value(arg)?;
}
protect_lua!(state, nargs + 1, 1, fn(state) {
protect_lua!(lua.state, nargs + 1, 1, fn(state) {
ffi::lua_pushcclosure(state, args_wrapper_impl, ffi::lua_gettop(state));
})?;
@@ -272,93 +242,10 @@ impl<'lua> Function<'lua> {
"#,
)
.try_cache()
.set_name("__mlua_bind")
.set_name("_mlua_bind")?
.call((self.clone(), args_wrapper))
}
/// Returns the environment of the Lua function.
///
/// By default Lua functions shares a global environment.
///
/// This function always returns `None` for Rust/C functions.
pub fn environment(&self) -> Option<Table> {
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 1);
lua.push_ref(&self.0);
if ffi::lua_iscfunction(state, -1) != 0 {
return None;
}
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
ffi::lua_getfenv(state, -1);
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
for i in 1..=255 {
// Traverse upvalues until we find the _ENV one
match ffi::lua_getupvalue(state, -1, i) {
s if s.is_null() => break,
s if std::ffi::CStr::from_ptr(s as _).to_bytes() == b"_ENV" => break,
_ => ffi::lua_pop(state, 1),
}
}
if ffi::lua_type(state, -1) != ffi::LUA_TTABLE {
return None;
}
Some(Table(lua.pop_ref()))
}
}
/// Sets the environment of the Lua function.
///
/// The environment is a table that is used as the global environment for the function.
/// Returns `true` if environment successfully changed, `false` otherwise.
///
/// This function does nothing for Rust/C functions.
pub fn set_environment(&self, env: Table) -> Result<bool> {
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 2)?;
lua.push_ref(&self.0);
if ffi::lua_iscfunction(state, -1) != 0 {
return Ok(false);
}
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
{
lua.push_ref(&env.0);
ffi::lua_setfenv(state, -2);
}
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
for i in 1..=255 {
match ffi::lua_getupvalue(state, -1, i) {
s if s.is_null() => return Ok(false),
s if std::ffi::CStr::from_ptr(s as _).to_bytes() == b"_ENV" => {
ffi::lua_pop(state, 1);
// Create an anonymous function with the new environment
let f_with_env = lua
.load("return _ENV")
.set_environment(env)
.try_cache()
.into_function()?;
lua.push_ref(&f_with_env.0);
ffi::lua_upvaluejoin(state, -2, i, -1, 1);
break;
}
_ => ffi::lua_pop(state, 1),
}
}
Ok(true)
}
}
/// Returns information about the function.
///
/// Corresponds to the `>Sn` what mask for [`lua_getinfo`] when applied to the function.
@@ -366,39 +253,33 @@ impl<'lua> Function<'lua> {
/// [`lua_getinfo`]: https://www.lua.org/manual/5.4/manual.html#lua_getinfo
pub fn info(&self) -> FunctionInfo {
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 1);
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 1);
let mut ar: ffi::lua_Debug = mem::zeroed();
lua.push_ref(&self.0);
#[cfg(not(feature = "luau"))]
let res = ffi::lua_getinfo(state, cstr!(">Sn"), &mut ar);
let res = ffi::lua_getinfo(lua.state, cstr!(">Sn"), &mut ar);
#[cfg(feature = "luau")]
let res = ffi::lua_getinfo(state, -1, cstr!("sn"), &mut ar);
let res = ffi::lua_getinfo(lua.state, -1, cstr!("sn"), &mut ar);
mlua_assert!(res != 0, "lua_getinfo failed with `>Sn`");
FunctionInfo {
name: ptr_to_lossy_str(ar.name).map(|s| s.into_owned()),
name: ptr_to_cstr_bytes(ar.name).map(|s| s.to_vec()),
#[cfg(not(feature = "luau"))]
name_what: match ptr_to_str(ar.namewhat) {
Some("") => None,
val => val,
},
name_what: ptr_to_cstr_bytes(ar.namewhat).map(|s| s.to_vec()),
#[cfg(feature = "luau")]
name_what: None,
what: ptr_to_str(ar.what).unwrap_or("main"),
source: ptr_to_lossy_str(ar.source).map(|s| s.into_owned()),
what: ptr_to_cstr_bytes(ar.what).map(|s| s.to_vec()),
source: ptr_to_cstr_bytes(ar.source).map(|s| s.to_vec()),
#[cfg(not(feature = "luau"))]
short_src: ptr_to_lossy_str(ar.short_src.as_ptr()).map(|s| s.into_owned()),
short_src: ptr_to_cstr_bytes(ar.short_src.as_ptr()).map(|s| s.to_vec()),
#[cfg(feature = "luau")]
short_src: ptr_to_lossy_str(ar.short_src).map(|s| s.into_owned()),
line_defined: linenumber_to_usize(ar.linedefined),
short_src: ptr_to_cstr_bytes(ar.short_src).map(|s| s.to_vec()),
line_defined: ar.linedefined,
#[cfg(not(feature = "luau"))]
last_line_defined: linenumber_to_usize(ar.lastlinedefined),
#[cfg(feature = "luau")]
last_line_defined: None,
last_line_defined: ar.lastlinedefined,
}
}
}
@@ -414,7 +295,7 @@ impl<'lua> Function<'lua> {
#[cfg(not(feature = "luau"))]
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
pub fn dump(&self, strip: bool) -> Vec<u8> {
unsafe extern "C-unwind" fn writer(
unsafe extern "C" fn writer(
_state: *mut ffi::lua_State,
buf: *const c_void,
buf_len: usize,
@@ -427,16 +308,15 @@ impl<'lua> Function<'lua> {
}
let lua = self.0.lua;
let state = lua.state();
let mut data: Vec<u8> = Vec::new();
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 1);
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;
ffi::lua_dump(state, writer, data_ptr, strip as i32);
ffi::lua_pop(state, 1);
ffi::lua_dump(lua.state, writer, data_ptr, strip as i32);
ffi::lua_pop(lua.state, 1);
}
data
@@ -452,7 +332,7 @@ impl<'lua> Function<'lua> {
/// Requires `feature = "luau"`
///
/// [`Compiler::set_coverage_level`]: crate::chunk::Compiler::set_coverage_level
#[cfg(any(feature = "luau", doc))]
#[cfg(any(feature = "luau", docsrs))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub fn coverage<F>(&self, mut func: F)
where
@@ -461,7 +341,7 @@ impl<'lua> Function<'lua> {
use std::ffi::CStr;
use std::os::raw::c_char;
unsafe extern "C-unwind" fn callback<F: FnMut(CoverageInfo)>(
unsafe extern "C" fn callback<F: FnMut(CoverageInfo)>(
data: *mut c_void,
function: *const c_char,
line_defined: c_int,
@@ -484,24 +364,15 @@ impl<'lua> Function<'lua> {
}
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 1);
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 1);
lua.push_ref(&self.0);
let func_ptr = &mut func as *mut F as *mut c_void;
ffi::lua_getcoverage(state, -1, func_ptr, callback::<F>);
ffi::lua_getcoverage(lua.state, -1, func_ptr, callback::<F>);
}
}
/// Convert this handle to owned version.
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
#[inline]
pub fn into_owned(self) -> OwnedFunction {
OwnedFunction(self.0.into_owned())
}
}
impl<'lua> PartialEq for Function<'lua> {
@@ -509,118 +380,3 @@ impl<'lua> PartialEq for Function<'lua> {
self.0 == other.0
}
}
// Additional shortcuts
#[cfg(feature = "unstable")]
impl OwnedFunction {
/// Calls the function, passing `args` as function arguments.
///
/// This is a shortcut for [`Function::call()`].
#[inline]
pub fn call<'lua, A, R>(&'lua self, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
self.to_ref().call(args)
}
/// Returns a future that, when polled, calls `self`, passing `args` as function arguments,
/// and drives the execution.
///
/// This is a shortcut for [`Function::call_async()`].
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
#[inline]
pub async fn call_async<'lua, A, R>(&'lua self, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua,
{
self.to_ref().call_async(args).await
}
}
pub(crate) struct WrappedFunction<'lua>(pub(crate) Callback<'lua, 'static>);
#[cfg(feature = "async")]
pub(crate) struct WrappedAsyncFunction<'lua>(pub(crate) AsyncCallback<'lua, 'static>);
impl<'lua> Function<'lua> {
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`] trait.
#[inline]
pub fn wrap<A, R, F>(func: F) -> impl IntoLua<'lua>
where
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
{
WrappedFunction(Box::new(move |lua, nargs| unsafe {
let args = A::from_stack_args(nargs, 1, None, lua)?;
func(lua, args)?.push_into_stack_multi(lua)
}))
}
/// Wraps a Rust mutable closure, returning an opaque type that implements [`IntoLua`] trait.
#[inline]
pub fn wrap_mut<A, R, F>(func: F) -> impl IntoLua<'lua>
where
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
{
let func = RefCell::new(func);
WrappedFunction(Box::new(move |lua, nargs| unsafe {
let mut func = func
.try_borrow_mut()
.map_err(|_| Error::RecursiveMutCallback)?;
let args = A::from_stack_args(nargs, 1, None, lua)?;
func(lua, args)?.push_into_stack_multi(lua)
}))
}
/// Wraps a Rust async function or closure, returning an opaque type that implements [`IntoLua`] trait.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn wrap_async<A, R, F, FR>(func: F) -> impl IntoLua<'lua>
where
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
FR: Future<Output = Result<R>> + 'lua,
{
WrappedAsyncFunction(Box::new(move |lua, args| unsafe {
let args = match A::from_lua_args(args, 1, None, lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
let fut = func(lua, args);
Box::pin(async move { fut.await?.push_into_stack_multi(lua) })
}))
}
}
impl<'lua> IntoLua<'lua> for WrappedFunction<'lua> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
lua.create_callback(self.0).map(Value::Function)
}
}
#[cfg(feature = "async")]
impl<'lua> IntoLua<'lua> for WrappedAsyncFunction<'lua> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
lua.create_async_callback(self.0).map(Value::Function)
}
}
#[cfg(test)]
mod assertions {
use super::*;
static_assertions::assert_not_impl_any!(Function: Send);
#[cfg(all(feature = "unstable", not(feature = "send")))]
static_assertions::assert_not_impl_any!(OwnedFunction: Send);
}
+56 -83
View File
@@ -1,13 +1,11 @@
use std::borrow::Cow;
use std::cell::UnsafeCell;
#[cfg(not(feature = "luau"))]
use std::ops::{BitOr, BitOrAssign};
use std::os::raw::c_int;
use ffi::lua_Debug;
use crate::ffi::{self, lua_Debug};
use crate::lua::Lua;
use crate::util::{linenumber_to_usize, ptr_to_lossy_str, ptr_to_str};
use crate::util::ptr_to_cstr_bytes;
/// Contains information about currently executing Lua code.
///
@@ -69,22 +67,19 @@ impl<'lua> Debug<'lua> {
unsafe {
#[cfg(not(feature = "luau"))]
mlua_assert!(
ffi::lua_getinfo(self.lua.state(), cstr!("n"), self.ar.get()) != 0,
ffi::lua_getinfo(self.lua.state, cstr!("n"), self.ar.get()) != 0,
"lua_getinfo failed with `n`"
);
#[cfg(feature = "luau")]
mlua_assert!(
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("n"), self.ar.get()) != 0,
ffi::lua_getinfo(self.lua.state, self.level, cstr!("n"), self.ar.get()) != 0,
"lua_getinfo failed with `n`"
);
DebugNames {
name: ptr_to_lossy_str((*self.ar.get()).name),
name: ptr_to_cstr_bytes((*self.ar.get()).name),
#[cfg(not(feature = "luau"))]
name_what: match ptr_to_str((*self.ar.get()).namewhat) {
Some("") => None,
val => val,
},
name_what: ptr_to_cstr_bytes((*self.ar.get()).namewhat),
#[cfg(feature = "luau")]
name_what: None,
}
@@ -96,27 +91,25 @@ impl<'lua> Debug<'lua> {
unsafe {
#[cfg(not(feature = "luau"))]
mlua_assert!(
ffi::lua_getinfo(self.lua.state(), cstr!("S"), self.ar.get()) != 0,
ffi::lua_getinfo(self.lua.state, cstr!("S"), self.ar.get()) != 0,
"lua_getinfo failed with `S`"
);
#[cfg(feature = "luau")]
mlua_assert!(
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("s"), self.ar.get()) != 0,
ffi::lua_getinfo(self.lua.state, self.level, cstr!("s"), self.ar.get()) != 0,
"lua_getinfo failed with `s`"
);
DebugSource {
source: ptr_to_lossy_str((*self.ar.get()).source),
source: ptr_to_cstr_bytes((*self.ar.get()).source),
#[cfg(not(feature = "luau"))]
short_src: ptr_to_lossy_str((*self.ar.get()).short_src.as_ptr()),
short_src: ptr_to_cstr_bytes((*self.ar.get()).short_src.as_ptr()),
#[cfg(feature = "luau")]
short_src: ptr_to_lossy_str((*self.ar.get()).short_src),
line_defined: linenumber_to_usize((*self.ar.get()).linedefined),
short_src: ptr_to_cstr_bytes((*self.ar.get()).short_src),
line_defined: (*self.ar.get()).linedefined,
#[cfg(not(feature = "luau"))]
last_line_defined: linenumber_to_usize((*self.ar.get()).lastlinedefined),
#[cfg(feature = "luau")]
last_line_defined: None,
what: ptr_to_str((*self.ar.get()).what).unwrap_or("main"),
last_line_defined: (*self.ar.get()).lastlinedefined,
what: ptr_to_cstr_bytes((*self.ar.get()).what),
}
}
}
@@ -126,12 +119,12 @@ impl<'lua> Debug<'lua> {
unsafe {
#[cfg(not(feature = "luau"))]
mlua_assert!(
ffi::lua_getinfo(self.lua.state(), cstr!("l"), self.ar.get()) != 0,
ffi::lua_getinfo(self.lua.state, cstr!("l"), self.ar.get()) != 0,
"lua_getinfo failed with `l`"
);
#[cfg(feature = "luau")]
mlua_assert!(
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("l"), self.ar.get()) != 0,
ffi::lua_getinfo(self.lua.state, self.level, cstr!("l"), self.ar.get()) != 0,
"lua_getinfo failed with `l`"
);
@@ -146,7 +139,7 @@ impl<'lua> Debug<'lua> {
pub fn is_tail_call(&self) -> bool {
unsafe {
mlua_assert!(
ffi::lua_getinfo(self.lua.state(), cstr!("t"), self.ar.get()) != 0,
ffi::lua_getinfo(self.lua.state, cstr!("t"), self.ar.get()) != 0,
"lua_getinfo failed with `t`"
);
(*self.ar.get()).currentline != 0
@@ -158,20 +151,20 @@ impl<'lua> Debug<'lua> {
unsafe {
#[cfg(not(feature = "luau"))]
mlua_assert!(
ffi::lua_getinfo(self.lua.state(), cstr!("u"), self.ar.get()) != 0,
ffi::lua_getinfo(self.lua.state, cstr!("u"), self.ar.get()) != 0,
"lua_getinfo failed with `u`"
);
#[cfg(feature = "luau")]
mlua_assert!(
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("a"), self.ar.get()) != 0,
ffi::lua_getinfo(self.lua.state, self.level, cstr!("a"), self.ar.get()) != 0,
"lua_getinfo failed with `a`"
);
#[cfg(not(feature = "luau"))]
let stack = DebugStack {
num_ups: (*self.ar.get()).nups as _,
num_ups: (*self.ar.get()).nups as i32,
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
num_params: (*self.ar.get()).nparams as _,
num_params: (*self.ar.get()).nparams as i32,
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
is_vararg: (*self.ar.get()).isvararg != 0,
};
@@ -216,26 +209,18 @@ pub enum DebugEvent {
#[derive(Clone, Debug)]
pub struct DebugNames<'a> {
/// A (reasonable) name of the function (`None` if the name cannot be found).
pub name: Option<Cow<'a, str>>,
/// Explains the `name` field (can be `global`/`local`/`method`/`field`/`upvalue`/etc).
///
/// Always `None` for Luau.
pub name_what: Option<&'static str>,
pub name: Option<&'a [u8]>,
pub name_what: Option<&'a [u8]>,
}
#[derive(Clone, Debug)]
pub struct DebugSource<'a> {
/// Source of the chunk that created the function.
pub source: Option<Cow<'a, str>>,
/// A "printable" version of `source`, to be used in error messages.
pub short_src: Option<Cow<'a, str>>,
/// The line number where the definition of the function starts.
pub line_defined: Option<usize>,
/// The line number where the definition of the function ends (not set by Luau).
pub last_line_defined: Option<usize>,
/// A string `Lua` if the function is a Lua function, `C` if it is a C function, `main` if it is the main part of a chunk.
pub what: &'static str,
pub source: Option<&'a [u8]>,
pub short_src: Option<&'a [u8]>,
pub line_defined: i32,
#[cfg(not(feature = "luau"))]
pub last_line_defined: i32,
pub what: Option<&'a [u8]>,
}
#[derive(Copy, Clone, Debug)]
@@ -281,59 +266,48 @@ pub struct HookTriggers {
#[cfg(not(feature = "luau"))]
impl HookTriggers {
/// An instance of `HookTriggers` with `on_calls` trigger set.
pub const ON_CALLS: Self = HookTriggers::new().on_calls();
/// An instance of `HookTriggers` with `on_returns` trigger set.
pub const ON_RETURNS: Self = HookTriggers::new().on_returns();
/// An instance of `HookTriggers` with `every_line` trigger set.
pub const EVERY_LINE: Self = HookTriggers::new().every_line();
/// Returns a new instance of `HookTriggers` with all triggers disabled.
pub const fn new() -> Self {
/// Returns a new instance of `HookTriggers` with [`on_calls`] trigger set.
///
/// [`on_calls`]: #structfield.on_calls
pub fn on_calls() -> Self {
HookTriggers {
on_calls: false,
on_returns: false,
every_line: false,
every_nth_instruction: None,
on_calls: true,
..Default::default()
}
}
/// Returns an instance of `HookTriggers` with [`on_calls`] trigger set.
///
/// [`on_calls`]: #structfield.on_calls
pub const fn on_calls(mut self) -> Self {
self.on_calls = true;
self
}
/// Returns an instance of `HookTriggers` with [`on_returns`] trigger set.
/// Returns a new instance of `HookTriggers` with [`on_returns`] trigger set.
///
/// [`on_returns`]: #structfield.on_returns
pub const fn on_returns(mut self) -> Self {
self.on_returns = true;
self
pub fn on_returns() -> Self {
HookTriggers {
on_returns: true,
..Default::default()
}
}
/// Returns an instance of `HookTriggers` with [`every_line`] trigger set.
/// Returns a new instance of `HookTriggers` with [`every_line`] trigger set.
///
/// [`every_line`]: #structfield.every_line
pub const fn every_line(mut self) -> Self {
self.every_line = true;
self
pub fn every_line() -> Self {
HookTriggers {
every_line: true,
..Default::default()
}
}
/// Returns an instance of `HookTriggers` with [`every_nth_instruction`] trigger set.
/// Returns a new instance of `HookTriggers` with [`every_nth_instruction`] trigger set.
///
/// [`every_nth_instruction`]: #structfield.every_nth_instruction
pub const fn every_nth_instruction(mut self, n: u32) -> Self {
self.every_nth_instruction = Some(n);
self
pub fn every_nth_instruction(n: u32) -> Self {
HookTriggers {
every_nth_instruction: Some(n),
..Default::default()
}
}
// Compute the mask to pass to `lua_sethook`.
pub(crate) const fn mask(&self) -> c_int {
pub(crate) fn mask(&self) -> c_int {
let mut mask: c_int = 0;
if self.on_calls {
mask |= ffi::LUA_MASKCALL
@@ -352,9 +326,8 @@ impl HookTriggers {
// Returns the `count` parameter to pass to `lua_sethook`, if applicable. Otherwise, zero is
// returned.
pub(crate) const fn count(&self) -> c_int {
let Some(n) = self.every_nth_instruction else { return 0 };
n as c_int
pub(crate) fn count(&self) -> c_int {
self.every_nth_instruction.unwrap_or(0) as c_int
}
}
+14 -74
View File
@@ -10,10 +10,10 @@
//!
//! # Converting data
//!
//! The [`IntoLua`] and [`FromLua`] traits allow conversion from Rust types to Lua values and vice
//! The [`ToLua`] and [`FromLua`] traits allow conversion from Rust types to Lua values and vice
//! versa. They are implemented for many data structures found in Rust's standard library.
//!
//! For more general conversions, the [`IntoLuaMulti`] and [`FromLuaMulti`] traits allow converting
//! For more general conversions, the [`ToLuaMulti`] and [`FromLuaMulti`] traits allow converting
//! between Rust types and *any number* of Lua values.
//!
//! Most code in `mlua` is generic over implementors of those traits, so in most places the normal
@@ -54,9 +54,9 @@
//! [executing]: crate::Chunk::exec
//! [evaluating]: crate::Chunk::eval
//! [globals]: crate::Lua::globals
//! [`IntoLua`]: crate::IntoLua
//! [`ToLua`]: crate::ToLua
//! [`FromLua`]: crate::FromLua
//! [`IntoLuaMulti`]: crate::IntoLuaMulti
//! [`ToLuaMulti`]: crate::ToLuaMulti
//! [`FromLuaMulti`]: crate::FromLuaMulti
//! [`Function`]: crate::Function
//! [`UserData`]: crate::UserData
@@ -82,12 +82,12 @@ mod macros;
mod chunk;
mod conversion;
mod error;
mod ffi;
mod function;
mod hook;
mod lua;
#[cfg(feature = "luau")]
mod luau;
mod memory;
mod multi;
mod scope;
mod stdlib;
@@ -96,17 +96,16 @@ mod table;
mod thread;
mod types;
mod userdata;
mod userdata_ext;
mod userdata_impl;
mod util;
mod value;
pub mod prelude;
pub use ffi::{self, lua_CFunction, lua_State};
pub use crate::{ffi::lua_CFunction, ffi::lua_State};
pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
pub use crate::error::{Error, ErrorContext, ExternalError, ExternalResult, Result};
pub use crate::error::{Error, ExternalError, ExternalResult, Result};
pub use crate::function::{Function, FunctionInfo};
pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack};
pub use crate::lua::{GCMode, Lua, LuaOptions};
@@ -116,25 +115,18 @@ pub use crate::stdlib::StdLib;
pub use crate::string::String;
pub use crate::table::{Table, TableExt, TablePairs, TableSequence};
pub use crate::thread::{Thread, ThreadStatus};
pub use crate::types::{AppDataRef, AppDataRefMut, Integer, LightUserData, Number, RegistryKey};
pub use crate::types::{Integer, LightUserData, Number, RegistryKey};
pub use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods,
UserDataRef, UserDataRefMut,
};
pub use crate::userdata_ext::AnyUserDataExt;
pub use crate::userdata_impl::UserDataRegistry;
pub use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil, Value};
pub use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
#[cfg(not(feature = "luau"))]
pub use crate::hook::HookTriggers;
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub use crate::{
chunk::Compiler,
function::CoverageInfo,
types::{Vector, VmState},
};
pub use crate::{chunk::Compiler, function::CoverageInfo, types::VmState};
#[cfg(feature = "async")]
pub use crate::thread::AsyncThread;
@@ -149,24 +141,17 @@ pub use crate::serde::{
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
pub mod serde;
#[cfg(feature = "mlua_derive")]
#[cfg(any(feature = "mlua_derive"))]
#[allow(unused_imports)]
#[macro_use]
extern crate mlua_derive;
// Unstable features
#[cfg(feature = "unstable")]
pub use crate::{
function::OwnedFunction, string::OwnedString, table::OwnedTable, thread::OwnedThread,
userdata::OwnedAnyUserData,
};
/// 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's Rust types needs to implement [`UserData`] or [`IntoLua`] traits.
/// User's Rust types needs to implement [`UserData`] or [`ToLua`] traits.
///
/// Captured variables are **moved** into the chunk.
///
@@ -212,19 +197,11 @@ pub use crate::{
///
/// [`AsChunk`]: crate::AsChunk
/// [`UserData`]: crate::UserData
/// [`IntoLua`]: crate::IntoLua
#[cfg(feature = "macros")]
/// [`ToLua`]: crate::ToLua
#[cfg(any(feature = "macros"))]
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
pub use mlua_derive::chunk;
/// Derive [`FromLua`] for a Rust type.
///
/// Current implementation generate code that takes [`UserData`] value, borrow it (of the Rust type)
/// and clone.
#[cfg(feature = "macros")]
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
pub use mlua_derive::FromLua;
/// Registers Lua module entrypoint.
///
/// You can register multiple entrypoints as required.
@@ -242,43 +219,6 @@ pub use mlua_derive::FromLua;
///
/// Internally in the code above the compiler defines C function `luaopen_my_module`.
///
/// You can also pass options to the attribute:
///
/// * name - name of the module, defaults to the name of the function
///
/// ```ignore
/// #[mlua::lua_module(name = "alt_module")]
/// fn my_module(lua: &Lua) -> Result<Table> {
/// ...
/// }
/// ```
///
/// * skip_memory_check - skip memory allocation checks for some operations.
///
/// In module mode, mlua runs in unknown environment and cannot say are there any memory
/// limits or not. As result, some operations that require memory allocation runs in
/// protected mode. Setting this attribute will improve performance of such operations
/// with risk of having uncaught exceptions and memory leaks.
///
/// ```ignore
/// #[mlua::lua_module(skip_memory_check)]
/// fn my_module(lua: &Lua) -> Result<Table> {
/// ...
/// }
/// ```
///
#[cfg(any(feature = "module", docsrs))]
#[cfg_attr(docsrs, doc(cfg(feature = "module")))]
pub use mlua_derive::lua_module;
pub(crate) mod private {
use super::*;
pub trait Sealed {}
impl Sealed for Error {}
impl<T> Sealed for std::result::Result<T, Error> {}
impl Sealed for Lua {}
impl Sealed for Table<'_> {}
impl Sealed for AnyUserData<'_> {}
}
+958 -1197
View File
File diff suppressed because it is too large Load Diff
+11 -18
View File
@@ -1,9 +1,9 @@
use std::ffi::CStr;
use std::os::raw::{c_float, c_int};
use std::string::String as StdString;
use crate::chunk::ChunkMode;
use crate::error::{Error, Result};
use crate::ffi;
use crate::lua::Lua;
use crate::table::Table;
use crate::util::{check_stack, StackGuard};
@@ -24,7 +24,7 @@ impl Lua {
// Set `_VERSION` global to include version number
// The environment variable `LUAU_VERSION` set by the build script
if let Some(version) = ffi::luau_version() {
if let Some(version) = option_env!("LUAU_VERSION") {
globals.raw_set("_VERSION", format!("Luau {version}"))?;
}
@@ -32,7 +32,7 @@ impl Lua {
}
}
unsafe extern "C-unwind" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_int {
unsafe extern "C" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_int {
let option = ffi::luaL_optstring(state, 1, cstr!("collect"));
let option = CStr::from_ptr(option);
let arg = ffi::luaL_optinteger(state, 2, 0);
@@ -69,15 +69,14 @@ unsafe extern "C-unwind" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_
}
}
fn lua_require(lua: &Lua, name: Option<StdString>) -> Result<Value> {
let name = name.ok_or_else(|| Error::runtime("invalid module name"))?;
fn lua_require(lua: &Lua, name: Option<std::string::String>) -> Result<Value> {
let name = name.ok_or_else(|| Error::RuntimeError("invalid module name".into()))?;
// Find module in the cache
let state = lua.state();
let loaded = unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 2)?;
protect_lua!(state, 0, 1, fn(state) {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 2)?;
protect_lua!(lua.state, 0, 1, fn(state) {
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("_LOADED"));
})?;
Table(lua.pop_ref())
@@ -101,11 +100,11 @@ fn lua_require(lua: &Lua, name: Option<StdString>) -> Result<Value> {
break;
}
}
let source = source.ok_or_else(|| Error::runtime(format!("cannot find '{name}'")))?;
let source = source.ok_or_else(|| Error::RuntimeError(format!("cannot find '{}'", name)))?;
let value = lua
.load(&source)
.set_name(&format!("={source_name}"))
.set_name(&format!("={}", source_name))?
.set_mode(ChunkMode::Text)
.call::<_, Value>(())?;
@@ -122,16 +121,10 @@ fn lua_require(lua: &Lua, name: Option<StdString>) -> Result<Value> {
}
// Luau vector datatype constructor
unsafe extern "C-unwind" fn lua_vector(state: *mut ffi::lua_State) -> c_int {
unsafe extern "C" fn lua_vector(state: *mut ffi::lua_State) -> c_int {
let x = ffi::luaL_checknumber(state, 1) as c_float;
let y = ffi::luaL_checknumber(state, 2) as c_float;
let z = ffi::luaL_checknumber(state, 3) as c_float;
#[cfg(feature = "luau-vector4")]
let w = ffi::luaL_checknumber(state, 4) as c_float;
#[cfg(not(feature = "luau-vector4"))]
ffi::lua_pushvector(state, x, y, z);
#[cfg(feature = "luau-vector4")]
ffi::lua_pushvector(state, x, y, z, w);
1
}
+2 -8
View File
@@ -101,15 +101,9 @@ macro_rules! protect_lua {
};
($state:expr, $nargs:expr, $nresults:expr, fn($state_inner:ident) $code:expr) => {{
use ::std::os::raw::c_int;
unsafe extern "C-unwind" fn do_call($state_inner: *mut ffi::lua_State) -> c_int {
unsafe extern "C" fn do_call($state_inner: *mut ffi::lua_State) -> ::std::os::raw::c_int {
$code;
let nresults = $nresults;
if nresults == ::ffi::LUA_MULTRET {
ffi::lua_gettop($state_inner)
} else {
nresults
}
$nresults
}
crate::util::protect_lua_call($state, $nargs, do_call)
-152
View File
@@ -1,152 +0,0 @@
use std::alloc::{self, Layout};
use std::os::raw::c_void;
use std::ptr;
#[cfg(feature = "luau")]
use crate::lua::ExtraData;
pub(crate) static ALLOCATOR: ffi::lua_Alloc = allocator;
#[derive(Default)]
pub(crate) struct MemoryState {
used_memory: isize,
memory_limit: isize,
// Can be set to temporary ignore the memory limit.
// This is used when calling `lua_pushcfunction` for lua5.1/jit/luau.
ignore_limit: bool,
// Indicates that the memory limit was reached on the last allocation.
#[cfg(feature = "luau")]
limit_reached: bool,
}
impl MemoryState {
#[inline]
pub(crate) fn used_memory(&self) -> usize {
self.used_memory as usize
}
#[inline]
pub(crate) fn memory_limit(&self) -> usize {
self.memory_limit as usize
}
#[inline]
pub(crate) fn set_memory_limit(&mut self, limit: usize) -> usize {
let prev_limit = self.memory_limit;
self.memory_limit = limit as isize;
prev_limit as usize
}
// This function is used primarily for calling `lua_pushcfunction` in lua5.1/jit
// to bypass the memory limit (if set).
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline]
pub(crate) unsafe fn relax_limit_with(state: *mut ffi::lua_State, f: impl FnOnce()) {
let mut mem_state: *mut c_void = ptr::null_mut();
if ffi::lua_getallocf(state, &mut mem_state) == ALLOCATOR {
(*(mem_state as *mut MemoryState)).ignore_limit = true;
f();
(*(mem_state as *mut MemoryState)).ignore_limit = false;
} else {
f();
}
}
// Same as the above but for Luau
// It does not have `lua_getallocf` function, so instead we use `lua_callbacks`
#[cfg(feature = "luau")]
#[inline]
pub(crate) unsafe fn relax_limit_with(state: *mut ffi::lua_State, f: impl FnOnce()) {
let extra = (*ffi::lua_callbacks(state)).userdata as *mut ExtraData;
if extra.is_null() {
return f();
}
let mem_state = (*extra).mem_state();
(*mem_state.as_ptr()).ignore_limit = true;
f();
(*mem_state.as_ptr()).ignore_limit = false;
}
// Does nothing apart from calling `f()`, we don't need to bypass any limits
#[cfg(any(feature = "lua52", feature = "lua53", feature = "lua54"))]
#[inline]
pub(crate) unsafe fn relax_limit_with(_state: *mut ffi::lua_State, f: impl FnOnce()) {
f();
}
// Returns `true` if the memory limit was reached on the last memory operation
#[cfg(feature = "luau")]
pub(crate) unsafe fn limit_reached(state: *mut ffi::lua_State) -> bool {
let extra = (*ffi::lua_callbacks(state)).userdata as *mut ExtraData;
if extra.is_null() {
return false;
}
(*(*extra).mem_state().as_ptr()).limit_reached
}
}
unsafe extern "C-unwind" fn allocator(
extra: *mut c_void,
ptr: *mut c_void,
osize: usize,
nsize: usize,
) -> *mut c_void {
let mem_state = &mut *(extra as *mut MemoryState);
#[cfg(feature = "luau")]
{
// Reset the flag
mem_state.limit_reached = false;
}
if nsize == 0 {
// Free memory
if !ptr.is_null() {
let layout = Layout::from_size_align_unchecked(osize, ffi::SYS_MIN_ALIGN);
alloc::dealloc(ptr as *mut u8, layout);
mem_state.used_memory -= osize as isize;
}
return ptr::null_mut();
}
// Do not allocate more than isize::MAX
if nsize > isize::MAX as usize {
return ptr::null_mut();
}
// Are we fit to the memory limits?
let mut mem_diff = nsize as isize;
if !ptr.is_null() {
mem_diff -= osize as isize;
}
let mem_limit = mem_state.memory_limit;
let new_used_memory = mem_state.used_memory + mem_diff;
if mem_limit > 0 && new_used_memory > mem_limit && !mem_state.ignore_limit {
#[cfg(feature = "luau")]
{
mem_state.limit_reached = true;
}
return ptr::null_mut();
}
mem_state.used_memory += mem_diff;
if ptr.is_null() {
// Allocate new memory
let new_layout = match Layout::from_size_align(nsize, ffi::SYS_MIN_ALIGN) {
Ok(layout) => layout,
Err(_) => return ptr::null_mut(),
};
let new_ptr = alloc::alloc(new_layout) as *mut c_void;
if new_ptr.is_null() {
alloc::handle_alloc_error(new_layout);
}
return new_ptr;
}
// Reallocate memory
let old_layout = Layout::from_size_align_unchecked(osize, ffi::SYS_MIN_ALIGN);
let new_ptr = alloc::realloc(ptr as *mut u8, old_layout, nsize) as *mut c_void;
if new_ptr.is_null() {
alloc::handle_alloc_error(old_layout);
}
new_ptr
}
+43 -145
View File
@@ -1,23 +1,23 @@
#![allow(clippy::wrong_self_convention)]
use std::iter::FromIterator;
use std::ops::{Deref, DerefMut};
use std::os::raw::c_int;
use std::result::Result as StdResult;
use crate::error::Result;
use crate::lua::Lua;
use crate::util::check_stack;
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil};
use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti};
/// Result is convertible to `MultiValue` following the common Lua idiom of returning the result
/// on success, or in the case of an error, returning `nil` and an error message.
impl<'lua, T: IntoLua<'lua>, E: IntoLua<'lua>> IntoLuaMulti<'lua> for StdResult<T, E> {
impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult<T, E> {
#[inline]
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let mut result = MultiValue::with_lua_and_capacity(lua, 2);
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let mut result = MultiValue::new_or_cached(lua);
match self {
Ok(v) => result.push_front(v.into_lua(lua)?),
Ok(v) => result.push_front(v.to_lua(lua)?),
Err(e) => {
result.push_front(e.into_lua(lua)?);
result.push_front(e.to_lua(lua)?);
result.push_front(Nil);
}
}
@@ -25,62 +25,27 @@ impl<'lua, T: IntoLua<'lua>, E: IntoLua<'lua>> IntoLuaMulti<'lua> for StdResult<
}
}
impl<'lua, T: IntoLua<'lua>> IntoLuaMulti<'lua> for T {
impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for T {
#[inline]
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let mut v = MultiValue::with_lua_and_capacity(lua, 1);
v.push_front(self.into_lua(lua)?);
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let mut v = MultiValue::new_or_cached(lua);
v.push_front(self.to_lua(lua)?);
Ok(v)
}
#[inline]
unsafe fn push_into_stack_multi(self, lua: &'lua Lua) -> Result<c_int> {
self.push_into_stack(lua)?;
Ok(1)
}
}
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for T {
#[inline]
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
T::from_lua(values.pop_front().unwrap_or(Nil), lua)
}
#[inline]
fn from_lua_args(
mut args: MultiValue<'lua>,
i: usize,
to: Option<&str>,
lua: &'lua Lua,
) -> Result<Self> {
T::from_lua_arg(args.pop_front().unwrap_or(Nil), i, to, lua)
}
#[inline]
unsafe fn from_stack_multi(nvals: c_int, lua: &'lua Lua) -> Result<Self> {
if nvals == 0 {
return T::from_lua(Nil, lua);
}
T::from_stack(-nvals, lua)
}
#[inline]
unsafe fn from_stack_args(
nargs: c_int,
i: usize,
to: Option<&str>,
lua: &'lua Lua,
) -> Result<Self> {
if nargs == 0 {
return T::from_lua_arg(Nil, i, to, lua);
}
T::from_stack_arg(-nargs, i, to, lua)
let res = T::from_lua(values.pop_front().unwrap_or(Nil), lua);
lua.cache_multivalue(values);
res
}
}
impl<'lua> IntoLuaMulti<'lua> for MultiValue<'lua> {
impl<'lua> ToLuaMulti<'lua> for MultiValue<'lua> {
#[inline]
fn into_lua_multi(self, _: &'lua Lua) -> Result<MultiValue<'lua>> {
fn to_lua_multi(self, _: &'lua Lua) -> Result<MultiValue<'lua>> {
Ok(self)
}
}
@@ -163,11 +128,11 @@ impl<T> DerefMut for Variadic<T> {
}
}
impl<'lua, T: IntoLua<'lua>> IntoLuaMulti<'lua> for Variadic<T> {
impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for Variadic<T> {
#[inline]
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let mut values = MultiValue::with_lua_and_capacity(lua, self.0.len());
values.refill(self.0.into_iter().map(|e| e.into_lua(lua)))?;
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let mut values = MultiValue::new_or_cached(lua);
values.refill(self.0.into_iter().map(|e| e.to_lua(lua)))?;
Ok(values)
}
}
@@ -175,74 +140,48 @@ impl<'lua, T: IntoLua<'lua>> IntoLuaMulti<'lua> for Variadic<T> {
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for Variadic<T> {
#[inline]
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
values
let res = values
.drain_all()
.map(|e| T::from_lua(e, lua))
.collect::<Result<Vec<T>>>()
.map(Variadic)
.map(Variadic);
lua.cache_multivalue(values);
res
}
}
macro_rules! impl_tuple {
() => (
impl<'lua> IntoLuaMulti<'lua> for () {
impl<'lua> ToLuaMulti<'lua> for () {
#[inline]
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
Ok(MultiValue::with_lua_and_capacity(lua, 0))
}
#[inline]
unsafe fn push_into_stack_multi(self, _lua: &'lua Lua) -> Result<c_int> {
Ok(0)
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
Ok(MultiValue::new_or_cached(lua))
}
}
impl<'lua> FromLuaMulti<'lua> for () {
#[inline]
fn from_lua_multi(_values: MultiValue<'lua>, _lua: &'lua Lua) -> Result<Self> {
Ok(())
}
#[inline]
unsafe fn from_stack_multi(nvals: c_int, lua: &'lua Lua) -> Result<Self> {
if nvals > 0 {
ffi::lua_pop(lua.state(), nvals);
}
fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
lua.cache_multivalue(values);
Ok(())
}
}
);
($last:ident $($name:ident)*) => (
impl<'lua, $($name,)* $last> IntoLuaMulti<'lua> for ($($name,)* $last,)
where $($name: IntoLua<'lua>,)*
$last: IntoLuaMulti<'lua>
impl<'lua, $($name,)* $last> ToLuaMulti<'lua> for ($($name,)* $last,)
where $($name: ToLua<'lua>,)*
$last: ToLuaMulti<'lua>
{
#[allow(unused_mut, non_snake_case)]
#[inline]
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let ($($name,)* $last,) = self;
let mut results = $last.into_lua_multi(lua)?;
push_reverse!(results, $($name.into_lua(lua)?,)*);
Ok(results)
}
#[allow(unused_mut)]
#[allow(non_snake_case)]
#[inline]
unsafe fn push_into_stack_multi(self, lua: &'lua Lua) -> Result<c_int> {
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let ($($name,)* $last,) = self;
let mut nresults = 0;
$(
_ = $name;
nresults += 1;
)*
check_stack(lua.state(), nresults + 1)?;
$(
$name.push_into_stack(lua)?;
)*
nresults += $last.push_into_stack_multi(lua)?;
Ok(nresults)
let mut results = $last.to_lua_multi(lua)?;
push_reverse!(results, $($name.to_lua(lua)?,)*);
Ok(results)
}
}
@@ -250,54 +189,13 @@ macro_rules! impl_tuple {
where $($name: FromLua<'lua>,)*
$last: FromLuaMulti<'lua>
{
#[allow(unused_mut, non_snake_case)]
#[allow(unused_mut)]
#[allow(non_snake_case)]
#[inline]
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
$(let $name = FromLua::from_lua(values.pop_front().unwrap_or(Nil), lua)?;)*
$(let $name = values.pop_front().unwrap_or(Nil);)*
let $last = FromLuaMulti::from_lua_multi(values, lua)?;
Ok(($($name,)* $last,))
}
#[allow(unused_mut, non_snake_case)]
#[inline]
fn from_lua_args(mut args: MultiValue<'lua>, mut i: usize, to: Option<&str>, lua: &'lua Lua) -> Result<Self> {
$(
let $name = FromLua::from_lua_arg(args.pop_front().unwrap_or(Nil), i, to, lua)?;
i += 1;
)*
let $last = FromLuaMulti::from_lua_args(args, i, to, lua)?;
Ok(($($name,)* $last,))
}
#[allow(unused_mut, non_snake_case)]
#[inline]
unsafe fn from_stack_multi(mut nvals: c_int, lua: &'lua Lua) -> Result<Self> {
$(
let $name = if nvals > 0 {
nvals -= 1;
FromLua::from_stack(-(nvals + 1), lua)
} else {
FromLua::from_lua(Nil, lua)
}?;
)*
let $last = FromLuaMulti::from_stack_multi(nvals, lua)?;
Ok(($($name,)* $last,))
}
#[allow(unused_mut, non_snake_case)]
#[inline]
unsafe fn from_stack_args(mut nargs: c_int, mut i: usize, to: Option<&str>, lua: &'lua Lua) -> Result<Self> {
$(
let $name = if nargs > 0 {
nargs -= 1;
FromLua::from_stack_arg(-(nargs + 1), i, to, lua)
} else {
FromLua::from_lua_arg(Nil, i, to, lua)
}?;
i += 1;
)*
let $last = FromLuaMulti::from_stack_args(nargs, i, to, lua)?;
Ok(($($name,)* $last,))
Ok(($(FromLua::from_lua($name, lua)?,)* $last,))
}
}
);
+11 -20
View File
@@ -2,18 +2,16 @@
#[doc(no_inline)]
pub use crate::{
AnyUserData as LuaAnyUserData, AnyUserDataExt as LuaAnyUserDataExt, Chunk as LuaChunk,
Error as LuaError, ErrorContext as LuaErrorContext, ExternalError as LuaExternalError,
ExternalResult as LuaExternalResult, FromLua, FromLuaMulti, Function as LuaFunction,
FunctionInfo as LuaFunctionInfo, GCMode as LuaGCMode, Integer as LuaInteger, IntoLua,
IntoLuaMulti, LightUserData as LuaLightUserData, Lua, LuaOptions, MetaMethod as LuaMetaMethod,
MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber, RegistryKey as LuaRegistryKey,
Result as LuaResult, StdLib as LuaStdLib, String as LuaString, Table as LuaTable,
TableExt as LuaTableExt, TablePairs as LuaTablePairs, TableSequence as LuaTableSequence,
Thread as LuaThread, ThreadStatus as LuaThreadStatus, UserData as LuaUserData,
UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable,
UserDataMethods as LuaUserDataMethods, UserDataRef as LuaUserDataRef,
UserDataRefMut as LuaUserDataRefMut, UserDataRegistry as LuaUserDataRegistry,
AnyUserData as LuaAnyUserData, Chunk as LuaChunk, Error as LuaError,
ExternalError as LuaExternalError, ExternalResult as LuaExternalResult, FromLua, FromLuaMulti,
Function as LuaFunction, FunctionInfo as LuaFunctionInfo, GCMode as LuaGCMode,
Integer as LuaInteger, LightUserData as LuaLightUserData, Lua, LuaOptions,
MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber,
RegistryKey as LuaRegistryKey, Result as LuaResult, StdLib as LuaStdLib, 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,
};
@@ -23,7 +21,7 @@ pub use crate::HookTriggers as LuaHookTriggers;
#[cfg(feature = "luau")]
#[doc(no_inline)]
pub use crate::{CoverageInfo as LuaCoverageInfo, Vector as LuaVector, VmState as LuaVmState};
pub use crate::{CoverageInfo as LuaCoverageInfo, VmState as LuaVmState};
#[cfg(feature = "async")]
#[doc(no_inline)]
@@ -35,10 +33,3 @@ pub use crate::{
DeserializeOptions as LuaDeserializeOptions, LuaSerdeExt,
SerializeOptions as LuaSerializeOptions,
};
#[cfg(feature = "unstable")]
#[doc(no_inline)]
pub use crate::{
OwnedAnyUserData as LuaOwnedAnyUserData, OwnedFunction as LuaOwnedFunction,
OwnedString as LuaOwnedString, OwnedTable as LuaOwnedTable, OwnedThread as LuaOwnedThread,
};
+529 -574
View File
File diff suppressed because it is too large Load Diff
+28 -117
View File
@@ -2,7 +2,6 @@ use std::cell::RefCell;
use std::convert::TryInto;
use std::os::raw::c_void;
use std::rc::Rc;
use std::result::Result as StdResult;
use std::string::String as StdString;
use rustc_hash::FxHashSet;
@@ -10,7 +9,6 @@ use serde::de::{self, IntoDeserializer};
use crate::error::{Error, Result};
use crate::table::{Table, TablePairs, TableSequence};
use crate::userdata::AnyUserData;
use crate::value::Value;
/// A struct for deserializing Lua values into Rust values.
@@ -25,14 +23,14 @@ pub struct Deserializer<'lua> {
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub struct Options {
/// If true, an attempt to serialize types such as [`Function`], [`Thread`], [`LightUserData`]
/// If true, an attempt to serialize types such as [`Thread`], [`UserData`], [`LightUserData`]
/// and [`Error`] will cause an error.
/// Otherwise these types skipped when iterating or serialized as unit type.
///
/// Default: **true**
///
/// [`Function`]: crate::Function
/// [`Thread`]: crate::Thread
/// [`UserData`]: crate::UserData
/// [`LightUserData`]: crate::LightUserData
/// [`Error`]: crate::Error
pub deny_unsupported_types: bool,
@@ -43,11 +41,6 @@ pub struct Options {
///
/// Default: **true**
pub deny_recursive_tables: bool,
/// If true, keys in tables will be iterated in sorted order.
///
/// Default: **false**
pub sort_keys: bool,
}
impl Default for Options {
@@ -62,7 +55,6 @@ impl Options {
Options {
deny_unsupported_types: true,
deny_recursive_tables: true,
sort_keys: false,
}
}
@@ -83,15 +75,6 @@ impl Options {
self.deny_recursive_tables = enabled;
self
}
/// Sets [`sort_keys`] option.
///
/// [`sort_keys`]: #structfield.sort_keys
#[must_use]
pub const fn sort_keys(mut self, enabled: bool) -> Self {
self.sort_keys = enabled;
self
}
}
impl<'lua> Deserializer<'lua> {
@@ -140,7 +123,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
#[allow(clippy::useless_conversion)]
Value::Number(n) => visitor.visit_f64(n.into()),
#[cfg(feature = "luau")]
Value::Vector(_) => self.deserialize_seq(visitor),
Value::Vector(_, _, _) => self.deserialize_seq(visitor),
Value::String(s) => match s.to_str() {
Ok(s) => visitor.visit_str(s),
Err(_) => visitor.visit_bytes(s.as_bytes()),
@@ -148,17 +131,16 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
Value::Table(ref t) if t.raw_len() > 0 || t.is_array() => self.deserialize_seq(visitor),
Value::Table(_) => self.deserialize_map(visitor),
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_none(),
Value::UserData(ud) if ud.is_serializable() => {
serde_userdata(ud, |value| value.deserialize_any(visitor))
}
Value::Function(_)
| Value::Thread(_)
| Value::UserData(_)
| Value::LightUserData(_)
| Value::Error(_) => {
if self.options.deny_unsupported_types {
let msg = format!("unsupported value type `{}`", self.value.type_name());
Err(de::Error::custom(msg))
Err(de::Error::custom(format!(
"unsupported value type `{}`",
self.value.type_name()
)))
} else {
visitor.visit_unit()
}
@@ -181,8 +163,8 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
#[inline]
fn deserialize_enum<V>(
self,
name: &'static str,
variants: &'static [&'static str],
_name: &str,
_variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value>
where
@@ -209,18 +191,13 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
&"map with a single key",
));
}
let skip = check_value_for_skip(&value, self.options, &self.visited)
.map_err(|err| Error::DeserializeError(err.to_string()))?;
if skip {
if check_value_if_skip(&value, self.options, &self.visited)? {
return Err(de::Error::custom("bad enum value"));
}
(variant, Some(value), Some(_guard))
}
Value::String(variant) => (variant.to_str()?.to_owned(), None, None),
Value::UserData(ud) if ud.is_serializable() => {
return serde_userdata(ud, |value| value.deserialize_enum(name, variants, visitor));
}
_ => return Err(de::Error::custom("bad enum value")),
};
@@ -239,9 +216,9 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
{
match self.value {
#[cfg(feature = "luau")]
Value::Vector(vec) => {
Value::Vector(x, y, z) => {
let mut deserializer = VecDeserializer {
vec,
vec: [x, y, z],
next: 0,
options: self.options,
visited: self.visited,
@@ -251,9 +228,9 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
Value::Table(t) => {
let _guard = RecursionGuard::new(&t, &self.visited);
let len = t.raw_len();
let len = t.raw_len() as usize;
let mut deserializer = SeqDeserializer {
seq: t.sequence_values(),
seq: t.raw_sequence_values(),
options: self.options,
visited: self.visited,
};
@@ -267,9 +244,6 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
))
}
}
Value::UserData(ud) if ud.is_serializable() => {
serde_userdata(ud, |value| value.deserialize_seq(visitor))
}
value => Err(de::Error::invalid_type(
de::Unexpected::Other(value.type_name()),
&"table",
@@ -308,7 +282,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
let _guard = RecursionGuard::new(&t, &self.visited);
let mut deserializer = MapDeserializer {
pairs: MapPairs::new(t, self.options.sort_keys)?,
pairs: t.pairs(),
value: None,
options: self.options,
visited: self.visited,
@@ -325,9 +299,6 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
))
}
}
Value::UserData(ud) if ud.is_serializable() => {
serde_userdata(ud, |value| value.deserialize_map(visitor))
}
value => Err(de::Error::invalid_type(
de::Unexpected::Other(value.type_name()),
&"table",
@@ -349,16 +320,11 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
}
#[inline]
fn deserialize_newtype_struct<V>(self, name: &'static str, visitor: V) -> Result<V::Value>
fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
match self.value {
Value::UserData(ud) if ud.is_serializable() => {
serde_userdata(ud, |value| value.deserialize_newtype_struct(name, visitor))
}
_ => visitor.visit_newtype_struct(self),
}
visitor.visit_newtype_struct(self)
}
#[inline]
@@ -406,9 +372,7 @@ impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
match self.seq.next() {
Some(value) => {
let value = value?;
let skip = check_value_for_skip(&value, self.options, &self.visited)
.map_err(|err| Error::DeserializeError(err.to_string()))?;
if skip {
if check_value_if_skip(&value, self.options, &self.visited)? {
continue;
}
let visited = Rc::clone(&self.visited);
@@ -430,7 +394,7 @@ impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
#[cfg(feature = "luau")]
struct VecDeserializer {
vec: crate::types::Vector,
vec: [f32; 3],
next: usize,
options: Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
@@ -444,7 +408,7 @@ impl<'de> de::SeqAccess<'de> for VecDeserializer {
where
T: de::DeserializeSeed<'de>,
{
match self.vec.0.get(self.next) {
match self.vec.get(self.next) {
Some(&n) => {
self.next += 1;
let visited = Rc::clone(&self.visited);
@@ -457,54 +421,12 @@ impl<'de> de::SeqAccess<'de> for VecDeserializer {
}
fn size_hint(&self) -> Option<usize> {
Some(crate::types::Vector::SIZE)
}
}
pub(crate) enum MapPairs<'lua> {
Iter(TablePairs<'lua, Value<'lua>, Value<'lua>>),
Vec(Vec<(Value<'lua>, Value<'lua>)>),
}
impl<'lua> MapPairs<'lua> {
pub(crate) fn new(t: Table<'lua>, sort_keys: bool) -> Result<Self> {
if sort_keys {
let mut pairs = t.pairs::<Value, Value>().collect::<Result<Vec<_>>>()?;
pairs.sort_by(|(a, _), (b, _)| b.cmp(a)); // reverse order as we pop values from the end
Ok(MapPairs::Vec(pairs))
} else {
Ok(MapPairs::Iter(t.pairs::<Value, Value>()))
}
}
pub(crate) fn count(self) -> usize {
match self {
MapPairs::Iter(iter) => iter.count(),
MapPairs::Vec(vec) => vec.len(),
}
}
pub(crate) fn size_hint(&self) -> (usize, Option<usize>) {
match self {
MapPairs::Iter(iter) => iter.size_hint(),
MapPairs::Vec(vec) => (vec.len(), Some(vec.len())),
}
}
}
impl<'lua> Iterator for MapPairs<'lua> {
type Item = Result<(Value<'lua>, Value<'lua>)>;
fn next(&mut self) -> Option<Self::Item> {
match self {
MapPairs::Iter(iter) => iter.next(),
MapPairs::Vec(vec) => vec.pop().map(Ok),
}
Some(3)
}
}
struct MapDeserializer<'lua> {
pairs: MapPairs<'lua>,
pairs: TablePairs<'lua, Value<'lua>, Value<'lua>>,
value: Option<Value<'lua>>,
options: Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
@@ -522,11 +444,9 @@ impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
match self.pairs.next() {
Some(item) => {
let (key, value) = item?;
let skip_key = check_value_for_skip(&key, self.options, &self.visited)
.map_err(|err| Error::DeserializeError(err.to_string()))?;
let skip_value = check_value_for_skip(&value, self.options, &self.visited)
.map_err(|err| Error::DeserializeError(err.to_string()))?;
if skip_key || skip_value {
if check_value_if_skip(&key, self.options, &self.visited)?
|| check_value_if_skip(&value, self.options, &self.visited)?
{
continue;
}
self.processed += 1;
@@ -677,22 +597,21 @@ impl Drop for RecursionGuard {
}
// Checks `options` and decides should we emit an error or skip next element
pub(crate) fn check_value_for_skip(
fn check_value_if_skip(
value: &Value,
options: Options,
visited: &RefCell<FxHashSet<*const c_void>>,
) -> StdResult<bool, &'static str> {
) -> Result<bool> {
match value {
Value::Table(table) => {
let ptr = table.to_pointer();
if visited.borrow().contains(&ptr) {
if options.deny_recursive_tables {
return Err("recursive table detected");
return Err(de::Error::custom("recursive table detected"));
}
return Ok(true); // skip
}
}
Value::UserData(ud) if ud.is_serializable() => {}
Value::Function(_)
| Value::Thread(_)
| Value::UserData(_)
@@ -706,11 +625,3 @@ pub(crate) fn check_value_for_skip(
}
Ok(false) // do not skip
}
fn serde_userdata<V>(
ud: AnyUserData,
f: impl FnOnce(serde_value::Value) -> std::result::Result<V, serde_value::DeserializerError>,
) -> Result<V> {
let value = serde_value::to_value(ud).map_err(|err| Error::SerializeError(err.to_string()))?;
f(value).map_err(|err| Error::DeserializeError(err.to_string()))
}
+25 -20
View File
@@ -1,19 +1,21 @@
//! (De)Serialization support using serde.
use std::os::raw::c_void;
use std::ptr;
use serde::{de::DeserializeOwned, ser::Serialize};
use serde::{Deserialize, Serialize};
use crate::error::Result;
use crate::ffi;
use crate::lua::Lua;
use crate::private::Sealed;
use crate::table::Table;
use crate::types::LightUserData;
use crate::util::check_stack;
use crate::value::Value;
/// Trait for serializing/deserializing Lua values using Serde.
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
pub trait LuaSerdeExt: Sealed {
pub trait LuaSerdeExt<'lua> {
/// A special value (lightuserdata) to encode/decode optional (none) values.
///
/// Requires `feature = "serialize"`
@@ -35,7 +37,7 @@ pub trait LuaSerdeExt: Sealed {
/// Ok(())
/// }
/// ```
fn null(&self) -> Value;
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
@@ -66,7 +68,7 @@ pub trait LuaSerdeExt: Sealed {
/// Ok(())
/// }
/// ```
fn array_metatable(&self) -> Table;
fn array_metatable(&'lua self) -> Table<'lua>;
/// Converts `T` into a [`Value`] instance.
///
@@ -99,7 +101,7 @@ pub trait LuaSerdeExt: Sealed {
/// "#).exec()
/// }
/// ```
fn to_value<'lua, T: Serialize + ?Sized>(&'lua self, t: &T) -> Result<Value<'lua>>;
fn to_value<T: Serialize + ?Sized>(&'lua self, t: &T) -> Result<Value<'lua>>;
/// Converts `T` into a [`Value`] instance with options.
///
@@ -124,7 +126,7 @@ pub trait LuaSerdeExt: Sealed {
/// "#).exec()
/// }
/// ```
fn to_value_with<'lua, T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
fn to_value_with<T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
where
T: Serialize + ?Sized;
@@ -157,7 +159,7 @@ pub trait LuaSerdeExt: Sealed {
/// }
/// ```
#[allow(clippy::wrong_self_convention)]
fn from_value<T: DeserializeOwned>(&self, value: Value) -> Result<T>;
fn from_value<T: Deserialize<'lua>>(&'lua self, value: Value<'lua>) -> Result<T>;
/// Deserializes a [`Value`] into any serde deserializable object with options.
///
@@ -189,46 +191,49 @@ pub trait LuaSerdeExt: Sealed {
/// }
/// ```
#[allow(clippy::wrong_self_convention)]
fn from_value_with<T: DeserializeOwned>(&self, value: Value, options: de::Options)
-> Result<T>;
fn from_value_with<T: Deserialize<'lua>>(
&'lua self,
value: Value<'lua>,
options: de::Options,
) -> Result<T>;
}
impl LuaSerdeExt for Lua {
fn null(&self) -> Value {
Value::NULL
impl<'lua> LuaSerdeExt<'lua> for Lua {
fn null(&'lua self) -> Value<'lua> {
Value::LightUserData(LightUserData(ptr::null_mut()))
}
fn array_metatable(&self) -> Table {
fn array_metatable(&'lua self) -> Table<'lua> {
unsafe {
push_array_metatable(self.ref_thread());
Table(self.pop_ref_thread())
}
}
fn to_value<'lua, T>(&'lua self, t: &T) -> Result<Value<'lua>>
fn to_value<T>(&'lua self, t: &T) -> Result<Value<'lua>>
where
T: Serialize + ?Sized,
{
t.serialize(ser::Serializer::new(self))
}
fn to_value_with<'lua, T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
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>(&self, value: Value) -> Result<T>
fn from_value<T>(&'lua self, value: Value<'lua>) -> Result<T>
where
T: DeserializeOwned,
T: Deserialize<'lua>,
{
T::deserialize(de::Deserializer::new(value))
}
fn from_value_with<T>(&self, value: Value, options: de::Options) -> Result<T>
fn from_value_with<T>(&'lua self, value: Value<'lua>, options: de::Options) -> Result<T>
where
T: DeserializeOwned,
T: Deserialize<'lua>,
{
T::deserialize(de::Deserializer::new_with_options(value, options))
}
+47 -67
View File
@@ -1,11 +1,16 @@
use std::os::raw::c_int;
use serde::{ser, Serialize};
use super::LuaSerdeExt;
use crate::error::{Error, Result};
use crate::ffi;
use crate::lua::Lua;
use crate::string::String;
use crate::table::Table;
use crate::value::{IntoLua, Value};
use crate::types::Integer;
use crate::util::{check_stack, StackGuard};
use crate::value::{ToLua, Value};
/// A struct for serializing Rust values into Lua values.
#[derive(Debug)]
@@ -105,7 +110,7 @@ macro_rules! lua_serialize_number {
($name:ident, $t:ty) => {
#[inline]
fn $name(self, value: $t) -> Result<Value<'lua>> {
value.into_lua(self.lua)
value.to_lua(self.lua)
}
};
}
@@ -116,9 +121,9 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
// Associated types for keeping track of additional state while serializing
// compound data structures like sequences and maps.
type SerializeSeq = SerializeSeq<'lua>;
type SerializeTuple = SerializeSeq<'lua>;
type SerializeTupleStruct = SerializeSeq<'lua>;
type SerializeSeq = SerializeVec<'lua>;
type SerializeTuple = SerializeVec<'lua>;
type SerializeTupleStruct = SerializeVec<'lua>;
type SerializeTupleVariant = SerializeTupleVariant<'lua>;
type SerializeMap = SerializeMap<'lua>;
type SerializeStruct = SerializeMap<'lua>;
@@ -231,11 +236,13 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
#[inline]
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
let table = self.lua.create_table_with_capacity(len.unwrap_or(0), 0)?;
let len = len.unwrap_or(0) as c_int;
let table = self.lua.create_table_with_capacity(len, 0)?;
if self.options.set_array_metatable {
table.set_metatable(Some(self.lua.array_metatable()));
}
Ok(SerializeSeq::new(table, self.options))
let options = self.options;
Ok(SerializeVec { table, options })
}
#[inline]
@@ -246,14 +253,9 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
#[inline]
fn serialize_tuple_struct(
self,
name: &'static str,
_name: &'static str,
len: usize,
) -> Result<Self::SerializeTupleStruct> {
#[cfg(feature = "luau")]
if name == "Vector" && len == crate::types::Vector::SIZE {
return Ok(SerializeSeq::new_vector(self.lua, self.options));
}
_ = name;
self.serialize_seq(Some(len))
}
@@ -274,9 +276,10 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
#[inline]
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
let len = len.unwrap_or(0) as c_int;
Ok(SerializeMap {
key: None,
table: self.lua.create_table_with_capacity(0, len.unwrap_or(0))?,
table: self.lua.create_table_with_capacity(0, len)?,
options: self.options,
})
}
@@ -296,47 +299,19 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
) -> Result<Self::SerializeStructVariant> {
Ok(SerializeStructVariant {
name: self.lua.create_string(variant)?,
table: self.lua.create_table_with_capacity(0, len)?,
table: self.lua.create_table_with_capacity(0, len as c_int)?,
options: self.options,
})
}
}
#[doc(hidden)]
pub struct SerializeSeq<'lua> {
lua: &'lua Lua,
#[cfg(feature = "luau")]
vector: Option<crate::types::Vector>,
table: Option<Table<'lua>>,
next: usize,
pub struct SerializeVec<'lua> {
table: Table<'lua>,
options: Options,
}
impl<'lua> SerializeSeq<'lua> {
const fn new(table: Table<'lua>, options: Options) -> Self {
Self {
lua: table.0.lua,
#[cfg(feature = "luau")]
vector: None,
table: Some(table),
next: 0,
options,
}
}
#[cfg(feature = "luau")]
const fn new_vector(lua: &'lua Lua, options: Options) -> Self {
Self {
lua,
vector: Some(crate::types::Vector::zero()),
table: None,
next: 0,
options,
}
}
}
impl<'lua> ser::SerializeSeq for SerializeSeq<'lua> {
impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
type Ok = Value<'lua>;
type Error = Error;
@@ -344,19 +319,34 @@ impl<'lua> ser::SerializeSeq for SerializeSeq<'lua> {
where
T: Serialize + ?Sized,
{
let value = self.lua.to_value_with(value, self.options)?;
let table = self.table.as_ref().unwrap();
table.raw_seti(self.next + 1, value)?;
self.next += 1;
Ok(())
let lua = self.table.0.lua;
let value = lua.to_value_with(value, self.options)?;
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 4)?;
lua.push_ref(&self.table.0);
lua.push_value(value)?;
if lua.unlikely_memory_error() {
let len = ffi::lua_rawlen(lua.state, -2) as Integer;
ffi::lua_rawseti(lua.state, -2, len + 1);
ffi::lua_pop(lua.state, 1);
Ok(())
} else {
protect_lua!(lua.state, 2, 0, fn(state) {
let len = ffi::lua_rawlen(state, -2) as Integer;
ffi::lua_rawseti(state, -2, len + 1);
})
}
}
}
fn end(self) -> Result<Value<'lua>> {
Ok(Value::Table(self.table.unwrap()))
Ok(Value::Table(self.table))
}
}
impl<'lua> ser::SerializeTuple for SerializeSeq<'lua> {
impl<'lua> ser::SerializeTuple for SerializeVec<'lua> {
type Ok = Value<'lua>;
type Error = Error;
@@ -372,7 +362,7 @@ impl<'lua> ser::SerializeTuple for SerializeSeq<'lua> {
}
}
impl<'lua> ser::SerializeTupleStruct for SerializeSeq<'lua> {
impl<'lua> ser::SerializeTupleStruct for SerializeVec<'lua> {
type Ok = Value<'lua>;
type Error = Error;
@@ -380,22 +370,10 @@ impl<'lua> ser::SerializeTupleStruct for SerializeSeq<'lua> {
where
T: Serialize + ?Sized,
{
#[cfg(feature = "luau")]
if let Some(vector) = self.vector.as_mut() {
let value = self.lua.to_value_with(value, self.options)?;
let value = self.lua.unpack(value)?;
vector.0[self.next] = value;
self.next += 1;
return Ok(());
}
ser::SerializeSeq::serialize_element(self, value)
}
fn end(self) -> Result<Value<'lua>> {
#[cfg(feature = "luau")]
if let Some(vector) = self.vector {
return Ok(Value::Vector(vector));
}
ser::SerializeSeq::end(self)
}
}
@@ -416,7 +394,9 @@ impl<'lua> ser::SerializeTupleVariant for SerializeTupleVariant<'lua> {
T: Serialize + ?Sized,
{
let lua = self.table.0.lua;
self.table.raw_push(lua.to_value_with(value, self.options)?)
let idx = self.table.raw_len() + 1;
self.table
.raw_insert(idx, lua.to_value_with(value, self.options)?)
}
fn end(self) -> Result<Value<'lua>> {
+6 -100
View File
@@ -2,7 +2,7 @@ use std::borrow::{Borrow, Cow};
use std::hash::{Hash, Hasher};
use std::os::raw::c_void;
use std::string::String as StdString;
use std::{fmt, slice, str};
use std::{slice, str};
#[cfg(feature = "serialize")]
use {
@@ -11,35 +11,15 @@ use {
};
use crate::error::{Error, Result};
use crate::ffi;
use crate::types::LuaRef;
/// Handle to an internal Lua string.
///
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct String<'lua>(pub(crate) LuaRef<'lua>);
/// Owned handle to an internal Lua string.
///
/// The owned handle holds a *strong* reference to the current Lua instance.
/// Be warned, if you place it into a Lua type (eg. [`UserData`] or a Rust callback), it is *very easy*
/// to accidentally cause reference cycles that would prevent destroying Lua instance.
///
/// [`UserData`]: crate::UserData
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
#[derive(Clone)]
pub struct OwnedString(pub(crate) crate::types::LuaOwnedRef);
#[cfg(feature = "unstable")]
impl OwnedString {
/// Get borrowed handle to the underlying Lua string.
#[cfg_attr(feature = "send", allow(unused))]
pub const fn to_ref(&self) -> String {
String(self.0.to_ref())
}
}
impl<'lua> String<'lua> {
/// Get a `&str` slice if the Lua string is valid UTF-8.
///
@@ -139,44 +119,8 @@ impl<'lua> String<'lua> {
/// Typically this function is used only for hashing and debug information.
#[inline]
pub fn to_pointer(&self) -> *const c_void {
self.0.to_pointer()
}
/// Convert this handle to owned version.
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
#[inline]
pub fn into_owned(self) -> OwnedString {
OwnedString(self.0.into_owned())
}
}
impl<'lua> fmt::Debug for String<'lua> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let bytes = self.as_bytes();
// Check if the string is valid utf8
if let Ok(s) = str::from_utf8(bytes) {
return s.fmt(f);
}
// Format as bytes
write!(f, "b\"")?;
for &b in bytes {
// https://doc.rust-lang.org/reference/tokens.html#byte-escapes
match b {
b'\n' => write!(f, "\\n")?,
b'\r' => write!(f, "\\r")?,
b'\t' => write!(f, "\\t")?,
b'\\' | b'"' => write!(f, "\\{}", b as char)?,
b'\0' => write!(f, "\\0")?,
// ASCII printable
0x20..=0x7e => write!(f, "{}", b as char)?,
_ => write!(f, "\\x{b:02x}")?,
}
}
write!(f, "\"")?;
Ok(())
let ref_thread = self.0.lua.ref_thread();
unsafe { ffi::lua_topointer(ref_thread, self.0.index) }
}
}
@@ -202,7 +146,7 @@ impl<'lua> Borrow<[u8]> for String<'lua> {
// in other ways.
impl<'lua, T> PartialEq<T> for String<'lua>
where
T: AsRef<[u8]> + ?Sized,
T: AsRef<[u8]>,
{
fn eq(&self, other: &T) -> bool {
self.as_bytes() == other.as_ref()
@@ -229,41 +173,3 @@ impl<'lua> Serialize for String<'lua> {
}
}
}
// Additional shortcuts
#[cfg(feature = "unstable")]
impl OwnedString {
/// Get a `&str` slice if the Lua string is valid UTF-8.
///
/// This is a shortcut for [`String::to_str()`].
#[inline]
pub fn to_str(&self) -> Result<&str> {
let s = self.to_ref();
// Reattach lifetime to &self
unsafe { std::mem::transmute(s.to_str()) }
}
/// Get the bytes that make up this string.
///
/// This is a shortcut for [`String::as_bytes()`].
#[inline]
pub fn as_bytes(&self) -> &[u8] {
let s = self.to_ref();
// Reattach lifetime to &self
unsafe { std::mem::transmute(s.as_bytes()) }
}
}
#[cfg(feature = "unstable")]
impl fmt::Debug for OwnedString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.to_ref().fmt(f)
}
}
#[cfg(test)]
mod assertions {
use super::*;
static_assertions::assert_not_impl_any!(String: Send);
}
+240 -472
View File
File diff suppressed because it is too large Load Diff
+158 -251
View File
@@ -1,32 +1,35 @@
use std::cmp;
use std::os::raw::c_int;
use crate::error::{Error, Result};
#[allow(unused)]
use crate::lua::Lua;
use crate::ffi;
use crate::types::LuaRef;
use crate::util::{check_stack, error_traceback_thread, pop_error, StackGuard};
use crate::value::{FromLuaMulti, IntoLuaMulti};
use crate::value::{FromLuaMulti, ToLuaMulti};
#[cfg(not(feature = "luau"))]
use crate::{
hook::{Debug, HookTriggers},
types::MaybeSend,
};
#[cfg(any(
feature = "lua54",
all(feature = "luajit", feature = "vendored"),
feature = "luau",
))]
use crate::function::Function;
#[cfg(feature = "async")]
use {
crate::{lua::ASYNC_POLL_PENDING, value::MultiValue},
futures_util::stream::Stream,
crate::{
lua::{Lua, ASYNC_POLL_PENDING},
value::{MultiValue, Value},
},
futures_core::{future::Future, stream::Stream},
std::{
future::Future,
cell::RefCell,
marker::PhantomData,
pin::Pin,
ptr::NonNull,
task::{Context, Poll, Waker},
},
};
/// Status of a Lua thread (coroutine).
/// Status of a Lua thread (or coroutine).
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ThreadStatus {
/// The thread was just created, or is suspended because it has called `coroutine.yield`.
@@ -41,61 +44,27 @@ pub enum ThreadStatus {
Error,
}
/// Handle to an internal Lua thread (coroutine).
/// Handle to an internal Lua thread (or coroutine).
#[derive(Clone, Debug)]
pub struct Thread<'lua>(pub(crate) LuaRef<'lua>, pub(crate) *mut ffi::lua_State);
/// Owned handle to an internal Lua thread (coroutine).
///
/// The owned handle holds a *strong* reference to the current Lua instance.
/// Be warned, if you place it into a Lua type (eg. [`UserData`] or a Rust callback), it is *very easy*
/// to accidentally cause reference cycles that would prevent destroying Lua instance.
///
/// [`UserData`]: crate::UserData
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
#[derive(Clone, Debug)]
pub struct OwnedThread(
pub(crate) crate::types::LuaOwnedRef,
pub(crate) *mut ffi::lua_State,
);
#[cfg(feature = "unstable")]
impl OwnedThread {
/// Get borrowed handle to the underlying Lua table.
#[cfg_attr(feature = "send", allow(unused))]
pub const fn to_ref(&self) -> Thread {
Thread(self.0.to_ref(), self.1)
}
}
pub struct Thread<'lua>(pub(crate) LuaRef<'lua>);
/// Thread (coroutine) representation as an async [`Future`] or [`Stream`].
///
/// Requires `feature = "async"`
///
/// [`Future`]: std::future::Future
/// [`Stream`]: futures_util::stream::Stream
/// [`Future`]: futures_core::future::Future
/// [`Stream`]: futures_core::stream::Stream
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
#[must_use = "futures do nothing unless you `.await` or poll them"]
#[derive(Debug)]
pub struct AsyncThread<'lua, R> {
thread: Thread<'lua>,
init_args: Option<Result<MultiValue<'lua>>>,
args0: RefCell<Option<Result<MultiValue<'lua>>>>,
ret: PhantomData<R>,
recycle: bool,
}
impl<'lua> Thread<'lua> {
#[inline(always)]
pub(crate) fn new(r#ref: LuaRef<'lua>) -> Self {
let state = unsafe { ffi::lua_tothread(r#ref.lua.ref_thread(), r#ref.index) };
Thread(r#ref, state)
}
const fn state(&self) -> *mut ffi::lua_State {
self.1
}
/// Resumes execution of this thread.
///
/// Equivalent to `coroutine.resume`.
@@ -139,64 +108,59 @@ impl<'lua> Thread<'lua> {
/// ```
pub fn resume<A, R>(&self, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
let lua = self.0.lua;
let state = lua.state();
let thread_state = self.state();
unsafe {
let _sg = StackGuard::new(state);
let _thread_sg = StackGuard::with_top(thread_state, 0);
let mut args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int;
let results = unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, cmp::max(nargs + 1, 3))?;
let nresults = self.resume_inner(args)?;
check_stack(state, nresults + 1)?;
ffi::lua_xmove(thread_state, state, nresults);
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.0.index);
R::from_stack_multi(nresults, lua)
}
}
/// Resumes execution of this thread.
///
/// It's similar to `resume()` but leaves `nresults` values on the thread stack.
unsafe fn resume_inner<A: IntoLuaMulti<'lua>>(&self, args: A) -> Result<c_int> {
let lua = self.0.lua;
let state = lua.state();
let thread_state = self.state();
if self.status() != ThreadStatus::Resumable {
return Err(Error::CoroutineInactive);
}
let nargs = args.push_into_stack_multi(lua)?;
if nargs > 0 {
check_stack(thread_state, nargs)?;
ffi::lua_xmove(state, thread_state, nargs);
}
let mut nresults = 0;
let ret = ffi::lua_resume(thread_state, state, nargs, &mut nresults as *mut c_int);
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
if ret == ffi::LUA_ERRMEM {
// Don't call error handler for memory errors
return Err(pop_error(thread_state, ret));
let status = ffi::lua_status(thread_state);
if status != ffi::LUA_YIELD && ffi::lua_gettop(thread_state) == 0 {
return Err(Error::CoroutineInactive);
}
check_stack(state, 3)?;
protect_lua!(state, 0, 1, |state| error_traceback_thread(
state,
thread_state
))?;
return Err(pop_error(state, ret));
}
Ok(nresults)
check_stack(thread_state, nargs)?;
for arg in args.drain_all() {
lua.push_value(arg)?;
}
ffi::lua_xmove(lua.state, thread_state, nargs);
let mut nresults = 0;
let ret = ffi::lua_resume(thread_state, lua.state, nargs, &mut nresults as *mut c_int);
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
check_stack(lua.state, 3)?;
protect_lua!(lua.state, 0, 1, |state| error_traceback_thread(
state,
thread_state
))?;
return Err(pop_error(lua.state, ret));
}
let mut results = args; // Reuse MultiValue container
check_stack(lua.state, nresults + 2)?; // 2 is extra for `lua.pop_value()` below
ffi::lua_xmove(thread_state, lua.state, nresults);
for _ in 0..nresults {
results.push_front(lua.pop_value());
}
results
};
R::from_lua_multi(results, lua)
}
/// Gets the status of the thread.
pub fn status(&self) -> ThreadStatus {
let thread_state = self.state();
let lua = self.0.lua;
unsafe {
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.0.index);
let status = ffi::lua_status(thread_state);
if status != ffi::LUA_OK && status != ffi::LUA_YIELD {
ThreadStatus::Error
@@ -208,60 +172,55 @@ impl<'lua> Thread<'lua> {
}
}
/// Sets a 'hook' function that will periodically be called as Lua code executes.
///
/// This function is similar or [`Lua::set_hook()`] except that it sets for the thread.
/// To remove a hook call [`Lua::remove_hook()`].
#[cfg(not(feature = "luau"))]
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F)
where
F: Fn(&Lua, Debug) -> Result<()> + MaybeSend + 'static,
{
let lua = self.0.lua;
unsafe {
lua.set_thread_hook(self.state(), triggers, callback);
}
}
/// 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 Luau: resets to the initial state of a newly created Lua thread.
/// In [LuaJIT] and Luau: 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 = "luau"`.
/// Requires `feature = "lua54"` OR `feature = "luajit,vendored"` OR `feature = "luau"`
///
/// [Lua 5.4]: https://www.lua.org/manual/5.4/manual.html#lua_closethread
#[cfg(any(feature = "lua54", feature = "luau"))]
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
pub fn reset(&self, func: crate::function::Function<'lua>) -> Result<()> {
/// [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"),
feature = "luau",
))]
pub fn reset(&self, func: Function<'lua>) -> Result<()> {
let lua = self.0.lua;
let thread_state = self.state();
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);
#[cfg(all(feature = "lua54", not(feature = "vendored")))]
let status = ffi::lua_resetthread(thread_state);
#[cfg(all(feature = "lua54", feature = "vendored"))]
let status = ffi::lua_closethread(thread_state, lua.state());
let status = ffi::lua_closethread(thread_state, lua.state);
#[cfg(feature = "lua54")]
if status != ffi::LUA_OK {
return Err(pop_error(thread_state, status));
}
#[cfg(all(feature = "luajit", feature = "vendored"))]
ffi::lua_resetthread(lua.state, thread_state);
#[cfg(feature = "luau")]
ffi::lua_resetthread(thread_state);
// Push function to the top of the thread stack
ffi::lua_xpush(lua.ref_thread(), thread_state, func.0.index);
lua.push_ref(&func.0);
ffi::lua_xmove(lua.state, thread_state, 1);
#[cfg(feature = "luau")]
{
// Inherit `LUA_GLOBALSINDEX` from the main thread
ffi::lua_xpush(lua.main_state(), thread_state, ffi::LUA_GLOBALSINDEX);
// Inherit `LUA_GLOBALSINDEX` from the caller
ffi::lua_xpush(lua.state, thread_state, ffi::LUA_GLOBALSINDEX);
ffi::lua_replace(thread_state, ffi::LUA_GLOBALSINDEX);
}
@@ -281,8 +240,8 @@ impl<'lua> Thread<'lua> {
///
/// Requires `feature = "async"`
///
/// [`Future`]: std::future::Future
/// [`Stream`]: futures_util::stream::Stream
/// [`Future`]: futures_core::future::Future
/// [`Stream`]: futures_core::stream::Stream
/// [`resume()`]: https://www.lua.org/manual/5.4/manual.html#lua_resume
///
/// # Examples
@@ -318,13 +277,13 @@ impl<'lua> Thread<'lua> {
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn into_async<A, R>(self, args: A) -> AsyncThread<'lua, R>
where
A: IntoLuaMulti<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
let args = args.into_lua_multi(self.0.lua);
let args = args.to_lua_multi(self.0.lua);
AsyncThread {
thread: self,
init_args: Some(args),
args0: RefCell::new(Some(args)),
ret: PhantomData,
recycle: false,
}
@@ -366,22 +325,16 @@ impl<'lua> Thread<'lua> {
#[doc(hidden)]
pub fn sandbox(&self) -> Result<()> {
let lua = self.0.lua;
let state = lua.state();
let thread_state = self.state();
unsafe {
check_stack(thread_state, 3)?;
check_stack(state, 3)?;
protect_lua!(state, 0, 0, |_| ffi::luaL_sandboxthread(thread_state))
let thread = ffi::lua_tothread(lua.ref_thread(), self.0.index);
check_stack(thread, 1)?;
check_stack(lua.state, 3)?;
// Inherit `LUA_GLOBALSINDEX` from the caller
ffi::lua_xpush(lua.state, thread, ffi::LUA_GLOBALSINDEX);
ffi::lua_replace(thread, ffi::LUA_GLOBALSINDEX);
protect_lua!(lua.state, 0, 0, |_| ffi::luaL_sandboxthread(thread))
}
}
/// Convert this handle to owned version.
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
#[inline]
pub fn into_owned(self) -> OwnedThread {
OwnedThread(self.0.into_owned(), self.1)
}
}
impl<'lua> PartialEq for Thread<'lua> {
@@ -390,26 +343,6 @@ impl<'lua> PartialEq for Thread<'lua> {
}
}
// Additional shortcuts
#[cfg(feature = "unstable")]
impl OwnedThread {
/// Resumes execution of this thread.
///
/// See [`Thread::resume()`] for more details.
pub fn resume<'lua, A, R>(&'lua self, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
self.to_ref().resume(args)
}
/// Gets the status of the thread.
pub fn status(&self) -> ThreadStatus {
self.to_ref().status()
}
}
#[cfg(feature = "async")]
impl<'lua, R> AsyncThread<'lua, R> {
#[inline]
@@ -419,7 +352,11 @@ impl<'lua, R> AsyncThread<'lua, R> {
}
#[cfg(feature = "async")]
#[cfg(any(feature = "lua54", feature = "luau"))]
#[cfg(any(
feature = "lua54",
all(feature = "luajit", feature = "vendored"),
feature = "luau",
))]
impl<'lua, R> Drop for AsyncThread<'lua, R> {
fn drop(&mut self) {
if self.recycle {
@@ -429,10 +366,8 @@ impl<'lua, R> Drop for AsyncThread<'lua, R> {
if !lua.recycle_thread(&mut self.thread) {
#[cfg(feature = "lua54")]
if self.thread.status() == ThreadStatus::Error {
#[cfg(not(feature = "vendored"))]
ffi::lua_resetthread(self.thread.state());
#[cfg(feature = "vendored")]
ffi::lua_closethread(self.thread.state(), lua.state());
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.thread.0.index);
ffi::lua_resetthread(thread_state);
}
}
}
@@ -448,36 +383,26 @@ where
type Item = Result<R>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
if self.thread.status() != ThreadStatus::Resumable {
return Poll::Ready(None);
}
let lua = self.thread.0.lua;
let state = lua.state();
let thread_state = self.thread.state();
unsafe {
let _sg = StackGuard::new(state);
let _thread_sg = StackGuard::with_top(thread_state, 0);
let _wg = WakerGuard::new(lua, cx.waker());
// This is safe as we are not moving the whole struct
let this = self.get_unchecked_mut();
let nresults = if let Some(args) = this.init_args.take() {
this.thread.resume_inner(args?)?
} else {
this.thread.resume_inner(())?
};
match self.thread.status() {
ThreadStatus::Resumable => {}
_ => return Poll::Ready(None),
};
if nresults == 1 && is_poll_pending(thread_state) {
return Poll::Pending;
}
let _wg = WakerGuard::new(lua, cx.waker().clone());
let ret: MultiValue = if let Some(args) = self.args0.borrow_mut().take() {
self.thread.resume(args?)?
} else {
self.thread.resume(())?
};
check_stack(state, nresults + 1)?;
ffi::lua_xmove(thread_state, state, nresults);
cx.waker().wake_by_ref();
Poll::Ready(Some(R::from_stack_multi(nresults, lua)))
if is_poll_pending(&ret) {
return Poll::Pending;
}
cx.waker().wake_by_ref();
Poll::Ready(Some(R::from_lua_multi(ret, lua)))
}
}
@@ -489,85 +414,67 @@ where
type Output = Result<R>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if self.thread.status() != ThreadStatus::Resumable {
return Poll::Ready(Err(Error::CoroutineInactive));
}
let lua = self.thread.0.lua;
let state = lua.state();
let thread_state = self.thread.state();
unsafe {
let _sg = StackGuard::new(state);
let _thread_sg = StackGuard::with_top(thread_state, 0);
let _wg = WakerGuard::new(lua, cx.waker());
// This is safe as we are not moving the whole struct
let this = self.get_unchecked_mut();
let nresults = if let Some(args) = this.init_args.take() {
this.thread.resume_inner(args?)?
} else {
this.thread.resume_inner(())?
};
match self.thread.status() {
ThreadStatus::Resumable => {}
_ => return Poll::Ready(Err(Error::CoroutineInactive)),
};
if nresults == 1 && is_poll_pending(thread_state) {
return Poll::Pending;
}
let _wg = WakerGuard::new(lua, cx.waker().clone());
let ret: MultiValue = if let Some(args) = self.args0.borrow_mut().take() {
self.thread.resume(args?)?
} else {
self.thread.resume(())?
};
if ffi::lua_status(thread_state) == ffi::LUA_YIELD {
// Ignore value returned via yield()
cx.waker().wake_by_ref();
return Poll::Pending;
}
check_stack(state, nresults + 1)?;
ffi::lua_xmove(thread_state, state, nresults);
Poll::Ready(R::from_stack_multi(nresults, lua))
if is_poll_pending(&ret) {
return Poll::Pending;
}
if let ThreadStatus::Resumable = self.thread.status() {
// Ignore value returned via yield()
cx.waker().wake_by_ref();
return Poll::Pending;
}
Poll::Ready(R::from_lua_multi(ret, lua))
}
}
#[cfg(feature = "async")]
#[inline(always)]
unsafe fn is_poll_pending(state: *mut ffi::lua_State) -> bool {
if ffi::lua_islightuserdata(state, -1) != 0 {
let stack_ptr = ffi::lua_touserdata(state, -1) as *const u8;
let pending_ptr = &ASYNC_POLL_PENDING as *const u8;
return std::ptr::eq(stack_ptr, pending_ptr);
fn is_poll_pending(val: &MultiValue) -> bool {
match val.iter().enumerate().last() {
Some((0, Value::LightUserData(ud))) => {
std::ptr::eq(ud.0 as *const u8, &ASYNC_POLL_PENDING as *const u8)
}
_ => false,
}
false
}
#[cfg(feature = "async")]
struct WakerGuard<'lua, 'a> {
struct WakerGuard<'lua> {
lua: &'lua Lua,
prev: NonNull<Waker>,
_phantom: PhantomData<&'a ()>,
prev: Option<Waker>,
}
#[cfg(feature = "async")]
impl<'lua, 'a> WakerGuard<'lua, 'a> {
impl<'lua> WakerGuard<'lua> {
#[inline]
pub fn new(lua: &'lua Lua, waker: &'a Waker) -> Result<WakerGuard<'lua, 'a>> {
let prev = unsafe { lua.set_waker(NonNull::from(waker)) };
Ok(WakerGuard {
lua,
prev,
_phantom: PhantomData,
})
pub fn new(lua: &Lua, waker: Waker) -> Result<WakerGuard> {
unsafe {
let prev = lua.set_waker(Some(waker));
Ok(WakerGuard { lua, prev })
}
}
}
#[cfg(feature = "async")]
impl<'lua, 'a> Drop for WakerGuard<'lua, 'a> {
impl<'lua> Drop for WakerGuard<'lua> {
fn drop(&mut self) {
unsafe { self.lua.set_waker(self.prev) };
unsafe {
self.lua.set_waker(self.prev.take());
}
}
}
#[cfg(test)]
mod assertions {
use super::*;
static_assertions::assert_not_impl_any!(Thread: Send);
}
+29 -345
View File
@@ -1,28 +1,23 @@
use std::any::{Any, TypeId};
use std::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell};
use std::cell::UnsafeCell;
use std::hash::{Hash, Hasher};
use std::ops::{Deref, DerefMut};
use std::os::raw::{c_int, c_void};
use std::result::Result as StdResult;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::{fmt, mem, ptr};
use rustc_hash::FxHashMap;
#[cfg(feature = "lua54")]
use std::ffi::CStr;
#[cfg(feature = "async")]
use futures_core::future::LocalBoxFuture;
use crate::error::Result;
use crate::ffi;
#[cfg(not(feature = "luau"))]
use crate::hook::Debug;
use crate::lua::{ExtraData, Lua};
#[cfg(feature = "async")]
use {crate::value::MultiValue, futures_util::future::LocalBoxFuture};
#[cfg(feature = "unstable")]
use {crate::lua::LuaInner, std::marker::PhantomData};
#[cfg(all(feature = "luau", feature = "serialize"))]
use serde::ser::{Serialize, SerializeTupleStruct, Serializer};
use crate::util::{assert_stack, StackGuard};
use crate::value::MultiValue;
/// Type of Lua integer numbers.
pub type Integer = ffi::lua_Integer;
@@ -33,7 +28,8 @@ pub type Number = ffi::lua_Number;
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct LightUserData(pub *mut c_void);
pub(crate) type Callback<'lua, 'a> = Box<dyn Fn(&'lua Lua, c_int) -> Result<c_int> + 'a>;
pub(crate) type Callback<'lua, 'a> =
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>> + 'a>;
pub(crate) struct Upvalue<T> {
pub(crate) data: T,
@@ -44,13 +40,13 @@ pub(crate) type CallbackUpvalue = Upvalue<Callback<'static, 'static>>;
#[cfg(feature = "async")]
pub(crate) type AsyncCallback<'lua, 'a> =
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> LocalBoxFuture<'lua, Result<c_int>> + 'a>;
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> LocalBoxFuture<'lua, Result<MultiValue<'lua>>> + 'a>;
#[cfg(feature = "async")]
pub(crate) type AsyncCallbackUpvalue = Upvalue<AsyncCallback<'static, 'static>>;
#[cfg(feature = "async")]
pub(crate) type AsyncPollUpvalue = Upvalue<LocalBoxFuture<'static, Result<c_int>>>;
pub(crate) type AsyncPollUpvalue = Upvalue<LocalBoxFuture<'static, Result<MultiValue<'static>>>>;
/// Type to set next Luau VM action after executing interrupt function.
#[cfg(any(feature = "luau", doc))]
@@ -67,16 +63,16 @@ pub(crate) type HookCallback = Arc<dyn Fn(&Lua, Debug) -> Result<()> + Send>;
pub(crate) type HookCallback = Arc<dyn Fn(&Lua, Debug) -> Result<()>>;
#[cfg(all(feature = "luau", feature = "send"))]
pub(crate) type InterruptCallback = Arc<dyn Fn(&Lua) -> Result<VmState> + Send>;
pub(crate) type InterruptCallback = Arc<dyn Fn() -> Result<VmState> + Send>;
#[cfg(all(feature = "luau", not(feature = "send")))]
pub(crate) type InterruptCallback = Arc<dyn Fn(&Lua) -> Result<VmState>>;
pub(crate) type InterruptCallback = Arc<dyn Fn() -> Result<VmState>>;
#[cfg(all(feature = "send", feature = "lua54"))]
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &str, bool) -> Result<()> + Send>;
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &CStr, bool) -> Result<()> + Send>;
#[cfg(all(not(feature = "send"), feature = "lua54"))]
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &str, bool) -> Result<()>>;
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &CStr, bool) -> Result<()>>;
#[cfg(feature = "send")]
pub trait MaybeSend: Send {}
@@ -88,92 +84,6 @@ pub trait MaybeSend {}
#[cfg(not(feature = "send"))]
impl<T> MaybeSend for T {}
/// A Luau vector type.
///
/// By default vectors are 3-dimensional, but can be 4-dimensional
/// if the `luau-vector4` feature is enabled.
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[derive(Debug, Default, Clone, Copy, PartialEq)]
pub struct Vector(pub(crate) [f32; Self::SIZE]);
#[cfg(any(feature = "luau", doc))]
impl fmt::Display for Vector {
#[rustfmt::skip]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(not(feature = "luau-vector4"))]
return write!(f, "vector({}, {}, {})", self.x(), self.y(), self.z());
#[cfg(feature = "luau-vector4")]
return write!(f, "vector({}, {}, {}, {})", self.x(), self.y(), self.z(), self.w());
}
}
#[cfg(any(feature = "luau", doc))]
impl Vector {
pub(crate) const SIZE: usize = if cfg!(feature = "luau-vector4") { 4 } else { 3 };
/// Creates a new vector.
#[cfg(not(feature = "luau-vector4"))]
pub const fn new(x: f32, y: f32, z: f32) -> Self {
Self([x, y, z])
}
/// Creates a new vector.
#[cfg(feature = "luau-vector4")]
pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
Self([x, y, z, w])
}
/// Creates a new vector with all components set to `0.0`.
#[doc(hidden)]
pub const fn zero() -> Self {
Self([0.0; Self::SIZE])
}
/// Returns 1st component of the vector.
pub const fn x(&self) -> f32 {
self.0[0]
}
/// Returns 2nd component of the vector.
pub const fn y(&self) -> f32 {
self.0[1]
}
/// Returns 3rd component of the vector.
pub const fn z(&self) -> f32 {
self.0[2]
}
/// Returns 4th component of the vector.
#[cfg(any(feature = "luau-vector4", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau-vector4")))]
pub const fn w(&self) -> f32 {
self.0[3]
}
}
#[cfg(all(feature = "luau", feature = "serialize"))]
impl Serialize for Vector {
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
let mut ts = serializer.serialize_tuple_struct("Vector", Self::SIZE)?;
ts.serialize_field(&self.x())?;
ts.serialize_field(&self.y())?;
ts.serialize_field(&self.z())?;
#[cfg(feature = "luau-vector4")]
ts.serialize_field(&self.w())?;
ts.end()
}
}
#[cfg(any(feature = "luau", doc))]
impl PartialEq<[f32; Self::SIZE]> for Vector {
#[inline]
fn eq(&self, other: &[f32; Self::SIZE]) -> bool {
self.0 == *other
}
}
pub(crate) struct DestructedUserdata;
/// An auto generated key into the Lua registry.
@@ -185,14 +95,14 @@ pub(crate) struct DestructedUserdata;
/// Be warned, If you place this into Lua via a [`UserData`] type or a rust callback, it is *very
/// easy* to accidentally cause reference cycles that the Lua garbage collector cannot resolve.
/// Instead of placing a [`RegistryKey`] into a [`UserData`] type, prefer instead to use
/// [`AnyUserData::set_user_value`] / [`AnyUserData::user_value`].
/// [`AnyUserData::set_user_value`] / [`AnyUserData::get_user_value`].
///
/// [`UserData`]: crate::UserData
/// [`RegistryKey`]: crate::RegistryKey
/// [`Lua::remove_registry_value`]: crate::Lua::remove_registry_value
/// [`Lua::expire_registry_values`]: crate::Lua::expire_registry_values
/// [`AnyUserData::set_user_value`]: crate::AnyUserData::set_user_value
/// [`AnyUserData::user_value`]: crate::AnyUserData::user_value
/// [`AnyUserData::get_user_value`]: crate::AnyUserData::get_user_value
pub struct RegistryKey {
pub(crate) registry_id: c_int,
pub(crate) is_nil: AtomicBool,
@@ -270,36 +180,11 @@ impl RegistryKey {
pub(crate) struct LuaRef<'lua> {
pub(crate) lua: &'lua Lua,
pub(crate) index: c_int,
pub(crate) drop: bool,
}
impl<'lua> LuaRef<'lua> {
pub(crate) const fn new(lua: &'lua Lua, index: c_int) -> Self {
LuaRef {
lua,
index,
drop: true,
}
}
#[inline]
pub(crate) fn to_pointer(&self) -> *const c_void {
unsafe { ffi::lua_topointer(self.lua.ref_thread(), self.index) }
}
#[cfg(feature = "unstable")]
#[inline]
pub(crate) fn into_owned(self) -> LuaOwnedRef {
assert!(self.drop, "Cannot turn non-drop reference into owned");
let owned_ref = LuaOwnedRef::new(self.lua.clone(), self.index);
mem::forget(self);
owned_ref
}
}
impl<'lua> fmt::Debug for LuaRef<'lua> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Ref({:p})", self.to_pointer())
write!(f, "Ref({})", self.index)
}
}
@@ -311,222 +196,21 @@ impl<'lua> Clone for LuaRef<'lua> {
impl<'lua> Drop for LuaRef<'lua> {
fn drop(&mut self) {
if self.drop {
self.lua.drop_ref_index(self.index);
if self.index > 0 {
self.lua.drop_ref(self);
}
}
}
impl<'lua> PartialEq for LuaRef<'lua> {
fn eq(&self, other: &Self) -> bool {
let ref_thread = self.lua.ref_thread();
assert!(
ref_thread == other.lua.ref_thread(),
"Lua instance passed Value created from a different main Lua state"
);
unsafe { ffi::lua_rawequal(ref_thread, self.index, other.index) == 1 }
}
}
#[cfg(feature = "unstable")]
pub(crate) struct LuaOwnedRef {
pub(crate) inner: Arc<LuaInner>,
pub(crate) index: c_int,
_non_send: PhantomData<*const ()>,
}
#[cfg(feature = "unstable")]
impl fmt::Debug for LuaOwnedRef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "OwnedRef({:p})", self.to_ref().to_pointer())
}
}
#[cfg(feature = "unstable")]
impl Clone for LuaOwnedRef {
fn clone(&self) -> Self {
self.to_ref().clone().into_owned()
}
}
#[cfg(feature = "unstable")]
impl Drop for LuaOwnedRef {
fn drop(&mut self) {
let lua: &Lua = unsafe { mem::transmute(&self.inner) };
lua.drop_ref_index(self.index);
}
}
#[cfg(feature = "unstable")]
impl LuaOwnedRef {
pub(crate) const fn new(inner: Arc<LuaInner>, index: c_int) -> Self {
LuaOwnedRef {
inner,
index,
_non_send: PhantomData,
}
}
pub(crate) const fn to_ref(&self) -> LuaRef {
LuaRef {
lua: unsafe { mem::transmute(&self.inner) },
index: self.index,
drop: false,
let lua = self.lua;
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 2);
lua.push_ref(self);
lua.push_ref(other);
ffi::lua_rawequal(lua.state, -1, -2) == 1
}
}
}
#[derive(Debug, Default)]
pub(crate) struct AppData {
#[cfg(not(feature = "send"))]
container: UnsafeCell<FxHashMap<TypeId, RefCell<Box<dyn Any>>>>,
#[cfg(feature = "send")]
container: UnsafeCell<FxHashMap<TypeId, RefCell<Box<dyn Any + Send>>>>,
borrow: Cell<usize>,
}
impl AppData {
#[track_caller]
pub(crate) fn insert<T: MaybeSend + 'static>(&self, data: T) -> Option<T> {
match self.try_insert(data) {
Ok(data) => data,
Err(_) => panic!("cannot mutably borrow app data container"),
}
}
pub(crate) fn try_insert<T: MaybeSend + 'static>(&self, data: T) -> StdResult<Option<T>, T> {
if self.borrow.get() != 0 {
return Err(data);
}
// SAFETY: we checked that there are no other references to the container
Ok(unsafe { &mut *self.container.get() }
.insert(TypeId::of::<T>(), RefCell::new(Box::new(data)))
.and_then(|data| data.into_inner().downcast::<T>().ok().map(|data| *data)))
}
#[track_caller]
pub(crate) fn borrow<T: 'static>(&self) -> Option<AppDataRef<T>> {
let data = unsafe { &*self.container.get() }
.get(&TypeId::of::<T>())?
.borrow();
self.borrow.set(self.borrow.get() + 1);
Some(AppDataRef {
data: Ref::filter_map(data, |data| data.downcast_ref()).ok()?,
borrow: &self.borrow,
})
}
#[track_caller]
pub(crate) fn borrow_mut<T: 'static>(&self) -> Option<AppDataRefMut<T>> {
let data = unsafe { &*self.container.get() }
.get(&TypeId::of::<T>())?
.borrow_mut();
self.borrow.set(self.borrow.get() + 1);
Some(AppDataRefMut {
data: RefMut::filter_map(data, |data| data.downcast_mut()).ok()?,
borrow: &self.borrow,
})
}
#[track_caller]
pub(crate) fn remove<T: 'static>(&self) -> Option<T> {
if self.borrow.get() != 0 {
panic!("cannot mutably borrow app data container");
}
// SAFETY: we checked that there are no other references to the container
unsafe { &mut *self.container.get() }
.remove(&TypeId::of::<T>())?
.into_inner()
.downcast::<T>()
.ok()
.map(|data| *data)
}
}
/// A wrapper type for an immutably borrowed value from an app data container.
///
/// This type is similar to [`Ref`].
pub struct AppDataRef<'a, T: ?Sized + 'a> {
data: Ref<'a, T>,
borrow: &'a Cell<usize>,
}
impl<T: ?Sized> Drop for AppDataRef<'_, T> {
fn drop(&mut self) {
self.borrow.set(self.borrow.get() - 1);
}
}
impl<T: ?Sized> Deref for AppDataRef<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<T: ?Sized + fmt::Display> fmt::Display for AppDataRef<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<T: ?Sized + fmt::Debug> fmt::Debug for AppDataRef<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
/// A wrapper type for a mutably borrowed value from an app data container.
///
/// This type is similar to [`RefMut`].
pub struct AppDataRefMut<'a, T: ?Sized + 'a> {
data: RefMut<'a, T>,
borrow: &'a Cell<usize>,
}
impl<T: ?Sized> Drop for AppDataRefMut<'_, T> {
fn drop(&mut self) {
self.borrow.set(self.borrow.get() - 1);
}
}
impl<T: ?Sized> Deref for AppDataRefMut<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<T: ?Sized> DerefMut for AppDataRefMut<'_, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data
}
}
impl<T: ?Sized + fmt::Display> fmt::Display for AppDataRefMut<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<T: ?Sized + fmt::Debug> fmt::Debug for AppDataRefMut<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
#[cfg(test)]
mod assertions {
use super::*;
static_assertions::assert_impl_all!(RegistryKey: Send, Sync);
static_assertions::assert_not_impl_any!(LuaRef: Send);
#[cfg(feature = "unstable")]
static_assertions::assert_not_impl_any!(LuaOwnedRef: Send);
}
+345 -529
View File
File diff suppressed because it is too large Load Diff
-190
View File
@@ -1,190 +0,0 @@
use crate::error::{Error, Result};
use crate::private::Sealed;
use crate::userdata::{AnyUserData, MetaMethod};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
#[cfg(feature = "async")]
use futures_util::future::{self, LocalBoxFuture};
/// An extension trait for [`AnyUserData`] that provides a variety of convenient functionality.
pub trait AnyUserDataExt<'lua>: Sealed {
/// Gets the value associated to `key` from the userdata, assuming it has `__index` metamethod.
fn get<K: IntoLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V>;
/// Sets the value associated to `key` in the userdata, assuming it has `__newindex` metamethod.
fn set<K: IntoLua<'lua>, V: IntoLua<'lua>>(&self, key: K, value: V) -> Result<()>;
/// Calls the userdata as a function assuming it has `__call` metamethod.
///
/// The metamethod is called with the userdata as its first argument, followed by the passed arguments.
fn call<A, R>(&self, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>;
/// Asynchronously calls the userdata as a function assuming it has `__call` metamethod.
///
/// The metamethod is called with the userdata as its first argument, followed by the passed arguments.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async<A, R>(&self, args: A) -> LocalBoxFuture<'lua, Result<R>>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua;
/// Calls the userdata method, assuming it has `__index` metamethod
/// and a function associated to `name`.
fn call_method<A, R>(&self, name: &str, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>;
/// Gets the function associated to `key` from the table and asynchronously executes it,
/// passing the table itself along with `args` as function arguments and returning Future.
///
/// Requires `feature = "async"`
///
/// This might invoke the `__index` metamethod.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async_method<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua;
/// Gets the function associated to `key` from the table and executes it,
/// passing `args` as function arguments.
///
/// This is a shortcut for
/// `table.get::<_, Function>(key)?.call(args)`
///
/// This might invoke the `__index` metamethod.
fn call_function<A, R>(&self, name: &str, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>;
/// Gets the function associated to `key` from the table and asynchronously executes it,
/// passing `args` as function arguments and returning Future.
///
/// Requires `feature = "async"`
///
/// This might invoke the `__index` metamethod.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async_function<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua;
}
impl<'lua> AnyUserDataExt<'lua> for AnyUserData<'lua> {
fn get<K: IntoLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
let metatable = self.get_metatable()?;
match metatable.get::<Value>(MetaMethod::Index)? {
Value::Table(table) => table.raw_get(key),
Value::Function(func) => func.call((self.clone(), key)),
_ => Err(Error::runtime("attempt to index a userdata value")),
}
}
fn set<K: IntoLua<'lua>, V: IntoLua<'lua>>(&self, key: K, value: V) -> Result<()> {
let metatable = self.get_metatable()?;
match metatable.get::<Value>(MetaMethod::NewIndex)? {
Value::Table(table) => table.raw_set(key, value),
Value::Function(func) => func.call((self.clone(), key, value)),
_ => Err(Error::runtime("attempt to index a userdata value")),
}
}
fn call<A, R>(&self, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
let metatable = self.get_metatable()?;
match metatable.get::<Value>(MetaMethod::Call)? {
Value::Function(func) => func.call((self.clone(), args)),
_ => Err(Error::runtime("attempt to call a userdata value")),
}
}
#[cfg(feature = "async")]
fn call_async<A, R>(&self, args: A) -> LocalBoxFuture<'lua, Result<R>>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua,
{
let metatable = match self.get_metatable() {
Ok(metatable) => metatable,
Err(err) => return Box::pin(future::err(err)),
};
match metatable.get::<Value>(MetaMethod::Call) {
Ok(Value::Function(func)) => {
let mut args = match args.into_lua_multi(self.0.lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
args.push_front(Value::UserData(self.clone()));
Box::pin(async move { func.call_async(args).await })
}
Ok(_) => Box::pin(future::err(Error::runtime(
"attempt to call a userdata value",
))),
Err(err) => Box::pin(future::err(err)),
}
}
fn call_method<A, R>(&self, name: &str, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
self.call_function(name, (self.clone(), args))
}
#[cfg(feature = "async")]
fn call_async_method<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua,
{
self.call_async_function(name, (self.clone(), args))
}
fn call_function<A, R>(&self, name: &str, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
match self.get(name)? {
Value::Function(func) => func.call(args),
val => {
let msg = format!("attempt to call a {} value", val.type_name());
Err(Error::runtime(msg))
}
}
}
#[cfg(feature = "async")]
fn call_async_function<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua,
{
match self.get(name) {
Ok(Value::Function(func)) => {
let args = match args.into_lua_multi(self.0.lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
Box::pin(async move { func.call_async(args).await })
}
Ok(val) => {
let msg = format!("attempt to call a {} value", val.type_name());
Box::pin(future::err(Error::runtime(msg)))
}
Err(err) => Box::pin(future::err(err)),
}
}
}
+470 -634
View File
File diff suppressed because it is too large Load Diff
+69 -105
View File
@@ -1,20 +1,17 @@
use std::any::{Any, TypeId};
use std::borrow::Cow;
use std::ffi::CStr;
use std::fmt::Write;
use std::mem::MaybeUninit;
use std::os::raw::{c_char, c_int, c_void};
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
use std::sync::Arc;
use std::{mem, ptr, slice, str};
use std::{mem, ptr, slice};
use once_cell::sync::Lazy;
use rustc_hash::FxHashMap;
use crate::error::{Error, Result};
use crate::memory::MemoryState;
pub(crate) use short_names::short_type_name;
use crate::ffi;
static METATABLE_CACHE: Lazy<FxHashMap<TypeId, u8>> = Lazy::new(|| {
let mut map = FxHashMap::with_capacity_and_hasher(32, Default::default());
@@ -50,6 +47,7 @@ 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 {
@@ -61,12 +59,18 @@ impl StackGuard {
StackGuard {
state,
top: ffi::lua_gettop(state),
extra: 0,
}
}
// Same as `new()`, but allows specifying the expected stack size at the end of the scope.
pub const fn with_top(state: *mut ffi::lua_State, top: c_int) -> StackGuard {
StackGuard { state, top }
// Similar to `new`, but checks and keeps `extra` elements from top of the stack on Drop.
#[inline]
pub unsafe fn new_extra(state: *mut ffi::lua_State, extra: c_int) -> StackGuard {
StackGuard {
state,
top: ffi::lua_gettop(state),
extra,
}
}
}
@@ -74,11 +78,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);
}
}
}
@@ -93,14 +100,12 @@ impl Drop for StackGuard {
pub unsafe fn protect_lua_call(
state: *mut ffi::lua_State,
nargs: c_int,
f: unsafe extern "C-unwind" fn(*mut ffi::lua_State) -> c_int,
f: unsafe extern "C" fn(*mut ffi::lua_State) -> c_int,
) -> Result<()> {
let stack_start = ffi::lua_gettop(state) - nargs;
MemoryState::relax_limit_with(state, || {
ffi::lua_pushcfunction(state, error_traceback);
ffi::lua_pushcfunction(state, f);
});
ffi::lua_pushcfunction(state, error_traceback);
ffi::lua_pushcfunction(state, f);
if nargs > 0 {
ffi::lua_rotate(state, stack_start + 1, 2);
}
@@ -138,7 +143,7 @@ where
nresults: c_int,
}
unsafe extern "C-unwind" fn do_call<F, R>(state: *mut ffi::lua_State) -> c_int
unsafe extern "C" fn do_call<F, R>(state: *mut ffi::lua_State) -> c_int
where
F: Fn(*mut ffi::lua_State) -> R,
R: Copy,
@@ -157,10 +162,8 @@ where
let stack_start = ffi::lua_gettop(state) - nargs;
MemoryState::relax_limit_with(state, || {
ffi::lua_pushcfunction(state, error_traceback);
ffi::lua_pushcfunction(state, do_call::<F, R>);
});
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);
}
@@ -257,12 +260,10 @@ pub unsafe fn push_string(state: *mut ffi::lua_State, s: &[u8], protect: bool) -
#[inline]
pub unsafe fn push_table(
state: *mut ffi::lua_State,
narr: usize,
nrec: usize,
narr: c_int,
nrec: c_int,
protect: bool,
) -> Result<()> {
let narr: c_int = narr.try_into().unwrap_or(c_int::MAX);
let nrec: c_int = nrec.try_into().unwrap_or(c_int::MAX);
if protect {
protect_lua!(state, 0, 1, |state| ffi::lua_createtable(state, narr, nrec))
} else {
@@ -272,7 +273,11 @@ pub unsafe fn push_table(
}
// Uses 4 stack spaces, does not call checkstack.
pub unsafe fn rawset_field(state: *mut ffi::lua_State, table: c_int, field: &str) -> Result<()> {
pub unsafe fn rawset_field<S>(state: *mut ffi::lua_State, table: c_int, field: &S) -> Result<()>
where
S: AsRef<[u8]> + ?Sized,
{
let field = field.as_ref();
ffi::lua_pushvalue(state, table);
protect_lua!(state, 2, 0, |state| {
ffi::lua_pushlstring(state, field.as_ptr() as *const c_char, field.len());
@@ -300,7 +305,7 @@ pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool)
#[cfg(feature = "luau")]
#[inline]
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> {
unsafe extern "C-unwind" fn destructor<T>(ud: *mut c_void) {
unsafe extern "C" fn destructor<T>(ud: *mut c_void) {
ptr::drop_in_place(ud as *mut T);
}
@@ -405,17 +410,14 @@ pub unsafe fn get_gc_userdata<T: Any>(
ud
}
unsafe extern "C-unwind" fn lua_error_impl(state: *mut ffi::lua_State) -> c_int {
unsafe extern "C" fn lua_error_impl(state: *mut ffi::lua_State) -> c_int {
ffi::lua_error(state);
}
unsafe extern "C-unwind" fn lua_isfunction_impl(state: *mut ffi::lua_State) -> c_int {
ffi::lua_pushboolean(state, ffi::lua_isfunction(state, -1));
1
}
unsafe extern "C-unwind" fn lua_istable_impl(state: *mut ffi::lua_State) -> c_int {
ffi::lua_pushboolean(state, ffi::lua_istable(state, -1));
unsafe extern "C" fn lua_isfunction_impl(state: *mut ffi::lua_State) -> c_int {
let t = ffi::lua_type(state, -1);
ffi::lua_pop(state, 1);
ffi::lua_pushboolean(state, (t == ffi::LUA_TFUNCTION) as c_int);
1
}
@@ -429,19 +431,14 @@ unsafe fn init_userdata_metatable_index(state: *mut ffi::lua_State) -> Result<()
// Create and cache `__index` generator
let code = cstr!(
r#"
local error, isfunction, istable = ...
local error, isfunction = ...
return function (__index, field_getters, methods)
-- Common case: has field getters and index is a table
if field_getters ~= nil and methods == nil and istable(__index) then
return function (self, key)
local field_getter = field_getters[key]
if field_getter ~= nil then
return field_getter(self)
end
return __index[key]
end
-- Fastpath to return methods table for index access
if __index == nil and field_getters == nil then
return methods
end
-- Alternatively return a function for index access
return function (self, key)
if field_getters ~= nil then
local field_getter = field_getters[key]
@@ -476,13 +473,7 @@ unsafe fn init_userdata_metatable_index(state: *mut ffi::lua_State) -> Result<()
}
ffi::lua_pushcfunction(state, lua_error_impl);
ffi::lua_pushcfunction(state, lua_isfunction_impl);
ffi::lua_pushcfunction(state, lua_istable_impl);
ffi::lua_call(state, 3, 1);
#[cfg(feature = "luau-jit")]
if ffi::luau_codegen_supported() != 0 {
ffi::luau_codegen_compile(state, -1);
}
ffi::lua_call(state, 2, 1);
// Store in the registry
ffi::lua_pushvalue(state, -1);
@@ -532,11 +523,6 @@ pub unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Re
ffi::lua_pushcfunction(state, lua_isfunction_impl);
ffi::lua_call(state, 2, 1);
#[cfg(feature = "luau-jit")]
if ffi::luau_codegen_supported() != 0 {
ffi::luau_codegen_compile(state, -1);
}
// Store in the registry
ffi::lua_pushvalue(state, -1);
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, newindex_key);
@@ -551,13 +537,12 @@ pub unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Re
// 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(
pub unsafe fn init_userdata_metatable<T>(
state: *mut ffi::lua_State,
metatable: c_int,
field_getters: Option<c_int>,
field_setters: Option<c_int>,
methods: Option<c_int>,
extra_init: Option<fn(*mut ffi::lua_State) -> Result<()>>,
) -> Result<()> {
ffi::lua_pushvalue(state, metatable);
@@ -604,9 +589,10 @@ pub unsafe fn init_userdata_metatable(
rawset_field(state, -2, "__newindex")?;
}
// Additional initialization
if let Some(extra_init) = extra_init {
extra_init(state)?;
#[cfg(not(feature = "luau"))]
{
ffi::lua_pushcfunction(state, userdata_destructor::<T>);
rawset_field(state, -2, "__gc")?;
}
ffi::lua_pushboolean(state, 0);
@@ -618,7 +604,7 @@ pub unsafe fn init_userdata_metatable(
}
#[cfg(not(feature = "luau"))]
pub unsafe extern "C-unwind" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int {
pub unsafe extern "C" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int {
// It's probably NOT a good idea to catch Rust panics in finalizer
// Lua 5.4 ignores it, other versions generates `LUA_ERRGCMM` without calling message handler
take_userdata::<T>(state);
@@ -663,6 +649,8 @@ where
Ok(Err(err)) => {
ffi::lua_settop(state, 1);
let wrapped_error = ud as *mut WrappedFailure;
// Build `CallbackError` with traceback
let traceback = if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
ffi::luaL_traceback(state, state, ptr::null(), 0);
@@ -673,8 +661,10 @@ where
"<not enough stack space for traceback>".to_string()
};
let cause = Arc::new(err);
let wrapped_error = WrappedFailure::Error(Error::CallbackError { traceback, cause });
ptr::write(ud, wrapped_error);
ptr::write(
wrapped_error,
WrappedFailure::Error(Error::CallbackError { traceback, cause }),
);
get_gc_metatable::<WrappedFailure>(state);
ffi::lua_setmetatable(state, -2);
@@ -682,7 +672,7 @@ where
}
Err(p) => {
ffi::lua_settop(state, 1);
ptr::write(ud, WrappedFailure::Panic(Some(p)));
ptr::write(ud as *mut WrappedFailure, WrappedFailure::Panic(Some(p)));
get_gc_metatable::<WrappedFailure>(state);
ffi::lua_setmetatable(state, -2);
ffi::lua_error(state)
@@ -690,14 +680,7 @@ where
}
}
pub unsafe extern "C-unwind" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
// Luau calls error handler for memory allocation errors, skip it
// See https://github.com/Roblox/luau/issues/880
#[cfg(feature = "luau")]
if MemoryState::limit_reached(state) {
return 0;
}
pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
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.
@@ -730,7 +713,7 @@ pub unsafe fn error_traceback_thread(state: *mut ffi::lua_State, thread: *mut ff
}
// A variant of `pcall` that does not allow Lua to catch Rust panics from `callback_error`.
pub unsafe extern "C-unwind" fn safe_pcall(state: *mut ffi::lua_State) -> c_int {
pub unsafe extern "C" fn safe_pcall(state: *mut ffi::lua_State) -> c_int {
ffi::luaL_checkstack(state, 2, ptr::null());
let top = ffi::lua_gettop(state);
@@ -756,8 +739,8 @@ pub unsafe extern "C-unwind" fn safe_pcall(state: *mut ffi::lua_State) -> c_int
}
// A variant of `xpcall` that does not allow Lua to catch Rust panics from `callback_error`.
pub unsafe extern "C-unwind" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
unsafe extern "C-unwind" fn xpcall_msgh(state: *mut ffi::lua_State) -> c_int {
pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
unsafe extern "C" fn xpcall_msgh(state: *mut ffi::lua_State) -> c_int {
ffi::luaL_checkstack(state, 2, ptr::null());
if let Some(WrappedFailure::Panic(_)) =
@@ -871,7 +854,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
// Create error and panic metatables
unsafe extern "C-unwind" fn error_tostring(state: *mut ffi::lua_State) -> c_int {
unsafe extern "C" fn error_tostring(state: *mut ffi::lua_State) -> c_int {
callback_error(state, |_| {
check_stack(state, 3)?;
@@ -886,7 +869,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
// Depending on how the API is used and what error types scripts are given, it may
// be possible to make this consume arbitrary amounts of memory (for example, some
// kind of recursive error structure?)
let _ = write!(&mut (*err_buf), "{error}");
let _ = write!(&mut (*err_buf), "{}", error);
Ok(err_buf)
}
Some(WrappedFailure::Panic(Some(ref panic))) => {
@@ -897,9 +880,9 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
ffi::lua_pop(state, 2);
if let Some(msg) = panic.downcast_ref::<&str>() {
let _ = write!(&mut (*err_buf), "{msg}");
let _ = write!(&mut (*err_buf), "{}", msg);
} else if let Some(msg) = panic.downcast_ref::<String>() {
let _ = write!(&mut (*err_buf), "{msg}");
let _ = write!(&mut (*err_buf), "{}", msg);
} else {
let _ = write!(&mut (*err_buf), "<panic>");
};
@@ -929,7 +912,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
// Create destructed userdata metatable
unsafe extern "C-unwind" fn destructed_error(state: *mut ffi::lua_State) -> c_int {
unsafe extern "C" fn destructed_error(state: *mut ffi::lua_State) -> c_int {
callback_error(state, |_| Err(Error::CallbackDestructed))
}
@@ -1012,7 +995,7 @@ impl WrappedFailure {
let size = mem::size_of::<WrappedFailure>();
#[cfg(feature = "luau")]
let ud = {
unsafe extern "C-unwind" fn destructor(p: *mut c_void) {
unsafe extern "C" fn destructor(p: *mut c_void) {
ptr::drop_in_place(p as *mut WrappedFailure);
}
ffi::lua_newuserdatadtor(state, size, destructor) as *mut Self
@@ -1048,10 +1031,7 @@ pub(crate) unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> Stri
let v = ffi::lua_tovector(state, index);
mlua_debug_assert!(!v.is_null(), "vector is null");
let (x, y, z) = (*v, *v.add(1), *v.add(2));
#[cfg(not(feature = "luau-vector4"))]
return format!("vector({x}, {y}, {z})");
#[cfg(feature = "luau-vector4")]
return format!("vector({x}, {y}, {z}, {w})", w = *v.add(3));
format!("vector({},{},{})", x, y, z)
}
ffi::LUA_TSTRING => {
let mut size = 0;
@@ -1073,30 +1053,14 @@ pub(crate) unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_Stat
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, key);
}
pub(crate) unsafe fn ptr_to_str<'a>(input: *const c_char) -> Option<&'a str> {
pub(crate) unsafe fn ptr_to_cstr_bytes<'a>(input: *const c_char) -> Option<&'a [u8]> {
if input.is_null() {
return None;
}
str::from_utf8(CStr::from_ptr(input).to_bytes()).ok()
}
pub(crate) unsafe fn ptr_to_lossy_str<'a>(input: *const c_char) -> Option<Cow<'a, str>> {
if input.is_null() {
return None;
}
Some(String::from_utf8_lossy(CStr::from_ptr(input).to_bytes()))
}
pub(crate) fn linenumber_to_usize(n: c_int) -> Option<usize> {
match n {
n if n < 0 => None,
n => Some(n as usize),
}
Some(CStr::from_ptr(input).to_bytes())
}
static DESTRUCTED_USERDATA_METATABLE: u8 = 0;
static ERROR_PRINT_BUFFER_KEY: u8 = 0;
static USERDATA_METATABLE_INDEX: u8 = 0;
static USERDATA_METATABLE_NEWINDEX: u8 = 0;
mod short_names;
-85
View File
@@ -1,85 +0,0 @@
//! Mostly copied from [bevy_utils]
//!
//! [bevy_utils]: https://github.com/bevyengine/bevy/blob/main/crates/bevy_utils/src/short_names.rs
use std::any::type_name;
/// Returns a short version of a type name `T` without all module paths.
///
/// The short name of a type is its full name as returned by
/// [`std::any::type_name`], but with the prefix of all paths removed. For
/// example, the short name of `alloc::vec::Vec<core::option::Option<u32>>`
/// would be `Vec<Option<u32>>`.
pub(crate) fn short_type_name<T: ?Sized>() -> String {
let full_name = type_name::<T>();
// Generics result in nested paths within <..> blocks.
// Consider "core::option::Option<alloc::string::String>".
// To tackle this, we parse the string from left to right, collapsing as we go.
let mut index: usize = 0;
let end_of_string = full_name.len();
let mut parsed_name = String::new();
while index < end_of_string {
let rest_of_string = full_name.get(index..end_of_string).unwrap_or_default();
// Collapse everything up to the next special character,
// then skip over it
if let Some(special_character_index) = rest_of_string
.find(|c: char| [' ', '<', '>', '(', ')', '[', ']', ',', ';'].contains(&c))
{
let segment_to_collapse = rest_of_string
.get(0..special_character_index)
.unwrap_or_default();
parsed_name += collapse_type_name(segment_to_collapse);
// Insert the special character
let special_character =
&rest_of_string[special_character_index..=special_character_index];
parsed_name.push_str(special_character);
match special_character {
">" | ")" | "]"
if rest_of_string[special_character_index + 1..].starts_with("::") =>
{
parsed_name.push_str("::");
// Move the index past the "::"
index += special_character_index + 3;
}
// Move the index just past the special character
_ => index += special_character_index + 1,
}
} else {
// If there are no special characters left, we're done!
parsed_name += collapse_type_name(rest_of_string);
index = end_of_string;
}
}
parsed_name
}
#[inline(always)]
fn collapse_type_name(string: &str) -> &str {
string.rsplit("::").next().unwrap()
}
#[cfg(test)]
mod tests {
use super::short_type_name;
use std::collections::HashMap;
#[test]
fn tests() {
assert_eq!(short_type_name::<String>(), "String");
assert_eq!(short_type_name::<Option<String>>(), "Option<String>");
assert_eq!(short_type_name::<(String, &str)>(), "(String, &str)");
assert_eq!(short_type_name::<[i32; 3]>(), "[i32; 3]");
assert_eq!(
short_type_name::<HashMap<String, Option<[i32; 3]>>>(),
"HashMap<String, Option<[i32; 3]>>"
);
assert_eq!(
short_type_name::<dyn Fn(i32) -> i32>(),
"dyn Fn(i32) -> i32"
);
}
}
+69 -694
View File
@@ -1,24 +1,17 @@
use std::borrow::Cow;
use std::cmp::Ordering;
use std::collections::HashSet;
use std::iter::{self, FromIterator};
use std::ops::Index;
use std::os::raw::{c_int, c_void};
use std::string::String as StdString;
use std::sync::Arc;
use std::{fmt, mem, ptr, slice, str, vec};
use num_traits::FromPrimitive;
use std::os::raw::c_void;
use std::{ptr, slice, str, vec};
#[cfg(feature = "serialize")]
use {
crate::table::SerializableTable,
rustc_hash::FxHashSet,
serde::ser::{self, Serialize, Serializer},
std::{cell::RefCell, convert::TryInto, rc::Rc, result::Result as StdResult},
std::convert::TryInto,
std::result::Result as StdResult,
};
use crate::error::{Error, Result};
use crate::ffi;
use crate::function::Function;
use crate::lua::Lua;
use crate::string::String;
@@ -26,12 +19,11 @@ use crate::table::Table;
use crate::thread::Thread;
use crate::types::{Integer, LightUserData, Number};
use crate::userdata::AnyUserData;
use crate::util::{check_stack, StackGuard};
/// 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(Clone)]
#[derive(Debug, Clone)]
pub enum Value<'lua> {
/// The Lua value `nil`.
Nil,
@@ -48,7 +40,7 @@ pub enum Value<'lua> {
/// A Luau vector.
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
Vector(crate::types::Vector),
Vector(f32, f32, f32),
/// An interned string, managed by Lua.
///
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
@@ -69,12 +61,6 @@ pub enum Value<'lua> {
pub use self::Value::Nil;
impl<'lua> Value<'lua> {
/// A special value (lightuserdata) to represent null value.
///
/// It can be used in Lua tables without downsides of `nil`.
pub const NULL: Value<'static> = Value::LightUserData(LightUserData(ptr::null_mut()));
/// Returns type name of this value.
pub const fn type_name(&self) -> &'static str {
match *self {
Value::Nil => "nil",
@@ -83,7 +69,7 @@ impl<'lua> Value<'lua> {
Value::Integer(_) => "integer",
Value::Number(_) => "number",
#[cfg(feature = "luau")]
Value::Vector(_) => "vector",
Value::Vector(_, _, _) => "vector",
Value::String(_) => "string",
Value::Table(_) => "table",
Value::Function(_) => "function",
@@ -107,7 +93,7 @@ impl<'lua> Value<'lua> {
match (self, other.as_ref()) {
(Value::Table(a), Value::Table(b)) => a.equals(b),
(Value::UserData(a), Value::UserData(b)) => a.equals(b),
(a, b) => Ok(a == b),
_ => Ok(self == other.as_ref()),
}
}
@@ -120,404 +106,19 @@ impl<'lua> Value<'lua> {
/// Typically this function is used only for hashing and debug information.
#[inline]
pub fn to_pointer(&self) -> *const c_void {
match self {
Value::LightUserData(ud) => ud.0,
Value::String(String(r))
| Value::Table(Table(r))
| Value::Function(Function(r))
| Value::Thread(Thread(r, ..))
| Value::UserData(AnyUserData(r)) => r.to_pointer(),
_ => ptr::null(),
}
}
/// Converts the value to a string.
///
/// If the value has a metatable with a `__tostring` method, then it will be called to get the result.
pub fn to_string(&self) -> Result<StdString> {
match self {
Value::Nil => Ok("nil".to_string()),
Value::Boolean(b) => Ok(b.to_string()),
Value::LightUserData(ud) if ud.0.is_null() => Ok("null".to_string()),
Value::LightUserData(ud) => Ok(format!("lightuserdata: {:p}", ud.0)),
Value::Integer(i) => Ok(i.to_string()),
Value::Number(n) => Ok(n.to_string()),
#[cfg(feature = "luau")]
Value::Vector(v) => Ok(v.to_string()),
Value::String(s) => Ok(s.to_str()?.to_string()),
Value::Table(Table(r))
| Value::Function(Function(r))
| Value::Thread(Thread(r, ..))
| Value::UserData(AnyUserData(r)) => unsafe {
let state = r.lua.state();
let _guard = StackGuard::new(state);
check_stack(state, 3)?;
r.lua.push_ref(r);
protect_lua!(state, 1, 1, fn(state) {
ffi::luaL_tolstring(state, -1, ptr::null_mut());
})?;
Ok(String(r.lua.pop_ref()).to_str()?.to_string())
},
Value::Error(err) => Ok(err.to_string()),
}
}
/// Returns `true` if the value is a [`Nil`].
#[inline]
pub fn is_nil(&self) -> bool {
self == &Nil
}
/// Returns `true` if the value is a [`NULL`].
#[inline]
pub fn is_null(&self) -> bool {
self == &Self::NULL
}
/// Returns `true` if the value is a boolean.
#[inline]
pub fn is_boolean(&self) -> bool {
self.as_boolean().is_some()
}
/// Cast the value to boolean.
///
/// If the value is a Boolean, returns it or `None` otherwise.
#[inline]
pub fn as_boolean(&self) -> Option<bool> {
match *self {
Value::Boolean(b) => Some(b),
_ => None,
}
}
/// Returns `true` if the value is a [`LightUserData`].
#[inline]
pub fn is_light_userdata(&self) -> bool {
self.as_light_userdata().is_some()
}
/// Cast the value to [`LightUserData`].
///
/// If the value is a [`LightUserData`], returns it or `None` otherwise.
#[inline]
pub fn as_light_userdata(&self) -> Option<LightUserData> {
match *self {
Value::LightUserData(l) => Some(l),
_ => None,
}
}
/// Returns `true` if the value is an [`Integer`].
#[inline]
pub fn is_integer(&self) -> bool {
self.as_integer().is_some()
}
/// Cast the value to [`Integer`].
///
/// If the value is a Lua [`Integer`], returns it or `None` otherwise.
#[inline]
pub fn as_integer(&self) -> Option<Integer> {
match *self {
Value::Integer(i) => Some(i),
_ => None,
}
}
/// Cast the value to `i32`.
///
/// If the value is a Lua [`Integer`], try to convert it to `i32` or return `None` otherwise.
#[inline]
pub fn as_i32(&self) -> Option<i32> {
#[allow(clippy::useless_conversion)]
self.as_integer().and_then(|i| i32::try_from(i).ok())
}
/// Cast the value to `u32`.
///
/// If the value is a Lua [`Integer`], try to convert it to `u32` or return `None` otherwise.
#[inline]
pub fn as_u32(&self) -> Option<u32> {
self.as_integer().and_then(|i| u32::try_from(i).ok())
}
/// Cast the value to `i64`.
///
/// If the value is a Lua [`Integer`], try to convert it to `i64` or return `None` otherwise.
#[inline]
pub fn as_i64(&self) -> Option<i64> {
#[allow(clippy::useless_conversion)]
self.as_integer().and_then(|i| i64::try_from(i).ok())
}
/// Cast the value to `u64`.
///
/// If the value is a Lua [`Integer`], try to convert it to `u64` or return `None` otherwise.
#[inline]
pub fn as_u64(&self) -> Option<u64> {
self.as_integer().and_then(|i| u64::try_from(i).ok())
}
/// Cast the value to `isize`.
///
/// If the value is a Lua [`Integer`], try to convert it to `isize` or return `None` otherwise.
#[inline]
pub fn as_isize(&self) -> Option<isize> {
self.as_integer().and_then(|i| isize::try_from(i).ok())
}
/// Cast the value to `usize`.
///
/// If the value is a Lua [`Integer`], try to convert it to `usize` or return `None` otherwise.
#[inline]
pub fn as_usize(&self) -> Option<usize> {
self.as_integer().and_then(|i| usize::try_from(i).ok())
}
/// Returns `true` if the value is a Lua [`Number`].
#[inline]
pub fn is_number(&self) -> bool {
self.as_number().is_some()
}
/// Cast the value to [`Number`].
///
/// If the value is a Lua [`Number`], returns it or `None` otherwise.
#[inline]
pub fn as_number(&self) -> Option<Number> {
match *self {
Value::Number(n) => Some(n),
_ => None,
}
}
/// Cast the value to `f32`.
///
/// If the value is a Lua [`Number`], try to convert it to `f32` or return `None` otherwise.
#[inline]
pub fn as_f32(&self) -> Option<f32> {
self.as_number().and_then(f32::from_f64)
}
/// Cast the value to `f64`.
///
/// If the value is a Lua [`Number`], try to convert it to `f64` or return `None` otherwise.
#[inline]
pub fn as_f64(&self) -> Option<f64> {
self.as_number()
}
/// Returns `true` if the value is a Lua [`String`].
#[inline]
pub fn is_string(&self) -> bool {
self.as_string().is_some()
}
/// Cast the value to Lua [`String`].
///
/// If the value is a Lua [`String`], returns it or `None` otherwise.
#[inline]
pub fn as_string(&self) -> Option<&String> {
match self {
Value::String(s) => Some(s),
_ => None,
}
}
/// Cast the value to [`str`].
///
/// If the value is a Lua [`String`], try to convert it to [`str`] or return `None` otherwise.
#[inline]
pub fn as_str(&self) -> Option<&str> {
self.as_string().and_then(|s| s.to_str().ok())
}
/// Cast the value to [`Cow<str>`].
///
/// If the value is a Lua [`String`], converts it to [`Cow<str>`] or returns `None` otherwise.
#[inline]
pub fn as_string_lossy(&self) -> Option<Cow<str>> {
self.as_string().map(|s| s.to_string_lossy())
}
/// Returns `true` if the value is a Lua [`Table`].
#[inline]
pub fn is_table(&self) -> bool {
self.as_table().is_some()
}
/// Cast the value to [`Table`].
///
/// If the value is a Lua [`Table`], returns it or `None` otherwise.
#[inline]
pub fn as_table(&self) -> Option<&Table> {
match self {
Value::Table(t) => Some(t),
_ => None,
}
}
/// Returns `true` if the value is a Lua [`Thread`].
#[inline]
pub fn is_thread(&self) -> bool {
self.as_thread().is_some()
}
/// Cast the value to [`Thread`].
///
/// If the value is a Lua [`Thread`], returns it or `None` otherwise.
#[inline]
pub fn as_thread(&self) -> Option<&Thread> {
match self {
Value::Thread(t) => Some(t),
_ => None,
}
}
/// Returns `true` if the value is a Lua [`Function`].
#[inline]
pub fn is_function(&self) -> bool {
self.as_function().is_some()
}
/// Cast the value to [`Function`].
///
/// If the value is a Lua [`Function`], returns it or `None` otherwise.
#[inline]
pub fn as_function(&self) -> Option<&Function> {
match self {
Value::Function(f) => Some(f),
_ => None,
}
}
/// Returns `true` if the value is an [`AnyUserData`].
#[inline]
pub fn is_userdata(&self) -> bool {
self.as_userdata().is_some()
}
/// Cast the value to [`AnyUserData`].
///
/// If the value is an [`AnyUserData`], returns it or `None` otherwise.
#[inline]
pub fn as_userdata(&self) -> Option<&AnyUserData> {
match self {
Value::UserData(ud) => Some(ud),
_ => None,
}
}
/// Wrap reference to this Value into [`SerializableValue`].
///
/// This allows customizing serialization behavior using serde.
#[cfg(feature = "serialize")]
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
#[doc(hidden)]
pub fn to_serializable(&self) -> SerializableValue<'_, 'lua> {
SerializableValue::new(self, Default::default(), None)
}
// Compares two values.
// Used to sort values for Debug printing.
pub(crate) fn cmp(&self, other: &Self) -> Ordering {
fn cmp_num(a: Number, b: Number) -> Ordering {
match (a, b) {
_ if a < b => Ordering::Less,
_ if a > b => Ordering::Greater,
_ => Ordering::Equal,
unsafe {
match self {
Value::LightUserData(ud) => ud.0,
Value::Table(t) => t.to_pointer(),
Value::String(s) => s.to_pointer(),
Value::Function(Function(r))
| Value::Thread(Thread(r))
| Value::UserData(AnyUserData(r)) => {
ffi::lua_topointer(r.lua.ref_thread(), r.index)
}
_ => ptr::null(),
}
}
match (self, other) {
// Nil
(Value::Nil, Value::Nil) => Ordering::Equal,
(Value::Nil, _) => Ordering::Less,
(_, Value::Nil) => Ordering::Greater,
// Null (a special case)
(Value::LightUserData(ud1), Value::LightUserData(ud2)) if ud1 == ud2 => Ordering::Equal,
(Value::LightUserData(ud1), _) if ud1.0.is_null() => Ordering::Less,
(_, Value::LightUserData(ud2)) if ud2.0.is_null() => Ordering::Greater,
// Boolean
(Value::Boolean(a), Value::Boolean(b)) => a.cmp(b),
(Value::Boolean(_), _) => Ordering::Less,
(_, Value::Boolean(_)) => Ordering::Greater,
// Integer && Number
(Value::Integer(a), Value::Integer(b)) => a.cmp(b),
(&Value::Integer(a), &Value::Number(b)) => cmp_num(a as Number, b),
(&Value::Number(a), &Value::Integer(b)) => cmp_num(a, b as Number),
(&Value::Number(a), &Value::Number(b)) => cmp_num(a, b),
(Value::Integer(_) | Value::Number(_), _) => Ordering::Less,
(_, Value::Integer(_) | Value::Number(_)) => Ordering::Greater,
// String
(Value::String(a), Value::String(b)) => a.as_bytes().cmp(b.as_bytes()),
(Value::String(_), _) => Ordering::Less,
(_, Value::String(_)) => Ordering::Greater,
// Other variants can be randomly ordered
(a, b) => a.to_pointer().cmp(&b.to_pointer()),
}
}
pub(crate) fn fmt_pretty(
&self,
fmt: &mut fmt::Formatter,
recursive: bool,
ident: usize,
visited: &mut HashSet<*const c_void>,
) -> fmt::Result {
match self {
Value::Nil => write!(fmt, "nil"),
Value::Boolean(b) => write!(fmt, "{b}"),
Value::LightUserData(ud) if ud.0.is_null() => write!(fmt, "null"),
Value::LightUserData(ud) => write!(fmt, "lightuserdata: {:?}", ud.0),
Value::Integer(i) => write!(fmt, "{i}"),
Value::Number(n) => write!(fmt, "{n}"),
#[cfg(feature = "luau")]
Value::Vector(v) => write!(fmt, "{v}"),
Value::String(s) => write!(fmt, "{s:?}"),
Value::Table(t) if recursive && !visited.contains(&t.to_pointer()) => {
t.fmt_pretty(fmt, ident, visited)
}
t @ Value::Table(_) => write!(fmt, "table: {:?}", t.to_pointer()),
f @ Value::Function(_) => write!(fmt, "function: {:?}", f.to_pointer()),
t @ Value::Thread(_) => write!(fmt, "thread: {:?}", t.to_pointer()),
u @ Value::UserData(ud) => {
// Try `__name/__type` first then `__tostring`
let name = ud.type_name().ok().flatten();
let s = name
.map(|name| format!("{name}: {:?}", u.to_pointer()))
.or_else(|| u.to_string().ok())
.unwrap_or_else(|| format!("userdata: {:?}", u.to_pointer()));
write!(fmt, "{s}")
}
Value::Error(e) if recursive => write!(fmt, "{e:?}"),
Value::Error(_) => write!(fmt, "error"),
}
}
}
impl fmt::Debug for Value<'_> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
if fmt.alternate() {
return self.fmt_pretty(fmt, true, 0, &mut HashSet::new());
}
match self {
Value::Nil => write!(fmt, "Nil"),
Value::Boolean(b) => write!(fmt, "Boolean({b})"),
Value::LightUserData(ud) => write!(fmt, "{ud:?}"),
Value::Integer(i) => write!(fmt, "Integer({i})"),
Value::Number(n) => write!(fmt, "Number({n})"),
#[cfg(feature = "luau")]
Value::Vector(v) => write!(fmt, "{v:?}"),
Value::String(s) => write!(fmt, "String({s:?})"),
Value::Table(t) => write!(fmt, "{t:?}"),
Value::Function(f) => write!(fmt, "{f:?}"),
Value::Thread(t) => write!(fmt, "{t:?}"),
Value::UserData(ud) => write!(fmt, "{ud:?}"),
Value::Error(e) => write!(fmt, "Error({e:?})"),
}
}
}
@@ -532,7 +133,7 @@ impl<'lua> PartialEq for Value<'lua> {
(Value::Number(a), Value::Integer(b)) => *a == *b as Number,
(Value::Number(a), Value::Number(b)) => *a == *b,
#[cfg(feature = "luau")]
(Value::Vector(v1), Value::Vector(v2)) => v1 == v2,
(Value::Vector(x1, y1, z1), Value::Vector(x2, y2, z2)) => (x1, y1, z1) == (x2, y2, z2),
(Value::String(a), Value::String(b)) => a == b,
(Value::Table(a), Value::Table(b)) => a == b,
(Value::Function(a), Value::Function(b)) => a == b,
@@ -550,219 +151,60 @@ impl<'lua> AsRef<Value<'lua>> for Value<'lua> {
}
}
/// A wrapped [`Value`] with customized serialization behavior.
#[cfg(feature = "serialize")]
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
pub struct SerializableValue<'a, 'lua> {
value: &'a Value<'lua>,
options: crate::serde::de::Options,
// In many cases we don't need `visited` map, so don't allocate memory by default
visited: Option<Rc<RefCell<FxHashSet<*const c_void>>>>,
}
#[cfg(feature = "serialize")]
impl<'lua> Serialize for Value<'lua> {
#[inline]
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
SerializableValue::new(self, Default::default(), None).serialize(serializer)
}
}
#[cfg(feature = "serialize")]
impl<'a, 'lua> SerializableValue<'a, 'lua> {
#[inline]
pub(crate) fn new(
value: &'a Value<'lua>,
options: crate::serde::de::Options,
visited: Option<&Rc<RefCell<FxHashSet<*const c_void>>>>,
) -> Self {
if let Value::Table(_) = value {
return Self {
value,
options,
// We need to always initialize the `visited` map for Tables
visited: visited.cloned().or_else(|| Some(Default::default())),
};
}
Self {
value,
options,
visited: None,
}
}
/// If true, an attempt to serialize types such as [`Function`], [`Thread`], [`LightUserData`]
/// and [`Error`] will cause an error.
/// Otherwise these types skipped when iterating or serialized as unit type.
///
/// Default: **true**
#[must_use]
pub const fn deny_unsupported_types(mut self, enabled: bool) -> Self {
self.options.deny_unsupported_types = enabled;
self
}
/// If true, an attempt to serialize a recursive table (table that refers to itself)
/// will cause an error.
/// Otherwise subsequent attempts to serialize the same table will be ignored.
///
/// Default: **true**
#[must_use]
pub const fn deny_recursive_tables(mut self, enabled: bool) -> Self {
self.options.deny_recursive_tables = enabled;
self
}
/// If true, keys in tables will be iterated (and serialized) in sorted order.
///
/// Default: **false**
#[must_use]
pub const fn sort_keys(mut self, enabled: bool) -> Self {
self.options.sort_keys = enabled;
self
}
}
#[cfg(feature = "serialize")]
impl<'a, 'lua> Serialize for SerializableValue<'a, 'lua> {
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
where
S: Serializer,
{
match self.value {
match self {
Value::Nil => serializer.serialize_unit(),
Value::Boolean(b) => serializer.serialize_bool(*b),
#[allow(clippy::useless_conversion)]
Value::Integer(i) => serializer
.serialize_i64((*i).try_into().expect("cannot convert Lua Integer to i64")),
.serialize_i64((*i).try_into().expect("cannot convert lua_Integer to i64")),
#[allow(clippy::useless_conversion)]
Value::Number(n) => serializer.serialize_f64(*n),
#[cfg(feature = "luau")]
Value::Vector(v) => v.serialize(serializer),
Value::Vector(x, y, z) => (x, y, z).serialize(serializer),
Value::String(s) => s.serialize(serializer),
Value::Table(t) => {
let visited = self.visited.as_ref().unwrap().clone();
SerializableTable::new(t, self.options, visited).serialize(serializer)
}
Value::Table(t) => t.serialize(serializer),
Value::UserData(ud) => ud.serialize(serializer),
Value::LightUserData(ud) if ud.0.is_null() => serializer.serialize_none(),
Value::UserData(ud) if ud.is_serializable() || self.options.deny_unsupported_types => {
ud.serialize(serializer)
}
Value::Function(_)
| Value::Thread(_)
| Value::UserData(_)
| Value::LightUserData(_)
| Value::Error(_) => {
if self.options.deny_unsupported_types {
let msg = format!("cannot serialize <{}>", self.value.type_name());
Err(ser::Error::custom(msg))
} else {
serializer.serialize_unit()
}
Value::Error(_) | Value::LightUserData(_) | Value::Function(_) | Value::Thread(_) => {
let msg = format!("cannot serialize <{}>", self.type_name());
Err(ser::Error::custom(msg))
}
}
}
}
/// Trait for types convertible to `Value`.
pub trait IntoLua<'lua>: Sized {
pub trait ToLua<'lua> {
/// Performs the conversion.
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>>;
/// Pushes the value into the Lua stack.
///
/// # Safety
/// This method does not check Lua stack space.
#[doc(hidden)]
#[inline]
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
lua.push_value(self.into_lua(lua)?)
}
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>>;
}
/// Trait for types convertible from `Value`.
pub trait FromLua<'lua>: Sized {
/// Performs the conversion.
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self>;
/// Performs the conversion for an argument (eg. function argument).
///
/// `i` is the argument index (position),
/// `to` is a function name that received the argument.
#[doc(hidden)]
#[inline]
fn from_lua_arg(arg: Value<'lua>, i: usize, to: Option<&str>, lua: &'lua Lua) -> Result<Self> {
Self::from_lua(arg, lua).map_err(|err| Error::BadArgument {
to: to.map(|s| s.to_string()),
pos: i,
name: None,
cause: Arc::new(err),
})
}
/// Performs the conversion for a value in the Lua stack at index `idx`.
#[doc(hidden)]
#[inline]
unsafe fn from_stack(idx: c_int, lua: &'lua Lua) -> Result<Self> {
Self::from_lua(lua.stack_value(idx), lua)
}
/// Same as `from_lua_arg` but for a value in the Lua stack at index `idx`.
#[doc(hidden)]
#[inline]
unsafe fn from_stack_arg(
idx: c_int,
i: usize,
to: Option<&str>,
lua: &'lua Lua,
) -> Result<Self> {
Self::from_stack(idx, lua).map_err(|err| Error::BadArgument {
to: to.map(|s| s.to_string()),
pos: i,
name: None,
cause: Arc::new(err),
})
}
fn from_lua(lua_value: Value<'lua>, lua: &'lua Lua) -> Result<Self>;
}
/// Multiple Lua values used for both argument passing and also for multiple return values.
#[derive(Debug, Clone)]
pub struct MultiValue<'lua> {
vec: Vec<Value<'lua>>,
lua: Option<&'lua Lua>,
}
impl Drop for MultiValue<'_> {
fn drop(&mut self) {
if let Some(lua) = self.lua {
let vec = mem::take(&mut self.vec);
lua.push_multivalue_to_pool(vec);
}
}
}
pub struct MultiValue<'lua>(Vec<Value<'lua>>);
impl<'lua> MultiValue<'lua> {
/// Creates an empty `MultiValue` containing no values.
pub const fn new() -> MultiValue<'lua> {
MultiValue {
vec: Vec::new(),
lua: None,
}
MultiValue(Vec::new())
}
/// Similar to `new` but can reuse previously used container with allocated capacity.
/// Similar to `new` but can return previously used container with allocated capacity.
#[inline]
pub(crate) fn with_lua_and_capacity(lua: &'lua Lua, capacity: usize) -> MultiValue<'lua> {
let vec = lua
.pop_multivalue_from_pool()
.map(|mut vec| {
vec.reserve(capacity);
vec
})
.unwrap_or_else(|| Vec::with_capacity(capacity));
MultiValue {
vec,
lua: Some(lua),
}
pub(crate) fn new_or_cached(lua: &'lua Lua) -> MultiValue<'lua> {
lua.new_or_cached_multivalue()
}
}
@@ -785,10 +227,8 @@ impl<'lua> IntoIterator for MultiValue<'lua> {
type IntoIter = iter::Rev<vec::IntoIter<Value<'lua>>>;
#[inline]
fn into_iter(mut self) -> Self::IntoIter {
let vec = mem::take(&mut self.vec);
mem::forget(self);
vec.into_iter().rev()
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter().rev()
}
}
@@ -798,7 +238,7 @@ impl<'a, 'lua> IntoIterator for &'a MultiValue<'lua> {
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.vec.iter().rev()
self.0.iter().rev()
}
}
@@ -821,60 +261,64 @@ impl<'lua> Index<usize> for MultiValue<'lua> {
impl<'lua> MultiValue<'lua> {
#[inline]
pub fn from_vec(mut vec: Vec<Value<'lua>>) -> MultiValue<'lua> {
vec.reverse();
MultiValue { vec, lua: None }
pub fn from_vec(mut v: Vec<Value<'lua>>) -> MultiValue<'lua> {
v.reverse();
MultiValue(v)
}
#[inline]
pub fn into_vec(mut self) -> Vec<Value<'lua>> {
let mut vec = mem::take(&mut self.vec);
mem::forget(self);
vec.reverse();
vec
pub fn into_vec(self) -> Vec<Value<'lua>> {
let mut v = self.0;
v.reverse();
v
}
#[inline]
pub fn get(&self, index: usize) -> Option<&Value<'lua>> {
if index < self.vec.len() {
return self.vec.get(self.vec.len() - index - 1);
if index < self.0.len() {
return self.0.get(self.0.len() - index - 1);
}
None
}
#[inline]
pub(crate) fn reserve(&mut self, size: usize) {
self.0.reserve(size);
}
#[inline]
pub fn pop_front(&mut self) -> Option<Value<'lua>> {
self.vec.pop()
self.0.pop()
}
#[inline]
pub fn push_front(&mut self, value: Value<'lua>) {
self.vec.push(value);
self.0.push(value);
}
#[inline]
pub fn clear(&mut self) {
self.vec.clear();
self.0.clear();
}
#[inline]
pub fn len(&self) -> usize {
self.vec.len()
self.0.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.vec.is_empty()
self.0.is_empty()
}
#[inline]
pub fn iter(&self) -> iter::Rev<slice::Iter<Value<'lua>>> {
self.vec.iter().rev()
self.0.iter().rev()
}
#[inline]
pub(crate) fn drain_all(&mut self) -> iter::Rev<vec::Drain<Value<'lua>>> {
self.vec.drain(..).rev()
self.0.drain(..).rev()
}
#[inline]
@@ -882,39 +326,22 @@ impl<'lua> MultiValue<'lua> {
&mut self,
iter: impl IntoIterator<Item = Result<Value<'lua>>>,
) -> Result<()> {
self.vec.clear();
self.0.clear();
for value in iter {
self.vec.push(value?);
self.0.push(value?);
}
self.vec.reverse();
self.0.reverse();
Ok(())
}
}
/// Trait for types convertible to any number of Lua values.
///
/// This is a generalization of `IntoLua`, allowing any number of resulting Lua values instead of just
/// one. Any type that implements `IntoLua` will automatically implement this trait.
pub trait IntoLuaMulti<'lua>: Sized {
/// This is a generalization of `ToLua`, allowing any number of resulting Lua values instead of just
/// one. Any type that implements `ToLua` will automatically implement this trait.
pub trait ToLuaMulti<'lua> {
/// Performs the conversion.
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>>;
/// Pushes the values into the Lua stack.
///
/// Returns number of pushed values.
#[doc(hidden)]
#[inline]
unsafe fn push_into_stack_multi(self, lua: &'lua Lua) -> Result<c_int> {
let mut values = self.into_lua_multi(lua)?;
let len: c_int = values.len().try_into().unwrap();
unsafe {
check_stack(lua.state(), len + 1)?;
for v in values.drain_all() {
lua.push_value(v)?;
}
}
Ok(len)
}
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>>;
}
/// Trait for types that can be created from an arbitrary number of Lua values.
@@ -929,56 +356,4 @@ pub trait FromLuaMulti<'lua>: Sized {
/// assigning values. Similarly, if not enough values are given, conversions should assume that
/// any missing values are nil.
fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self>;
/// Performs the conversion for a list of arguments.
///
/// `i` is an index (position) of the first argument,
/// `to` is a function name that received the arguments.
#[doc(hidden)]
#[inline]
fn from_lua_args(
args: MultiValue<'lua>,
i: usize,
to: Option<&str>,
lua: &'lua Lua,
) -> Result<Self> {
let _ = (i, to);
Self::from_lua_multi(args, lua)
}
/// Performs the conversion for a number of values in the Lua stack.
#[doc(hidden)]
#[inline]
unsafe fn from_stack_multi(nvals: c_int, lua: &'lua Lua) -> Result<Self> {
let mut values = MultiValue::with_lua_and_capacity(lua, nvals as usize);
for idx in 1..=nvals {
values.push_front(lua.stack_value(-idx));
}
if nvals > 0 {
// It's safe to clear the stack as all references moved to ref thread
ffi::lua_pop(lua.state(), nvals);
}
Self::from_lua_multi(values, lua)
}
/// Same as `from_lua_args` but for a number of values in the Lua stack.
#[doc(hidden)]
#[inline]
unsafe fn from_stack_args(
nargs: c_int,
i: usize,
to: Option<&str>,
lua: &'lua Lua,
) -> Result<Self> {
let _ = (i, to);
Self::from_stack_multi(nargs, lua)
}
}
#[cfg(test)]
mod assertions {
use super::*;
static_assertions::assert_not_impl_any!(Value: Send);
static_assertions::assert_not_impl_any!(MultiValue: Send);
}
+3 -3
View File
@@ -1,8 +1,8 @@
[lua54_coverage]
features = "lua54,vendored,async,serialize,macros,unstable"
features = "lua54,vendored,async,serialize,macros"
[lua51_coverage]
features = "lua51,vendored,async,serialize,macros,unstable"
features = "lua51,vendored,async,serialize,macros"
[luau_coverage]
features = "luau,async,serialize,macros,unstable"
features = "luau,async,serialize,macros"
+156 -128
View File
@@ -1,19 +1,21 @@
#![cfg(feature = "async")]
use std::sync::{Arc, Mutex};
use std::cell::Cell;
use std::rc::Rc;
use std::sync::{
atomic::{AtomicI64, AtomicU64, Ordering},
Arc,
};
use std::time::Duration;
use futures_timer::Delay;
use futures_util::stream::TryStreamExt;
use mlua::{
AnyUserDataExt, Error, Function, Lua, LuaOptions, MultiValue, Result, StdLib, Table, TableExt,
UserData, UserDataMethods, Value,
Error, Function, Lua, LuaOptions, Result, StdLib, Table, TableExt, Thread, UserData,
UserDataMethods, Value,
};
async fn sleep_ms(ms: u64) {
tokio::time::sleep(Duration::from_millis(ms)).await;
}
#[tokio::test]
async fn test_async_function() -> Result<()> {
let lua = Lua::new();
@@ -28,25 +30,12 @@ async fn test_async_function() -> Result<()> {
Ok(())
}
#[tokio::test]
async fn test_async_function_wrap() -> Result<()> {
let lua = Lua::new();
let f = Function::wrap_async(|_, s: String| async move { Ok(s) });
lua.globals().set("f", f)?;
let res: String = lua.load(r#"f("hello")"#).eval_async().await?;
assert_eq!(res, "hello");
Ok(())
}
#[tokio::test]
async fn test_async_sleep() -> Result<()> {
let lua = Lua::new();
let sleep = lua.create_async_function(move |_lua, n: u64| async move {
sleep_ms(n).await;
Delay::new(Duration::from_millis(n)).await;
Ok(format!("elapsed:{}ms", n))
})?;
lua.globals().set("sleep", sleep)?;
@@ -62,7 +51,7 @@ async fn test_async_call() -> Result<()> {
let lua = Lua::new();
let hello = lua.create_async_function(|_lua, name: String| async move {
sleep_ms(10).await;
Delay::new(Duration::from_millis(10)).await;
Ok(format!("hello, {}!", name))
})?;
@@ -82,25 +71,6 @@ async fn test_async_call() -> Result<()> {
Ok(())
}
#[tokio::test]
async fn test_async_call_many_returns() -> Result<()> {
let lua = Lua::new();
let hello = lua.create_async_function(|_lua, ()| async move {
sleep_ms(10).await;
Ok(("a", "b", "c", 1))
})?;
let vals = hello.call_async::<_, MultiValue>(()).await?;
assert_eq!(vals.len(), 4);
assert_eq!(vals[0].to_string()?, "a");
assert_eq!(vals[1].to_string()?, "b");
assert_eq!(vals[2].to_string()?, "c");
assert_eq!(vals[3], Value::Integer(1));
Ok(())
}
#[tokio::test]
async fn test_async_bind_call() -> Result<()> {
let lua = Lua::new();
@@ -124,7 +94,7 @@ async fn test_async_handle_yield() -> Result<()> {
let lua = Lua::new();
let sum = lua.create_async_function(|_lua, (a, b): (i64, i64)| async move {
sleep_ms(10).await;
Delay::new(Duration::from_millis(10)).await;
Ok(a + b)
})?;
@@ -182,10 +152,10 @@ async fn test_async_return_async_closure() -> Result<()> {
let lua = Lua::new();
let f = lua.create_async_function(|lua, a: i64| async move {
sleep_ms(10).await;
Delay::new(Duration::from_millis(10)).await;
let g = lua.create_async_function(move |_, b: i64| async move {
sleep_ms(10).await;
Delay::new(Duration::from_millis(10)).await;
return Ok(a + b);
})?;
@@ -275,7 +245,7 @@ async fn test_async_thread() -> Result<()> {
let f = lua.create_async_function(move |_lua, ()| {
let cnt3 = cnt2.clone();
async move {
sleep_ms(*cnt3.as_ref()).await;
Delay::new(Duration::from_millis(*cnt3.as_ref())).await;
Ok("done")
}
})?;
@@ -291,61 +261,49 @@ async fn test_async_thread() -> Result<()> {
Ok(())
}
#[test]
fn test_async_thread_capture() -> Result<()> {
let lua = Lua::new();
let f = lua.create_async_function(move |_lua, v: Value| async move {
tokio::task::yield_now().await;
drop(v);
Ok(())
})?;
let thread = lua.create_thread(f)?;
// After first resume, `v: Value` is captured in the coroutine
thread.resume::<_, ()>("abc").unwrap();
drop(thread);
Ok(())
}
#[tokio::test]
async fn test_async_table() -> Result<()> {
let options = LuaOptions::new().thread_pool_size(4);
let options = LuaOptions::new().thread_cache_size(4);
let lua = Lua::new_with(StdLib::ALL_SAFE, options)?;
let table = lua.create_table()?;
table.set("val", 10)?;
let get_value = lua.create_async_function(|_, table: Table| async move {
sleep_ms(10).await;
Delay::new(Duration::from_millis(10)).await;
table.get::<_, i64>("val")
})?;
table.set("get_value", get_value)?;
let set_value = lua.create_async_function(|_, (table, n): (Table, i64)| async move {
sleep_ms(10).await;
Delay::new(Duration::from_millis(10)).await;
table.set("val", n)
})?;
table.set("set_value", set_value)?;
let sleep = lua.create_async_function(|_, n| async move {
sleep_ms(n).await;
Delay::new(Duration::from_millis(n)).await;
Ok(format!("elapsed:{}ms", n))
})?;
table.set("sleep", sleep)?;
assert_eq!(
table.call_async_method::<_, i64>("get_value", ()).await?,
table
.call_async_method::<_, _, i64>("get_value", ())
.await?,
10
);
table.call_async_method("set_value", 15).await?;
assert_eq!(
table.call_async_method::<_, i64>("get_value", ()).await?,
table
.call_async_method::<_, _, i64>("get_value", ())
.await?,
15
);
assert_eq!(
table.call_async_function::<_, String>("sleep", 7).await?,
table
.call_async_function::<_, _, String>("sleep", 7)
.await?,
"elapsed:7ms"
);
@@ -353,17 +311,17 @@ async fn test_async_table() -> Result<()> {
}
#[tokio::test]
async fn test_async_thread_pool() -> Result<()> {
let options = LuaOptions::new().thread_pool_size(4);
async fn test_async_thread_cache() -> Result<()> {
let options = LuaOptions::new().thread_cache_size(4);
let lua = Lua::new_with(StdLib::ALL_SAFE, options)?;
let error_f = lua.create_async_function(|_, ()| async move {
sleep_ms(10).await;
Err::<(), _>(Error::runtime("test"))
Delay::new(Duration::from_millis(10)).await;
Err::<(), _>(Error::RuntimeError("test".to_string()))
})?;
let sleep = lua.create_async_function(|_, n| async move {
sleep_ms(n).await;
Delay::new(Duration::from_millis(n)).await;
Ok(format!("elapsed:{}ms", n))
})?;
@@ -376,30 +334,31 @@ async fn test_async_thread_pool() -> Result<()> {
#[tokio::test]
async fn test_async_userdata() -> Result<()> {
struct MyUserData(u64);
#[derive(Clone)]
struct MyUserData(Arc<AtomicU64>);
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("get_value", |_, data, ()| async move {
sleep_ms(10).await;
Ok(data.0)
Delay::new(Duration::from_millis(10)).await;
Ok(data.0.load(Ordering::Relaxed))
});
methods.add_async_method_mut("set_value", |_, data, n| async move {
sleep_ms(10).await;
data.0 = n;
methods.add_async_method("set_value", |_, data, n| async move {
Delay::new(Duration::from_millis(10)).await;
data.0.store(n, Ordering::Relaxed);
Ok(())
});
methods.add_async_function("sleep", |_, n| async move {
sleep_ms(n).await;
Delay::new(Duration::from_millis(n)).await;
Ok(format!("elapsed:{}ms", n))
});
#[cfg(not(any(feature = "lua51", feature = "luau")))]
methods.add_async_meta_method(mlua::MetaMethod::Call, |_, data, ()| async move {
let n = data.0;
sleep_ms(n).await;
let n = data.0.load(Ordering::Relaxed);
Delay::new(Duration::from_millis(n)).await;
Ok(format!("elapsed:{}ms", n))
});
@@ -407,26 +366,25 @@ async fn test_async_userdata() -> Result<()> {
methods.add_async_meta_method(
mlua::MetaMethod::Index,
|_, data, key: String| async move {
sleep_ms(10).await;
Delay::new(Duration::from_millis(10)).await;
match key.as_str() {
"ms" => Ok(Some(data.0 as f64)),
"s" => Ok(Some((data.0 as f64) / 1000.0)),
"ms" => Ok(Some(data.0.load(Ordering::Relaxed) as f64)),
"s" => Ok(Some((data.0.load(Ordering::Relaxed) as f64) / 1000.0)),
_ => Ok(None),
}
},
);
#[cfg(not(any(feature = "lua51", feature = "luau")))]
methods.add_async_meta_method_mut(
methods.add_async_meta_method(
mlua::MetaMethod::NewIndex,
|_, data, (key, value): (String, f64)| async move {
sleep_ms(10).await;
Delay::new(Duration::from_millis(10)).await;
match key.as_str() {
"ms" => data.0 = value as u64,
"s" => data.0 = (value * 1000.0) as u64,
_ => return Err(Error::external(format!("key '{}' not found", key))),
"ms" => Ok(data.0.store(value as u64, Ordering::Relaxed)),
"s" => Ok(data.0.store((value * 1000.0) as u64, Ordering::Relaxed)),
_ => Err(Error::external(format!("key '{}' not found", key))),
}
Ok(())
},
);
}
@@ -435,7 +393,7 @@ async fn test_async_userdata() -> Result<()> {
let lua = Lua::new();
let globals = lua.globals();
let userdata = lua.create_userdata(MyUserData(11))?;
let userdata = lua.create_userdata(MyUserData(Arc::new(AtomicU64::new(11))))?;
globals.set("userdata", userdata.clone())?;
lua.load(
@@ -465,14 +423,6 @@ async fn test_async_userdata() -> Result<()> {
.exec_async()
.await?;
userdata.call_async_method("set_value", 24).await?;
let n: u64 = userdata.call_async_method("get_value", ()).await?;
assert_eq!(n, 24);
userdata.call_async_function("sleep", 15).await?;
#[cfg(not(any(feature = "lua51", feature = "luau")))]
assert_eq!(userdata.call_async::<_, String>(()).await?, "elapsed:24ms");
Ok(())
}
@@ -489,7 +439,7 @@ async fn test_async_thread_error() -> Result<()> {
let lua = Lua::new();
let result = lua
.load("function x(...) error(...) end x(...)")
.set_name("chunk")
.set_name("chunk")?
.call_async::<_, ()>(MyUserData)
.await;
assert!(
@@ -500,42 +450,120 @@ async fn test_async_thread_error() -> Result<()> {
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[tokio::test]
async fn test_owned_async_call() -> Result<()> {
let lua = Lua::new();
async fn test_async_scope() -> Result<()> {
let ref lua = Lua::new();
let hello = lua
.create_async_function(|_, name: String| async move {
sleep_ms(10).await;
Ok(format!("hello, {}!", name))
})?
.into_owned();
drop(lua);
let ref rc = Rc::new(Cell::new(0));
assert_eq!(hello.call_async::<_, String>("alex").await?, "hello, alex!");
let fut = lua.async_scope(|scope| async move {
let f = scope.create_async_function(move |_, n: u64| {
let rc2 = rc.clone();
async move {
rc2.set(42);
Delay::new(Duration::from_millis(n)).await;
assert_eq!(Rc::strong_count(&rc2), 2);
Ok(())
}
})?;
lua.globals().set("f", f.clone())?;
assert_eq!(Rc::strong_count(rc), 1);
let _ = f.call_async::<u64, ()>(10).await?;
assert_eq!(Rc::strong_count(rc), 1);
// Create future in partialy polled state (Poll::Pending)
let g = lua.create_thread(f)?;
g.resume::<u64, ()>(10)?;
lua.globals().set("g", g)?;
assert_eq!(Rc::strong_count(rc), 2);
Ok(())
});
assert_eq!(Rc::strong_count(rc), 1);
let _ = fut.await?;
assert_eq!(Rc::strong_count(rc), 1);
match lua
.globals()
.get::<_, Function>("f")?
.call_async::<_, ()>(10)
.await
{
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
Error::CallbackDestructed => {}
e => panic!("expected `CallbackDestructed` error cause, got {:?}", e),
},
r => panic!("improper return for destructed function: {:?}", r),
};
match lua.globals().get::<_, Thread>("g")?.resume::<_, Value>(()) {
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
Error::CallbackDestructed => {}
e => panic!("expected `CallbackDestructed` error cause, got {:?}", e),
},
r => panic!("improper return for destructed function: {:?}", r),
};
Ok(())
}
#[tokio::test]
async fn test_async_terminate() -> Result<()> {
let lua = Lua::new();
async fn test_async_scope_userdata() -> Result<()> {
#[derive(Clone)]
struct MyUserData(Arc<AtomicI64>);
let mutex = Arc::new(Mutex::new(0u32));
let mutex2 = mutex.clone();
let func = lua.create_async_function(move |_, ()| {
let mutex = mutex2.clone();
async move {
let _guard = mutex.lock();
sleep_ms(100).await;
Ok(())
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("get_value", |_, data, ()| async move {
Delay::new(Duration::from_millis(10)).await;
Ok(data.0.load(Ordering::Relaxed))
});
methods.add_async_method("set_value", |_, data, n| async move {
Delay::new(Duration::from_millis(10)).await;
data.0.store(n, Ordering::Relaxed);
Ok(())
});
methods.add_async_function("sleep", |_, n| async move {
Delay::new(Duration::from_millis(n)).await;
Ok(format!("elapsed:{}ms", n))
});
}
})?;
}
let _ = tokio::time::timeout(Duration::from_millis(30), func.call_async::<_, ()>(())).await;
lua.gc_collect()?;
assert!(mutex.try_lock().is_ok());
let ref lua = Lua::new();
let ref arc = Arc::new(AtomicI64::new(11));
lua.async_scope(|scope| async move {
let ud = scope.create_userdata(MyUserData(arc.clone()))?;
lua.globals().set("userdata", ud)?;
lua.load(
r#"
assert(userdata:get_value() == 11)
userdata:set_value(12)
assert(userdata.sleep(5) == "elapsed:5ms")
assert(userdata:get_value() == 12)
"#,
)
.exec_async()
.await
})
.await?;
assert_eq!(Arc::strong_count(arc), 1);
match lua.load("userdata:get_value()").exec_async().await {
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
Error::CallbackDestructed => {}
e => panic!("expected `CallbackDestructed` error cause, got {:?}", e),
},
r => panic!("improper return for destructed userdata: {:?}", r),
};
Ok(())
}
+5 -16
View File
@@ -1,7 +1,7 @@
use std::fs;
use std::io;
use mlua::{Lua, Result};
use mlua::{Error, Lua, Result};
#[test]
fn test_chunk_path() -> Result<()> {
@@ -14,11 +14,12 @@ fn test_chunk_path() -> Result<()> {
return 321
"#,
)?;
let i: i32 = lua.load(&*temp_dir.path().join("module.lua")).eval()?;
let i: i32 = lua.load(&temp_dir.path().join("module.lua")).eval()?;
assert_eq!(i, 321);
match lua.load(&*temp_dir.path().join("module2.lua")).exec() {
Err(err) if err.downcast_ref::<io::Error>().unwrap().kind() == io::ErrorKind::NotFound => {}
match lua.load(&temp_dir.path().join("module2.lua")).exec() {
Err(Error::ExternalError(err))
if err.downcast_ref::<io::Error>().unwrap().kind() == io::ErrorKind::NotFound => {}
res => panic!("expected io::Error, got {:?}", res),
};
@@ -36,24 +37,12 @@ fn test_chunk_macro() -> Result<()> {
let data = lua.create_table()?;
data.raw_set("num", 1)?;
let ud = mlua::AnyUserData::wrap("hello");
let f = mlua::Function::wrap(|_lua, ()| Ok(()));
lua.globals().set("g", 123)?;
let string = String::new();
let str = string.as_str();
lua.load(mlua::chunk! {
assert($name == "Rustacean")
assert(type($table) == "table")
assert($table[1] == 1)
assert(type($data) == "table")
assert($data.num == 1)
assert(type($ud) == "userdata")
assert(type($f) == "function")
assert(type($str) == "string")
assert($str == "")
assert(g == 123)
s = 321
})
+1 -5
View File
@@ -15,11 +15,7 @@ fn test_compilation() {
t.compile_fail("tests/compile/static_callback_args.rs");
#[cfg(feature = "async")]
{
t.compile_fail("tests/compile/async_any_userdata_method.rs");
t.compile_fail("tests/compile/async_nonstatic_userdata.rs");
t.compile_fail("tests/compile/async_userdata_method.rs");
}
t.compile_fail("tests/compile/async_nonstatic_userdata.rs");
#[cfg(feature = "send")]
t.compile_fail("tests/compile/non_send.rs");
@@ -1,14 +0,0 @@
use mlua::{UserDataMethods, Lua};
fn main() {
let lua = Lua::new();
lua.register_userdata_type::<String>(|reg| {
let s = String::new();
let mut s = &s;
reg.add_async_method("t", |_, this: &String, ()| async {
s = this;
Ok(())
});
}).unwrap();
}
@@ -1,81 +0,0 @@
error: lifetime may not live long enough
--> tests/compile/async_any_userdata_method.rs:9:58
|
9 | reg.add_async_method("t", |_, this: &String, ()| async {
| ___________________________________----------------------_^
| | | |
| | | return type of closure `[async block@$DIR/tests/compile/async_any_userdata_method.rs:9:58: 12:10]` contains a lifetime `'2`
| | lifetime `'1` represents this closure's body
10 | | s = this;
11 | | Ok(())
12 | | });
| |_________^ returning this value requires that `'1` must outlive `'2`
|
= note: closure implements `Fn`, so references to captured variables can't escape the closure
error[E0596]: cannot borrow `s` as mutable, as it is a captured variable in a `Fn` closure
--> tests/compile/async_any_userdata_method.rs:9:58
|
9 | reg.add_async_method("t", |_, this: &String, ()| async {
| __________________________________________________________^
10 | | s = this;
| | - mutable borrow occurs due to use of `s` in closure
11 | | Ok(())
12 | | });
| |_________^ cannot borrow as mutable
error[E0597]: `s` does not live long enough
--> tests/compile/async_any_userdata_method.rs:8:21
|
8 | let mut s = &s;
| ^^ borrowed value does not live long enough
9 | / reg.add_async_method("t", |_, this: &String, ()| async {
10 | | s = this;
11 | | Ok(())
12 | | });
| |__________- argument requires that `s` is borrowed for `'static`
13 | }).unwrap();
| - `s` dropped here while still borrowed
error[E0521]: borrowed data escapes outside of closure
--> tests/compile/async_any_userdata_method.rs:9:9
|
6 | lua.register_userdata_type::<String>(|reg| {
| ---
| |
| `reg` is a reference that is only valid in the closure body
| has type `&mut LuaUserDataRegistry<'1, std::string::String>`
...
9 | / reg.add_async_method("t", |_, this: &String, ()| async {
10 | | s = this;
11 | | Ok(())
12 | | });
| | ^
| | |
| |__________`reg` escapes the closure body here
| argument requires that `'1` must outlive `'static`
|
= note: requirement occurs because of a mutable reference to `LuaUserDataRegistry<'_, std::string::String>`
= note: mutable references are invariant over their type parameter
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error[E0373]: closure may outlive the current function, but it borrows `s`, which is owned by the current function
--> tests/compile/async_any_userdata_method.rs:9:35
|
9 | reg.add_async_method("t", |_, this: &String, ()| async {
| ^^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `s`
10 | s = this;
| - `s` is borrowed here
|
note: function requires argument type to outlive `'static`
--> tests/compile/async_any_userdata_method.rs:9:9
|
9 | / reg.add_async_method("t", |_, this: &String, ()| async {
10 | | s = this;
11 | | Ok(())
12 | | });
| |__________^
help: to force the closure to take ownership of `s` (and any other referenced variables), use the `move` keyword
|
9 | reg.add_async_method("t", move |_, this: &String, ()| async {
| ++++
@@ -4,8 +4,11 @@ error: lifetime may not live long enough
7 | impl<'a> UserData for MyUserData<'a> {
| -- lifetime `'a` defined here
8 | fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
| ---- lifetime `'lua` defined here
9 | / methods.add_async_method("print", |_, data, ()| async move {
10 | | println!("{}", data.0);
11 | | Ok(())
12 | | });
| |______________^ requires that `'a` must outlive `'static`
| |______________^ argument requires that `'a` must outlive `'lua`
|
= help: consider adding the following bound: `'a: 'lua`
-14
View File
@@ -1,14 +0,0 @@
use mlua::{UserData, UserDataMethods};
struct MyUserData;
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("method", |_, this: &'static Self, ()| async {
Ok(())
});
// ^ lifetime may not live long enough
}
}
fn main() {}
@@ -1,17 +0,0 @@
warning: unused variable: `this`
--> tests/compile/async_userdata_method.rs:7:48
|
7 | methods.add_async_method("method", |_, this: &'static Self, ()| async {
| ^^^^ help: if this is intentional, prefix it with an underscore: `_this`
|
= note: `#[warn(unused_variables)]` on by default
error: lifetime may not live long enough
--> tests/compile/async_userdata_method.rs:7:9
|
6 | fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
| ---- lifetime `'lua` defined here
7 | / methods.add_async_method("method", |_, this: &'static Self, ()| async {
8 | | Ok(())
9 | | });
| |__________^ argument requires that `'lua` must outlive `'static`
+20 -26
View File
@@ -1,28 +1,22 @@
error[E0277]: the type `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
7 | catch_unwind(|| lua.create_table().unwrap());
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
| |
| required by a bound introduced by this call
|
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::ExtraData>`
= note: required because it appears within the type `ArcInner<UnsafeCell<ExtraData>>`
= note: required because it appears within the type `PhantomData<ArcInner<UnsafeCell<ExtraData>>>`
= note: required because it appears within the type `Arc<UnsafeCell<ExtraData>>`
= note: required because it appears within the type `LuaInner`
= note: required because it appears within the type `ArcInner<LuaInner>`
= note: required because it appears within the type `PhantomData<ArcInner<LuaInner>>`
= note: required because it appears within the type `Arc<LuaInner>`
= note: required because it appears within the type `Lua`
= note: required for `&Lua` to implement `UnwindSafe`
error[E0277]: the type `UnsafeCell<mlua::lua::LuaInner>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> tests/compile/lua_norefunwindsafe.rs:7:5
|
7 | catch_unwind(|| lua.create_table().unwrap());
| ^^^^^^^^^^^^ `UnsafeCell<mlua::lua::LuaInner>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::LuaInner>`
= note: required because it appears within the type `alloc::sync::ArcInner<UnsafeCell<mlua::lua::LuaInner>>`
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<UnsafeCell<mlua::lua::LuaInner>>>`
= note: required because it appears within the type `Arc<UnsafeCell<mlua::lua::LuaInner>>`
= note: required because it appears within the type `Lua`
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
note: required because it's used within this closure
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
7 | catch_unwind(|| lua.create_table().unwrap());
| ^^
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
7 | catch_unwind(|| lua.create_table().unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: required by a bound in `catch_unwind`
--> $RUST/std/src/panic.rs
|
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
| ^^^^^^^^^^ required by this bound in `catch_unwind`
--> $RUST/std/src/panic.rs
|
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
| ^^^^^^^^^^ required by this bound in `catch_unwind`
+23 -22
View File
@@ -1,25 +1,26 @@
error[E0277]: `Rc<Cell<i32>>` cannot be sent between threads safely
--> tests/compile/non_send.rs:11:25
|
11 | lua.create_function(move |_, ()| {
| --------------- ^-----------
| | |
| _________|_______________within this `[closure@$DIR/tests/compile/non_send.rs:11:25: 11:37]`
| | |
| | required by a bound introduced by this call
12 | | Ok(data.get())
13 | | })?
| |_____^ `Rc<Cell<i32>>` cannot be sent between threads safely
|
= help: within `[closure@$DIR/tests/compile/non_send.rs:11:25: 11:37]`, the trait `Send` is not implemented for `Rc<Cell<i32>>`
--> tests/compile/non_send.rs:11:9
|
11 | lua.create_function(move |_, ()| {
| _________^^^^^^^^^^^^^^^_-
| | |
| | `Rc<Cell<i32>>` cannot be sent between threads safely
12 | | Ok(data.get())
13 | | })?
| |_____- within this `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`
|
= help: within `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`, the trait `Send` is not implemented for `Rc<Cell<i32>>`
note: required because it's used within this closure
--> tests/compile/non_send.rs:11:25
|
11 | lua.create_function(move |_, ()| {
| ^^^^^^^^^^^^
= note: required for `[closure@$DIR/tests/compile/non_send.rs:11:25: 11:37]` to implement `mlua::types::MaybeSend`
--> tests/compile/non_send.rs:11:25
|
11 | lua.create_function(move |_, ()| {
| _________________________^
12 | | Ok(data.get())
13 | | })?
| |_____^
= note: required because of the requirements on the impl of `mlua::types::MaybeSend` for `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`
note: required by a bound in `Lua::create_function`
--> src/lua.rs
|
| F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
| ^^^^^^^^^ required by this bound in `Lua::create_function`
--> src/lua.rs
|
| F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
| ^^^^^^^^^ required by this bound in `Lua::create_function`
+22 -28
View File
@@ -1,30 +1,24 @@
error[E0277]: the type `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> tests/compile/ref_nounwindsafe.rs:8:18
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
| |
| required by a bound introduced by this call
|
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::ExtraData>`
= note: required because it appears within the type `ArcInner<UnsafeCell<ExtraData>>`
= note: required because it appears within the type `PhantomData<ArcInner<UnsafeCell<ExtraData>>>`
= note: required because it appears within the type `Arc<UnsafeCell<ExtraData>>`
= note: required because it appears within the type `LuaInner`
= note: required because it appears within the type `ArcInner<LuaInner>`
= note: required because it appears within the type `PhantomData<ArcInner<LuaInner>>`
= note: required because it appears within the type `Arc<LuaInner>`
= note: required because it appears within the type `Lua`
= note: required for `&Lua` to implement `UnwindSafe`
= note: required because it appears within the type `LuaRef<'_>`
= note: required because it appears within the type `Table<'_>`
error[E0277]: the type `UnsafeCell<mlua::lua::LuaInner>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> tests/compile/ref_nounwindsafe.rs:8:5
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ^^^^^^^^^^^^ `UnsafeCell<mlua::lua::LuaInner>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::LuaInner>`
= note: required because it appears within the type `alloc::sync::ArcInner<UnsafeCell<mlua::lua::LuaInner>>`
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<UnsafeCell<mlua::lua::LuaInner>>>`
= note: required because it appears within the type `Arc<UnsafeCell<mlua::lua::LuaInner>>`
= note: required because it appears within the type `Lua`
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
= note: required because it appears within the type `LuaTable<'_>`
note: required because it's used within this closure
--> tests/compile/ref_nounwindsafe.rs:8:18
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ^^^^^^^
--> tests/compile/ref_nounwindsafe.rs:8:18
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: required by a bound in `catch_unwind`
--> $RUST/std/src/panic.rs
|
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
| ^^^^^^^^^^ required by this bound in `catch_unwind`
--> $RUST/std/src/panic.rs
|
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
| ^^^^^^^^^^ required by this bound in `catch_unwind`
+12 -16
View File
@@ -1,17 +1,15 @@
error[E0597]: `lua` does not live long enough
--> tests/compile/static_callback_args.rs:12:5
|
10 | let lua = Lua::new();
| --- binding `lua` declared here
11 |
12 | / lua.create_function(|_, table: Table| {
13 | |/ BAD_TIME.with(|bt| {
12 | / lua.create_function(|_, table: Table| {
13 | | BAD_TIME.with(|bt| {
| |_________-
14 | || *bt.borrow_mut() = Some(table);
15 | || });
| ||__________- argument requires that `lua` is borrowed for `'static`
16 | | Ok(())
17 | | })?
| |_______^ borrowed value does not live long enough
16 | | Ok(())
17 | | })?
| |______^ borrowed value does not live long enough
...
32 | }
| - `lua` dropped here while still borrowed
@@ -19,17 +17,15 @@ error[E0597]: `lua` does not live long enough
error[E0505]: cannot move out of `lua` because it is borrowed
--> tests/compile/static_callback_args.rs:22:10
|
10 | let lua = Lua::new();
| --- binding `lua` declared here
11 |
12 | / lua.create_function(|_, table: Table| {
13 | |/ BAD_TIME.with(|bt| {
12 | / lua.create_function(|_, table: Table| {
13 | | BAD_TIME.with(|bt| {
| |_________-
14 | || *bt.borrow_mut() = Some(table);
15 | || });
| ||__________- argument requires that `lua` is borrowed for `'static`
16 | | Ok(())
17 | | })?
| |_______- borrow of `lua` occurs here
16 | | Ok(())
17 | | })?
| |______- borrow of `lua` occurs here
...
22 | drop(lua);
| ^^^ move out of `lua` occurs here
-15
View File
@@ -1,15 +0,0 @@
use mlua::{Lua, Result};
#[test]
fn test_debug_format() -> Result<()> {
let lua = Lua::new();
// Globals
let globals = lua.globals();
let dump = format!("{globals:#?}");
assert!(dump.starts_with("{\n [\"_G\"] = table:"));
// TODO: Other cases
Ok(())
}
-50
View File
@@ -1,50 +0,0 @@
use std::io;
use mlua::{Error, ErrorContext, Lua, Result};
#[test]
fn test_error_context() -> Result<()> {
let lua = Lua::new();
let func = lua.create_function(|_, ()| {
Err::<(), _>(Error::runtime("runtime error")).context("some context")
})?;
lua.globals().set("func", func)?;
let msg = lua
.load("local _, err = pcall(func); return tostring(err)")
.eval::<String>()?;
assert!(msg.contains("some context"));
assert!(msg.contains("runtime error"));
let func2 = lua.create_function(|lua, ()| {
lua.globals()
.get::<_, String>("nonextant")
.with_context(|_| "failed to find global")
})?;
lua.globals().set("func2", func2)?;
let msg2 = lua
.load("local _, err = pcall(func2); return tostring(err)")
.eval::<String>()?;
assert!(msg2.contains("failed to find global"));
println!("{msg2}");
assert!(msg2.contains("error converting Lua nil to String"));
// Rewrite context message and test `downcast_ref`
let func3 = lua.create_function(|_, ()| {
Err::<(), _>(Error::external(io::Error::new(
io::ErrorKind::Other,
"other",
)))
.context("some context")
.context("some new context")
})?;
let res = func3.call::<_, ()>(()).err().unwrap();
let Error::CallbackError { cause, .. } = &res else { unreachable!() };
assert!(!res.to_string().contains("some context"));
assert!(res.to_string().contains("some new context"));
assert!(cause.downcast_ref::<io::Error>().is_some());
Ok(())
}
+21 -158
View File
@@ -1,4 +1,4 @@
use mlua::{Function, Lua, Result, String, Table};
use mlua::{Function, Lua, Result, String};
#[test]
fn test_function() -> Result<()> {
@@ -86,7 +86,7 @@ fn test_rust_function() -> Result<()> {
fn test_c_function() -> Result<()> {
let lua = Lua::new();
unsafe extern "C-unwind" fn c_function(state: *mut mlua::lua_State) -> std::os::raw::c_int {
unsafe extern "C" fn c_function(state: *mut mlua::lua_State) -> std::os::raw::c_int {
let lua = Lua::init_from_ptr(state);
lua.globals().set("c_function", true).unwrap();
0
@@ -114,66 +114,6 @@ fn test_dump() -> Result<()> {
Ok(())
}
#[test]
fn test_function_environment() -> Result<()> {
let lua = Lua::new();
// We must not get or set environment for C functions
let rust_func = lua.create_function(|_, ()| Ok("hello"))?;
assert_eq!(rust_func.environment(), None);
assert_eq!(rust_func.set_environment(lua.globals()).ok(), Some(false));
// Test getting Lua function environment
lua.globals().set("hello", "global")?;
let lua_func = lua
.load(
r#"
local t = ""
return function()
-- two upvalues
return t .. hello
end
"#,
)
.eval::<Function>()?;
let lua_func2 = lua.load("return hello").into_function()?;
assert_eq!(lua_func.call::<_, String>(())?, "global");
assert_eq!(lua_func.environment(), Some(lua.globals()));
// Test changing the environment
let env = lua.create_table_from([("hello", "local")])?;
assert!(lua_func.set_environment(env.clone())?);
assert_eq!(lua_func.call::<_, String>(())?, "local");
assert_eq!(lua_func2.call::<_, String>(())?, "global");
// More complex case
lua.load(
r#"
local number = 15
function lucky() return tostring("number is "..number) end
new_env = {
tostring = function() return tostring(number) end,
}
"#,
)
.exec()?;
let lucky = lua.globals().get::<_, Function>("lucky")?;
assert_eq!(lucky.call::<_, String>(())?, "number is 15");
let new_env = lua.globals().get::<_, Table>("new_env")?;
lucky.set_environment(new_env)?;
assert_eq!(lucky.call::<_, String>(())?, "15");
// Test inheritance
let lua_func2 = lua
.load(r#"return function() return (function() return hello end)() end"#)
.eval::<Function>()?;
assert!(lua_func2.set_environment(env.clone())?);
lua.gc_collect()?;
assert_eq!(lua_func2.call::<_, String>(())?, "local");
Ok(())
}
#[test]
fn test_function_info() -> Result<()> {
let lua = Lua::new();
@@ -186,7 +126,7 @@ fn test_function_info() -> Result<()> {
end
"#,
)
.set_name("source1")
.set_name("source1")?
.exec()?;
let function1 = globals.get::<_, Function>("function1")?;
@@ -195,112 +135,35 @@ fn test_function_info() -> Result<()> {
let function1_info = function1.info();
#[cfg(feature = "luau")]
assert_eq!(function1_info.name.as_deref(), Some("function1"));
assert_eq!(function1_info.source.as_deref(), Some("source1"));
assert_eq!(function1_info.line_defined, Some(2));
assert_eq!(function1_info.name, Some(b"function1".to_vec()));
assert_eq!(function1_info.source, Some(b"source1".to_vec()));
assert_eq!(function1_info.line_defined, 2);
#[cfg(not(feature = "luau"))]
assert_eq!(function1_info.last_line_defined, Some(4));
#[cfg(feature = "luau")]
assert_eq!(function1_info.last_line_defined, None);
assert_eq!(function1_info.what, "Lua");
assert_eq!(function1_info.last_line_defined, 4);
assert_eq!(function1_info.what, Some(b"Lua".to_vec()));
let function2_info = function2.info();
assert_eq!(function2_info.name, None);
assert_eq!(function2_info.source.as_deref(), Some("source1"));
assert_eq!(function2_info.line_defined, Some(3));
assert_eq!(function2_info.source, Some(b"source1".to_vec()));
assert_eq!(function2_info.line_defined, 3);
#[cfg(not(feature = "luau"))]
assert_eq!(function2_info.last_line_defined, Some(3));
#[cfg(feature = "luau")]
assert_eq!(function2_info.last_line_defined, None);
assert_eq!(function2_info.what, "Lua");
assert_eq!(function2_info.last_line_defined, 3);
assert_eq!(function2_info.what, Some(b"Lua".to_vec()));
let function3_info = function3.info();
assert_eq!(function3_info.name, None);
assert_eq!(function3_info.source.as_deref(), Some("=[C]"));
assert_eq!(function3_info.line_defined, None);
assert_eq!(function3_info.last_line_defined, None);
assert_eq!(function3_info.what, "C");
assert_eq!(function3_info.source, Some(b"=[C]".to_vec()));
assert_eq!(function3_info.line_defined, -1);
#[cfg(not(feature = "luau"))]
assert_eq!(function3_info.last_line_defined, -1);
assert_eq!(function3_info.what, Some(b"C".to_vec()));
let print_info = globals.get::<_, Function>("print")?.info();
#[cfg(feature = "luau")]
assert_eq!(print_info.name.as_deref(), Some("print"));
assert_eq!(print_info.source.as_deref(), Some("=[C]"));
assert_eq!(print_info.what, "C");
assert_eq!(print_info.line_defined, None);
Ok(())
}
#[test]
fn test_function_wrap() -> Result<()> {
use mlua::Error;
let lua = Lua::new();
lua.globals()
.set("f", Function::wrap(|_, s: String| Ok(s)))?;
lua.load(r#"assert(f("hello") == "hello")"#).exec().unwrap();
let mut _i = false;
lua.globals().set(
"f",
Function::wrap_mut(move |lua, ()| {
_i = true;
lua.globals().get::<_, Function>("f")?.call::<_, ()>(())
}),
)?;
match lua.globals().get::<_, Function>("f")?.call::<_, ()>(()) {
Err(Error::CallbackError { ref cause, .. }) => match *cause.as_ref() {
Error::CallbackError { ref cause, .. } => match *cause.as_ref() {
Error::RecursiveMutCallback { .. } => {}
ref other => panic!("incorrect result: {other:?}"),
},
ref other => panic!("incorrect result: {other:?}"),
},
other => panic!("incorrect result: {other:?}"),
};
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[test]
fn test_owned_function() -> Result<()> {
let lua = Lua::new();
let f = lua
.create_function(|_, ()| Ok("hello, world!"))?
.into_owned();
drop(lua);
// We still should be able to call the function despite Lua is dropped
let s = f.call::<_, String>(())?;
assert_eq!(s.to_string_lossy(), "hello, world!");
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[test]
fn test_owned_function_drop() -> Result<()> {
let rc = std::sync::Arc::new(());
{
let lua = Lua::new();
lua.set_app_data(rc.clone());
let f1 = lua
.create_function(|_, ()| Ok("hello, world!"))?
.into_owned();
let f2 =
lua.create_function(move |_, ()| f1.to_ref().call::<_, std::string::String>(()))?;
assert_eq!(f2.call::<_, String>(())?.to_string_lossy(), "hello, world!");
}
// Check that Lua is properly destroyed
// It works because we collect garbage when Lua goes out of scope
assert_eq!(std::sync::Arc::strong_count(&rc), 1);
assert_eq!(print_info.name, Some(b"print".to_vec()));
assert_eq!(print_info.source, Some(b"=[C]".to_vec()));
assert_eq!(print_info.what, Some(b"C".to_vec()));
assert_eq!(print_info.line_defined, -1);
Ok(())
}
+38 -66
View File
@@ -2,15 +2,18 @@
use std::cell::RefCell;
use std::ops::Deref;
use std::str;
use std::sync::atomic::{AtomicI64, Ordering};
use std::sync::{Arc, Mutex};
use mlua::{DebugEvent, Error, HookTriggers, Lua, Result, Value};
#[test]
fn test_hook_triggers() {
let trigger = HookTriggers::new().on_calls().on_returns()
| HookTriggers::new().every_line().every_nth_instruction(5);
fn test_hook_triggers_bitor() {
let trigger = HookTriggers::on_calls()
| HookTriggers::on_returns()
| HookTriggers::every_line()
| HookTriggers::every_nth_instruction(5);
assert!(trigger.on_calls);
assert!(trigger.on_returns);
@@ -24,11 +27,11 @@ fn test_line_counts() -> Result<()> {
let hook_output = output.clone();
let lua = Lua::new();
lua.set_hook(HookTriggers::EVERY_LINE, move |_lua, debug| {
lua.set_hook(HookTriggers::every_line(), move |_lua, debug| {
assert_eq!(debug.event(), DebugEvent::Line);
hook_output.lock().unwrap().push(debug.curr_line());
Ok(())
});
})?;
lua.load(
r#"
local x = 2 + 3
@@ -56,14 +59,15 @@ fn test_function_calls() -> Result<()> {
let hook_output = output.clone();
let lua = Lua::new();
lua.set_hook(HookTriggers::ON_CALLS, move |_lua, debug| {
lua.set_hook(HookTriggers::on_calls(), move |_lua, debug| {
assert_eq!(debug.event(), DebugEvent::Call);
let names = debug.names();
let source = debug.source();
let name = names.name.map(|s| s.into_owned());
hook_output.lock().unwrap().push((name, source.what));
let name = names.name.map(|s| str::from_utf8(s).unwrap().to_owned());
let what = source.what.map(|s| str::from_utf8(s).unwrap().to_owned());
hook_output.lock().unwrap().push((name, what));
Ok(())
});
})?;
lua.load(
r#"
@@ -78,12 +82,18 @@ fn test_function_calls() -> Result<()> {
if cfg!(feature = "luajit") && lua.load("jit.version_num").eval::<i64>()? >= 20100 {
assert_eq!(
*output,
vec![(None, "main"), (Some("len".to_string()), "Lua")]
vec![
(None, Some("main".to_string())),
(Some("len".to_string()), Some("Lua".to_string()))
]
);
} else {
assert_eq!(
*output,
vec![(None, "main"), (Some("len".to_string()), "C")]
vec![
(None, Some("main".to_string())),
(Some("len".to_string()), Some("C".to_string()))
]
);
}
@@ -94,9 +104,11 @@ fn test_function_calls() -> Result<()> {
fn test_error_within_hook() -> Result<()> {
let lua = Lua::new();
lua.set_hook(HookTriggers::EVERY_LINE, |_lua, _debug| {
Err(Error::runtime("Something happened in there!"))
});
lua.set_hook(HookTriggers::every_line(), |_lua, _debug| {
Err(Error::RuntimeError(
"Something happened in there!".to_string(),
))
})?;
let err = lua
.load("x = 1")
@@ -124,16 +136,16 @@ fn test_limit_execution_instructions() -> Result<()> {
let max_instructions = AtomicI64::new(10000);
lua.set_hook(
HookTriggers::new().every_nth_instruction(30),
HookTriggers::every_nth_instruction(30),
move |_lua, debug| {
assert_eq!(debug.event(), DebugEvent::Count);
if max_instructions.fetch_sub(30, Ordering::Relaxed) <= 30 {
Err(Error::runtime("time's up"))
Err(Error::RuntimeError("time's up".to_string()))
} else {
Ok(())
}
},
);
)?;
lua.globals().set("x", Value::Integer(0))?;
let _ = lua
@@ -154,14 +166,11 @@ fn test_limit_execution_instructions() -> Result<()> {
fn test_hook_removal() -> Result<()> {
let lua = Lua::new();
lua.set_hook(
HookTriggers::new().every_nth_instruction(1),
|_lua, _debug| {
Err(Error::runtime(
"this hook should've been removed by this time",
))
},
);
lua.set_hook(HookTriggers::every_nth_instruction(1), |_lua, _debug| {
Err(Error::RuntimeError(
"this hook should've been removed by this time".to_string(),
))
})?;
assert!(lua.load("local x = 1").exec().is_err());
lua.remove_hook();
@@ -184,11 +193,11 @@ fn test_hook_swap_within_hook() -> Result<()> {
tl.borrow()
.as_ref()
.unwrap()
.set_hook(HookTriggers::EVERY_LINE, move |lua, _debug| {
.set_hook(HookTriggers::every_line(), move |lua, _debug| {
lua.globals().set("ok", 1i64)?;
TL_LUA.with(|tl| {
tl.borrow().as_ref().unwrap().set_hook(
HookTriggers::EVERY_LINE,
HookTriggers::every_line(),
move |lua, _debug| {
lua.load(
r#"
@@ -205,10 +214,9 @@ fn test_hook_swap_within_hook() -> Result<()> {
Ok(())
},
)
});
Ok(())
})
})
});
})?;
TL_LUA.with(|tl| {
let tl = tl.borrow();
@@ -225,39 +233,3 @@ fn test_hook_swap_within_hook() -> Result<()> {
Ok(())
})
}
#[test]
fn test_hook_threads() -> Result<()> {
let lua = Lua::new();
let func = lua
.load(
r#"
local x = 2 + 3
local y = x * 63
local z = string.len(x..", "..y)
"#,
)
.into_function()?;
let co = lua.create_thread(func)?;
let output = Arc::new(Mutex::new(Vec::new()));
let hook_output = output.clone();
co.set_hook(HookTriggers::EVERY_LINE, move |_lua, debug| {
assert_eq!(debug.event(), DebugEvent::Line);
hook_output.lock().unwrap().push(debug.curr_line());
Ok(())
});
co.resume(())?;
lua.remove_hook();
let output = output.lock().unwrap();
if cfg!(feature = "luajit") && lua.load("jit.version_num").eval::<i64>()? >= 20100 {
assert_eq!(*output, vec![2, 3, 4, 0, 4]);
} else {
assert_eq!(*output, vec![2, 3, 4]);
}
Ok(())
}
+8 -107
View File
@@ -7,19 +7,7 @@ use std::panic::{catch_unwind, AssertUnwindSafe};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use mlua::{
Compiler, CoverageInfo, Error, Lua, Result, Table, ThreadStatus, Value, Vector, VmState,
};
#[test]
fn test_version() -> Result<()> {
let lua = Lua::new();
assert!(lua
.globals()
.get::<_, String>("_VERSION")?
.starts_with("Luau 0."));
Ok(())
}
use mlua::{Compiler, CoverageInfo, Error, Lua, Result, Table, ThreadStatus, Value, VmState};
#[test]
fn test_require() -> Result<()> {
@@ -52,18 +40,13 @@ fn test_require() -> Result<()> {
.exec()
}
#[cfg(not(feature = "luau-vector4"))]
#[test]
fn test_vectors() -> Result<()> {
let lua = Lua::new();
let v: Vector = lua.load("vector(1, 2, 3) + vector(3, 2, 1)").eval()?;
let v: [f32; 3] = lua.load("vector(1, 2, 3) + vector(3, 2, 1)").eval()?;
assert_eq!(v, [4.0, 4.0, 4.0]);
// Test conversion into Rust array
let v: [f64; 3] = lua.load("vector(1, 2, 3)").eval()?;
assert!(v == [1.0, 2.0, 3.0]);
// Test vector methods
lua.load(
r#"
@@ -84,89 +67,7 @@ fn test_vectors() -> Result<()> {
assert(v.z == 3)
"#,
)
.set_compiler(Compiler::new().set_vector_ctor("vector"))
.exec()?;
Ok(())
}
#[cfg(feature = "luau-vector4")]
#[test]
fn test_vectors() -> Result<()> {
let lua = Lua::new();
let v: Vector = lua.load("vector(1, 2, 3, 4) + vector(4, 3, 2, 1)").eval()?;
assert_eq!(v, [5.0, 5.0, 5.0, 5.0]);
// Test conversion into Rust array
let v: [f64; 4] = lua.load("vector(1, 2, 3, 4)").eval()?;
assert!(v == [1.0, 2.0, 3.0, 4.0]);
// Test vector methods
lua.load(
r#"
local v = vector(1, 2, 3, 4)
assert(v.x == 1)
assert(v.y == 2)
assert(v.z == 3)
assert(v.w == 4)
"#,
)
.exec()?;
// Test vector methods (fastcall)
lua.load(
r#"
local v = vector(1, 2, 3, 4)
assert(v.x == 1)
assert(v.y == 2)
assert(v.z == 3)
assert(v.w == 4)
"#,
)
.set_compiler(Compiler::new().set_vector_ctor("vector"))
.exec()?;
Ok(())
}
#[cfg(all(not(feature = "luau-vector4"), feature = "unstable"))]
#[test]
fn test_vector_metatable() -> Result<()> {
let lua = Lua::new();
let vector_mt = lua
.load(
r#"
{
__index = {
new = vector,
product = function(a, b)
return vector(a.x * b.x, a.y * b.y, a.z * b.z)
end
}
}
"#,
)
.eval::<Table>()?;
vector_mt.set_metatable(Some(vector_mt.clone()));
lua.set_vector_metatable(Some(vector_mt.clone()));
lua.globals().set("Vector3", vector_mt)?;
let compiler = Compiler::new()
.set_vector_lib("Vector3")
.set_vector_ctor("new");
// Test vector methods (fastcall)
lua.load(
r#"
local v = Vector3.new(1, 2, 3)
local v2 = v:product(Vector3.new(2, 3, 4))
assert(v2.x == 2 and v2.y == 6 and v2.z == 12)
"#,
)
.set_compiler(compiler)
.set_compiler(Compiler::new().set_vector_ctor(Some("vector".to_string())))
.exec()?;
Ok(())
@@ -272,7 +173,7 @@ fn test_interrupts() -> Result<()> {
let interrupts_count = Arc::new(AtomicU64::new(0));
let interrupts_count2 = interrupts_count.clone();
lua.set_interrupt(move |_| {
lua.set_interrupt(move || {
interrupts_count2.fetch_add(1, Ordering::Relaxed);
Ok(VmState::Continue)
});
@@ -294,7 +195,7 @@ fn test_interrupts() -> Result<()> {
//
let yield_count = Arc::new(AtomicU64::new(0));
let yield_count2 = yield_count.clone();
lua.set_interrupt(move |_| {
lua.set_interrupt(move || {
if yield_count2.fetch_add(1, Ordering::Relaxed) == 1 {
return Ok(VmState::Yield);
}
@@ -321,7 +222,7 @@ fn test_interrupts() -> Result<()> {
//
// Test errors in interrupts
//
lua.set_interrupt(|_| Err(Error::runtime("error from interrupt")));
lua.set_interrupt(|| Err(Error::RuntimeError("error from interrupt".into())));
match f.call::<_, ()>(()) {
Err(Error::CallbackError { cause, .. }) => match *cause {
Error::RuntimeError(ref m) if m == "error from interrupt" => {}
@@ -343,8 +244,8 @@ fn test_coverage() -> Result<()> {
let f = lua
.load(
r#"local s = "abc"
assert(#s == 3)
r#"local v = vector(1, 2, 3)
assert(v.x == 1 and v.y == 2 and v.z == 3)
function abc(i)
if i < 5 then
+5 -29
View File
@@ -1,7 +1,11 @@
use std::sync::Arc;
use mlua::{Error, GCMode, Lua, Result, UserData};
use mlua::{GCMode, Lua, Result, UserData};
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
use mlua::Error;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[test]
fn test_memory_limit() -> Result<()> {
let lua = Lua::new();
@@ -17,11 +21,6 @@ fn test_memory_limit() -> Result<()> {
.into_function()?;
f.call::<_, ()>(()).expect("should trigger no memory limit");
if cfg!(feature = "luajit") && lua.set_memory_limit(0).is_err() {
// seems this luajit version does not support memory limit
return Ok(());
}
lua.set_memory_limit(initial_memory + 10000)?;
match f.call::<_, ()>(()) {
Err(Error::MemoryError(_)) => {}
@@ -34,29 +33,6 @@ fn test_memory_limit() -> Result<()> {
Ok(())
}
#[test]
fn test_memory_limit_thread() -> Result<()> {
let lua = Lua::new();
let f = lua
.load("local t = {}; for i = 1,10000 do t[i] = i end")
.into_function()?;
if cfg!(feature = "luajit") && lua.set_memory_limit(0).is_err() {
// seems this luajit version does not support memory limit
return Ok(());
}
lua.set_memory_limit(lua.used_memory() + 10000)?;
let thread = lua.create_thread(f)?;
match thread.resume::<_, ()>(()) {
Err(Error::MemoryError(_)) => {}
something_else => panic!("did not trigger memory error: {:?}", something_else),
};
Ok(())
}
#[test]
fn test_gc_control() -> Result<()> {
let lua = Lua::new();

Some files were not shown because too many files have changed in this diff Show More