mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
68 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f0e06fb66 | |||
| 181c9d07b7 | |||
| 4e827179d1 | |||
| cc26dcd4ff | |||
| 201e30bc07 | |||
| 4e028d8409 | |||
| 3d1ae981d3 | |||
| 8c93948f2f | |||
| f2b5cc44de | |||
| 27f91dfd1b | |||
| 75ff11f795 | |||
| df6097ab38 | |||
| 65bb6279ee | |||
| 31b88e85bb | |||
| 3be4745190 | |||
| c52deec988 | |||
| 3ab3c997b3 | |||
| 5872ed70f5 | |||
| e7e92b4f6f | |||
| d27693b61a | |||
| c9848d6faf | |||
| 9126bb8ce0 | |||
| 7f1d716a44 | |||
| be56e2205c | |||
| c5aadc68cd | |||
| a5ae2a1fc3 | |||
| 59872da63d | |||
| a24d2151af | |||
| 56c227fd7e | |||
| d5d66abe42 | |||
| a2d8b21964 | |||
| a9604c4946 | |||
| 81ae8e1393 | |||
| a959b98d30 | |||
| efd0856033 | |||
| c91066006f | |||
| a45fe9bb93 | |||
| f1a97e4193 | |||
| 47e6a37323 | |||
| bf0c96908f | |||
| 8817720362 | |||
| 35294359ad | |||
| eb76db59da | |||
| 33bf3ffde7 | |||
| 0f3fdb0539 | |||
| 79d438aaad | |||
| 30cf4bef58 | |||
| 5776c72208 | |||
| 943c3aed58 | |||
| 8fcb6a8416 | |||
| 452dc8be88 | |||
| 63a255bbc9 | |||
| 151adc0e87 | |||
| 7f3ec63ab5 | |||
| f19c6aac3b | |||
| 29af448ad9 | |||
| 497d84828a | |||
| 0e489901a5 | |||
| 88063e756f | |||
| c8436e2b80 | |||
| 613748ec16 | |||
| 2fbd266da6 | |||
| c79b5e9cdb | |||
| 2ace892613 | |||
| c1ffd4e790 | |||
| d9c139b55f | |||
| 0c4206c97d | |||
| a985dc7a37 |
@@ -0,0 +1,68 @@
|
||||
name: Documentation (main)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
# Allow only one concurrent deployment
|
||||
concurrency:
|
||||
group: pages
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Documentation
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Build documentation
|
||||
env:
|
||||
RUSTDOCFLAGS: "--cfg docsrs"
|
||||
run: |
|
||||
cargo +nightly doc --no-deps \
|
||||
--features "lua55,vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||
|
||||
- name: Create index redirect
|
||||
run: |
|
||||
echo '<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Redirecting to mlua documentation</title>
|
||||
<meta http-equiv="refresh" content="0; URL=mlua/index.html">
|
||||
<link rel="canonical" href="mlua/index.html">
|
||||
</head>
|
||||
<body>
|
||||
<p>Redirecting to <a href="mlua/index.html">mlua documentation</a>...</p>
|
||||
</body>
|
||||
</html>' > target/doc/index.html
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v5
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v4
|
||||
with:
|
||||
path: target/doc
|
||||
|
||||
deploy:
|
||||
name: Deploy to GitHub Pages
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
@@ -1,3 +1,22 @@
|
||||
## v0.12.0-rc.1 (Apr 21, 2026)
|
||||
|
||||
- Rust 2024 edition
|
||||
- Removed `Error::ToLuaConversionError` variant as it was unused (and not practically useful)
|
||||
- New modules to group data types: `chunk`, `debug`, `error`, `function`, `table`, `string`, `state`, `thread`, `userdata`, `luau`
|
||||
- Support `__todebugstring` metamethod for pretty formatting userdata value (for debugging)
|
||||
- New `MaybeSync` trait that is required for userdata types
|
||||
- Removed lifetime from `BorrowedStr` and `BorrowedBytes`
|
||||
- New `Thread` methods: `is_resumable`, `is_running`, `is_finished`, `is_error`
|
||||
- Added `Thread::state` to get raw Lua state pointer
|
||||
- Luau `TextRequirer` is renamed to `FsRequirer`
|
||||
- GC interface refactor: `Lua::gc_inc/Lua::gc_gen` is replaced with `gc_set_mode`
|
||||
- Added `GcIncParams` and `GcGenParams` for GC tuning
|
||||
- New `UserDataMethods::add_method_once` and `UserDataMethods::add_async_method_once`
|
||||
- Initial Luau integer64 type support
|
||||
- Changed interface of `Function::wrap/wrap_mut/wrap_async` to support any Error type
|
||||
- Changed `AnyUserData::type_name` to return `LuaString` instead
|
||||
- Added `UserDataOwned<T>` wrapper to take ownership of userdata `T` and implements `FromLua`
|
||||
|
||||
## v0.11.6 (Jan 27, 2026)
|
||||
|
||||
- Added Lua 5.5 support (`lua55` feature flag)
|
||||
|
||||
+7
-8
@@ -1,9 +1,9 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.11.6" # remember to update mlua_derive
|
||||
version = "0.12.0-rc.1" # remember to update mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@kyju.org>"]
|
||||
rust-version = "1.85.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.88"
|
||||
edition = "2024"
|
||||
repository = "https://github.com/mlua-rs/mlua"
|
||||
documentation = "https://docs.rs/mlua"
|
||||
readme = "README.md"
|
||||
@@ -61,10 +61,9 @@ erased-serde = { version = "0.4", optional = true }
|
||||
serde-value = { version = "0.7", optional = true }
|
||||
parking_lot = { version = "0.12", features = ["arc_lock"] }
|
||||
anyhow = { version = "1.0", optional = true }
|
||||
rustversion = "1.0"
|
||||
libc = "0.2"
|
||||
|
||||
ffi = { package = "mlua-sys", version = "0.10.0", path = "mlua-sys" }
|
||||
ffi = { package = "mlua-sys", version = "0.11.0-rc.1", path = "mlua-sys" }
|
||||
|
||||
[dev-dependencies]
|
||||
trybuild = "1.0"
|
||||
@@ -78,10 +77,10 @@ static_assertions = "1.0"
|
||||
hyper = { version = "1.2", features = ["full"] }
|
||||
hyper-util = { version = "0.1.3", features = ["full"] }
|
||||
http-body-util = "0.1.1"
|
||||
reqwest = { version = "0.12", features = ["json"] }
|
||||
reqwest = { version = "0.13", features = ["json"] }
|
||||
tempfile = "3"
|
||||
criterion = { version = "0.7", features = ["async_tokio"] }
|
||||
rustyline = "17.0"
|
||||
criterion = { version = "0.8", features = ["async_tokio"] }
|
||||
rustyline = "18.0"
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
|
||||
[lints.rust]
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
[Benchmarks]: https://github.com/khvzak/script-bench-rs
|
||||
[FAQ]: FAQ.md
|
||||
|
||||
## The main branch is the development version of `mlua`. Please see the [v0.11](https://github.com/mlua-rs/mlua/tree/v0.11) branch for the stable versions of `mlua`.
|
||||
|
||||
`mlua` is a set of bindings to the [Lua](https://www.lua.org) programming language for Rust with a goal of providing a
|
||||
_safe_ (as much as possible), high level, easy to use, practical and flexible API.
|
||||
|
||||
@@ -125,7 +127,7 @@ my_project $ LUA_LIB=$HOME/tmp/lua-5.2.4/src LUA_LIB_NAME=lua LUA_LINK=static ca
|
||||
Just enable the `vendored` feature and cargo will automatically build and link the specified Lua/LuaJIT version. This is the easiest way to get started with `mlua`.
|
||||
|
||||
### Standalone mode
|
||||
In standalone mode, `mlua` allows adding scripting support to your application with a gently configured Lua runtime to ensure safety and soundness.
|
||||
In standalone mode, `mlua` allows adding scripting support to your application with a properly configured Lua runtime to ensure safety and soundness.
|
||||
|
||||
Add to `Cargo.toml`:
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::time::Duration;
|
||||
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
|
||||
use tokio::runtime::Runtime;
|
||||
use tokio::task;
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
|
||||
|
||||
use mlua::prelude::*;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## mlua v0.10 release notes
|
||||
|
||||
The v0.10 version of mlua has goal to improve the user experience while keeping the same performance and safety guarantees.
|
||||
The v0.10 version of mlua has a goal to improve the user experience while keeping the same performance and safety guarantees.
|
||||
This document highlights the most notable features. For a full list of changes, see the [CHANGELOG].
|
||||
|
||||
[CHANGELOG]: https://github.com/mlua-rs/mlua/blob/main/CHANGELOG.md
|
||||
@@ -40,7 +40,7 @@ assert_eq!(lua.globals().get::<i32>("i")?, 20);
|
||||
|
||||
Under the hood, to synchronize access to the Lua state, mlua uses [`ReentrantMutex`] which can be recursively locked by a single thread. Only one thread can execute Lua code at a time, but it's possible to share Lua values between threads.
|
||||
|
||||
This has some performance penalties (about 10-20%) compared to the lock free mode. This flag is disabled by default and does not supported in module mode.
|
||||
This has some performance penalties (about 10-20%) compared to the lock free mode. This flag is disabled by default and is not supported in module mode.
|
||||
|
||||
[`ReentrantMutex`]: https://docs.rs/parking_lot/latest/parking_lot/type.ReentrantMutex.html
|
||||
|
||||
@@ -144,7 +144,7 @@ The following `Scope` methods were changed:
|
||||
Instead, scope has comprehensive support for borrowed userdata: `create_any_userdata_ref`, `create_any_userdata_ref_mut`, `create_userdata_ref`, `create_userdata_ref_mut`.
|
||||
|
||||
`UserDataRef` and `UserDataRefMut` are no longer acceptable for scoped userdata access as they require owned underlying data.
|
||||
In mlua v0.9 this can cause read-after-free bug in some edge cases.
|
||||
In mlua v0.9 this could cause a read-after-free bug in some edge cases.
|
||||
|
||||
To temporarily borrow underlying data, the `AnyUserData::borrow_scoped` and `AnyUserData::borrow_mut_scoped` methods were introduced:
|
||||
|
||||
|
||||
@@ -152,9 +152,9 @@ It will automatically trigger JIT compilation for new Lua chunks. To disable it,
|
||||
|
||||
#### 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.
|
||||
When calling a Rust function from Lua and passing wrong arguments, previous mlua versions reported an 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:
|
||||
In v0.9 it reports an error message with the argument index and expected type:
|
||||
|
||||
```rust
|
||||
let func = lua.create_function(|_, _a: i32| Ok(()))?;
|
||||
@@ -327,7 +327,7 @@ Under the hood a new function `luaopen_alt_module` will be created for the Lua m
|
||||
|
||||
- `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
|
||||
In module mode, mlua runs in an unknown environment and cannot tell whether there are any memory limits or not. As a result, some operations that require memory allocation run in
|
||||
protected mode. Setting this attribute will improve performance of such operations with risk of having uncaught exceptions and memory leaks.
|
||||
|
||||
#### Improved Windows target
|
||||
|
||||
@@ -5,7 +5,7 @@ use hyper::body::Incoming;
|
||||
use hyper_util::client::legacy::Client as HyperClient;
|
||||
use hyper_util::rt::TokioExecutor;
|
||||
|
||||
use mlua::{chunk, ExternalResult, Lua, Result, UserData, UserDataMethods};
|
||||
use mlua::{ExternalResult, Lua, Result, UserData, UserDataMethods, chunk};
|
||||
|
||||
struct BodyReader(Incoming);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use mlua::{chunk, ExternalResult, Lua, LuaSerdeExt, Result, Value};
|
||||
use mlua::{ExternalResult, Lua, LuaSerdeExt, Result, Value, chunk};
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> Result<()> {
|
||||
|
||||
@@ -11,7 +11,7 @@ use hyper::{Request, Response};
|
||||
use hyper_util::rt::TokioIo;
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
use mlua::{chunk, Error as LuaError, Function, Lua, String as LuaString, Table, UserData, UserDataMethods};
|
||||
use mlua::{Error as LuaError, Function, Lua, String as LuaString, Table, UserData, UserDataMethods, chunk};
|
||||
|
||||
/// Wrapper around incoming request that implements UserData
|
||||
struct LuaRequest(SocketAddr, Request<Incoming>);
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::net::SocketAddr;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
|
||||
use mlua::{chunk, BString, Function, Lua, UserData, UserDataMethods};
|
||||
use mlua::{BString, Function, Lua, UserData, UserDataMethods, chunk};
|
||||
|
||||
struct LuaTcpStream(TcpStream);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::f32;
|
||||
use std::iter::FromIterator;
|
||||
|
||||
use mlua::{chunk, FromLua, Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Value, Variadic};
|
||||
use mlua::{FromLua, Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Value, Variadic, chunk};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// You can create a new Lua state with `Lua::new()`. This loads the default Lua std library
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use mlua::{chunk, Lua, MetaMethod, Result, UserData};
|
||||
use mlua::{Lua, MetaMethod, Result, UserData, chunk};
|
||||
|
||||
#[derive(Default)]
|
||||
struct Rectangle {
|
||||
|
||||
+5
-5
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "mlua-sys"
|
||||
version = "0.10.0"
|
||||
version = "0.11.0-rc.1"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
rust-version = "1.85"
|
||||
rust-version = "1.88"
|
||||
edition = "2024"
|
||||
repository = "https://github.com/mlua-rs/mlua"
|
||||
documentation = "https://docs.rs/mlua-sys"
|
||||
@@ -41,9 +41,9 @@ libc = "0.2"
|
||||
cc = "1.0"
|
||||
cfg-if = "1.0"
|
||||
pkg-config = "0.3.17"
|
||||
lua-src = { version = ">= 550.0.0, < 550.1.0", optional = true }
|
||||
luajit-src = { version = ">= 210.6.0, < 210.7.0", optional = true }
|
||||
luau0-src = { version = "0.18.0", optional = true }
|
||||
lua-src = { version = ">= 550.1.0, < 550.2.0", optional = true }
|
||||
luajit-src = { version = ">= 210.7.0, < 210.8.0", optional = true }
|
||||
luau0-src = { version = "0.20.0", optional = true }
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(raw_dylib)'] }
|
||||
|
||||
@@ -38,8 +38,10 @@ unsafe extern "C-unwind" {
|
||||
|
||||
#[link_name = "luaL_checkinteger"]
|
||||
pub fn luaL_checkinteger_(L: *mut lua_State, narg: c_int) -> c_int;
|
||||
pub fn luaL_checkinteger64(L: *mut lua_State, narg: c_int) -> i64;
|
||||
#[link_name = "luaL_optinteger"]
|
||||
pub fn luaL_optinteger_(L: *mut lua_State, narg: c_int, def: c_int) -> c_int;
|
||||
pub fn luaL_optinteger64(L: *mut lua_State, narg: c_int, def: i64) -> i64;
|
||||
pub fn luaL_checkunsigned(L: *mut lua_State, narg: c_int) -> lua_Unsigned;
|
||||
pub fn luaL_optunsigned(L: *mut lua_State, narg: c_int, def: lua_Unsigned) -> lua_Unsigned;
|
||||
|
||||
|
||||
@@ -65,14 +65,15 @@ pub const LUA_TBOOLEAN: c_int = 1;
|
||||
|
||||
pub const LUA_TLIGHTUSERDATA: c_int = 2;
|
||||
pub const LUA_TNUMBER: c_int = 3;
|
||||
pub const LUA_TVECTOR: c_int = 4;
|
||||
pub const LUA_TINTEGER: c_int = 4;
|
||||
pub const LUA_TVECTOR: c_int = 5;
|
||||
|
||||
pub const LUA_TSTRING: c_int = 5;
|
||||
pub const LUA_TTABLE: c_int = 6;
|
||||
pub const LUA_TFUNCTION: c_int = 7;
|
||||
pub const LUA_TUSERDATA: c_int = 8;
|
||||
pub const LUA_TTHREAD: c_int = 9;
|
||||
pub const LUA_TBUFFER: c_int = 10;
|
||||
pub const LUA_TSTRING: c_int = 6;
|
||||
pub const LUA_TTABLE: c_int = 7;
|
||||
pub const LUA_TFUNCTION: c_int = 8;
|
||||
pub const LUA_TUSERDATA: c_int = 9;
|
||||
pub const LUA_TTHREAD: c_int = 10;
|
||||
pub const LUA_TBUFFER: c_int = 11;
|
||||
|
||||
/// Guaranteed number of Lua stack slots available to a C function.
|
||||
pub const LUA_MINSTACK: c_int = 20;
|
||||
@@ -153,6 +154,7 @@ unsafe extern "C-unwind" {
|
||||
pub fn lua_tounsignedx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Unsigned;
|
||||
pub fn lua_tovector(L: *mut lua_State, idx: c_int) -> *const c_float;
|
||||
pub fn lua_toboolean(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_tointeger64(L: *mut lua_State, idx: c_int, isinteger: *mut c_int) -> i64;
|
||||
pub fn lua_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
|
||||
pub fn lua_tostringatom(L: *mut lua_State, idx: c_int, atom: *mut c_int) -> *const c_char;
|
||||
pub fn lua_tolstringatom(
|
||||
@@ -182,6 +184,7 @@ unsafe extern "C-unwind" {
|
||||
pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number);
|
||||
#[link_name = "lua_pushinteger"]
|
||||
pub fn lua_pushinteger_(L: *mut lua_State, n: c_int);
|
||||
pub fn lua_pushinteger64(L: *mut lua_State, n: i64);
|
||||
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);
|
||||
@@ -412,6 +415,11 @@ pub unsafe fn lua_isboolean(L: *mut lua_State, n: c_int) -> c_int {
|
||||
(lua_type(L, n) == LUA_TBOOLEAN) as c_int
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_isinteger64(L: *mut lua_State, n: c_int) -> c_int {
|
||||
(lua_type(L, n) == LUA_TINTEGER) as c_int
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_isvector(L: *mut lua_State, n: c_int) -> c_int {
|
||||
(lua_type(L, n) == LUA_TVECTOR) as c_int
|
||||
@@ -501,6 +509,12 @@ pub type lua_Coverage = unsafe extern "C-unwind" fn(
|
||||
size: usize,
|
||||
);
|
||||
|
||||
pub type lua_CounterFunction =
|
||||
unsafe extern "C-unwind" fn(context: *mut c_void, function: *const c_char, linedefined: c_int);
|
||||
|
||||
pub type lua_CounterValue =
|
||||
unsafe extern "C-unwind" fn(context: *mut c_void, kind: c_int, line: c_int, hits: u64);
|
||||
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn lua_stackdepth(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_getinfo(L: *mut lua_State, level: c_int, what: *const c_char, ar: *mut lua_Debug) -> c_int;
|
||||
@@ -515,6 +529,14 @@ unsafe extern "C-unwind" {
|
||||
|
||||
pub fn lua_getcoverage(L: *mut lua_State, funcindex: c_int, context: *mut c_void, callback: lua_Coverage);
|
||||
|
||||
pub fn lua_getcounters(
|
||||
L: *mut lua_State,
|
||||
funcindex: c_int,
|
||||
context: *mut c_void,
|
||||
functionvisit: lua_CounterFunction,
|
||||
countervisit: lua_CounterValue,
|
||||
);
|
||||
|
||||
pub fn lua_debugtrace(L: *mut lua_State) -> *const c_char;
|
||||
}
|
||||
|
||||
@@ -551,8 +573,8 @@ pub struct lua_Callbacks {
|
||||
|
||||
/// 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)>,
|
||||
/// 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>,
|
||||
/// gets called when a string is created to assign an atom id
|
||||
pub useratom: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, 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)>,
|
||||
|
||||
@@ -80,6 +80,7 @@ unsafe extern "C" {
|
||||
pub fn luau_set_compile_constant_nil(cons: *mut lua_CompileConstant);
|
||||
pub fn luau_set_compile_constant_boolean(cons: *mut lua_CompileConstant, b: c_int);
|
||||
pub fn luau_set_compile_constant_number(cons: *mut lua_CompileConstant, n: f64);
|
||||
pub fn luau_set_compile_constant_integer64(cons: *mut lua_CompileConstant, l: i64);
|
||||
pub fn luau_set_compile_constant_vector(cons: *mut lua_CompileConstant, x: f32, y: f32, z: f32, w: f32);
|
||||
pub fn luau_set_compile_constant_string(cons: *mut lua_CompileConstant, s: *const c_char, l: usize);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ pub const LUA_UTF8LIBNAME: *const c_char = cstr!("utf8");
|
||||
pub const LUA_MATHLIBNAME: *const c_char = cstr!("math");
|
||||
pub const LUA_DBLIBNAME: *const c_char = cstr!("debug");
|
||||
pub const LUA_VECLIBNAME: *const c_char = cstr!("vector");
|
||||
pub const LUA_INTLIBNAME: *const c_char = cstr!("integer");
|
||||
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn luaopen_base(L: *mut lua_State) -> c_int;
|
||||
@@ -27,6 +28,7 @@ unsafe extern "C-unwind" {
|
||||
pub fn luaopen_math(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_debug(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_vector(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_integer(L: *mut lua_State) -> c_int;
|
||||
|
||||
// open all builtin libraries
|
||||
pub fn luaL_openlibs(L: *mut lua_State);
|
||||
|
||||
+4
-4
@@ -59,7 +59,7 @@ impl Buffer {
|
||||
/// Returns an adaptor implementing [`io::Read`], [`io::Write`] and [`io::Seek`] over the
|
||||
/// buffer.
|
||||
///
|
||||
/// Buffer operations are infallible, none of the read/write functions will return a Err.
|
||||
/// Buffer operations are infallible, none of the read/write functions will return an Err.
|
||||
pub fn cursor(self) -> impl io::Read + io::Write + io::Seek {
|
||||
BufferCursor(self, 0)
|
||||
}
|
||||
@@ -97,7 +97,7 @@ struct BufferCursor(Buffer, usize);
|
||||
|
||||
impl io::Read for BufferCursor {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let lua = self.0 .0.lua.lock();
|
||||
let lua = self.0.0.lua.lock();
|
||||
let data = self.0.as_slice(&lua);
|
||||
if self.1 == data.len() {
|
||||
return Ok(0);
|
||||
@@ -111,7 +111,7 @@ impl io::Read for BufferCursor {
|
||||
|
||||
impl io::Write for BufferCursor {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let lua = self.0 .0.lua.lock();
|
||||
let lua = self.0.0.lua.lock();
|
||||
let data = self.0.as_slice_mut(&lua);
|
||||
if self.1 == data.len() {
|
||||
return Ok(0);
|
||||
@@ -129,7 +129,7 @@ impl io::Write for BufferCursor {
|
||||
|
||||
impl io::Seek for BufferCursor {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
|
||||
let lua = self.0 .0.lua.lock();
|
||||
let lua = self.0.0.lua.lock();
|
||||
let data = self.0.as_slice(&lua);
|
||||
let new_offset = match pos {
|
||||
io::SeekFrom::Start(offset) => offset as i64,
|
||||
|
||||
+75
-72
@@ -1,10 +1,16 @@
|
||||
//! Lua chunk loading and execution.
|
||||
//!
|
||||
//! This module provides types for loading Lua source code or bytecode into a [`Chunk`],
|
||||
//! configuring how it is compiled and executed, and converting it into a callable [`Function`].
|
||||
//!
|
||||
//! Chunks can be loaded from strings, byte slices, or files via the [`AsChunk`] trait.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::CString;
|
||||
use std::io::Result as IoResult;
|
||||
use std::panic::Location;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
@@ -20,7 +26,7 @@ pub trait AsChunk {
|
||||
/// Returns optional chunk name
|
||||
///
|
||||
/// See [`Chunk::set_name`] for possible name prefixes.
|
||||
fn name(&self) -> Option<StdString> {
|
||||
fn name(&self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -52,13 +58,13 @@ impl AsChunk for &str {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChunk for StdString {
|
||||
impl AsChunk for String {
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Owned(self.clone().into_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChunk for &StdString {
|
||||
impl AsChunk for &String {
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>>
|
||||
where
|
||||
Self: 'a,
|
||||
@@ -92,7 +98,7 @@ impl AsChunk for &Vec<u8> {
|
||||
}
|
||||
|
||||
impl AsChunk for &Path {
|
||||
fn name(&self) -> Option<StdString> {
|
||||
fn name(&self) -> Option<String> {
|
||||
Some(format!("@{}", self.display()))
|
||||
}
|
||||
|
||||
@@ -102,7 +108,7 @@ impl AsChunk for &Path {
|
||||
}
|
||||
|
||||
impl AsChunk for PathBuf {
|
||||
fn name(&self) -> Option<StdString> {
|
||||
fn name(&self) -> Option<String> {
|
||||
Some(format!("@{}", self.display()))
|
||||
}
|
||||
|
||||
@@ -112,7 +118,7 @@ impl AsChunk for PathBuf {
|
||||
}
|
||||
|
||||
impl<C: AsChunk + ?Sized> AsChunk for Box<C> {
|
||||
fn name(&self) -> Option<StdString> {
|
||||
fn name(&self) -> Option<String> {
|
||||
(**self).name()
|
||||
}
|
||||
|
||||
@@ -136,7 +142,7 @@ impl<C: AsChunk + ?Sized> AsChunk for Box<C> {
|
||||
#[must_use = "`Chunk`s do nothing unless one of `exec`, `eval`, `call`, or `into_function` are called on them"]
|
||||
pub struct Chunk<'a> {
|
||||
pub(crate) lua: WeakLua,
|
||||
pub(crate) name: StdString,
|
||||
pub(crate) name: String,
|
||||
pub(crate) env: Result<Option<Table>>,
|
||||
pub(crate) mode: Option<ChunkMode>,
|
||||
pub(crate) source: IoResult<Cow<'a, [u8]>>,
|
||||
@@ -154,13 +160,14 @@ pub enum ChunkMode {
|
||||
/// Represents a constant value that can be used by Luau compiler.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum CompileConstant {
|
||||
Nil,
|
||||
Boolean(bool),
|
||||
Number(crate::Number),
|
||||
Vector(crate::Vector),
|
||||
String(StdString),
|
||||
String(String),
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
@@ -192,7 +199,7 @@ impl From<&str> for CompileConstant {
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
type LibraryMemberConstantMap = HashMap<(StdString, StdString), CompileConstant>;
|
||||
type LibraryMemberConstantMap = HashMap<(String, String), CompileConstant>;
|
||||
|
||||
/// Luau compiler
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
@@ -203,14 +210,14 @@ pub struct Compiler {
|
||||
debug_level: u8,
|
||||
type_info_level: u8,
|
||||
coverage_level: u8,
|
||||
vector_lib: Option<StdString>,
|
||||
vector_ctor: Option<StdString>,
|
||||
vector_type: Option<StdString>,
|
||||
mutable_globals: Vec<StdString>,
|
||||
userdata_types: Vec<StdString>,
|
||||
libraries_with_known_members: Vec<StdString>,
|
||||
vector_lib: Option<String>,
|
||||
vector_ctor: Option<String>,
|
||||
vector_type: Option<String>,
|
||||
mutable_globals: Vec<String>,
|
||||
userdata_types: Vec<String>,
|
||||
libraries_with_known_members: Vec<String>,
|
||||
library_constants: Option<LibraryMemberConstantMap>,
|
||||
disabled_builtins: Vec<StdString>,
|
||||
disabled_builtins: Vec<String>,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
@@ -294,7 +301,7 @@ impl Compiler {
|
||||
/// To set the library and method name, use the `lib.ctor` format.
|
||||
#[doc(hidden)]
|
||||
#[must_use]
|
||||
pub fn set_vector_ctor(mut self, ctor: impl Into<StdString>) -> Self {
|
||||
pub fn set_vector_ctor(mut self, ctor: impl Into<String>) -> Self {
|
||||
let ctor = ctor.into();
|
||||
let lib_ctor = ctor.split_once('.');
|
||||
self.vector_lib = lib_ctor.as_ref().map(|&(lib, _)| lib.to_owned());
|
||||
@@ -307,7 +314,7 @@ impl Compiler {
|
||||
/// Sets alternative vector type name for type tables, in addition to default type `vector`.
|
||||
#[doc(hidden)]
|
||||
#[must_use]
|
||||
pub fn set_vector_type(mut self, r#type: impl Into<StdString>) -> Self {
|
||||
pub fn set_vector_type(mut self, r#type: impl Into<String>) -> Self {
|
||||
self.vector_type = Some(r#type.into());
|
||||
self
|
||||
}
|
||||
@@ -316,7 +323,7 @@ impl Compiler {
|
||||
///
|
||||
/// It disables the import optimization for fields accessed through it.
|
||||
#[must_use]
|
||||
pub fn add_mutable_global(mut self, global: impl Into<StdString>) -> Self {
|
||||
pub fn add_mutable_global(mut self, global: impl Into<String>) -> Self {
|
||||
self.mutable_globals.push(global.into());
|
||||
self
|
||||
}
|
||||
@@ -325,21 +332,21 @@ impl Compiler {
|
||||
///
|
||||
/// It disables the import optimization for fields accessed through these.
|
||||
#[must_use]
|
||||
pub fn set_mutable_globals<S: Into<StdString>>(mut self, globals: impl IntoIterator<Item = S>) -> Self {
|
||||
pub fn set_mutable_globals<S: Into<String>>(mut self, globals: impl IntoIterator<Item = S>) -> Self {
|
||||
self.mutable_globals = globals.into_iter().map(|s| s.into()).collect();
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a userdata type to the list that will be included in the type information.
|
||||
#[must_use]
|
||||
pub fn add_userdata_type(mut self, r#type: impl Into<StdString>) -> Self {
|
||||
pub fn add_userdata_type(mut self, r#type: impl Into<String>) -> Self {
|
||||
self.userdata_types.push(r#type.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a list of userdata types that will be included in the type information.
|
||||
#[must_use]
|
||||
pub fn set_userdata_types<S: Into<StdString>>(mut self, types: impl IntoIterator<Item = S>) -> Self {
|
||||
pub fn set_userdata_types<S: Into<String>>(mut self, types: impl IntoIterator<Item = S>) -> Self {
|
||||
self.userdata_types = types.into_iter().map(|s| s.into()).collect();
|
||||
self
|
||||
}
|
||||
@@ -366,24 +373,21 @@ impl Compiler {
|
||||
self.libraries_with_known_members.push(lib.clone());
|
||||
}
|
||||
self.library_constants
|
||||
.get_or_insert_with(HashMap::new)
|
||||
.get_or_insert_default()
|
||||
.insert((lib, member), r#const.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a builtin that should be disabled.
|
||||
#[must_use]
|
||||
pub fn add_disabled_builtin(mut self, builtin: impl Into<StdString>) -> Self {
|
||||
pub fn add_disabled_builtin(mut self, builtin: impl Into<String>) -> Self {
|
||||
self.disabled_builtins.push(builtin.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a list of builtins that should be disabled.
|
||||
#[must_use]
|
||||
pub fn set_disabled_builtins<S: Into<StdString>>(
|
||||
mut self,
|
||||
builtins: impl IntoIterator<Item = S>,
|
||||
) -> Self {
|
||||
pub fn set_disabled_builtins<S: Into<String>>(mut self, builtins: impl IntoIterator<Item = S>) -> Self {
|
||||
self.disabled_builtins = builtins.into_iter().map(|s| s.into()).collect();
|
||||
self
|
||||
}
|
||||
@@ -477,11 +481,11 @@ impl Compiler {
|
||||
options.mutableGlobals = mutable_globals_ptr;
|
||||
options.userdataTypes = userdata_types_ptr;
|
||||
options.librariesWithKnownMembers = libraries_with_known_members_ptr;
|
||||
if let Some(map) = self.library_constants.as_ref() {
|
||||
if !self.libraries_with_known_members.is_empty() {
|
||||
LIBRARY_MEMBER_CONSTANT_MAP.with_borrow_mut(|gmap| *gmap = map.clone());
|
||||
options.libraryMemberConstantCallback = Some(library_member_constant_callback);
|
||||
}
|
||||
if let Some(map) = self.library_constants.as_ref()
|
||||
&& !self.libraries_with_known_members.is_empty()
|
||||
{
|
||||
LIBRARY_MEMBER_CONSTANT_MAP.with_borrow_mut(|gmap| *gmap = map.clone());
|
||||
options.libraryMemberConstantCallback = Some(library_member_constant_callback);
|
||||
}
|
||||
options.disabledBuiltins = disabled_builtins_ptr;
|
||||
ffi::luau_compile(source.as_ref(), options)
|
||||
@@ -490,7 +494,7 @@ impl Compiler {
|
||||
if bytecode.first() == Some(&0) {
|
||||
// The rest of the bytecode is the error message starting with `:`
|
||||
// See https://github.com/luau-lang/luau/blob/0.640/Compiler/src/Compiler.cpp#L4336
|
||||
let message = StdString::from_utf8_lossy(&bytecode[2..]).into_owned();
|
||||
let message = String::from_utf8_lossy(&bytecode[2..]).into_owned();
|
||||
return Err(Error::SyntaxError {
|
||||
incomplete_input: message.ends_with("<eof>"),
|
||||
message,
|
||||
@@ -513,7 +517,7 @@ impl Chunk<'_> {
|
||||
/// - `@` - file path (when truncation is needed, the end of the file path is kept, as this is
|
||||
/// more useful for identifying the file)
|
||||
/// - `=` - custom chunk name (when truncation is needed, the beginning of the name is kept)
|
||||
pub fn set_name(mut self, name: impl Into<StdString>) -> Self {
|
||||
pub fn set_name(mut self, name: impl Into<String>) -> Self {
|
||||
self.name = name.into();
|
||||
self
|
||||
}
|
||||
@@ -662,19 +666,19 @@ impl Chunk<'_> {
|
||||
///
|
||||
/// It does nothing if the chunk is already binary or invalid.
|
||||
fn compile(&mut self) {
|
||||
if let Ok(ref source) = self.source {
|
||||
if self.detect_mode() == ChunkMode::Text {
|
||||
#[cfg(feature = "luau")]
|
||||
if let Ok(data) = self.compiler.get_or_insert_with(Default::default).compile(source) {
|
||||
self.source = Ok(Cow::Owned(data));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
}
|
||||
#[cfg(not(feature = "luau"))]
|
||||
if let Ok(func) = self.lua.lock().load_chunk(None, None, None, source.as_ref()) {
|
||||
let data = func.dump(false);
|
||||
self.source = Ok(Cow::Owned(data));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
}
|
||||
if let Ok(ref source) = self.source
|
||||
&& self.detect_mode() == ChunkMode::Text
|
||||
{
|
||||
#[cfg(feature = "luau")]
|
||||
if let Ok(data) = self.compiler.get_or_insert_default().compile(source) {
|
||||
self.source = Ok(Cow::Owned(data));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
}
|
||||
#[cfg(not(feature = "luau"))]
|
||||
if let Ok(func) = self.lua.lock().load_chunk(None, None, None, source.as_ref()) {
|
||||
let data = func.dump(false);
|
||||
self.source = Ok(Cow::Owned(data));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -687,33 +691,33 @@ impl Chunk<'_> {
|
||||
|
||||
// Try to fetch compiled chunk from cache
|
||||
let mut text_source = None;
|
||||
if let Ok(ref source) = self.source {
|
||||
if self.detect_mode() == ChunkMode::Text {
|
||||
let lua = self.lua.lock();
|
||||
if let Some(cache) = lua.priv_app_data_ref::<ChunksCache>() {
|
||||
if let Some(data) = cache.0.get(source.as_ref()) {
|
||||
self.source = Ok(Cow::Owned(data.clone()));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
return self;
|
||||
}
|
||||
}
|
||||
text_source = Some(source.as_ref().to_vec());
|
||||
if let Ok(ref source) = self.source
|
||||
&& self.detect_mode() == ChunkMode::Text
|
||||
{
|
||||
let lua = self.lua.lock();
|
||||
if let Some(cache) = lua.priv_app_data_ref::<ChunksCache>()
|
||||
&& let Some(data) = cache.0.get(source.as_ref())
|
||||
{
|
||||
self.source = Ok(Cow::Owned(data.clone()));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
return self;
|
||||
}
|
||||
text_source = Some(source.as_ref().to_vec());
|
||||
}
|
||||
|
||||
// Compile and cache the chunk
|
||||
if let Some(text_source) = text_source {
|
||||
self.compile();
|
||||
if let Ok(ref binary_source) = self.source {
|
||||
if self.detect_mode() == ChunkMode::Binary {
|
||||
let lua = self.lua.lock();
|
||||
if let Some(mut cache) = lua.priv_app_data_mut::<ChunksCache>() {
|
||||
cache.0.insert(text_source, binary_source.to_vec());
|
||||
} else {
|
||||
let mut cache = ChunksCache(HashMap::new());
|
||||
cache.0.insert(text_source, binary_source.to_vec());
|
||||
lua.set_priv_app_data(cache);
|
||||
};
|
||||
if let Ok(ref binary_source) = self.source
|
||||
&& self.detect_mode() == ChunkMode::Binary
|
||||
{
|
||||
let lua = self.lua.lock();
|
||||
if let Some(mut cache) = lua.priv_app_data_mut::<ChunksCache>() {
|
||||
cache.0.insert(text_source, binary_source.to_vec());
|
||||
} else {
|
||||
let mut cache = ChunksCache(HashMap::new());
|
||||
cache.0.insert(text_source, binary_source.to_vec());
|
||||
lua.set_priv_app_data(cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -761,7 +765,7 @@ impl Chunk<'_> {
|
||||
ChunkMode::Text
|
||||
}
|
||||
|
||||
fn convert_name(name: StdString) -> Result<CString> {
|
||||
fn convert_name(name: String) -> Result<CString> {
|
||||
CString::new(name).map_err(|err| Error::runtime(format!("invalid name: {err}")))
|
||||
}
|
||||
|
||||
@@ -783,7 +787,6 @@ impl Chunk<'_> {
|
||||
///
|
||||
/// The resulted `IntoLua` implementation will convert the chunk into a Lua function without
|
||||
/// executing it.
|
||||
#[doc(hidden)]
|
||||
#[track_caller]
|
||||
pub fn wrap(chunk: impl AsChunk) -> impl IntoLua {
|
||||
WrappedChunk {
|
||||
|
||||
+154
-231
@@ -4,20 +4,19 @@ use std::ffi::{CStr, CString, OsStr, OsString};
|
||||
use std::hash::{BuildHasher, Hash};
|
||||
use std::os::raw::c_int;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::string::String as StdString;
|
||||
use std::{mem, slice, str};
|
||||
use std::{slice, str};
|
||||
|
||||
use bstr::{BStr, BString, ByteSlice, ByteVec};
|
||||
use bstr::{BStr, BString, ByteVec};
|
||||
use num_traits::cast;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::string::{BorrowedBytes, BorrowedStr, String};
|
||||
use crate::string::{BorrowedBytes, BorrowedStr, LuaString};
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::traits::{FromLua, IntoLua, ShortTypeName as _};
|
||||
use crate::types::{Either, LightUserData, MaybeSend, RegistryKey};
|
||||
use crate::types::{Either, LightUserData, MaybeSend, MaybeSync, RegistryKey};
|
||||
use crate::userdata::{AnyUserData, UserData};
|
||||
use crate::value::{Nil, Value};
|
||||
|
||||
@@ -47,14 +46,14 @@ impl FromLua for Value {
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for String {
|
||||
impl IntoLua for LuaString {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for &String {
|
||||
impl IntoLua for &LuaString {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(self.clone()))
|
||||
@@ -67,16 +66,12 @@ impl IntoLua for &String {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for String {
|
||||
impl FromLua for LuaString {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<String> {
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<LuaString> {
|
||||
let ty = value.type_name();
|
||||
lua.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "string".to_string(),
|
||||
message: Some("expected string or number".to_string()),
|
||||
})
|
||||
.ok_or_else(|| Error::from_lua_conversion(ty, "string", "expected string or number".to_string()))
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
@@ -84,98 +79,86 @@ impl FromLua for String {
|
||||
let type_id = ffi::lua_type(state, idx);
|
||||
if type_id == ffi::LUA_TSTRING {
|
||||
ffi::lua_xpush(state, lua.ref_thread(), idx);
|
||||
return Ok(String(lua.pop_ref_thread()));
|
||||
return Ok(LuaString(lua.pop_ref_thread()));
|
||||
}
|
||||
// Fallback to default
|
||||
Self::from_lua(lua.stack_value(idx, Some(type_id)), lua.lua())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for BorrowedStr<'_> {
|
||||
impl IntoLua for BorrowedStr {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(self.borrow.into_owned()))
|
||||
Ok(Value::String(LuaString(self.vref)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
lua.push_ref(&self.borrow.0);
|
||||
lua.push_ref(&self.vref);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for &BorrowedStr<'_> {
|
||||
impl IntoLua for &BorrowedStr {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(self.borrow.clone().into_owned()))
|
||||
Ok(Value::String(LuaString(self.vref.clone())))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
lua.push_ref(&self.borrow.0);
|
||||
lua.push_ref(&self.vref);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for BorrowedStr<'_> {
|
||||
impl FromLua for BorrowedStr {
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||
let s = String::from_lua(value, lua)?;
|
||||
let BorrowedStr { buf, _lua, .. } = BorrowedStr::try_from(&s)?;
|
||||
let buf = unsafe { mem::transmute::<&str, &'static str>(buf) };
|
||||
let borrow = Cow::Owned(s);
|
||||
Ok(Self { buf, borrow, _lua })
|
||||
let s = LuaString::from_lua(value, lua)?;
|
||||
BorrowedStr::try_from(&s)
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let s = String::from_stack(idx, lua)?;
|
||||
let BorrowedStr { buf, _lua, .. } = BorrowedStr::try_from(&s)?;
|
||||
let buf = unsafe { mem::transmute::<&str, &'static str>(buf) };
|
||||
let borrow = Cow::Owned(s);
|
||||
Ok(Self { buf, borrow, _lua })
|
||||
let s = LuaString::from_stack(idx, lua)?;
|
||||
BorrowedStr::try_from(&s)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for BorrowedBytes<'_> {
|
||||
impl IntoLua for BorrowedBytes {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(self.borrow.into_owned()))
|
||||
Ok(Value::String(LuaString(self.vref)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
lua.push_ref(&self.borrow.0);
|
||||
lua.push_ref(&self.vref);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for &BorrowedBytes<'_> {
|
||||
impl IntoLua for &BorrowedBytes {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(self.borrow.clone().into_owned()))
|
||||
Ok(Value::String(LuaString(self.vref.clone())))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
lua.push_ref(&self.borrow.0);
|
||||
lua.push_ref(&self.vref);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for BorrowedBytes<'_> {
|
||||
impl FromLua for BorrowedBytes {
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||
let s = String::from_lua(value, lua)?;
|
||||
let BorrowedBytes { buf, _lua, .. } = BorrowedBytes::from(&s);
|
||||
let buf = unsafe { mem::transmute::<&[u8], &'static [u8]>(buf) };
|
||||
let borrow = Cow::Owned(s);
|
||||
Ok(Self { buf, borrow, _lua })
|
||||
let s = LuaString::from_lua(value, lua)?;
|
||||
Ok(BorrowedBytes::from(&s))
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let s = String::from_stack(idx, lua)?;
|
||||
let BorrowedBytes { buf, _lua, .. } = BorrowedBytes::from(&s);
|
||||
let buf = unsafe { mem::transmute::<&[u8], &'static [u8]>(buf) };
|
||||
let borrow = Cow::Owned(s);
|
||||
Ok(Self { buf, borrow, _lua })
|
||||
let s = LuaString::from_stack(idx, lua)?;
|
||||
Ok(BorrowedBytes::from(&s))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,11 +187,7 @@ impl FromLua for Table {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Table> {
|
||||
match value {
|
||||
Value::Table(table) => Ok(table),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "table".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(value.type_name(), "table", None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,11 +217,7 @@ impl FromLua for Function {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Function> {
|
||||
match value {
|
||||
Value::Function(table) => Ok(table),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "function".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(value.type_name(), "function", None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -272,11 +247,7 @@ impl FromLua for Thread {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Thread> {
|
||||
match value {
|
||||
Value::Thread(t) => Ok(t),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "thread".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(value.type_name(), "thread", None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,16 +277,12 @@ impl FromLua for AnyUserData {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<AnyUserData> {
|
||||
match value {
|
||||
Value::UserData(ud) => Ok(ud),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "userdata".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(value.type_name(), "userdata", None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UserData + MaybeSend + 'static> IntoLua for T {
|
||||
impl<T: UserData + MaybeSend + MaybeSync + 'static> IntoLua for T {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
Ok(Value::UserData(lua.create_userdata(self)?))
|
||||
@@ -428,11 +395,11 @@ impl FromLua for LightUserData {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
match value {
|
||||
Value::LightUserData(ud) => Ok(ud),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "lightuserdata".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(
|
||||
value.type_name(),
|
||||
"lightuserdata",
|
||||
None,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -451,11 +418,7 @@ impl FromLua for crate::Vector {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
match value {
|
||||
Value::Vector(v) => Ok(v),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "vector".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(value.type_name(), "vector", None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -488,16 +451,12 @@ impl FromLua for crate::Buffer {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
match value {
|
||||
Value::Buffer(buf) => Ok(buf),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "buffer".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(value.type_name(), "buffer", None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for StdString {
|
||||
impl IntoLua for String {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
#[cfg(feature = "lua55")]
|
||||
@@ -519,16 +478,14 @@ impl IntoLua for StdString {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for StdString {
|
||||
impl FromLua for String {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
Ok(lua
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: Self::type_name(),
|
||||
message: Some("expected string or number".to_string()),
|
||||
.ok_or_else(|| {
|
||||
Error::from_lua_conversion(ty, Self::type_name(), "expected string or number".to_string())
|
||||
})?
|
||||
.to_str()?
|
||||
.to_owned())
|
||||
@@ -544,11 +501,7 @@ impl FromLua for StdString {
|
||||
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: Self::type_name(),
|
||||
message: Some(e.to_string()),
|
||||
});
|
||||
.map_err(|e| Error::from_lua_conversion("string", Self::type_name(), e.to_string()));
|
||||
}
|
||||
// Fallback to default
|
||||
Self::from_lua(lua.stack_value(idx, Some(type_id)), lua.lua())
|
||||
@@ -570,7 +523,10 @@ impl IntoLua for &str {
|
||||
impl IntoLua for Cow<'_, str> {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(lua.create_string(self.as_bytes())?))
|
||||
match self {
|
||||
Cow::Borrowed(s) => s.into_lua(lua),
|
||||
Cow::Owned(s) => s.into_lua(lua),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,10 +543,8 @@ impl FromLua for Box<str> {
|
||||
let ty = value.type_name();
|
||||
Ok(lua
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: Self::type_name(),
|
||||
message: Some("expected string or number".to_string()),
|
||||
.ok_or_else(|| {
|
||||
Error::from_lua_conversion(ty, Self::type_name(), "expected string or number".to_string())
|
||||
})?
|
||||
.to_str()?
|
||||
.to_owned()
|
||||
@@ -614,21 +568,12 @@ impl FromLua for CString {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
let string = lua
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: Self::type_name(),
|
||||
message: Some("expected string or number".to_string()),
|
||||
})?;
|
||||
|
||||
let string = lua.coerce_string(value)?.ok_or_else(|| {
|
||||
Error::from_lua_conversion(ty, Self::type_name(), "expected string or number".to_string())
|
||||
})?;
|
||||
match CStr::from_bytes_with_nul(&string.as_bytes_with_nul()) {
|
||||
Ok(s) => Ok(s.into()),
|
||||
Err(_) => Err(Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: Self::type_name(),
|
||||
message: Some("invalid C-style string".to_string()),
|
||||
}),
|
||||
Err(err) => Err(Error::from_lua_conversion(ty, Self::type_name(), err.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -643,7 +588,10 @@ impl IntoLua for &CStr {
|
||||
impl IntoLua for Cow<'_, CStr> {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(lua.create_string(self.to_bytes())?))
|
||||
match self {
|
||||
Cow::Borrowed(s) => s.into_lua(lua),
|
||||
Cow::Owned(s) => s.into_lua(lua),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -668,10 +616,8 @@ impl FromLua for BString {
|
||||
Value::Buffer(buf) => Ok(buf.to_vec().into()),
|
||||
_ => Ok((*lua
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: Self::type_name(),
|
||||
message: Some("expected string or number".to_string()),
|
||||
.ok_or_else(|| {
|
||||
Error::from_lua_conversion(ty, Self::type_name(), "expected string or number".to_string())
|
||||
})?
|
||||
.as_bytes())
|
||||
.into()),
|
||||
@@ -722,23 +668,22 @@ impl FromLua for OsString {
|
||||
let bs = BString::from_lua(value, lua)?;
|
||||
Vec::from(bs)
|
||||
.into_os_string()
|
||||
.map_err(|err| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "OsString".into(),
|
||||
message: Some(err.to_string()),
|
||||
})
|
||||
.map_err(|err| Error::from_lua_conversion(ty, "OsString", err.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for &OsStr {
|
||||
#[cfg(unix)]
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
let s = <[u8]>::from_os_str(self).ok_or_else(|| Error::ToLuaConversionError {
|
||||
from: "OsStr".into(),
|
||||
to: "string",
|
||||
message: Some("invalid utf-8 encoding".into()),
|
||||
})?;
|
||||
Ok(Value::String(lua.create_string(s)?))
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
Ok(Value::String(lua.create_string(self.as_bytes())?))
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
self.display().to_string().into_lua(lua)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -776,34 +721,25 @@ impl FromLua for char {
|
||||
fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
match value {
|
||||
Value::Integer(i) => {
|
||||
cast(i)
|
||||
.and_then(char::from_u32)
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "char".to_string(),
|
||||
message: Some("integer out of range when converting to char".to_string()),
|
||||
})
|
||||
}
|
||||
Value::Integer(i) => cast(i).and_then(char::from_u32).ok_or_else(|| {
|
||||
let msg = "integer out of range when converting to char";
|
||||
Error::from_lua_conversion(ty, "char", msg.to_string())
|
||||
}),
|
||||
Value::String(s) => {
|
||||
let str = s.to_str()?;
|
||||
let mut str_iter = str.chars();
|
||||
match (str_iter.next(), str_iter.next()) {
|
||||
(Some(char), None) => Ok(char),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "char".to_string(),
|
||||
message: Some(
|
||||
"expected string to have exactly one char when converting to char".to_string(),
|
||||
),
|
||||
}),
|
||||
_ => {
|
||||
let msg = "expected string to have exactly one char when converting to char";
|
||||
Err(Error::from_lua_conversion(ty, "char", msg.to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: Self::type_name(),
|
||||
message: Some("expected string or integer".to_string()),
|
||||
}),
|
||||
_ => {
|
||||
let msg = "expected string or integer";
|
||||
Err(Error::from_lua_conversion(ty, Self::type_name(), msg.to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -854,24 +790,14 @@ macro_rules! lua_convert_int {
|
||||
if let Some(i) = lua.coerce_integer(value.clone())? {
|
||||
cast(i)
|
||||
} else {
|
||||
cast(
|
||||
lua.coerce_number(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: stringify!($x).to_string(),
|
||||
message: Some(
|
||||
"expected number or string coercible to number".to_string(),
|
||||
),
|
||||
})?,
|
||||
)
|
||||
cast(lua.coerce_number(value)?.ok_or_else(|| {
|
||||
let msg = "expected number or string coercible to number";
|
||||
Error::from_lua_conversion(ty, stringify!($x), msg.to_string())
|
||||
})?)
|
||||
}
|
||||
}
|
||||
})
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: stringify!($x).to_string(),
|
||||
message: Some("out of range".to_owned()),
|
||||
})
|
||||
.ok_or_else(|| Error::from_lua_conversion(ty, stringify!($x), "out of range".to_string()))
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
@@ -881,13 +807,18 @@ macro_rules! lua_convert_int {
|
||||
let mut ok = 0;
|
||||
let i = ffi::lua_tointegerx(state, idx, &mut ok);
|
||||
if ok != 0 {
|
||||
return cast(i).ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: "integer",
|
||||
to: stringify!($x).to_string(),
|
||||
message: Some("out of range".to_owned()),
|
||||
return cast(i).ok_or_else(|| {
|
||||
Error::from_lua_conversion("integer", stringify!($x), "out of range".to_string())
|
||||
});
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
if type_id == ffi::LUA_TINTEGER {
|
||||
let i = ffi::lua_tointeger64(state, idx, std::ptr::null_mut());
|
||||
return cast(i).ok_or_else(|| {
|
||||
Error::from_lua_conversion("integer", stringify!($x), "out of range".to_string())
|
||||
});
|
||||
}
|
||||
// Fallback to default
|
||||
Self::from_lua(lua.stack_value(idx, Some(type_id)), lua.lua())
|
||||
}
|
||||
@@ -921,13 +852,10 @@ macro_rules! lua_convert_float {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
lua.coerce_number(value)?
|
||||
.map(|n| n as $x)
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: stringify!($x).to_string(),
|
||||
message: Some("expected number or string coercible to number".to_string()),
|
||||
})
|
||||
lua.coerce_number(value)?.map(|n| n as $x).ok_or_else(|| {
|
||||
let msg = "expected number or string coercible to number";
|
||||
Error::from_lua_conversion(ty, stringify!($x), msg.to_string())
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
@@ -987,18 +915,16 @@ where
|
||||
},
|
||||
Value::Table(table) => {
|
||||
let vec = table.sequence_values().collect::<Result<Vec<_>>>()?;
|
||||
vec.try_into()
|
||||
.map_err(|vec: Vec<T>| Error::FromLuaConversionError {
|
||||
from: "table",
|
||||
to: Self::type_name(),
|
||||
message: Some(format!("expected table of length {N}, got {}", vec.len())),
|
||||
})
|
||||
vec.try_into().map_err(|vec: Vec<T>| {
|
||||
let msg = format!("expected table of length {N}, got {}", vec.len());
|
||||
Error::from_lua_conversion("table", Self::type_name(), msg)
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
let msg = format!("expected table of length {N}");
|
||||
let err = Error::from_lua_conversion(value.type_name(), Self::type_name(), msg.to_string());
|
||||
Err(err)
|
||||
}
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: Self::type_name(),
|
||||
message: Some("expected table".to_string()),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1029,11 +955,11 @@ impl<T: FromLua> FromLua for Vec<T> {
|
||||
fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
|
||||
match value {
|
||||
Value::Table(table) => table.sequence_values().collect(),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: Self::type_name(),
|
||||
message: Some("expected table".to_string()),
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(
|
||||
value.type_name(),
|
||||
Self::type_name(),
|
||||
"expected table".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1048,14 +974,13 @@ impl<K: Eq + Hash + IntoLua, V: IntoLua, S: BuildHasher> IntoLua for HashMap<K,
|
||||
impl<K: Eq + Hash + FromLua, V: FromLua, S: BuildHasher + Default> FromLua for HashMap<K, V, S> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
if let Value::Table(table) = value {
|
||||
table.pairs().collect()
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: Self::type_name(),
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
match value {
|
||||
Value::Table(table) => table.pairs().collect(),
|
||||
_ => Err(Error::from_lua_conversion(
|
||||
value.type_name(),
|
||||
Self::type_name(),
|
||||
"expected table".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1070,14 +995,13 @@ impl<K: Ord + IntoLua, V: IntoLua> IntoLua for BTreeMap<K, V> {
|
||||
impl<K: Ord + FromLua, V: FromLua> FromLua for BTreeMap<K, V> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
if let Value::Table(table) = value {
|
||||
table.pairs().collect()
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: Self::type_name(),
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
match value {
|
||||
Value::Table(table) => table.pairs().collect(),
|
||||
_ => Err(Error::from_lua_conversion(
|
||||
value.type_name(),
|
||||
Self::type_name(),
|
||||
"expected table".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1097,11 +1021,11 @@ impl<T: Eq + Hash + FromLua, S: BuildHasher + Default> FromLua for HashSet<T, S>
|
||||
match value {
|
||||
Value::Table(table) if table.raw_len() > 0 => table.sequence_values().collect(),
|
||||
Value::Table(table) => table.pairs::<T, Value>().map(|res| res.map(|(k, _)| k)).collect(),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: Self::type_name(),
|
||||
message: Some("expected table".to_string()),
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(
|
||||
value.type_name(),
|
||||
Self::type_name(),
|
||||
"expected table".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1121,11 +1045,11 @@ impl<T: Ord + FromLua> FromLua for BTreeSet<T> {
|
||||
match value {
|
||||
Value::Table(table) if table.raw_len() > 0 => table.sequence_values().collect(),
|
||||
Value::Table(table) => table.pairs::<T, Value>().map(|res| res.map(|(k, _)| k)).collect(),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: Self::type_name(),
|
||||
message: Some("expected table".to_string()),
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(
|
||||
value.type_name(),
|
||||
Self::type_name(),
|
||||
"expected table".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1195,11 +1119,11 @@ impl<L: FromLua, R: FromLua> FromLua for Either<L, R> {
|
||||
// Try the right type
|
||||
Err(_) => match R::from_lua(value, lua).map(Either::Right) {
|
||||
Ok(r) => Ok(r),
|
||||
Err(_) => Err(Error::FromLuaConversionError {
|
||||
from: value_type_name,
|
||||
to: Self::type_name(),
|
||||
message: None,
|
||||
}),
|
||||
Err(_) => Err(Error::from_lua_conversion(
|
||||
value_type_name,
|
||||
Self::type_name(),
|
||||
None,
|
||||
)),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1211,13 +1135,12 @@ impl<L: FromLua, R: FromLua> FromLua for Either<L, R> {
|
||||
Err(_) => match R::from_stack(idx, lua).map(Either::Right) {
|
||||
Ok(r) => Ok(r),
|
||||
Err(_) => {
|
||||
let value_type_name =
|
||||
CStr::from_ptr(ffi::lua_typename(lua.state(), ffi::lua_type(lua.state(), idx)));
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: value_type_name.to_str().unwrap(),
|
||||
to: Self::type_name(),
|
||||
message: None,
|
||||
})
|
||||
let state = lua.state();
|
||||
let from_type_name = CStr::from_ptr(ffi::lua_typename(state, ffi::lua_type(state, idx)))
|
||||
.to_str()
|
||||
.unwrap_or("unknown");
|
||||
let err = Error::from_lua_conversion(from_type_name, Self::type_name(), None);
|
||||
Err(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
+32
-49
@@ -1,3 +1,9 @@
|
||||
//! Lua debugging interface.
|
||||
//!
|
||||
//! This module provides access to the Lua debug interface, allowing inspection of the call stack,
|
||||
//! and function information. The main types are [`struct@Debug`] for accessing debug information
|
||||
//! and [`HookTriggers`] for configuring debug hooks.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
@@ -5,7 +11,7 @@ use ffi::{lua_Debug, lua_State};
|
||||
|
||||
use crate::function::Function;
|
||||
use crate::state::RawLua;
|
||||
use crate::util::{assert_stack, linenumber_to_usize, ptr_to_lossy_str, ptr_to_str, StackGuard};
|
||||
use crate::util::{StackGuard, assert_stack, linenumber_to_usize, ptr_to_lossy_str, ptr_to_str};
|
||||
|
||||
/// Contains information about currently executing Lua code.
|
||||
///
|
||||
@@ -133,12 +139,6 @@ impl<'a> Debug<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[deprecated(note = "Use `current_line` instead")]
|
||||
pub fn curr_line(&self) -> i32 {
|
||||
self.current_line().map(|n| n as i32).unwrap_or(-1)
|
||||
}
|
||||
|
||||
/// Corresponds to the `l` "what" mask. Returns the current line.
|
||||
pub fn current_line(&self) -> Option<usize> {
|
||||
unsafe {
|
||||
@@ -190,15 +190,15 @@ impl<'a> Debug<'a> {
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let stack = DebugStack {
|
||||
num_ups: (*self.ar).nups as _,
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
num_upvalues: (*self.ar).nups as _,
|
||||
#[cfg(not(any(feature = "lua51", feature = "luajit")))]
|
||||
num_params: (*self.ar).nparams as _,
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
#[cfg(not(any(feature = "lua51", feature = "luajit")))]
|
||||
is_vararg: (*self.ar).isvararg != 0,
|
||||
};
|
||||
#[cfg(feature = "luau")]
|
||||
let stack = DebugStack {
|
||||
num_ups: (*self.ar).nupvals,
|
||||
num_upvalues: (*self.ar).nupvals,
|
||||
num_params: (*self.ar).nparams,
|
||||
is_vararg: (*self.ar).isvararg != 0,
|
||||
};
|
||||
@@ -208,6 +208,8 @@ impl<'a> Debug<'a> {
|
||||
}
|
||||
|
||||
/// Represents a specific event that triggered the hook.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum DebugEvent {
|
||||
Call,
|
||||
@@ -218,6 +220,9 @@ pub enum DebugEvent {
|
||||
Unknown(c_int),
|
||||
}
|
||||
|
||||
/// Contains the name information of a function in the call stack.
|
||||
///
|
||||
/// Returned by the [`Debug::names`] method.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DebugNames<'a> {
|
||||
/// A (reasonable) name of the function (`None` if the name cannot be found).
|
||||
@@ -228,6 +233,9 @@ pub struct DebugNames<'a> {
|
||||
pub name_what: Option<&'static str>,
|
||||
}
|
||||
|
||||
/// Contains the source information of a function in the call stack.
|
||||
///
|
||||
/// Returned by the [`Debug::source`] method.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DebugSource<'a> {
|
||||
/// Source of the chunk that created the function.
|
||||
@@ -243,47 +251,20 @@ pub struct DebugSource<'a> {
|
||||
pub what: &'static str,
|
||||
}
|
||||
|
||||
/// Contains stack information about a function in the call stack.
|
||||
///
|
||||
/// Returned by the [`Debug::stack`] method.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct DebugStack {
|
||||
/// Number of upvalues.
|
||||
pub num_ups: u8,
|
||||
/// Number of parameters.
|
||||
#[cfg(any(
|
||||
feature = "lua55",
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "luau"
|
||||
))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(
|
||||
feature = "lua55",
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "luau"
|
||||
)))
|
||||
)]
|
||||
/// The number of upvalues of the function.
|
||||
pub num_upvalues: u8,
|
||||
/// The number of parameters of the function (always 0 for C).
|
||||
#[cfg(any(not(any(feature = "lua51", feature = "luajit")), doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(any(feature = "lua51", feature = "luajit")))))]
|
||||
pub num_params: u8,
|
||||
/// Whether the function is a vararg function.
|
||||
#[cfg(any(
|
||||
feature = "lua55",
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "luau"
|
||||
))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(
|
||||
feature = "lua55",
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "luau"
|
||||
)))
|
||||
)]
|
||||
/// Whether the function is a variadic function (always true for C).
|
||||
#[cfg(any(not(any(feature = "lua51", feature = "luajit")), doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(any(feature = "lua51", feature = "luajit")))))]
|
||||
pub is_vararg: bool,
|
||||
}
|
||||
|
||||
@@ -361,6 +342,7 @@ impl HookTriggers {
|
||||
}
|
||||
|
||||
// Compute the mask to pass to `lua_sethook`.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) const fn mask(&self) -> c_int {
|
||||
let mut mask: c_int = 0;
|
||||
if self.on_calls {
|
||||
@@ -380,6 +362,7 @@ impl HookTriggers {
|
||||
|
||||
// Returns the `count` parameter to pass to `lua_sethook`, if applicable. Otherwise, zero is
|
||||
// returned.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) const fn count(&self) -> c_int {
|
||||
match self.every_nth_instruction {
|
||||
Some(n) => n as c_int,
|
||||
|
||||
+42
-46
@@ -1,10 +1,14 @@
|
||||
//! Lua error handling.
|
||||
//!
|
||||
//! This module provides the [`Error`] type returned by all fallible `mlua` operations, together
|
||||
//! with extension traits for adapting Rust errors for use within Lua.
|
||||
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
use std::io::Error as IoError;
|
||||
use std::net::AddrParseError;
|
||||
use std::result::Result as StdResult;
|
||||
use std::str::Utf8Error;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::private::Sealed;
|
||||
@@ -22,7 +26,7 @@ pub enum Error {
|
||||
/// Syntax error while parsing Lua source code.
|
||||
SyntaxError {
|
||||
/// The error message as returned by Lua.
|
||||
message: StdString,
|
||||
message: String,
|
||||
/// `true` if the error can likely be fixed by appending more input to the source code.
|
||||
///
|
||||
/// This is useful for implementing REPLs as they can query the user for more input if this
|
||||
@@ -34,20 +38,20 @@ pub enum Error {
|
||||
/// The Lua VM returns this error when a builtin operation is performed on incompatible types.
|
||||
/// Among other things, this includes invoking operators on wrong types (such as calling or
|
||||
/// indexing a `nil` value).
|
||||
RuntimeError(StdString),
|
||||
RuntimeError(String),
|
||||
/// Lua memory error, aka `LUA_ERRMEM`
|
||||
///
|
||||
/// The Lua VM returns this error when the allocator does not return the requested memory, aka
|
||||
/// it is an out-of-memory error.
|
||||
MemoryError(StdString),
|
||||
MemoryError(String),
|
||||
/// Lua garbage collector error, aka `LUA_ERRGCMM`.
|
||||
///
|
||||
/// The Lua VM returns this error when there is an error running a `__gc` metamethod.
|
||||
#[cfg(any(feature = "lua53", feature = "lua52", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua53", feature = "lua52"))))]
|
||||
GarbageCollectorError(StdString),
|
||||
GarbageCollectorError(String),
|
||||
/// Potentially unsafe action in safe mode.
|
||||
SafetyError(StdString),
|
||||
SafetyError(String),
|
||||
/// Memory control is not available.
|
||||
///
|
||||
/// This error can only happen when Lua state was not created by us and does not have the
|
||||
@@ -80,23 +84,14 @@ pub enum Error {
|
||||
/// (which is stored in the corresponding field).
|
||||
BadArgument {
|
||||
/// Function that was called.
|
||||
to: Option<StdString>,
|
||||
to: Option<String>,
|
||||
/// Argument position (usually starts from 1).
|
||||
pos: usize,
|
||||
/// Argument name.
|
||||
name: Option<StdString>,
|
||||
name: Option<String>,
|
||||
/// 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.
|
||||
from: String,
|
||||
/// Name of the Lua type that could not be created.
|
||||
to: &'static str,
|
||||
/// A message indicating why the conversion failed in more detail.
|
||||
message: Option<StdString>,
|
||||
},
|
||||
/// A Lua value could not be converted to the expected Rust type.
|
||||
FromLuaConversionError {
|
||||
/// Name of the Lua type that could not be converted.
|
||||
@@ -104,7 +99,7 @@ pub enum Error {
|
||||
/// Name of the Rust type that could not be created.
|
||||
to: String,
|
||||
/// A string containing more detailed error information.
|
||||
message: Option<StdString>,
|
||||
message: Option<String>,
|
||||
},
|
||||
/// [`Thread::resume`] was called on an unresumable coroutine.
|
||||
///
|
||||
@@ -154,17 +149,17 @@ pub enum Error {
|
||||
/// A [`MetaMethod`] operation is restricted (typically for `__gc` or `__metatable`).
|
||||
///
|
||||
/// [`MetaMethod`]: crate::MetaMethod
|
||||
MetaMethodRestricted(StdString),
|
||||
MetaMethodRestricted(String),
|
||||
/// A [`MetaMethod`] (eg. `__index` or `__newindex`) has invalid type.
|
||||
///
|
||||
/// [`MetaMethod`]: crate::MetaMethod
|
||||
MetaMethodTypeError {
|
||||
/// Name of the metamethod.
|
||||
method: StdString,
|
||||
method: String,
|
||||
/// Passed value type.
|
||||
type_name: &'static str,
|
||||
/// A string containing more detailed error information.
|
||||
message: Option<StdString>,
|
||||
message: Option<String>,
|
||||
},
|
||||
/// A [`RegistryKey`] produced from a different Lua state was used.
|
||||
///
|
||||
@@ -173,7 +168,7 @@ pub enum Error {
|
||||
/// A Rust callback returned `Err`, raising the contained `Error` as a Lua error.
|
||||
CallbackError {
|
||||
/// Lua call stack backtrace.
|
||||
traceback: StdString,
|
||||
traceback: String,
|
||||
/// Original error returned by the Rust code.
|
||||
cause: Arc<Error>,
|
||||
},
|
||||
@@ -185,11 +180,11 @@ pub enum Error {
|
||||
/// Serialization error.
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
SerializeError(StdString),
|
||||
SerializeError(String),
|
||||
/// Deserialization error.
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
DeserializeError(StdString),
|
||||
DeserializeError(String),
|
||||
/// A custom error.
|
||||
///
|
||||
/// This can be used for returning user-defined errors from callbacks.
|
||||
@@ -201,7 +196,7 @@ pub enum Error {
|
||||
/// An error with additional context.
|
||||
WithContext {
|
||||
/// A string containing additional context.
|
||||
context: StdString,
|
||||
context: String,
|
||||
/// Underlying error.
|
||||
cause: Arc<Error>,
|
||||
},
|
||||
@@ -225,7 +220,7 @@ impl fmt::Display for Error {
|
||||
}
|
||||
Error::SafetyError(msg) => {
|
||||
write!(fmt, "safety error: {msg}")
|
||||
},
|
||||
}
|
||||
Error::MemoryControlNotAvailable => {
|
||||
write!(fmt, "memory control is not available")
|
||||
}
|
||||
@@ -238,10 +233,7 @@ impl fmt::Display for Error {
|
||||
fmt,
|
||||
"out of Lua stack, too many arguments to a Lua function or too many return values from a callback"
|
||||
),
|
||||
Error::BindError => write!(
|
||||
fmt,
|
||||
"too many arguments to Function::bind"
|
||||
),
|
||||
Error::BindError => write!(fmt, "too many arguments to Function::bind"),
|
||||
Error::BadArgument { to, pos, name, cause } => {
|
||||
if let Some(name) = name {
|
||||
write!(fmt, "bad argument `{name}`")?;
|
||||
@@ -252,13 +244,6 @@ impl fmt::Display for Error {
|
||||
write!(fmt, " to `{to}`")?;
|
||||
}
|
||||
write!(fmt, ": {cause}")
|
||||
},
|
||||
Error::ToLuaConversionError { from, to, message } => {
|
||||
write!(fmt, "error converting {from} to Lua {to}")?;
|
||||
match message {
|
||||
None => Ok(()),
|
||||
Some(message) => write!(fmt, " ({message})"),
|
||||
}
|
||||
}
|
||||
Error::FromLuaConversionError { from, to, message } => {
|
||||
write!(fmt, "error converting Lua {from} to {to}")?;
|
||||
@@ -273,7 +258,11 @@ impl fmt::Display for Error {
|
||||
Error::UserDataBorrowError => write!(fmt, "error borrowing userdata"),
|
||||
Error::UserDataBorrowMutError => write!(fmt, "error mutably borrowing userdata"),
|
||||
Error::MetaMethodRestricted(method) => write!(fmt, "metamethod {method} is restricted"),
|
||||
Error::MetaMethodTypeError { method, type_name, message } => {
|
||||
Error::MetaMethodTypeError {
|
||||
method,
|
||||
type_name,
|
||||
message,
|
||||
} => {
|
||||
write!(fmt, "metamethod {method} has unsupported type {type_name}")?;
|
||||
match message {
|
||||
None => Ok(()),
|
||||
@@ -286,7 +275,11 @@ impl fmt::Display for Error {
|
||||
Error::CallbackError { cause, traceback } => {
|
||||
// Trace errors down to the root
|
||||
let (mut cause, mut full_traceback) = (cause, None);
|
||||
while let Error::CallbackError { cause: cause2, traceback: traceback2 } = &**cause {
|
||||
while let Error::CallbackError {
|
||||
cause: cause2,
|
||||
traceback: traceback2,
|
||||
} = &**cause
|
||||
{
|
||||
cause = cause2;
|
||||
full_traceback = Some(traceback2);
|
||||
}
|
||||
@@ -312,11 +305,11 @@ impl fmt::Display for Error {
|
||||
#[cfg(feature = "serde")]
|
||||
Error::SerializeError(err) => {
|
||||
write!(fmt, "serialize error: {err}")
|
||||
},
|
||||
}
|
||||
#[cfg(feature = "serde")]
|
||||
Error::DeserializeError(err) => {
|
||||
write!(fmt, "deserialize error: {err}")
|
||||
},
|
||||
}
|
||||
Error::ExternalError(err) => err.fmt(fmt),
|
||||
Error::WithContext { context, cause } => {
|
||||
writeln!(fmt, "{context}")?;
|
||||
@@ -352,7 +345,11 @@ impl Error {
|
||||
/// Wraps an external error object.
|
||||
#[inline]
|
||||
pub fn external<T: Into<Box<DynStdError>>>(err: T) -> Self {
|
||||
Error::ExternalError(err.into().into())
|
||||
let boxed = err.into();
|
||||
match boxed.downcast::<Self>() {
|
||||
Ok(err) => *err,
|
||||
Err(boxed) => Error::ExternalError(boxed.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to downcast the external error object to a concrete type by reference.
|
||||
@@ -394,6 +391,7 @@ impl Error {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn from_lua_conversion(
|
||||
from: &'static str,
|
||||
to: impl ToString,
|
||||
@@ -561,10 +559,8 @@ impl<'a> Iterator for Chain<'a> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(not(feature = "error-send"))]
|
||||
static_assertions::assert_not_impl_any!(Error: Send, Sync);
|
||||
static_assertions::assert_not_impl_any!(super::Error: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(Error: Send, Sync);
|
||||
static_assertions::assert_impl_all!(super::Error: Send, Sync);
|
||||
}
|
||||
|
||||
+186
-15
@@ -1,24 +1,99 @@
|
||||
//! Lua function handling.
|
||||
//!
|
||||
//! This module provides types for working with Lua functions from Rust, including
|
||||
//! both Lua-defined functions and native Rust callbacks.
|
||||
//!
|
||||
//! # Calling Functions
|
||||
//!
|
||||
//! Use [`Function::call`] to invoke a Lua function synchronously:
|
||||
//!
|
||||
//! ```
|
||||
//! # use mlua::{Function, Lua, Result};
|
||||
//! # fn main() -> Result<()> {
|
||||
//! let lua = Lua::new();
|
||||
//!
|
||||
//! // Get a built-in function
|
||||
//! let print: Function = lua.globals().get("print")?;
|
||||
//! print.call::<()>("Hello from Rust!")?;
|
||||
//!
|
||||
//! // Call a function that returns values
|
||||
//! let tonumber: Function = lua.globals().get("tonumber")?;
|
||||
//! let n: i32 = tonumber.call("42")?;
|
||||
//! assert_eq!(n, 42);
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! For asynchronous execution, use `Function::call_async` (requires `async` feature):
|
||||
//!
|
||||
//! ```ignore
|
||||
//! let result: String = my_async_func.call_async(args).await?;
|
||||
//! ```
|
||||
//!
|
||||
//! # Creating Functions
|
||||
//!
|
||||
//! Functions can be created from Rust closures using [`Lua::create_function`]:
|
||||
//!
|
||||
//! ```
|
||||
//! # use mlua::{Lua, Result};
|
||||
//! # fn main() -> Result<()> {
|
||||
//! let lua = Lua::new();
|
||||
//!
|
||||
//! let greet = lua.create_function(|_, name: String| {
|
||||
//! Ok(format!("Hello, {}!", name))
|
||||
//! })?;
|
||||
//!
|
||||
//! lua.globals().set("greet", greet)?;
|
||||
//! let result: String = lua.load(r#"greet("World")"#).eval()?;
|
||||
//! assert_eq!(result, "Hello, World!");
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! For simpler cases, use [`Function::wrap`] or [`Function::wrap_raw`] to convert a Rust function
|
||||
//! directly:
|
||||
//!
|
||||
//! ```
|
||||
//! # use mlua::{Function, Lua, Result};
|
||||
//! # fn main() -> Result<()> {
|
||||
//! let lua = Lua::new();
|
||||
//!
|
||||
//! fn add(a: i32, b: i32) -> i32 { a + b }
|
||||
//!
|
||||
//! lua.globals().set("add", Function::wrap_raw(add))?;
|
||||
//! let sum: i32 = lua.load("add(2, 3)").eval()?;
|
||||
//! assert_eq!(sum, 5);
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! # Function Environments
|
||||
//!
|
||||
//! Lua functions have an associated environment table that determines how global
|
||||
//! variables are resolved. Use [`Function::environment`] and [`Function::set_environment`]
|
||||
//! to inspect or modify this environment.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::result::Result as StdResult;
|
||||
use std::{mem, ptr, slice};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::error::{Error, ExternalError, ExternalResult, Result};
|
||||
use crate::state::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::traits::{FromLuaMulti, IntoLua, IntoLuaMulti, LuaNativeFn, LuaNativeFnMut};
|
||||
use crate::traits::{FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
use crate::types::{Callback, LuaType, MaybeSend, ValueRef};
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, linenumber_to_usize, pop_error, ptr_to_lossy_str, ptr_to_str, StackGuard,
|
||||
StackGuard, assert_stack, check_stack, linenumber_to_usize, pop_error, ptr_to_lossy_str, ptr_to_str,
|
||||
};
|
||||
use crate::value::Value;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::thread::AsyncThread,
|
||||
crate::traits::LuaNativeAsyncFn,
|
||||
crate::types::AsyncCallback,
|
||||
std::future::{self, Future},
|
||||
std::pin::{pin, Pin},
|
||||
std::pin::{Pin, pin},
|
||||
std::task::{Context, Poll},
|
||||
};
|
||||
|
||||
@@ -165,7 +240,7 @@ impl Function {
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`AsyncThread`]: crate::AsyncThread
|
||||
/// [`AsyncThread`]: crate::thread::AsyncThread
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn call_async<R>(&self, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
|
||||
@@ -555,30 +630,32 @@ impl Function {
|
||||
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`]
|
||||
/// trait.
|
||||
#[inline]
|
||||
pub fn wrap<F, A, R>(func: F) -> impl IntoLua
|
||||
pub fn wrap<F, A, R, E>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeFn<A, Output = Result<R>> + MaybeSend + 'static,
|
||||
F: LuaNativeFn<A, Output = StdResult<R, E>> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
E: ExternalError,
|
||||
{
|
||||
WrappedFunction(Box::new(move |lua, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 1, None, lua)?;
|
||||
func.call(args)?.push_into_stack_multi(lua)
|
||||
func.call(args).into_lua_err()?.push_into_stack_multi(lua)
|
||||
}))
|
||||
}
|
||||
|
||||
/// Wraps a Rust mutable closure, returning an opaque type that implements [`IntoLua`] trait.
|
||||
pub fn wrap_mut<F, A, R>(func: F) -> impl IntoLua
|
||||
pub fn wrap_mut<F, A, R, E>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeFnMut<A, Output = Result<R>> + MaybeSend + 'static,
|
||||
F: LuaNativeFnMut<A, Output = StdResult<R, E>> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
E: ExternalError,
|
||||
{
|
||||
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.call(args)?.push_into_stack_multi(lua)
|
||||
func.call(args).into_lua_err()?.push_into_stack_multi(lua)
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -591,6 +668,7 @@ impl Function {
|
||||
pub fn wrap_raw<F, A>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeFn<A> + MaybeSend + 'static,
|
||||
F::Output: IntoLuaMulti,
|
||||
A: FromLuaMulti,
|
||||
{
|
||||
WrappedFunction(Box::new(move |lua, nargs| unsafe {
|
||||
@@ -607,6 +685,7 @@ impl Function {
|
||||
pub fn wrap_raw_mut<F, A>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeFnMut<A> + MaybeSend + 'static,
|
||||
F::Output: IntoLuaMulti,
|
||||
A: FromLuaMulti,
|
||||
{
|
||||
let func = RefCell::new(func);
|
||||
@@ -621,11 +700,12 @@ impl Function {
|
||||
/// trait.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn wrap_async<F, A, R>(func: F) -> impl IntoLua
|
||||
pub fn wrap_async<F, A, R, E>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeAsyncFn<A, Output = Result<R>> + MaybeSend + 'static,
|
||||
F: LuaNativeAsyncFn<A, Output = StdResult<R, E>> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
E: ExternalError,
|
||||
{
|
||||
WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe {
|
||||
let args = match A::from_stack_args(nargs, 1, None, rawlua) {
|
||||
@@ -634,7 +714,7 @@ impl Function {
|
||||
};
|
||||
let lua = rawlua.lua();
|
||||
let fut = func.call(args);
|
||||
Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) })
|
||||
Box::pin(async move { fut.await.into_lua_err()?.push_into_stack_multi(lua.raw_lua()) })
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -648,6 +728,7 @@ impl Function {
|
||||
pub fn wrap_raw_async<F, A>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeAsyncFn<A> + MaybeSend + 'static,
|
||||
F::Output: IntoLuaMulti,
|
||||
A: FromLuaMulti,
|
||||
{
|
||||
WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe {
|
||||
@@ -681,7 +762,9 @@ impl LuaType for Function {
|
||||
const TYPE_ID: c_int = ffi::LUA_TFUNCTION;
|
||||
}
|
||||
|
||||
/// Future for asynchronous function calls.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct AsyncCallFuture<R: FromLuaMulti>(Result<AsyncThread<R>>);
|
||||
|
||||
@@ -705,6 +788,94 @@ impl<R: FromLuaMulti> Future for AsyncCallFuture<R> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for types that can be used as Lua functions.
|
||||
pub trait LuaNativeFn<A: FromLuaMulti> {
|
||||
type Output;
|
||||
|
||||
fn call(&self, args: A) -> Self::Output;
|
||||
}
|
||||
|
||||
/// A trait for types with mutable state that can be used as Lua functions.
|
||||
pub trait LuaNativeFnMut<A: FromLuaMulti> {
|
||||
type Output;
|
||||
|
||||
fn call(&mut self, args: A) -> Self::Output;
|
||||
}
|
||||
|
||||
/// A trait for types that returns a future and can be used as Lua functions.
|
||||
#[cfg(feature = "async")]
|
||||
pub trait LuaNativeAsyncFn<A: FromLuaMulti> {
|
||||
type Output;
|
||||
|
||||
fn call(&self, args: A) -> impl Future<Output = Self::Output> + MaybeSend + 'static;
|
||||
}
|
||||
|
||||
macro_rules! impl_lua_native_fn {
|
||||
($($A:ident),*) => {
|
||||
impl<FN, $($A,)* R> LuaNativeFn<($($A,)*)> for FN
|
||||
where
|
||||
FN: Fn($($A,)*) -> R + MaybeSend + 'static,
|
||||
($($A,)*): FromLuaMulti,
|
||||
{
|
||||
type Output = R;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn call(&self, args: ($($A,)*)) -> Self::Output {
|
||||
let ($($A,)*) = args;
|
||||
self($($A,)*)
|
||||
}
|
||||
}
|
||||
|
||||
impl<FN, $($A,)* R> LuaNativeFnMut<($($A,)*)> for FN
|
||||
where
|
||||
FN: FnMut($($A,)*) -> R + MaybeSend + 'static,
|
||||
($($A,)*): FromLuaMulti,
|
||||
{
|
||||
type Output = R;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn call(&mut self, args: ($($A,)*)) -> Self::Output {
|
||||
let ($($A,)*) = args;
|
||||
self($($A,)*)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<FN, $($A,)* Fut, R> LuaNativeAsyncFn<($($A,)*)> for FN
|
||||
where
|
||||
FN: Fn($($A,)*) -> Fut + MaybeSend + 'static,
|
||||
($($A,)*): FromLuaMulti,
|
||||
Fut: Future<Output = R> + MaybeSend + 'static,
|
||||
{
|
||||
type Output = R;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn call(&self, args: ($($A,)*)) -> impl Future<Output = Self::Output> + MaybeSend + 'static {
|
||||
let ($($A,)*) = args;
|
||||
self($($A,)*)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_lua_native_fn!();
|
||||
impl_lua_native_fn!(A);
|
||||
impl_lua_native_fn!(A, B);
|
||||
impl_lua_native_fn!(A, B, C);
|
||||
impl_lua_native_fn!(A, B, C, D);
|
||||
impl_lua_native_fn!(A, B, C, D, E);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
+61
-47
@@ -61,91 +61,105 @@
|
||||
//! [`Future`]: std::future::Future
|
||||
//! [`serde::Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html
|
||||
//! [`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
|
||||
//! [`AsyncThread`]: crate::thread::AsyncThread
|
||||
|
||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||
// warnings at all.
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(not(send), allow(clippy::arc_with_non_send_sync))]
|
||||
#![allow(clippy::ptr_eq)]
|
||||
#![allow(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
mod buffer;
|
||||
mod chunk;
|
||||
mod conversion;
|
||||
mod debug;
|
||||
mod error;
|
||||
mod function;
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
mod luau;
|
||||
mod memory;
|
||||
mod multi;
|
||||
mod scope;
|
||||
mod state;
|
||||
mod stdlib;
|
||||
mod string;
|
||||
mod table;
|
||||
mod thread;
|
||||
mod traits;
|
||||
mod types;
|
||||
mod userdata;
|
||||
mod util;
|
||||
mod value;
|
||||
mod vector;
|
||||
|
||||
pub mod chunk;
|
||||
pub mod debug;
|
||||
pub mod error;
|
||||
pub mod function;
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub mod luau;
|
||||
pub mod prelude;
|
||||
pub mod state;
|
||||
pub mod string;
|
||||
pub mod table;
|
||||
pub mod thread;
|
||||
pub mod userdata;
|
||||
|
||||
pub use bstr::BString;
|
||||
pub use ffi::{self, lua_CFunction, lua_State};
|
||||
|
||||
pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
|
||||
pub use crate::debug::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack};
|
||||
pub use crate::error::{Error, ErrorContext, ExternalError, ExternalResult, Result};
|
||||
pub use crate::function::{Function, FunctionInfo};
|
||||
#[doc(inline)]
|
||||
pub use crate::error::{Error, Result};
|
||||
#[doc(inline)]
|
||||
pub use crate::function::Function;
|
||||
pub use crate::multi::{MultiValue, Variadic};
|
||||
pub use crate::scope::Scope;
|
||||
pub use crate::state::{GCMode, Lua, LuaOptions, WeakLua};
|
||||
#[doc(inline)]
|
||||
pub use crate::state::{Lua, LuaOptions, WeakLua};
|
||||
pub use crate::stdlib::StdLib;
|
||||
pub use crate::string::{BorrowedBytes, BorrowedStr, String};
|
||||
pub use crate::table::{Table, TablePairs, TableSequence};
|
||||
pub use crate::thread::{Thread, ThreadStatus};
|
||||
pub use crate::traits::{
|
||||
FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, LuaNativeFn, LuaNativeFnMut, ObjectLike,
|
||||
};
|
||||
#[doc(inline)]
|
||||
pub use crate::string::{BorrowedBytes, BorrowedStr, LuaString};
|
||||
#[doc(inline)]
|
||||
pub use crate::table::Table;
|
||||
#[doc(inline)]
|
||||
pub use crate::thread::Thread;
|
||||
#[doc(inline)]
|
||||
pub use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike};
|
||||
pub use crate::types::{
|
||||
AppDataRef, AppDataRefMut, Either, Integer, LightUserData, MaybeSend, Number, RegistryKey, VmState,
|
||||
};
|
||||
pub use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods, UserDataRef,
|
||||
UserDataRefMut, UserDataRegistry,
|
||||
AppDataRef, AppDataRefMut, Either, Integer, LightUserData, MaybeSend, MaybeSync, Number, RegistryKey,
|
||||
VmState,
|
||||
};
|
||||
#[doc(inline)]
|
||||
pub use crate::userdata::AnyUserData;
|
||||
pub use crate::value::{Nil, Value};
|
||||
|
||||
// Re-export some types to keep backward compatibility and avoid breaking changes in the public API.
|
||||
#[doc(hidden)]
|
||||
pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
|
||||
#[cfg(feature = "luau")]
|
||||
#[doc(hidden)]
|
||||
pub use crate::chunk::{CompileConstant, Compiler};
|
||||
#[doc(hidden)]
|
||||
pub use crate::error::{ErrorContext, ExternalError, ExternalResult};
|
||||
#[doc(hidden)]
|
||||
pub use crate::string::LuaString as String;
|
||||
#[doc(hidden)]
|
||||
pub use crate::table::{TablePairs, TableSequence};
|
||||
#[doc(hidden)]
|
||||
pub use crate::thread::ThreadStatus;
|
||||
#[doc(hidden)]
|
||||
pub use crate::userdata::{
|
||||
MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods, UserDataOwned, UserDataRef,
|
||||
UserDataRefMut, UserDataRegistry,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[doc(inline)]
|
||||
pub use crate::debug::HookTriggers;
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub use crate::{
|
||||
buffer::Buffer,
|
||||
chunk::{CompileConstant, Compiler},
|
||||
function::CoverageInfo,
|
||||
luau::{HeapDump, NavigateError, Require, TextRequirer},
|
||||
vector::Vector,
|
||||
};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub use crate::{thread::AsyncThread, traits::LuaNativeAsyncFn};
|
||||
pub use crate::{buffer::Buffer, vector::Vector};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[doc(hidden)]
|
||||
pub use crate::serde::{DeserializeOptions, SerializeOptions};
|
||||
#[cfg(feature = "serde")]
|
||||
#[doc(inline)]
|
||||
pub use crate::{
|
||||
serde::{de::Options as DeserializeOptions, ser::Options as SerializeOptions, LuaSerdeExt},
|
||||
value::SerializableValue,
|
||||
};
|
||||
pub use crate::{serde::LuaSerdeExt, value::SerializableValue};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
@@ -246,10 +260,10 @@ pub use mlua_derive::FromLua;
|
||||
///
|
||||
/// * 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.
|
||||
/// In module mode, mlua runs in an unknown environment and cannot tell whether there are any memory
|
||||
/// limits or not. As a result, some operations that require memory allocation run 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)]
|
||||
|
||||
+13
-13
@@ -79,10 +79,10 @@ impl HeapDump {
|
||||
let mut size_by_type = HashMap::new();
|
||||
let objects = self.data["objects"].as_object()?;
|
||||
for obj in objects.values() {
|
||||
if let Some(cat_id) = category_id {
|
||||
if obj["cat"].as_i64()? != cat_id {
|
||||
continue;
|
||||
}
|
||||
if let Some(cat_id) = category_id
|
||||
&& obj["cat"].as_i64()? != cat_id
|
||||
{
|
||||
continue;
|
||||
}
|
||||
update_size(&mut size_by_type, obj["type"].as_str()?, obj["size"].as_u64()?);
|
||||
}
|
||||
@@ -123,18 +123,18 @@ impl HeapDump {
|
||||
if obj["type"] != "userdata" {
|
||||
continue;
|
||||
}
|
||||
if let Some(cat_id) = category_id {
|
||||
if obj["cat"].as_i64()? != cat_id {
|
||||
continue;
|
||||
}
|
||||
if let Some(cat_id) = category_id
|
||||
&& obj["cat"].as_i64()? != cat_id
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine userdata type from metatable
|
||||
let mut ud_type = "unknown";
|
||||
if let Some(metatable_addr) = obj["metatable"].as_str() {
|
||||
if let Some(t) = get_key(objects, &objects[metatable_addr], "__type") {
|
||||
ud_type = t;
|
||||
}
|
||||
if let Some(metatable_addr) = obj["metatable"].as_str()
|
||||
&& let Some(t) = get_key(objects, &objects[metatable_addr], "__type")
|
||||
{
|
||||
ud_type = t;
|
||||
}
|
||||
update_size(&mut size_by_userdata, ud_type, obj["size"].as_u64()?);
|
||||
}
|
||||
@@ -155,7 +155,7 @@ impl HeapDump {
|
||||
|
||||
/// Updates the size mapping for a given key.
|
||||
fn update_size<K: Eq + Hash>(size_type: &mut HashMap<K, (usize, u64)>, key: K, size: u64) {
|
||||
let (ref mut count, ref mut total_size) = size_type.entry(key).or_insert((0, 0));
|
||||
let (count, total_size) = size_type.entry(key).or_insert((0, 0));
|
||||
*count += 1;
|
||||
*total_size += size;
|
||||
}
|
||||
|
||||
+10
-3
@@ -1,3 +1,10 @@
|
||||
//! Luau-specific extensions and types.
|
||||
//!
|
||||
//! This module provides Luau-specific functionality including custom [`require`] implementations,
|
||||
//! heap memory analysis, and Luau VM integration utilities.
|
||||
//!
|
||||
//! [`require`]: crate::Lua::create_require_function
|
||||
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::os::raw::c_int;
|
||||
use std::ptr;
|
||||
@@ -5,12 +12,12 @@ use std::ptr;
|
||||
use crate::chunk::ChunkMode;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::{callback_error_ext, ExtraData, Lua};
|
||||
use crate::state::{ExtraData, Lua, callback_error_ext};
|
||||
use crate::traits::{FromLuaMulti, IntoLua};
|
||||
use crate::types::MaybeSend;
|
||||
|
||||
pub use heap_dump::HeapDump;
|
||||
pub use require::{NavigateError, Require, TextRequirer};
|
||||
pub use require::{FsRequirer, NavigateError, Require};
|
||||
|
||||
// Since Luau has some missing standard functions, we re-implement them here
|
||||
|
||||
@@ -86,7 +93,7 @@ impl Lua {
|
||||
}
|
||||
|
||||
// Enable default `require` implementation
|
||||
let require = self.create_require_function(require::TextRequirer::new())?;
|
||||
let require = self.create_require_function(FsRequirer::new())?;
|
||||
self.globals().raw_set("require", require)?;
|
||||
|
||||
Ok(())
|
||||
|
||||
+2
-3
@@ -8,12 +8,11 @@ use std::{fmt, mem, ptr};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::{callback_error_ext, Lua};
|
||||
use crate::state::{Lua, callback_error_ext};
|
||||
use crate::table::Table;
|
||||
use crate::types::MaybeSend;
|
||||
|
||||
// TODO: Rename to FsRequirer
|
||||
pub use fs::TextRequirer;
|
||||
pub use fs::FsRequirer;
|
||||
|
||||
/// An error that can occur during navigation in the Luau `require-by-string` system.
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
+10
-10
@@ -12,7 +12,7 @@ use super::{NavigateError, Require};
|
||||
|
||||
/// The standard implementation of Luau `require-by-string` navigation.
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TextRequirer {
|
||||
pub struct FsRequirer {
|
||||
/// An absolute path to the current Luau module (not mapped to a physical file)
|
||||
abs_path: PathBuf,
|
||||
/// A relative path to the current Luau module (not mapped to a physical file)
|
||||
@@ -22,7 +22,7 @@ pub struct TextRequirer {
|
||||
resolved_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl TextRequirer {
|
||||
impl FsRequirer {
|
||||
/// The prefix used for chunk names in the require system.
|
||||
/// Only chunk names starting with this prefix are allowed to be used in `require`.
|
||||
const CHUNK_PREFIX: &str = "@";
|
||||
@@ -36,16 +36,16 @@ impl TextRequirer {
|
||||
/// The filename for the Luau configuration file.
|
||||
const LUAU_CONFIG_FILENAME: &str = ".config.luau";
|
||||
|
||||
/// Creates a new `TextRequirer` instance.
|
||||
/// Creates a new `FsRequirer` instance.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn normalize_chunk_name(chunk_name: &str) -> &str {
|
||||
if let Some((path, line)) = chunk_name.rsplit_once(':') {
|
||||
if line.parse::<u32>().is_ok() {
|
||||
return path;
|
||||
}
|
||||
if let Some((path, line)) = chunk_name.rsplit_once(':')
|
||||
&& line.parse::<u32>().is_ok()
|
||||
{
|
||||
return path;
|
||||
}
|
||||
chunk_name
|
||||
}
|
||||
@@ -114,7 +114,7 @@ impl TextRequirer {
|
||||
}
|
||||
}
|
||||
|
||||
impl Require for TextRequirer {
|
||||
impl Require for FsRequirer {
|
||||
fn is_require_allowed(&self, chunk_name: &str) -> bool {
|
||||
chunk_name.starts_with(Self::CHUNK_PREFIX)
|
||||
}
|
||||
@@ -231,7 +231,7 @@ impl Require for TextRequirer {
|
||||
mod tests {
|
||||
use std::path::Path;
|
||||
|
||||
use super::TextRequirer;
|
||||
use super::FsRequirer;
|
||||
|
||||
#[test]
|
||||
fn test_path_normalize() {
|
||||
@@ -267,7 +267,7 @@ mod tests {
|
||||
// '..' disappears if path is absolute and component is non-erasable
|
||||
("/../", "/"),
|
||||
] {
|
||||
let path = TextRequirer::normalize_path(input.as_ref());
|
||||
let path = FsRequirer::normalize_path(input.as_ref());
|
||||
assert_eq!(
|
||||
&path,
|
||||
expected.as_ref() as &Path,
|
||||
|
||||
@@ -28,9 +28,7 @@ impl MemoryState {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[rustversion::since(1.85)]
|
||||
#[inline]
|
||||
#[allow(clippy::incompatible_msrv)]
|
||||
pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
|
||||
let mut mem_state = ptr::null_mut();
|
||||
if !ptr::fn_addr_eq(ffi::lua_getallocf(state, &mut mem_state), ALLOCATOR) {
|
||||
@@ -39,17 +37,6 @@ impl MemoryState {
|
||||
mem_state as *mut MemoryState
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[rustversion::before(1.85)]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
|
||||
let mut mem_state = ptr::null_mut();
|
||||
if ffi::lua_getallocf(state, &mut mem_state) != ALLOCATOR {
|
||||
mem_state = ptr::null_mut();
|
||||
}
|
||||
mem_state as *mut MemoryState
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn used_memory(&self) -> usize {
|
||||
self.used_memory as usize
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
use std::collections::{vec_deque, VecDeque};
|
||||
use std::collections::{VecDeque, vec_deque};
|
||||
use std::iter::FromIterator;
|
||||
use std::mem;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
+23
-14
@@ -3,35 +3,44 @@
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{
|
||||
AnyUserData as LuaAnyUserData, BorrowedBytes as LuaBorrowedBytes, BorrowedStr as LuaBorrowedStr,
|
||||
Chunk as LuaChunk, Either as LuaEither, 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, LuaNativeFn, LuaNativeFnMut, LuaOptions,
|
||||
MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber,
|
||||
Either as LuaEither, Error as LuaError, FromLua, FromLuaMulti, Function as LuaFunction,
|
||||
Integer as LuaInteger, IntoLua, IntoLuaMulti, LightUserData as LuaLightUserData, Lua, LuaOptions,
|
||||
LuaString, MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber,
|
||||
ObjectLike as LuaObjectLike, RegistryKey as LuaRegistryKey, Result as LuaResult, StdLib as LuaStdLib,
|
||||
String as LuaString, Table as LuaTable, 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, Value as LuaValue,
|
||||
Variadic as LuaVariadic, VmState as LuaVmState, WeakLua,
|
||||
Table as LuaTable, Thread as LuaThread, UserData as LuaUserData, UserDataFields as LuaUserDataFields,
|
||||
UserDataMetatable as LuaUserDataMetatable, UserDataMethods as LuaUserDataMethods,
|
||||
UserDataOwned as LuaUserDataOwned, UserDataRef as LuaUserDataRef, UserDataRefMut as LuaUserDataRefMut,
|
||||
UserDataRegistry as LuaUserDataRegistry, Value as LuaValue, Variadic as LuaVariadic,
|
||||
VmState as LuaVmState, WeakLua, chunk::AsChunk as AsLuaChunk, chunk::Chunk as LuaChunk,
|
||||
chunk::ChunkMode as LuaChunkMode, error::ErrorContext as LuaErrorContext,
|
||||
error::ExternalError as LuaExternalError, error::ExternalResult as LuaExternalResult,
|
||||
function::FunctionInfo as LuaFunctionInfo, function::LuaNativeFn, function::LuaNativeFnMut,
|
||||
state::GcIncParams as LuaGcIncParams, state::GcMode as LuaGcMode, table::TablePairs as LuaTablePairs,
|
||||
table::TableSequence as LuaTableSequence, thread::ThreadStatus as LuaThreadStatus,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::HookTriggers as LuaHookTriggers;
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua55"))]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::state::GcGenParams as LuaGcGenParams;
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{
|
||||
CompileConstant as LuaCompileConstant, CoverageInfo as LuaCoverageInfo,
|
||||
NavigateError as LuaNavigateError, Require as LuaRequire, TextRequirer as LuaTextRequirer,
|
||||
Vector as LuaVector,
|
||||
chunk::{CompileConstant as LuaCompileConstant, Compiler as LuaCompiler},
|
||||
luau::{
|
||||
FsRequirer as LuaFsRequirer, HeapDump as LuaHeapDump, NavigateError as LuaNavigateError,
|
||||
Require as LuaRequire,
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{AsyncThread as LuaAsyncThread, LuaNativeAsyncFn};
|
||||
pub use crate::{function::LuaNativeAsyncFn, thread::AsyncThread as LuaAsyncThread};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[doc(no_inline)]
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ use crate::state::{Lua, LuaGuard, RawLua};
|
||||
use crate::traits::{FromLuaMulti, IntoLuaMulti};
|
||||
use crate::types::{Callback, CallbackUpvalue, ScopedCallback, ValueRef};
|
||||
use crate::userdata::{AnyUserData, UserData, UserDataRegistry, UserDataStorage};
|
||||
use crate::util::{self, check_stack, get_metatable_ptr, get_userdata, take_userdata, StackGuard};
|
||||
use crate::util::{self, StackGuard, check_stack, get_metatable_ptr, get_userdata, take_userdata};
|
||||
|
||||
/// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and
|
||||
/// callbacks that are not required to be `Send` or `'static`.
|
||||
|
||||
+3
-4
@@ -4,7 +4,6 @@ use std::cell::RefCell;
|
||||
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;
|
||||
use serde::de::{self, IntoDeserializer};
|
||||
@@ -243,14 +242,14 @@ impl<'de> serde::Deserializer<'de> for Deserializer {
|
||||
Value::Table(table) => {
|
||||
let _guard = RecursionGuard::new(&table, &self.visited);
|
||||
|
||||
let mut iter = table.pairs::<StdString, Value>();
|
||||
let mut iter = table.pairs::<String, Value>();
|
||||
let (variant, value) = match iter.next() {
|
||||
Some(v) => v?,
|
||||
None => {
|
||||
return Err(de::Error::invalid_value(
|
||||
de::Unexpected::Map,
|
||||
&"map with a single key",
|
||||
))
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -621,7 +620,7 @@ impl<'de> de::MapAccess<'de> for MapDeserializer<'_> {
|
||||
}
|
||||
|
||||
struct EnumDeserializer {
|
||||
variant: StdString,
|
||||
variant: String,
|
||||
value: Option<Value>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
|
||||
+4
-6
@@ -37,8 +37,8 @@ pub trait LuaSerdeExt: Sealed {
|
||||
fn null(&self) -> Value;
|
||||
|
||||
/// 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
|
||||
/// as the `#` operator on that table.
|
||||
/// As a result, encoded Array will contain only sequence part of the table, with the same
|
||||
/// length as the `#` operator on that table.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@@ -242,7 +242,5 @@ static ARRAY_METATABLE_REGISTRY_KEY: u8 = 0;
|
||||
pub mod de;
|
||||
pub mod ser;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use de::Deserializer;
|
||||
#[doc(inline)]
|
||||
pub use ser::Serializer;
|
||||
pub use de::{Deserializer, Options as DeserializeOptions};
|
||||
pub use ser::{Options as SerializeOptions, Serializer};
|
||||
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
//! Serialize a Rust data structure into Lua value.
|
||||
|
||||
use serde::{ser, Serialize};
|
||||
use serde::{Serialize, ser};
|
||||
|
||||
use super::LuaSerdeExt;
|
||||
use crate::error::{Error, Result};
|
||||
@@ -531,10 +531,10 @@ impl ser::SerializeStruct for SerializeStruct<'_> {
|
||||
Some(table @ Value::Table(_)) => Ok(table),
|
||||
Some(value @ Value::String(_)) if self.options.detect_serde_json_arbitrary_precision => {
|
||||
let number_s = value.to_string()?;
|
||||
if number_s.contains(['.', 'e', 'E']) {
|
||||
if let Ok(number) = number_s.parse().map(Value::Number) {
|
||||
return Ok(number);
|
||||
}
|
||||
if number_s.contains(['.', 'e', 'E'])
|
||||
&& let Ok(number) = number_s.parse().map(Value::Number)
|
||||
{
|
||||
return Ok(number);
|
||||
}
|
||||
Ok(number_s
|
||||
.parse()
|
||||
|
||||
+261
-211
@@ -1,3 +1,8 @@
|
||||
//! Lua state management.
|
||||
//!
|
||||
//! This module provides the main [`Lua`] state handle together with state-specific
|
||||
//! configuration and garbage collector controls.
|
||||
|
||||
use std::any::TypeId;
|
||||
use std::cell::{BorrowError, BorrowMutError, RefCell};
|
||||
use std::marker::PhantomData;
|
||||
@@ -15,16 +20,16 @@ use crate::memory::MemoryState;
|
||||
use crate::multi::MultiValue;
|
||||
use crate::scope::Scope;
|
||||
use crate::stdlib::StdLib;
|
||||
use crate::string::String;
|
||||
use crate::string::LuaString;
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
use crate::types::{
|
||||
AppDataRef, AppDataRefMut, ArcReentrantMutexGuard, Integer, LuaType, MaybeSend, Number, ReentrantMutex,
|
||||
ReentrantMutexGuard, RegistryKey, VmState, XRc, XWeak,
|
||||
AppDataRef, AppDataRefMut, ArcReentrantMutexGuard, Integer, LuaType, MaybeSend, MaybeSync, Number,
|
||||
ReentrantMutex, ReentrantMutexGuard, RegistryKey, VmState, XRc, XWeak,
|
||||
};
|
||||
use crate::userdata::{AnyUserData, UserData, UserDataProxy, UserDataRegistry, UserDataStorage};
|
||||
use crate::util::{assert_stack, check_stack, protect_lua_closure, push_string, rawset_field, StackGuard};
|
||||
use crate::util::{StackGuard, assert_stack, check_stack, protect_lua_closure, push_string, rawset_field};
|
||||
use crate::value::{Nil, Value};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
@@ -44,6 +49,7 @@ use {
|
||||
use serde::Serialize;
|
||||
|
||||
pub(crate) use extra::ExtraData;
|
||||
#[doc(hidden)]
|
||||
pub use raw::RawLua;
|
||||
pub(crate) use util::callback_error_ext;
|
||||
|
||||
@@ -62,20 +68,126 @@ pub struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
|
||||
|
||||
pub(crate) struct LuaGuard(ArcReentrantMutexGuard<RawLua>);
|
||||
|
||||
/// Mode of the Lua garbage collector (GC).
|
||||
///
|
||||
/// In Lua 5.4 GC can work in two modes: incremental and generational.
|
||||
/// Previous Lua versions support only incremental GC.
|
||||
/// Tuning parameters for the incremental GC collector.
|
||||
///
|
||||
/// More information can be found in the Lua [documentation].
|
||||
///
|
||||
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#2.5
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum GCMode {
|
||||
Incremental,
|
||||
/// [documentation]: https://www.lua.org/manual/5.5/manual.html#2.5.1
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct GcIncParams {
|
||||
/// Pause between successive GC cycles, expressed as a percentage of live memory.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub pause: Option<c_int>,
|
||||
|
||||
/// Target heap size as a percentage of live data, controlling how aggressively
|
||||
/// the GC reclaims memory (`LUA_GCSETGOAL`).
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub goal: Option<c_int>,
|
||||
|
||||
/// GC work performed per unit of memory allocated.
|
||||
pub step_multiplier: Option<c_int>,
|
||||
|
||||
/// Granularity of each GC step (see Lua reference for details).
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua55", feature = "lua54", feature = "luau"))))]
|
||||
pub step_size: Option<c_int>,
|
||||
}
|
||||
|
||||
impl GcIncParams {
|
||||
/// Sets the `pause` parameter.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn pause(mut self, v: c_int) -> Self {
|
||||
self.pause = Some(v);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `goal` parameter.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn goal(mut self, v: c_int) -> Self {
|
||||
self.goal = Some(v);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `step_multiplier` parameter.
|
||||
pub fn step_multiplier(mut self, v: c_int) -> Self {
|
||||
self.step_multiplier = Some(v);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `step_size` parameter.
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua55", feature = "lua54", feature = "luau"))))]
|
||||
pub fn step_size(mut self, v: c_int) -> Self {
|
||||
self.step_size = Some(v);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Tuning parameters for the generational GC collector (Lua 5.4+).
|
||||
///
|
||||
/// More information can be found in the Lua [documentation].
|
||||
///
|
||||
/// [documentation]: https://www.lua.org/manual/5.5/manual.html#2.5.2
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua55", feature = "lua54"))))]
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct GcGenParams {
|
||||
/// Frequency of minor (young-generation) collection steps.
|
||||
pub minor_multiplier: Option<c_int>,
|
||||
|
||||
/// Threshold controlling how large the young generation can grow before triggering
|
||||
/// a shift from minor to major collection.
|
||||
pub minor_to_major: Option<c_int>,
|
||||
|
||||
/// Threshold controlling how much the major collection must shrink the heap before
|
||||
/// switching back to minor (young-generation) collection.
|
||||
#[cfg(feature = "lua55")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua55")))]
|
||||
pub major_to_minor: Option<c_int>,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
impl GcGenParams {
|
||||
/// Sets the `minor_multiplier` parameter.
|
||||
pub fn minor_multiplier(mut self, v: c_int) -> Self {
|
||||
self.minor_multiplier = Some(v);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `minor_to_major` threshold.
|
||||
pub fn minor_to_major(mut self, v: c_int) -> Self {
|
||||
self.minor_to_major = Some(v);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `major_to_minor` parameter.
|
||||
#[cfg(feature = "lua55")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua55")))]
|
||||
pub fn major_to_minor(mut self, v: c_int) -> Self {
|
||||
self.major_to_minor = Some(v);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Lua garbage collector (GC) operating mode.
|
||||
///
|
||||
/// Use [`Lua::gc_set_mode`] to switch the collector mode and/or tune its parameters.
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum GcMode {
|
||||
/// Incremental mark-and-sweep
|
||||
Incremental(GcIncParams),
|
||||
|
||||
/// Generational
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua55", feature = "lua54"))))]
|
||||
Generational,
|
||||
Generational(GcGenParams),
|
||||
}
|
||||
|
||||
/// Controls Lua interpreter behavior such as Rust panics handling.
|
||||
@@ -337,30 +449,27 @@ impl Lua {
|
||||
R::from_stack_multi(nresults, &lua)
|
||||
}
|
||||
|
||||
/// Runs callback with the inner RawLua value. It can be used to manually push and get values on
|
||||
/// the stack.
|
||||
/// Calls provided function passing a reference to the [`RawLua`] handle.
|
||||
///
|
||||
/// This function is safe because all unsafe actions with RawLua can only be done with unsafe
|
||||
/// Provided [`RawLua`] handle can be used to manually pushing/popping values to/from the stack.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use mlua::{Lua, Result, FromLua, IntoLua};
|
||||
/// # use mlua::{Lua, Result, FromLua, IntoLua, IntoLuaMulti};
|
||||
/// # fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// let n: i32 = {
|
||||
/// let num = 11i32;
|
||||
/// lua.exec_raw_lua(|lua| {
|
||||
/// unsafe {
|
||||
/// <i32 as IntoLua>::push_into_stack(num, lua)?;
|
||||
/// let nums = (3, 4, 5);
|
||||
/// lua.exec_raw_lua(|rawlua| unsafe {
|
||||
/// nums.push_into_stack_multi(rawlua)?;
|
||||
/// let mut sum = 0;
|
||||
/// for _ in 0..3 {
|
||||
/// sum += rawlua.pop::<i32>()?;
|
||||
/// }
|
||||
///
|
||||
/// let n = unsafe {
|
||||
/// <i32 as FromLua>::from_stack(-1, lua)?
|
||||
/// };
|
||||
/// Result::Ok(n)
|
||||
/// Result::Ok(sum)
|
||||
/// })
|
||||
/// }?;
|
||||
/// assert_eq!(n, 11);
|
||||
/// assert_eq!(n, 12);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
@@ -437,31 +546,6 @@ impl Lua {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[deprecated(since = "0.11.0", note = "Use `register_module` instead")]
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
pub fn load_from_function<T: FromLua>(&self, modname: &str, func: Function) -> Result<T> {
|
||||
let loaded = unsafe {
|
||||
self.exec_raw::<Table>((), |state| {
|
||||
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_LOADED_TABLE);
|
||||
})?
|
||||
};
|
||||
|
||||
let value = match loaded.raw_get(modname)? {
|
||||
Value::Nil => {
|
||||
let result = match func.call(modname)? {
|
||||
Value::Nil => Value::Boolean(true),
|
||||
res => res,
|
||||
};
|
||||
loaded.raw_set(modname, &result)?;
|
||||
result
|
||||
}
|
||||
res => res,
|
||||
};
|
||||
T::from_lua(value, self)
|
||||
}
|
||||
|
||||
/// Unloads module `modname`.
|
||||
///
|
||||
/// This method does not support unloading binary Lua modules since they are internally cached
|
||||
@@ -855,7 +939,6 @@ impl Lua {
|
||||
{
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_char, c_void};
|
||||
use std::string::String as StdString;
|
||||
|
||||
unsafe extern "C-unwind" fn warn_proc(ud: *mut c_void, msg: *const c_char, tocont: c_int) {
|
||||
let extra = ud as *mut ExtraData;
|
||||
@@ -865,7 +948,7 @@ impl Lua {
|
||||
if XRc::strong_count(&warn_callback) > 2 {
|
||||
return Ok(());
|
||||
}
|
||||
let msg = StdString::from_utf8_lossy(CStr::from_ptr(msg).to_bytes());
|
||||
let msg = String::from_utf8_lossy(CStr::from_ptr(msg).to_bytes());
|
||||
warn_callback((*extra).lua(), &msg, tocont != 0)
|
||||
});
|
||||
}
|
||||
@@ -910,8 +993,8 @@ impl Lua {
|
||||
|
||||
/// Gets information about the interpreter runtime stack at the given level.
|
||||
///
|
||||
/// This function calls callback `f`, passing the [`Debug`] structure that can be used to get
|
||||
/// information about the function executing at a given level.
|
||||
/// This function calls callback `f`, passing the [`struct@Debug`] structure that can be used to
|
||||
/// get information about the function executing at a given level.
|
||||
/// Level `0` is the current running function, whereas level `n+1` is the function that has
|
||||
/// called level `n` (except for tail calls, which do not count in the stack).
|
||||
pub fn inspect_stack<R>(&self, level: usize, f: impl FnOnce(&Debug) -> R) -> Option<R> {
|
||||
@@ -936,7 +1019,7 @@ impl Lua {
|
||||
///
|
||||
/// The `msg` parameter, if provided, is added at the beginning of the traceback.
|
||||
/// The `level` parameter works the same way as in [`Lua::inspect_stack`].
|
||||
pub fn traceback(&self, msg: Option<&str>, level: usize) -> Result<String> {
|
||||
pub fn traceback(&self, msg: Option<&str>, level: usize) -> Result<LuaString> {
|
||||
let lua = self.lock();
|
||||
unsafe {
|
||||
check_stack(lua.state(), 3)?;
|
||||
@@ -948,7 +1031,7 @@ impl Lua {
|
||||
// `protect_lua` adds it's own call frame, so we need to increase level by 1
|
||||
ffi::luaL_traceback(state, state, msg, (level + 1) as c_int);
|
||||
})?;
|
||||
Ok(String(lua.pop_ref()))
|
||||
Ok(LuaString(lua.pop_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -999,19 +1082,19 @@ impl Lua {
|
||||
unsafe { ffi::lua_gc(lua.main_state(), ffi::LUA_GCISRUNNING, 0) != 0 }
|
||||
}
|
||||
|
||||
/// Stop the Lua GC from running
|
||||
/// Stops the Lua GC from running.
|
||||
pub fn gc_stop(&self) {
|
||||
let lua = self.lock();
|
||||
unsafe { ffi::lua_gc(lua.main_state(), ffi::LUA_GCSTOP, 0) };
|
||||
}
|
||||
|
||||
/// Restarts the Lua GC if it is not running
|
||||
/// Restarts the Lua GC if it is not running.
|
||||
pub fn gc_restart(&self) {
|
||||
let lua = self.lock();
|
||||
unsafe { ffi::lua_gc(lua.main_state(), ffi::LUA_GCRESTART, 0) };
|
||||
}
|
||||
|
||||
/// Perform a full garbage-collection cycle.
|
||||
/// Performs a full garbage-collection cycle.
|
||||
///
|
||||
/// It may be necessary to call this function twice to collect all currently unreachable
|
||||
/// objects. Once to finish the current gc cycle, and once to start and finish the next cycle.
|
||||
@@ -1024,153 +1107,128 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
/// Steps the garbage collector one indivisible step.
|
||||
/// Performs a basic step of garbage collection.
|
||||
///
|
||||
/// Returns `true` if this has finished a collection cycle.
|
||||
/// In incremental mode, a basic step corresponds to the current step size. In generational
|
||||
/// mode, a basic step performs a full minor collection or an incremental step, if the collector
|
||||
/// has scheduled one.
|
||||
///
|
||||
/// In incremental mode, returns `true` if this step has finished a collection cycle.
|
||||
/// In generational mode, returns `true` if the step finished a major collection.
|
||||
pub fn gc_step(&self) -> Result<bool> {
|
||||
self.gc_step_kbytes(0)
|
||||
}
|
||||
|
||||
/// Steps the garbage collector as though memory had been allocated.
|
||||
///
|
||||
/// if `kbytes` is 0, then this is the same as calling `gc_step`. Returns true if this step has
|
||||
/// finished a collection cycle.
|
||||
pub fn gc_step_kbytes(&self, kbytes: c_int) -> Result<bool> {
|
||||
let lua = self.lock();
|
||||
let state = lua.main_state();
|
||||
unsafe {
|
||||
check_stack(state, 3)?;
|
||||
protect_lua!(state, 0, 0, |state| {
|
||||
ffi::lua_gc(state, ffi::LUA_GCSTEP, kbytes) != 0
|
||||
ffi::lua_gc(state, ffi::LUA_GCSTEP, 0) != 0
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the `pause` value of the collector.
|
||||
/// Switches the GC to the given mode with the provided parameters.
|
||||
///
|
||||
/// Returns the previous value of `pause`. More information can be found in the Lua
|
||||
/// [documentation].
|
||||
/// Returns the previous [`GcMode`]. The returned value's parameter fields are always
|
||||
/// `None` because Lua's C API does not provide a way to read back current parameter values
|
||||
/// without changing them.
|
||||
///
|
||||
/// For Luau this parameter sets GC goal
|
||||
/// # Examples
|
||||
///
|
||||
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#2.5
|
||||
pub fn gc_set_pause(&self, pause: c_int) -> c_int {
|
||||
/// Switch to generational mode (Lua 5.4+):
|
||||
/// ```ignore
|
||||
/// let prev = lua.gc_set_mode(GcMode::Generational(GcGenParams::default()));
|
||||
/// ```
|
||||
///
|
||||
/// Switch to incremental mode with custom parameters:
|
||||
/// ```ignore
|
||||
/// lua.gc_set_mode(GcMode::Incremental(
|
||||
/// GcIncParams::default().pause(200).step_multiplier(100)
|
||||
/// ));
|
||||
/// ```
|
||||
pub fn gc_set_mode(&self, mode: GcMode) -> GcMode {
|
||||
let lua = self.lock();
|
||||
let state = lua.main_state();
|
||||
unsafe {
|
||||
|
||||
match mode {
|
||||
#[cfg(feature = "lua55")]
|
||||
return ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPPAUSE, pause);
|
||||
|
||||
#[cfg(not(any(feature = "lua55", feature = "luau")))]
|
||||
return ffi::lua_gc(state, ffi::LUA_GCSETPAUSE, pause);
|
||||
|
||||
GcMode::Incremental(params) => unsafe {
|
||||
if let Some(v) = params.pause {
|
||||
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPPAUSE, v);
|
||||
}
|
||||
if let Some(v) = params.step_multiplier {
|
||||
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPSTEPMUL, v);
|
||||
}
|
||||
if let Some(v) = params.step_size {
|
||||
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPSTEPSIZE, v);
|
||||
}
|
||||
match ffi::lua_gc(state, ffi::LUA_GCINC) {
|
||||
ffi::LUA_GCINC => GcMode::Incremental(GcIncParams::default()),
|
||||
ffi::LUA_GCGEN => GcMode::Generational(GcGenParams::default()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
},
|
||||
#[cfg(feature = "lua54")]
|
||||
GcMode::Incremental(params) => unsafe {
|
||||
let pause = params.pause.unwrap_or(0);
|
||||
let step_mul = params.step_multiplier.unwrap_or(0);
|
||||
let step_size = params.step_size.unwrap_or(0);
|
||||
match ffi::lua_gc(state, ffi::LUA_GCINC, pause, step_mul, step_size) {
|
||||
ffi::LUA_GCINC => GcMode::Incremental(GcIncParams::default()),
|
||||
ffi::LUA_GCGEN => GcMode::Generational(GcGenParams::default()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
},
|
||||
#[cfg(any(feature = "lua53", feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||
GcMode::Incremental(params) => unsafe {
|
||||
if let Some(v) = params.pause {
|
||||
ffi::lua_gc(state, ffi::LUA_GCSETPAUSE, v);
|
||||
}
|
||||
if let Some(v) = params.step_multiplier {
|
||||
ffi::lua_gc(state, ffi::LUA_GCSETSTEPMUL, v);
|
||||
}
|
||||
GcMode::Incremental(GcIncParams::default())
|
||||
},
|
||||
#[cfg(feature = "luau")]
|
||||
return ffi::lua_gc(state, ffi::LUA_GCSETGOAL, pause);
|
||||
}
|
||||
}
|
||||
GcMode::Incremental(params) => unsafe {
|
||||
if let Some(v) = params.goal {
|
||||
ffi::lua_gc(state, ffi::LUA_GCSETGOAL, v);
|
||||
}
|
||||
if let Some(v) = params.step_multiplier {
|
||||
ffi::lua_gc(state, ffi::LUA_GCSETSTEPMUL, v);
|
||||
}
|
||||
if let Some(v) = params.step_size {
|
||||
ffi::lua_gc(state, ffi::LUA_GCSETSTEPSIZE, v);
|
||||
}
|
||||
GcMode::Incremental(GcIncParams::default())
|
||||
},
|
||||
|
||||
/// Sets the `step multiplier` value of the collector.
|
||||
///
|
||||
/// Returns the previous value of the `step multiplier`. More information can be found in the
|
||||
/// Lua [documentation].
|
||||
///
|
||||
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#2.5
|
||||
pub fn gc_set_step_multiplier(&self, step_multiplier: c_int) -> c_int {
|
||||
let lua = self.lock();
|
||||
unsafe {
|
||||
#[cfg(feature = "lua55")]
|
||||
return ffi::lua_gc(
|
||||
lua.main_state(),
|
||||
ffi::LUA_GCPARAM,
|
||||
ffi::LUA_GCPSTEPMUL,
|
||||
step_multiplier,
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "lua55"))]
|
||||
return ffi::lua_gc(lua.main_state(), ffi::LUA_GCSETSTEPMUL, step_multiplier);
|
||||
}
|
||||
}
|
||||
|
||||
/// Changes the collector to incremental mode with the given parameters.
|
||||
///
|
||||
/// Returns the previous mode (always `GCMode::Incremental` in Lua < 5.4).
|
||||
/// More information can be found in the Lua [documentation].
|
||||
///
|
||||
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#2.5.1
|
||||
pub fn gc_inc(&self, pause: c_int, step_multiplier: c_int, step_size: c_int) -> GCMode {
|
||||
let lua = self.lock();
|
||||
let state = lua.main_state();
|
||||
|
||||
#[cfg(any(
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "lua51",
|
||||
feature = "luajit",
|
||||
feature = "luau"
|
||||
))]
|
||||
unsafe {
|
||||
if pause > 0 {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
ffi::lua_gc(state, ffi::LUA_GCSETPAUSE, pause);
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_gc(state, ffi::LUA_GCSETGOAL, pause);
|
||||
}
|
||||
|
||||
if step_multiplier > 0 {
|
||||
ffi::lua_gc(state, ffi::LUA_GCSETSTEPMUL, step_multiplier);
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
if step_size > 0 {
|
||||
ffi::lua_gc(state, ffi::LUA_GCSETSTEPSIZE, step_size);
|
||||
}
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let _ = step_size; // Ignored
|
||||
|
||||
return GCMode::Incremental;
|
||||
}
|
||||
|
||||
#[cfg(feature = "lua55")]
|
||||
let prev_mode = unsafe {
|
||||
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPPAUSE, pause);
|
||||
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPSTEPMUL, step_multiplier);
|
||||
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPSTEPSIZE, step_size);
|
||||
ffi::lua_gc(state, ffi::LUA_GCINC)
|
||||
};
|
||||
#[cfg(feature = "lua54")]
|
||||
let prev_mode = unsafe { ffi::lua_gc(state, ffi::LUA_GCINC, pause, step_multiplier, step_size) };
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
match prev_mode {
|
||||
ffi::LUA_GCINC => GCMode::Incremental,
|
||||
ffi::LUA_GCGEN => GCMode::Generational,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Changes the collector to generational mode with the given parameters.
|
||||
///
|
||||
/// Returns the previous mode. More information about the generational GC
|
||||
/// can be found in the Lua 5.4 [documentation][lua_doc].
|
||||
///
|
||||
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#2.5.2
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua55", feature = "lua54"))))]
|
||||
pub fn gc_gen(&self, minor_multiplier: c_int, major_multiplier: c_int) -> GCMode {
|
||||
let lua = self.lock();
|
||||
let state = lua.main_state();
|
||||
#[cfg(feature = "lua55")]
|
||||
let prev_mode = unsafe {
|
||||
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPMINORMUL, minor_multiplier);
|
||||
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPMINORMAJOR, major_multiplier);
|
||||
// TODO: LUA_GCPMAJORMINOR
|
||||
ffi::lua_gc(state, ffi::LUA_GCGEN)
|
||||
};
|
||||
#[cfg(not(feature = "lua55"))]
|
||||
let prev_mode = unsafe { ffi::lua_gc(state, ffi::LUA_GCGEN, minor_multiplier, major_multiplier) };
|
||||
match prev_mode {
|
||||
ffi::LUA_GCGEN => GCMode::Generational,
|
||||
ffi::LUA_GCINC => GCMode::Incremental,
|
||||
_ => unreachable!(),
|
||||
GcMode::Generational(params) => unsafe {
|
||||
if let Some(v) = params.minor_multiplier {
|
||||
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPMINORMUL, v);
|
||||
}
|
||||
if let Some(v) = params.minor_to_major {
|
||||
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPMINORMAJOR, v);
|
||||
}
|
||||
if let Some(v) = params.major_to_minor {
|
||||
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPMAJORMINOR, v);
|
||||
}
|
||||
match ffi::lua_gc(state, ffi::LUA_GCGEN) {
|
||||
ffi::LUA_GCGEN => GcMode::Generational(GcGenParams::default()),
|
||||
ffi::LUA_GCINC => GcMode::Incremental(GcIncParams::default()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
},
|
||||
#[cfg(feature = "lua54")]
|
||||
GcMode::Generational(params) => unsafe {
|
||||
let minor = params.minor_multiplier.unwrap_or(0);
|
||||
let minor_to_major = params.minor_to_major.unwrap_or(0);
|
||||
match ffi::lua_gc(state, ffi::LUA_GCGEN, minor, minor_to_major) {
|
||||
ffi::LUA_GCGEN => GcMode::Generational(GcGenParams::default()),
|
||||
ffi::LUA_GCINC => GcMode::Incremental(GcIncParams::default()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1205,10 +1263,10 @@ impl Lua {
|
||||
#[doc(hidden)]
|
||||
#[allow(clippy::result_unit_err)]
|
||||
pub fn set_fflag(name: &str, enabled: bool) -> StdResult<(), ()> {
|
||||
if let Ok(name) = std::ffi::CString::new(name) {
|
||||
if unsafe { ffi::luau_setfflag(name.as_ptr(), enabled as c_int) != 0 } {
|
||||
return Ok(());
|
||||
}
|
||||
if let Ok(name) = std::ffi::CString::new(name)
|
||||
&& unsafe { ffi::luau_setfflag(name.as_ptr(), enabled as c_int) != 0 }
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
Err(())
|
||||
}
|
||||
@@ -1248,7 +1306,7 @@ impl Lua {
|
||||
/// Lua strings can be arbitrary `[u8]` data including embedded nulls, so in addition to `&str`
|
||||
/// and `&String`, you can also pass plain `&[u8]` here.
|
||||
#[inline]
|
||||
pub fn create_string(&self, s: impl AsRef<[u8]>) -> Result<String> {
|
||||
pub fn create_string(&self, s: impl AsRef<[u8]>) -> Result<LuaString> {
|
||||
unsafe { self.lock().create_string(s.as_ref()) }
|
||||
}
|
||||
|
||||
@@ -1259,7 +1317,7 @@ impl Lua {
|
||||
#[cfg(feature = "lua55")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua55")))]
|
||||
#[inline]
|
||||
pub fn create_external_string(&self, s: impl Into<Vec<u8>>) -> Result<String> {
|
||||
pub fn create_external_string(&self, s: impl Into<Vec<u8>>) -> Result<LuaString> {
|
||||
unsafe { self.lock().create_external_string(s.into()) }
|
||||
}
|
||||
|
||||
@@ -1457,7 +1515,7 @@ impl Lua {
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`AsyncThread`]: crate::AsyncThread
|
||||
/// [`AsyncThread`]: crate::thread::AsyncThread
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn create_async_function<F, A, FR, R>(&self, func: F) -> Result<Function>
|
||||
@@ -1493,7 +1551,7 @@ impl Lua {
|
||||
#[inline]
|
||||
pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
||||
where
|
||||
T: UserData + MaybeSend + 'static,
|
||||
T: UserData + MaybeSend + MaybeSync + 'static,
|
||||
{
|
||||
unsafe { self.lock().make_userdata(UserDataStorage::new(data)) }
|
||||
}
|
||||
@@ -1504,7 +1562,7 @@ impl Lua {
|
||||
#[inline]
|
||||
pub fn create_ser_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
||||
where
|
||||
T: UserData + Serialize + MaybeSend + 'static,
|
||||
T: UserData + Serialize + MaybeSend + MaybeSync + 'static,
|
||||
{
|
||||
unsafe { self.lock().make_userdata(UserDataStorage::new_ser(data)) }
|
||||
}
|
||||
@@ -1519,7 +1577,7 @@ impl Lua {
|
||||
#[inline]
|
||||
pub fn create_any_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
||||
where
|
||||
T: MaybeSend + 'static,
|
||||
T: MaybeSend + MaybeSync + 'static,
|
||||
{
|
||||
unsafe { self.lock().make_any_userdata(UserDataStorage::new(data)) }
|
||||
}
|
||||
@@ -1532,7 +1590,7 @@ impl Lua {
|
||||
#[inline]
|
||||
pub fn create_ser_any_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
||||
where
|
||||
T: Serialize + MaybeSend + 'static,
|
||||
T: Serialize + MaybeSend + MaybeSync + 'static,
|
||||
{
|
||||
unsafe { (self.lock()).make_any_userdata(UserDataStorage::new_ser(data)) }
|
||||
}
|
||||
@@ -1741,7 +1799,7 @@ impl Lua {
|
||||
///
|
||||
/// To succeed, the value must be a string (in which case this is a no-op), an integer, or a
|
||||
/// number.
|
||||
pub fn coerce_string(&self, v: Value) -> Result<Option<String>> {
|
||||
pub fn coerce_string(&self, v: Value) -> Result<Option<LuaString>> {
|
||||
Ok(match v {
|
||||
Value::String(s) => Some(s),
|
||||
v => unsafe {
|
||||
@@ -1759,7 +1817,7 @@ impl Lua {
|
||||
})?
|
||||
};
|
||||
if !res.is_null() {
|
||||
Some(String(lua.pop_ref()))
|
||||
Some(LuaString(lua.pop_ref()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -1785,11 +1843,7 @@ impl Lua {
|
||||
lua.push_value(&v)?;
|
||||
let mut isint = 0;
|
||||
let i = ffi::lua_tointegerx(state, -1, &mut isint);
|
||||
if isint == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(i)
|
||||
}
|
||||
if isint == 0 { None } else { Some(i) }
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1811,11 +1865,7 @@ impl Lua {
|
||||
lua.push_value(&v)?;
|
||||
let mut isnum = 0;
|
||||
let n = ffi::lua_tonumberx(state, -1, &mut isnum);
|
||||
if isnum == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(n)
|
||||
}
|
||||
if isnum == 0 { None } else { Some(n) }
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ use crate::state::RawLua;
|
||||
use crate::stdlib::StdLib;
|
||||
use crate::types::{AppData, ReentrantMutex, XRc};
|
||||
use crate::userdata::RawUserDataRegistry;
|
||||
use crate::util::{get_internal_metatable, push_internal_userdata, TypeKey, WrappedFailure};
|
||||
use crate::util::{TypeKey, WrappedFailure, get_internal_metatable, push_internal_userdata};
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
use crate::chunk::Compiler;
|
||||
|
||||
+62
-38
@@ -10,26 +10,26 @@ use std::sync::Arc;
|
||||
use crate::chunk::ChunkMode;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::memory::{MemoryState, ALLOCATOR};
|
||||
use crate::memory::{ALLOCATOR, MemoryState};
|
||||
use crate::state::util::callback_error_ext;
|
||||
use crate::stdlib::StdLib;
|
||||
use crate::string::String;
|
||||
use crate::string::LuaString;
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::traits::IntoLua;
|
||||
use crate::traits::{FromLua, IntoLua};
|
||||
use crate::types::{
|
||||
AppDataRef, AppDataRefMut, Callback, CallbackUpvalue, DestructedUserdata, Integer, LightUserData,
|
||||
LuaType, MaybeSend, ReentrantMutex, RegistryKey, ValueRef, XRc,
|
||||
};
|
||||
use crate::userdata::{
|
||||
init_userdata_metatable, AnyUserData, MetaMethod, RawUserDataRegistry, UserData, UserDataRegistry,
|
||||
UserDataStorage,
|
||||
AnyUserData, MetaMethod, RawUserDataRegistry, UserData, UserDataRegistry, UserDataStorage,
|
||||
init_userdata_metatable,
|
||||
};
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, get_destructed_userdata_metatable, get_internal_userdata, get_main_state,
|
||||
get_metatable_ptr, get_userdata, init_error_registry, init_internal_metatable, pop_error,
|
||||
push_internal_userdata, push_string, push_table, push_userdata, rawset_field, safe_pcall, safe_xpcall,
|
||||
short_type_name, StackGuard, WrappedFailure,
|
||||
StackGuard, WrappedFailure, assert_stack, check_stack, get_destructed_userdata_metatable,
|
||||
get_internal_userdata, get_main_state, get_metatable_ptr, get_userdata, init_error_registry,
|
||||
init_internal_metatable, pop_error, push_internal_userdata, push_string, push_table, push_userdata,
|
||||
rawset_field, safe_pcall, safe_xpcall, short_type_name,
|
||||
};
|
||||
use crate::value::{Nil, Value};
|
||||
|
||||
@@ -50,7 +50,7 @@ use {
|
||||
std::task::{Context, Poll, Waker},
|
||||
};
|
||||
|
||||
/// An inner Lua struct which holds a raw Lua state.
|
||||
/// An internal Lua struct which holds a raw Lua state.
|
||||
#[doc(hidden)]
|
||||
pub struct RawLua {
|
||||
// The state is dynamic and depends on context
|
||||
@@ -516,34 +516,34 @@ impl RawLua {
|
||||
}
|
||||
|
||||
/// See [`Lua::create_string`]
|
||||
pub(crate) unsafe fn create_string(&self, s: &[u8]) -> Result<String> {
|
||||
pub(crate) unsafe fn create_string(&self, s: &[u8]) -> Result<LuaString> {
|
||||
let state = self.state();
|
||||
if self.unlikely_memory_error() {
|
||||
push_string(state, s, false)?;
|
||||
return Ok(String(self.pop_ref()));
|
||||
return Ok(LuaString(self.pop_ref()));
|
||||
}
|
||||
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
push_string(state, s, true)?;
|
||||
Ok(String(self.pop_ref()))
|
||||
Ok(LuaString(self.pop_ref()))
|
||||
}
|
||||
|
||||
/// Creates an external string, that is, a string that uses memory not managed by Lua.
|
||||
///
|
||||
/// Modifies the input data to add `\0` terminator.
|
||||
#[cfg(feature = "lua55")]
|
||||
pub(crate) unsafe fn create_external_string(&self, bytes: Vec<u8>) -> Result<String> {
|
||||
pub(crate) unsafe fn create_external_string(&self, bytes: Vec<u8>) -> Result<LuaString> {
|
||||
let state = self.state();
|
||||
if self.unlikely_memory_error() {
|
||||
crate::util::push_external_string(state, bytes, false)?;
|
||||
return Ok(String(self.pop_ref()));
|
||||
return Ok(LuaString(self.pop_ref()));
|
||||
}
|
||||
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
crate::util::push_external_string(state, bytes, true)?;
|
||||
Ok(String(self.pop_ref()))
|
||||
Ok(LuaString(self.pop_ref()))
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
@@ -681,10 +681,10 @@ impl RawLua {
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) unsafe fn recycle_thread(&self, thread: &mut Thread) {
|
||||
let extra = &mut *self.extra.get();
|
||||
if extra.thread_pool.len() < extra.thread_pool.capacity() {
|
||||
if let Some(index) = thread.0.index_count.take() {
|
||||
extra.thread_pool.push(index);
|
||||
}
|
||||
if extra.thread_pool.len() < extra.thread_pool.capacity()
|
||||
&& let Some(index) = thread.0.index_count.take()
|
||||
{
|
||||
extra.thread_pool.push(index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -731,14 +731,27 @@ impl RawLua {
|
||||
/// Pushes a value that implements `IntoLua` onto the Lua stack.
|
||||
///
|
||||
/// Uses up to 2 stack spaces to push a single value, does not call `checkstack`.
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
#[inline(always)]
|
||||
pub unsafe fn push(&self, value: impl IntoLua) -> Result<()> {
|
||||
value.push_into_stack(self)
|
||||
}
|
||||
|
||||
/// Pops a value that implements [`FromLua`] from the top of the Lua stack.
|
||||
///
|
||||
/// Uses up to 1 stack space, does not call `checkstack`.
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
#[inline(always)]
|
||||
pub unsafe fn pop<R: FromLua>(&self) -> Result<R> {
|
||||
let v = R::from_stack(-1, self)?;
|
||||
ffi::lua_pop(self.state(), 1);
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
/// Pushes a `Value` (by reference) onto the Lua stack.
|
||||
///
|
||||
/// Uses 2 stack spaces, does not call `checkstack`.
|
||||
/// Uses up to 2 stack spaces, does not call `checkstack`.
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
pub unsafe fn push_value(&self, value: &Value) -> Result<()> {
|
||||
let state = self.state();
|
||||
match value {
|
||||
@@ -773,6 +786,7 @@ impl RawLua {
|
||||
/// Pops a value from the Lua stack.
|
||||
///
|
||||
/// Uses up to 1 stack spaces, does not call `checkstack`.
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
#[inline]
|
||||
pub unsafe fn pop_value(&self) -> Value {
|
||||
let value = self.stack_value(-1, None);
|
||||
@@ -803,15 +817,22 @@ impl RawLua {
|
||||
|
||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
ffi::LUA_TNUMBER => {
|
||||
use crate::types::Number;
|
||||
|
||||
let n = ffi::lua_tonumber(state, idx);
|
||||
match num_traits::cast(n) {
|
||||
Some(i) if n.to_bits() == (i as Number).to_bits() => Value::Integer(i),
|
||||
Some(i) if n.to_bits() == (i as crate::types::Number).to_bits() => Value::Integer(i),
|
||||
_ => Value::Number(n),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::LUA_TINTEGER => {
|
||||
let i = ffi::lua_tointeger64(state, idx, ptr::null_mut());
|
||||
match num_traits::cast(i) {
|
||||
Some(i) => Value::Integer(i),
|
||||
_ => Value::Number(i as crate::types::Number),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::LUA_TVECTOR => {
|
||||
let v = ffi::lua_tovector(state, idx);
|
||||
@@ -824,7 +845,7 @@ impl RawLua {
|
||||
|
||||
ffi::LUA_TSTRING => {
|
||||
ffi::lua_xpush(state, self.ref_thread(), idx);
|
||||
Value::String(String(self.pop_ref_thread()))
|
||||
Value::String(LuaString(self.pop_ref_thread()))
|
||||
}
|
||||
|
||||
ffi::LUA_TTABLE => {
|
||||
@@ -949,7 +970,7 @@ impl RawLua {
|
||||
// Check if userdata/metatable is already registered
|
||||
let type_id = TypeId::of::<T>();
|
||||
if let Some(&table_id) = (*self.extra.get()).registered_userdata_t.get(&type_id) {
|
||||
return Ok(table_id as Integer);
|
||||
return Ok(table_id);
|
||||
}
|
||||
|
||||
// Create a new metatable from `UserData` definition
|
||||
@@ -968,7 +989,7 @@ impl RawLua {
|
||||
// Check if userdata/metatable is already registered
|
||||
let type_id = TypeId::of::<T>();
|
||||
if let Some(&table_id) = (*self.extra.get()).registered_userdata_t.get(&type_id) {
|
||||
return Ok(table_id as Integer);
|
||||
return Ok(table_id);
|
||||
}
|
||||
|
||||
// Check if metatable creation is pending or create an empty metatable otherwise
|
||||
@@ -983,7 +1004,7 @@ impl RawLua {
|
||||
unsafe fn make_userdata_with_metatable<T>(
|
||||
&self,
|
||||
data: UserDataStorage<T>,
|
||||
get_metatable_id: impl FnOnce() -> Result<Integer>,
|
||||
get_metatable_id: impl FnOnce() -> Result<c_int>,
|
||||
) -> Result<AnyUserData> {
|
||||
let state = self.state();
|
||||
let _sg = StackGuard::new(state);
|
||||
@@ -993,7 +1014,7 @@ impl RawLua {
|
||||
let mt_id = get_metatable_id()?;
|
||||
let protect = !self.unlikely_memory_error();
|
||||
push_userdata(state, data, protect)?;
|
||||
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, mt_id);
|
||||
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, mt_id as _);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
|
||||
// Set empty environment for Lua 5.1
|
||||
@@ -1011,7 +1032,7 @@ impl RawLua {
|
||||
Ok(AnyUserData(self.pop_ref()))
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn create_userdata_metatable(&self, registry: RawUserDataRegistry) -> Result<Integer> {
|
||||
pub(crate) unsafe fn create_userdata_metatable(&self, registry: RawUserDataRegistry) -> Result<c_int> {
|
||||
let state = self.state();
|
||||
let type_id = registry.type_id;
|
||||
|
||||
@@ -1027,7 +1048,7 @@ impl RawLua {
|
||||
}
|
||||
self.register_userdata_metatable(mt_ptr, type_id);
|
||||
|
||||
Ok(id as Integer)
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn push_userdata_metatable(&self, mut registry: RawUserDataRegistry) -> Result<()> {
|
||||
@@ -1126,7 +1147,7 @@ impl RawLua {
|
||||
#[cfg(feature = "luau")]
|
||||
if registry.enable_namecall {
|
||||
let map: &mut rustc_hash::FxHashMap<_, crate::types::CallbackPtr> =
|
||||
methods_map.get_or_insert_with(Default::default);
|
||||
methods_map.get_or_insert_default();
|
||||
for (k, m) in ®istry.methods {
|
||||
map.insert(k.as_bytes().to_vec(), &**m);
|
||||
}
|
||||
@@ -1219,13 +1240,11 @@ impl RawLua {
|
||||
Ok(type_id) => Ok(type_id),
|
||||
Err(Error::UserDataTypeMismatch) if ffi::lua_type(state, idx) != ffi::LUA_TUSERDATA => {
|
||||
// Report `FromLuaConversionError` instead
|
||||
// In Luau `luaL_typename` return heap-allocated string that is valid only for
|
||||
// the `state` lifetime.
|
||||
// `lua_typename` is used instead to get a truly static string.
|
||||
let idx_type_name = CStr::from_ptr(ffi::lua_typename(state, ffi::lua_type(state, idx)));
|
||||
let idx_type_name = idx_type_name.to_str().unwrap();
|
||||
let type_name = CStr::from_ptr(ffi::lua_typename(state, ffi::lua_type(state, idx)))
|
||||
.to_str()
|
||||
.unwrap_or("unknown");
|
||||
let message = format!("expected userdata of type '{}'", short_type_name::<T>());
|
||||
Err(Error::from_lua_conversion(idx_type_name, "userdata", message))
|
||||
Err(Error::from_lua_conversion(type_name, "userdata", message))
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
@@ -1586,6 +1605,11 @@ unsafe fn load_std_libs(state: *mut ffi::lua_State, libs: StdLib) -> Result<()>
|
||||
requiref(state, ffi::LUA_VECLIBNAME, ffi::luaopen_vector, 1)?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
if libs.contains(StdLib::INTEGER) {
|
||||
requiref(state, ffi::LUA_INTLIBNAME, ffi::luaopen_integer, 1)?;
|
||||
}
|
||||
|
||||
if libs.contains(StdLib::MATH) {
|
||||
requiref(state, ffi::LUA_MATHLIBNAME, ffi::luaopen_math, 1)?;
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
use std::os::raw::c_int;
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
use std::panic::{AssertUnwindSafe, catch_unwind};
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::{ExtraData, RawLua};
|
||||
use crate::util::{self, get_internal_metatable, WrappedFailure};
|
||||
use crate::util::{self, WrappedFailure, get_internal_metatable};
|
||||
|
||||
struct StateGuard<'a>(&'a RawLua, *mut ffi::lua_State);
|
||||
|
||||
|
||||
+6
-1
@@ -73,10 +73,15 @@ impl StdLib {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub const VECTOR: StdLib = StdLib(1 << 10);
|
||||
|
||||
/// [`integer`](https://luau.org/library#integer-library) library
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub const INTEGER: StdLib = StdLib(1 << 11);
|
||||
|
||||
/// [`jit`](http://luajit.org/ext_jit.html) library
|
||||
#[cfg(any(feature = "luajit", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luajit")))]
|
||||
pub const JIT: StdLib = StdLib(1 << 11);
|
||||
pub const JIT: StdLib = StdLib(1 << 12);
|
||||
|
||||
/// (**unsafe**) [`ffi`](http://luajit.org/ext_ffi.html) library
|
||||
#[cfg(any(feature = "luajit", doc))]
|
||||
|
||||
+85
-79
@@ -1,9 +1,12 @@
|
||||
use std::borrow::{Borrow, Cow};
|
||||
//! Lua string handling.
|
||||
//!
|
||||
//! This module provides types for working with Lua strings from Rust.
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::Deref;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::string::String as StdString;
|
||||
use std::{cmp, fmt, slice, str};
|
||||
use std::{cmp, fmt, mem, slice, str};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::Lua;
|
||||
@@ -21,37 +24,40 @@ use {
|
||||
///
|
||||
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
|
||||
#[derive(Clone)]
|
||||
pub struct String(pub(crate) ValueRef);
|
||||
pub struct LuaString(pub(crate) ValueRef);
|
||||
|
||||
impl String {
|
||||
impl LuaString {
|
||||
/// Get a [`BorrowedStr`] if the Lua string is valid UTF-8.
|
||||
///
|
||||
/// The returned `BorrowedStr` holds a strong reference to the Lua state to guarantee the
|
||||
/// validity of the underlying data.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use mlua::{Lua, Result, String};
|
||||
/// # use mlua::{Lua, LuaString, Result};
|
||||
/// # fn main() -> Result<()> {
|
||||
/// # let lua = Lua::new();
|
||||
/// let globals = lua.globals();
|
||||
///
|
||||
/// let version: String = globals.get("_VERSION")?;
|
||||
/// let version: LuaString = globals.get("_VERSION")?;
|
||||
/// assert!(version.to_str()?.contains("Lua"));
|
||||
///
|
||||
/// let non_utf8: String = lua.load(r#" "test\255" "#).eval()?;
|
||||
/// let non_utf8: LuaString = lua.load(r#" "test\255" "#).eval()?;
|
||||
/// assert!(non_utf8.to_str().is_err());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_str(&self) -> Result<BorrowedStr<'_>> {
|
||||
pub fn to_str(&self) -> Result<BorrowedStr> {
|
||||
BorrowedStr::try_from(self)
|
||||
}
|
||||
|
||||
/// Converts this string to a [`StdString`].
|
||||
/// Converts this Lua string to a [`String`].
|
||||
///
|
||||
/// Any non-Unicode sequences are replaced with [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD].
|
||||
///
|
||||
/// This method returns [`StdString`] instead of [`Cow<'_, str>`] because lifetime cannot be
|
||||
/// This method returns [`String`] instead of [`Cow<'_, str>`] because lifetime cannot be
|
||||
/// bound to a weak Lua object.
|
||||
///
|
||||
/// [U+FFFD]: std::char::REPLACEMENT_CHARACTER
|
||||
@@ -70,11 +76,11 @@ impl String {
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_string_lossy(&self) -> StdString {
|
||||
StdString::from_utf8_lossy(&self.as_bytes()).into_owned()
|
||||
pub fn to_string_lossy(&self) -> String {
|
||||
String::from_utf8_lossy(&self.as_bytes()).into_owned()
|
||||
}
|
||||
|
||||
/// Returns an object that implements [`Display`] for safely printing a Lua [`String`] that may
|
||||
/// Returns an object that implements [`Display`] for safely printing a [`LuaString`] that may
|
||||
/// contain non-Unicode data.
|
||||
///
|
||||
/// This may perform lossy conversion.
|
||||
@@ -86,32 +92,33 @@ impl String {
|
||||
|
||||
/// Get the bytes that make up this string.
|
||||
///
|
||||
/// The returned slice will not contain the terminating null byte, but will contain any null
|
||||
/// bytes embedded into the Lua string.
|
||||
/// The returned `BorrowedStr` holds a strong reference to the Lua state to guarantee the
|
||||
/// validity of the underlying data. The data will not contain the terminating null byte, but
|
||||
/// will contain any null bytes embedded into the Lua string.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use mlua::{Lua, Result, String};
|
||||
/// # use mlua::{Lua, LuaString, Result};
|
||||
/// # fn main() -> Result<()> {
|
||||
/// # let lua = Lua::new();
|
||||
/// let non_utf8: String = lua.load(r#" "test\255" "#).eval()?;
|
||||
/// let non_utf8: LuaString = lua.load(r#" "test\255" "#).eval()?;
|
||||
/// assert!(non_utf8.to_str().is_err()); // oh no :(
|
||||
/// assert_eq!(non_utf8.as_bytes(), &b"test\xff"[..]);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> BorrowedBytes<'_> {
|
||||
pub fn as_bytes(&self) -> BorrowedBytes {
|
||||
BorrowedBytes::from(self)
|
||||
}
|
||||
|
||||
/// Get the bytes that make up this string, including the trailing null byte.
|
||||
pub fn as_bytes_with_nul(&self) -> BorrowedBytes<'_> {
|
||||
let BorrowedBytes { buf, borrow, _lua } = BorrowedBytes::from(self);
|
||||
pub fn as_bytes_with_nul(&self) -> BorrowedBytes {
|
||||
let BorrowedBytes { buf, vref, _lua } = BorrowedBytes::from(self);
|
||||
// Include the trailing null byte (it's always present but excluded by default)
|
||||
let buf = unsafe { slice::from_raw_parts((*buf).as_ptr(), (*buf).len() + 1) };
|
||||
BorrowedBytes { buf, borrow, _lua }
|
||||
BorrowedBytes { buf, vref, _lua }
|
||||
}
|
||||
|
||||
// Does not return the terminating null byte
|
||||
@@ -135,7 +142,7 @@ impl String {
|
||||
(slice, lua)
|
||||
}
|
||||
|
||||
/// Converts this string to a generic C pointer.
|
||||
/// Converts this Lua string to a generic C pointer.
|
||||
///
|
||||
/// There is no way to convert the pointer back to its original value.
|
||||
///
|
||||
@@ -146,7 +153,7 @@ impl String {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for String {
|
||||
impl fmt::Debug for LuaString {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let bytes = self.as_bytes();
|
||||
// Check if the string is valid utf8
|
||||
@@ -162,12 +169,12 @@ impl fmt::Debug for String {
|
||||
|
||||
// Lua strings are basically `&[u8]` slices, so implement `PartialEq` for anything resembling that.
|
||||
//
|
||||
// This makes our `String` comparable with `Vec<u8>`, `[u8]`, `&str` and `String`.
|
||||
// This makes our `LuaString` comparable with `Vec<u8>`, `[u8]`, `&str` and `String`.
|
||||
//
|
||||
// The only downside is that this disallows a comparison with `Cow<str>`, as that only implements
|
||||
// `AsRef<str>`, which collides with this impl. Requiring `AsRef<str>` would fix that, but limit us
|
||||
// in other ways.
|
||||
impl<T> PartialEq<T> for String
|
||||
impl<T> PartialEq<T> for LuaString
|
||||
where
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
@@ -176,43 +183,43 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for String {
|
||||
fn eq(&self, other: &String) -> bool {
|
||||
impl PartialEq for LuaString {
|
||||
fn eq(&self, other: &LuaString) -> bool {
|
||||
self.as_bytes() == other.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for String {}
|
||||
impl Eq for LuaString {}
|
||||
|
||||
impl<T> PartialOrd<T> for String
|
||||
impl<T> PartialOrd<T> for LuaString
|
||||
where
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
|
||||
self.as_bytes().partial_cmp(&other.as_ref())
|
||||
<[u8]>::partial_cmp(&self.as_bytes(), other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for String {
|
||||
fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
|
||||
impl PartialOrd for LuaString {
|
||||
fn partial_cmp(&self, other: &LuaString) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for String {
|
||||
fn cmp(&self, other: &String) -> cmp::Ordering {
|
||||
impl Ord for LuaString {
|
||||
fn cmp(&self, other: &LuaString) -> cmp::Ordering {
|
||||
self.as_bytes().cmp(&other.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for String {
|
||||
impl Hash for LuaString {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.as_bytes().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for String {
|
||||
impl Serialize for LuaString {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
@@ -224,7 +231,7 @@ impl Serialize for String {
|
||||
}
|
||||
}
|
||||
|
||||
struct Display<'a>(&'a String);
|
||||
struct Display<'a>(&'a LuaString);
|
||||
|
||||
impl fmt::Display for Display<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
@@ -234,14 +241,14 @@ impl fmt::Display for Display<'_> {
|
||||
}
|
||||
|
||||
/// A borrowed string (`&str`) that holds a strong reference to the Lua state.
|
||||
pub struct BorrowedStr<'a> {
|
||||
pub struct BorrowedStr {
|
||||
// `buf` points to a readonly memory managed by Lua
|
||||
pub(crate) buf: &'a str,
|
||||
pub(crate) borrow: Cow<'a, String>,
|
||||
pub(crate) buf: &'static str,
|
||||
pub(crate) vref: ValueRef,
|
||||
pub(crate) _lua: Lua,
|
||||
}
|
||||
|
||||
impl Deref for BorrowedStr<'_> {
|
||||
impl Deref for BorrowedStr {
|
||||
type Target = str;
|
||||
|
||||
#[inline(always)]
|
||||
@@ -250,33 +257,33 @@ impl Deref for BorrowedStr<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for BorrowedStr<'_> {
|
||||
impl Borrow<str> for BorrowedStr {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &str {
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for BorrowedStr<'_> {
|
||||
impl AsRef<str> for BorrowedStr {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &str {
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BorrowedStr<'_> {
|
||||
impl fmt::Display for BorrowedStr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.buf.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BorrowedStr<'_> {
|
||||
impl fmt::Debug for BorrowedStr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.buf.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq<T> for BorrowedStr<'_>
|
||||
impl<T> PartialEq<T> for BorrowedStr
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
@@ -285,9 +292,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for BorrowedStr<'_> {}
|
||||
impl Eq for BorrowedStr {}
|
||||
|
||||
impl<T> PartialOrd<T> for BorrowedStr<'_>
|
||||
impl<T> PartialOrd<T> for BorrowedStr
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
@@ -296,36 +303,33 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for BorrowedStr<'_> {
|
||||
impl Ord for BorrowedStr {
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
self.buf.cmp(other.buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a String> for BorrowedStr<'a> {
|
||||
impl TryFrom<&LuaString> for BorrowedStr {
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn try_from(value: &'a String) -> Result<Self> {
|
||||
let BorrowedBytes { buf, borrow, _lua } = BorrowedBytes::from(value);
|
||||
let buf = str::from_utf8(buf).map_err(|e| Error::FromLuaConversionError {
|
||||
from: "string",
|
||||
to: "&str".to_string(),
|
||||
message: Some(e.to_string()),
|
||||
})?;
|
||||
Ok(Self { buf, borrow, _lua })
|
||||
fn try_from(value: &LuaString) -> Result<Self> {
|
||||
let BorrowedBytes { buf, vref, _lua } = BorrowedBytes::from(value);
|
||||
let buf =
|
||||
str::from_utf8(buf).map_err(|e| Error::from_lua_conversion("string", "&str", e.to_string()))?;
|
||||
Ok(Self { buf, vref, _lua })
|
||||
}
|
||||
}
|
||||
|
||||
/// A borrowed byte slice (`&[u8]`) that holds a strong reference to the Lua state.
|
||||
pub struct BorrowedBytes<'a> {
|
||||
pub struct BorrowedBytes {
|
||||
// `buf` points to a readonly memory managed by Lua
|
||||
pub(crate) buf: &'a [u8],
|
||||
pub(crate) borrow: Cow<'a, String>,
|
||||
pub(crate) buf: &'static [u8],
|
||||
pub(crate) vref: ValueRef,
|
||||
pub(crate) _lua: Lua,
|
||||
}
|
||||
|
||||
impl Deref for BorrowedBytes<'_> {
|
||||
impl Deref for BorrowedBytes {
|
||||
type Target = [u8];
|
||||
|
||||
#[inline(always)]
|
||||
@@ -334,27 +338,27 @@ impl Deref for BorrowedBytes<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<[u8]> for BorrowedBytes<'_> {
|
||||
impl Borrow<[u8]> for BorrowedBytes {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &[u8] {
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for BorrowedBytes<'_> {
|
||||
impl AsRef<[u8]> for BorrowedBytes {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BorrowedBytes<'_> {
|
||||
impl fmt::Debug for BorrowedBytes {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.buf.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq<T> for BorrowedBytes<'_>
|
||||
impl<T> PartialEq<T> for BorrowedBytes
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
@@ -363,9 +367,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for BorrowedBytes<'_> {}
|
||||
impl Eq for BorrowedBytes {}
|
||||
|
||||
impl<T> PartialOrd<T> for BorrowedBytes<'_>
|
||||
impl<T> PartialOrd<T> for BorrowedBytes
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
@@ -374,13 +378,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for BorrowedBytes<'_> {
|
||||
impl Ord for BorrowedBytes {
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
self.buf.cmp(other.buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a BorrowedBytes<'_> {
|
||||
impl<'a> IntoIterator for &'a BorrowedBytes {
|
||||
type Item = &'a u8;
|
||||
type IntoIter = slice::Iter<'a, u8>;
|
||||
|
||||
@@ -389,18 +393,20 @@ impl<'a> IntoIterator for &'a BorrowedBytes<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a String> for BorrowedBytes<'a> {
|
||||
impl From<&LuaString> for BorrowedBytes {
|
||||
#[inline]
|
||||
fn from(value: &'a String) -> Self {
|
||||
fn from(value: &LuaString) -> Self {
|
||||
let (buf, _lua) = unsafe { value.to_slice() };
|
||||
let borrow = Cow::Borrowed(value);
|
||||
Self { buf, borrow, _lua }
|
||||
let vref = value.0.clone();
|
||||
// SAFETY: The `buf` is valid for the lifetime of the Lua state and occupied slot index
|
||||
let buf = unsafe { mem::transmute::<&[u8], &'static [u8]>(buf) };
|
||||
Self { buf, vref, _lua }
|
||||
}
|
||||
}
|
||||
|
||||
struct WrappedString<T: AsRef<[u8]>>(T);
|
||||
|
||||
impl String {
|
||||
impl LuaString {
|
||||
/// Wraps bytes, returning an opaque type that implements [`IntoLua`] trait.
|
||||
///
|
||||
/// This function uses [`Lua::create_string`] under the hood.
|
||||
@@ -415,7 +421,7 @@ impl<T: AsRef<[u8]>> IntoLua for WrappedString<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaType for String {
|
||||
impl LuaType for LuaString {
|
||||
const TYPE_ID: c_int = ffi::LUA_TSTRING;
|
||||
}
|
||||
|
||||
@@ -424,9 +430,9 @@ mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_any!(String: Send);
|
||||
static_assertions::assert_not_impl_any!(LuaString: Send);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(String: Send, Sync);
|
||||
static_assertions::assert_impl_all!(LuaString: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(BorrowedBytes: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
|
||||
+168
-19
@@ -1,15 +1,167 @@
|
||||
//! Lua table handling.
|
||||
//!
|
||||
//! Tables are Lua's primary data structure, used for arrays, dictionaries, objects, modules,
|
||||
//! and more. This module provides types for creating and manipulating Lua tables from Rust.
|
||||
//!
|
||||
//! # Basic Operations
|
||||
//!
|
||||
//! Tables support key-value access similar to Rust's `HashMap`:
|
||||
//!
|
||||
//! ```
|
||||
//! # use mlua::{Lua, Result};
|
||||
//! # fn main() -> Result<()> {
|
||||
//! let lua = Lua::new();
|
||||
//! let table = lua.create_table()?;
|
||||
//!
|
||||
//! // Set and get values
|
||||
//! table.set("key", "value")?;
|
||||
//! let value: String = table.get("key")?;
|
||||
//! assert_eq!(value, "value");
|
||||
//!
|
||||
//! // Keys and values can be any Lua-compatible type
|
||||
//! table.set(1, "first")?;
|
||||
//! table.set("nested", lua.create_table()?)?;
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! # Array Operations
|
||||
//!
|
||||
//! Tables can be used as arrays with 1-based indexing:
|
||||
//!
|
||||
//! ```
|
||||
//! # use mlua::{Lua, Result};
|
||||
//! # fn main() -> Result<()> {
|
||||
//! let lua = Lua::new();
|
||||
//! let array = lua.create_table()?;
|
||||
//!
|
||||
//! // Push values to the end (like Vec::push)
|
||||
//! array.push("first")?;
|
||||
//! array.push("second")?;
|
||||
//! array.push("third")?;
|
||||
//!
|
||||
//! // Pop from the end
|
||||
//! let last: String = array.pop()?;
|
||||
//! assert_eq!(last, "third");
|
||||
//!
|
||||
//! // Get length
|
||||
//! assert_eq!(array.raw_len(), 2);
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! # Iteration
|
||||
//!
|
||||
//! Iterate over all key-value pairs with [`Table::pairs`]:
|
||||
//!
|
||||
//! ```
|
||||
//! # use mlua::{Lua, Result, Value};
|
||||
//! # fn main() -> Result<()> {
|
||||
//! let lua = Lua::new();
|
||||
//! let table = lua.create_table()?;
|
||||
//! table.set("a", 1)?;
|
||||
//! table.set("b", 2)?;
|
||||
//!
|
||||
//! for pair in table.pairs::<String, i32>() {
|
||||
//! let (key, value) = pair?;
|
||||
//! println!("{key} = {value}");
|
||||
//! }
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! For array portions, use [`Table::sequence_values`]:
|
||||
//!
|
||||
//! ```
|
||||
//! # use mlua::{Lua, Result};
|
||||
//! # fn main() -> Result<()> {
|
||||
//! let lua = Lua::new();
|
||||
//! let array = lua.create_sequence_from(["a", "b", "c"])?;
|
||||
//!
|
||||
//! for value in array.sequence_values::<String>() {
|
||||
//! println!("{}", value?);
|
||||
//! }
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! # Raw vs Normal Access
|
||||
//!
|
||||
//! Methods prefixed with `raw_` (like [`Table::raw_get`], [`Table::raw_set`]) bypass
|
||||
//! metamethods, directly accessing the table's contents. Normal methods may trigger
|
||||
//! `__index`, `__newindex`, and other metamethods:
|
||||
//!
|
||||
//! ```
|
||||
//! # use mlua::{Lua, Result};
|
||||
//! # fn main() -> Result<()> {
|
||||
//! let lua = Lua::new();
|
||||
//!
|
||||
//! // raw_set bypasses __newindex metamethod
|
||||
//! let t = lua.create_table()?;
|
||||
//! t.raw_set("key", "value")?;
|
||||
//!
|
||||
//! // raw_get bypasses __index metamethod
|
||||
//! let v: String = t.raw_get("key")?;
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! # Metatables
|
||||
//!
|
||||
//! Tables can have metatables that customize their behavior:
|
||||
//!
|
||||
//! ```
|
||||
//! # use mlua::{Lua, Result};
|
||||
//! # fn main() -> Result<()> {
|
||||
//! let lua = Lua::new();
|
||||
//!
|
||||
//! let table = lua.create_table()?;
|
||||
//! let metatable = lua.create_table()?;
|
||||
//!
|
||||
//! // Set a default value via __index
|
||||
//! metatable.set("__index", lua.create_function(|_, _: ()| Ok("default"))?)?;
|
||||
//! table.set_metatable(Some(metatable))?;
|
||||
//!
|
||||
//! // Accessing missing keys returns "default"
|
||||
//! let value: String = table.get("missing")?;
|
||||
//! assert_eq!(value, "default");
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! # Global Table
|
||||
//!
|
||||
//! The Lua global environment is itself a table, accessible via [`Lua::globals`]:
|
||||
//!
|
||||
//! ```
|
||||
//! # use mlua::{Lua, Result};
|
||||
//! # fn main() -> Result<()> {
|
||||
//! let lua = Lua::new();
|
||||
//! let globals = lua.globals();
|
||||
//!
|
||||
//! // Set a global variable
|
||||
//! globals.set("my_var", 42)?;
|
||||
//!
|
||||
//! // Now accessible from Lua code
|
||||
//! let result: i32 = lua.load("my_var + 8").eval()?;
|
||||
//! assert_eq!(result, 50);
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! [`Lua::globals`]: crate::Lua::globals
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_void;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::{LuaGuard, RawLua, WeakLua};
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike};
|
||||
use crate::types::{Integer, ValueRef};
|
||||
use crate::util::{assert_stack, check_stack, get_metatable_ptr, StackGuard};
|
||||
use crate::util::{StackGuard, assert_stack, check_stack, get_metatable_ptr};
|
||||
use crate::value::{Nil, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
@@ -226,15 +378,15 @@ impl Table {
|
||||
// Compare using `__eq` metamethod if exists
|
||||
// First, check the self for the metamethod.
|
||||
// If self does not define it, then check the other table.
|
||||
if let Some(mt) = self.metatable() {
|
||||
if mt.contains_key("__eq")? {
|
||||
return mt.get::<Function>("__eq")?.call((self, other));
|
||||
}
|
||||
if let Some(mt) = self.metatable()
|
||||
&& let Some(eq_func) = mt.get::<Option<Function>>("__eq")?
|
||||
{
|
||||
return eq_func.call((self, other));
|
||||
}
|
||||
if let Some(mt) = other.metatable() {
|
||||
if mt.contains_key("__eq")? {
|
||||
return mt.get::<Function>("__eq")?.call((self, other));
|
||||
}
|
||||
if let Some(mt) = other.metatable()
|
||||
&& let Some(eq_func) = mt.get::<Option<Function>>("__eq")?
|
||||
{
|
||||
return eq_func.call((self, other));
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
@@ -626,10 +778,8 @@ impl Table {
|
||||
ffi::lua_pushnil(state);
|
||||
while ffi::lua_next(state, -2) != 0 {
|
||||
let k = K::from_stack(-2, &lua)?;
|
||||
let v = V::from_stack(-1, &lua)?;
|
||||
let v = lua.pop::<V>()?;
|
||||
f(k, v)?;
|
||||
// Keep key for next iteration
|
||||
ffi::lua_pop(state, 1);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -702,8 +852,7 @@ impl Table {
|
||||
if len.is_none() && t == ffi::LUA_TNIL {
|
||||
break;
|
||||
}
|
||||
f(V::from_stack(-1, &lua)?)?;
|
||||
ffi::lua_pop(state, 1);
|
||||
f(lua.pop::<V>()?)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -1008,7 +1157,7 @@ impl ObjectLike for Table {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_string(&self) -> Result<StdString> {
|
||||
fn to_string(&self) -> Result<String> {
|
||||
Value::Table(Table(self.0.clone())).to_string()
|
||||
}
|
||||
|
||||
@@ -1070,7 +1219,7 @@ impl Serialize for SerializableTable<'_> {
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
use crate::serde::de::{check_value_for_skip, MapPairs, RecursionGuard};
|
||||
use crate::serde::de::{MapPairs, RecursionGuard, check_value_for_skip};
|
||||
use crate::value::SerializableValue;
|
||||
|
||||
let convert_result = |res: Result<()>, serialize_err: Option<S::Error>| match res {
|
||||
@@ -1098,7 +1247,7 @@ impl Serialize for SerializableTable<'_> {
|
||||
seq.serialize_element(&SerializableValue::new(&value, options, Some(visited)))
|
||||
.map_err(|err| {
|
||||
serialize_err = Some(err);
|
||||
Error::SerializeError(StdString::new())
|
||||
Error::SerializeError(String::new())
|
||||
})
|
||||
});
|
||||
convert_result(res, serialize_err)?;
|
||||
@@ -1123,7 +1272,7 @@ impl Serialize for SerializableTable<'_> {
|
||||
)
|
||||
.map_err(|err| {
|
||||
serialize_err = Some(err);
|
||||
Error::SerializeError(StdString::new())
|
||||
Error::SerializeError(String::new())
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
+68
-10
@@ -1,3 +1,40 @@
|
||||
//! Lua thread (coroutine) handling.
|
||||
//!
|
||||
//! This module provides types for creating and working with Lua coroutines from Rust.
|
||||
//! Coroutines allow cooperative multitasking within a single Lua state by suspending and
|
||||
//! resuming execution at well-defined yield points.
|
||||
//!
|
||||
//! # Basic Usage
|
||||
//!
|
||||
//! Threads are created via [`Lua::create_thread`] and driven by calling [`Thread::resume`]:
|
||||
//!
|
||||
//! ```rust
|
||||
//! # use mlua::{Lua, Result, Thread};
|
||||
//! # fn main() -> Result<()> {
|
||||
//! let lua = Lua::new();
|
||||
//! let thread: Thread = lua.load(r#"
|
||||
//! coroutine.create(function(a, b)
|
||||
//! coroutine.yield(a + b)
|
||||
//! return a * b
|
||||
//! end)
|
||||
//! "#).eval()?;
|
||||
//!
|
||||
//! assert_eq!(thread.resume::<i32>((3, 4))?, 7);
|
||||
//! assert_eq!(thread.resume::<i32>(())?, 12);
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! # Async Support
|
||||
//!
|
||||
//! When the `async` feature is enabled, a [`Thread`] can be converted into an [`AsyncThread`]
|
||||
//! via [`Thread::into_async`], which implements both [`Future`] and [`Stream`].
|
||||
//! This integrates Lua coroutines naturally with Rust async runtimes such as Tokio.
|
||||
//!
|
||||
//! [`Lua::create_thread`]: crate::Lua::create_thread
|
||||
//! [`Future`]: std::future::Future
|
||||
//! [`Stream`]: futures_util::stream::Stream
|
||||
|
||||
use std::fmt;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
|
||||
@@ -6,7 +43,7 @@ use crate::function::Function;
|
||||
use crate::state::RawLua;
|
||||
use crate::traits::{FromLuaMulti, IntoLuaMulti};
|
||||
use crate::types::{LuaType, ValueRef};
|
||||
use crate::util::{check_stack, error_traceback_thread, pop_error, StackGuard};
|
||||
use crate::util::{StackGuard, check_stack, error_traceback_thread, pop_error};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
use crate::{
|
||||
@@ -69,7 +106,7 @@ impl ThreadStatusInner {
|
||||
}
|
||||
|
||||
/// Handle to an internal Lua thread (coroutine).
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Thread(pub(crate) ValueRef, pub(crate) *mut ffi::lua_State);
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
@@ -92,7 +129,6 @@ pub struct AsyncThread<R> {
|
||||
|
||||
impl Thread {
|
||||
/// Returns reference to the Lua state that this thread is associated with.
|
||||
#[doc(hidden)]
|
||||
#[inline(always)]
|
||||
pub fn state(&self) -> *mut ffi::lua_State {
|
||||
self.1
|
||||
@@ -259,6 +295,31 @@ impl Thread {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this thread is resumable (meaning it can be resumed by calling
|
||||
/// [`Thread::resume`]).
|
||||
#[inline(always)]
|
||||
pub fn is_resumable(&self) -> bool {
|
||||
self.status() == ThreadStatus::Resumable
|
||||
}
|
||||
|
||||
/// Returns `true` if this thread is currently running.
|
||||
#[inline(always)]
|
||||
pub fn is_running(&self) -> bool {
|
||||
self.status() == ThreadStatus::Running
|
||||
}
|
||||
|
||||
/// Returns `true` if this thread has finished executing.
|
||||
#[inline(always)]
|
||||
pub fn is_finished(&self) -> bool {
|
||||
self.status() == ThreadStatus::Finished
|
||||
}
|
||||
|
||||
/// Returns `true` if this thread has raised a Lua error during execution.
|
||||
#[inline(always)]
|
||||
pub fn is_error(&self) -> bool {
|
||||
self.status() == ThreadStatus::Error
|
||||
}
|
||||
|
||||
/// 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.
|
||||
@@ -295,7 +356,7 @@ impl Thread {
|
||||
/// 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
|
||||
/// Returns an 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.
|
||||
@@ -449,6 +510,8 @@ impl Thread {
|
||||
/// Please note that Luau links environment table with chunk when loading it into Lua state.
|
||||
/// Therefore you need to load chunks into a thread to link with the thread environment.
|
||||
///
|
||||
/// [`Lua::sandbox`]: crate::Lua::sandbox
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@@ -502,12 +565,6 @@ impl fmt::Debug for Thread {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Thread {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaType for Thread {
|
||||
const TYPE_ID: c_int = ffi::LUA_TTHREAD;
|
||||
}
|
||||
@@ -523,6 +580,7 @@ impl<R> AsyncThread<R> {
|
||||
#[cfg(feature = "async")]
|
||||
impl<R> Drop for AsyncThread<R> {
|
||||
fn drop(&mut self) {
|
||||
#[allow(clippy::collapsible_if)]
|
||||
if self.recycle {
|
||||
if let Some(lua) = self.thread.0.lua.try_lock() {
|
||||
unsafe {
|
||||
|
||||
+8
-96
@@ -1,17 +1,20 @@
|
||||
//! Core conversion and extension traits.
|
||||
//!
|
||||
//! This module provides the fundamental traits for converting values between Rust and Lua,
|
||||
//! and for defining native Lua callable functions.
|
||||
|
||||
use std::os::raw::c_int;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::multi::MultiValue;
|
||||
use crate::private::Sealed;
|
||||
use crate::state::{Lua, RawLua, WeakLua};
|
||||
use crate::types::MaybeSend;
|
||||
use crate::util::{check_stack, parse_lookup_path, short_type_name};
|
||||
use crate::value::Value;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {crate::function::AsyncCallFuture, std::future::Future};
|
||||
use crate::function::AsyncCallFuture;
|
||||
|
||||
/// Trait for types convertible to [`Value`].
|
||||
pub trait IntoLua: Sized {
|
||||
@@ -236,7 +239,7 @@ pub trait ObjectLike: Sealed {
|
||||
/// Converts the object to a string in a human-readable format.
|
||||
///
|
||||
/// This might invoke the `__tostring` metamethod.
|
||||
fn to_string(&self) -> Result<StdString>;
|
||||
fn to_string(&self) -> Result<String>;
|
||||
|
||||
/// Converts the object to a Lua value.
|
||||
fn to_value(&self) -> Value;
|
||||
@@ -246,100 +249,9 @@ pub trait ObjectLike: Sealed {
|
||||
fn weak_lua(&self) -> &WeakLua;
|
||||
}
|
||||
|
||||
/// A trait for types that can be used as Lua functions.
|
||||
pub trait LuaNativeFn<A: FromLuaMulti> {
|
||||
type Output: IntoLuaMulti;
|
||||
|
||||
fn call(&self, args: A) -> Self::Output;
|
||||
}
|
||||
|
||||
/// A trait for types with mutable state that can be used as Lua functions.
|
||||
pub trait LuaNativeFnMut<A: FromLuaMulti> {
|
||||
type Output: IntoLuaMulti;
|
||||
|
||||
fn call(&mut self, args: A) -> Self::Output;
|
||||
}
|
||||
|
||||
/// A trait for types that returns a future and can be used as Lua functions.
|
||||
#[cfg(feature = "async")]
|
||||
pub trait LuaNativeAsyncFn<A: FromLuaMulti> {
|
||||
type Output: IntoLuaMulti;
|
||||
|
||||
fn call(&self, args: A) -> impl Future<Output = Self::Output> + MaybeSend + 'static;
|
||||
}
|
||||
|
||||
macro_rules! impl_lua_native_fn {
|
||||
($($A:ident),*) => {
|
||||
impl<FN, $($A,)* R> LuaNativeFn<($($A,)*)> for FN
|
||||
where
|
||||
FN: Fn($($A,)*) -> R + MaybeSend + 'static,
|
||||
($($A,)*): FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
type Output = R;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn call(&self, args: ($($A,)*)) -> Self::Output {
|
||||
let ($($A,)*) = args;
|
||||
self($($A,)*)
|
||||
}
|
||||
}
|
||||
|
||||
impl<FN, $($A,)* R> LuaNativeFnMut<($($A,)*)> for FN
|
||||
where
|
||||
FN: FnMut($($A,)*) -> R + MaybeSend + 'static,
|
||||
($($A,)*): FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
type Output = R;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn call(&mut self, args: ($($A,)*)) -> Self::Output {
|
||||
let ($($A,)*) = args;
|
||||
self($($A,)*)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<FN, $($A,)* Fut, R> LuaNativeAsyncFn<($($A,)*)> for FN
|
||||
where
|
||||
FN: Fn($($A,)*) -> Fut + MaybeSend + 'static,
|
||||
($($A,)*): FromLuaMulti,
|
||||
Fut: Future<Output = R> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
type Output = R;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn call(&self, args: ($($A,)*)) -> impl Future<Output = Self::Output> + MaybeSend + 'static {
|
||||
let ($($A,)*) = args;
|
||||
self($($A,)*)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_lua_native_fn!();
|
||||
impl_lua_native_fn!(A);
|
||||
impl_lua_native_fn!(A, B);
|
||||
impl_lua_native_fn!(A, B, C);
|
||||
impl_lua_native_fn!(A, B, C, D);
|
||||
impl_lua_native_fn!(A, B, C, D, E);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
|
||||
|
||||
pub(crate) trait ShortTypeName {
|
||||
#[inline(always)]
|
||||
fn type_name() -> StdString {
|
||||
fn type_name() -> String {
|
||||
short_type_name::<Self>()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,6 +128,18 @@ pub trait MaybeSend {}
|
||||
#[cfg(not(feature = "send"))]
|
||||
impl<T> MaybeSend for T {}
|
||||
|
||||
/// A trait that adds `Sync` requirement if `send` feature is enabled.
|
||||
#[cfg(feature = "send")]
|
||||
pub trait MaybeSync: Sync {}
|
||||
#[cfg(feature = "send")]
|
||||
impl<T: Sync> MaybeSync for T {}
|
||||
|
||||
/// A trait that adds `Sync` requirement if `send` feature is enabled.
|
||||
#[cfg(not(feature = "send"))]
|
||||
pub trait MaybeSync {}
|
||||
#[cfg(not(feature = "send"))]
|
||||
impl<T> MaybeSync for T {}
|
||||
|
||||
pub(crate) struct DestructedUserdata;
|
||||
|
||||
pub(crate) trait LuaType {
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ mod inner {
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0 .0
|
||||
&self.0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,10 +55,10 @@ impl Drop for ValueRef {
|
||||
if let Some(ValueRefIndex(index)) = self.index_count.take() {
|
||||
// It's guaranteed that the inner value returns exactly once.
|
||||
// This means in particular that the value is not dropped.
|
||||
if XRc::into_inner(index).is_some() {
|
||||
if let Some(lua) = self.lua.try_lock() {
|
||||
unsafe { lua.drop_ref(self) };
|
||||
}
|
||||
if XRc::into_inner(index).is_some()
|
||||
&& let Some(lua) = self.lua.try_lock()
|
||||
{
|
||||
unsafe { lua.drop_ref(self) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+93
-43
@@ -1,18 +1,22 @@
|
||||
//! Lua userdata handling.
|
||||
//!
|
||||
//! This module provides types for creating and working with Lua userdata from Rust.
|
||||
|
||||
use std::any::TypeId;
|
||||
use std::ffi::CStr;
|
||||
use std::fmt;
|
||||
use std::hash::Hash;
|
||||
use std::os::raw::{c_char, c_void};
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::Either;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::Lua;
|
||||
use crate::string::String;
|
||||
use crate::string::LuaString;
|
||||
use crate::table::{Table, TablePairs};
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
use crate::types::{MaybeSend, ValueRef};
|
||||
use crate::util::{check_stack, get_userdata, push_string, short_type_name, take_userdata, StackGuard};
|
||||
use crate::types::{MaybeSend, MaybeSync, ValueRef};
|
||||
use crate::util::{StackGuard, check_stack, get_userdata, push_string, short_type_name, take_userdata};
|
||||
use crate::value::Value;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
@@ -26,12 +30,12 @@ use {
|
||||
|
||||
// Re-export for convenience
|
||||
pub(crate) use cell::UserDataStorage;
|
||||
pub use r#ref::{UserDataRef, UserDataRefMut};
|
||||
pub use r#ref::{UserDataOwned, UserDataRef, UserDataRefMut};
|
||||
pub use registry::UserDataRegistry;
|
||||
pub(crate) use registry::{RawUserDataRegistry, UserDataProxy};
|
||||
pub(crate) use util::{
|
||||
borrow_userdata_scoped, borrow_userdata_scoped_mut, collect_userdata, init_userdata_metatable,
|
||||
TypeIdHints,
|
||||
TypeIdHints, borrow_userdata_scoped, borrow_userdata_scoped_mut, collect_userdata,
|
||||
init_userdata_metatable,
|
||||
};
|
||||
|
||||
/// Kinds of metamethods that can be overridden.
|
||||
@@ -124,6 +128,11 @@ pub enum MetaMethod {
|
||||
///
|
||||
/// This is not an operator, but will be called by methods such as `tostring` and `print`.
|
||||
ToString,
|
||||
/// The `__todebugstring` metamethod for debug purposes.
|
||||
///
|
||||
/// This is an mlua-specific metamethod that can be used to provide debug representation for
|
||||
/// userdata.
|
||||
ToDebugString,
|
||||
/// The `__pairs` metamethod.
|
||||
///
|
||||
/// This is not an operator, but it will be called by the built-in `pairs` function.
|
||||
@@ -185,7 +194,7 @@ impl PartialEq<MetaMethod> for &str {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<MetaMethod> for StdString {
|
||||
impl PartialEq<MetaMethod> for String {
|
||||
fn eq(&self, other: &MetaMethod) -> bool {
|
||||
self == other.name()
|
||||
}
|
||||
@@ -233,6 +242,7 @@ impl MetaMethod {
|
||||
MetaMethod::NewIndex => "__newindex",
|
||||
MetaMethod::Call => "__call",
|
||||
MetaMethod::ToString => "__tostring",
|
||||
MetaMethod::ToDebugString => "__todebugstring",
|
||||
|
||||
#[cfg(any(
|
||||
feature = "lua55",
|
||||
@@ -279,7 +289,7 @@ impl AsRef<str> for MetaMethod {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MetaMethod> for StdString {
|
||||
impl From<MetaMethod> for String {
|
||||
#[inline]
|
||||
fn from(method: MetaMethod) -> Self {
|
||||
method.name().to_owned()
|
||||
@@ -295,7 +305,7 @@ pub trait UserDataMethods<T> {
|
||||
///
|
||||
/// If `add_meta_method` is used to set the `__index` metamethod, the `__index` metamethod will
|
||||
/// be used as a fall-back if no regular method is found.
|
||||
fn add_method<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_method<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -306,7 +316,7 @@ pub trait UserDataMethods<T> {
|
||||
/// Refer to [`add_method`] for more information about the implementation.
|
||||
///
|
||||
/// [`add_method`]: UserDataMethods::add_method
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -319,8 +329,7 @@ pub trait UserDataMethods<T> {
|
||||
///
|
||||
/// The method can be called only once per userdata instance, subsequent calls will result in a
|
||||
/// [`Error::UserDataDestructed`] error.
|
||||
#[doc(hidden)]
|
||||
fn add_method_once<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_method_once<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(&Lua, T, A) -> Result<R> + MaybeSend + 'static,
|
||||
@@ -342,7 +351,7 @@ pub trait UserDataMethods<T> {
|
||||
/// [`add_method`]: UserDataMethods::add_method
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -357,7 +366,7 @@ pub trait UserDataMethods<T> {
|
||||
/// [`add_method`]: UserDataMethods::add_method
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -374,8 +383,7 @@ pub trait UserDataMethods<T> {
|
||||
/// [`Error::UserDataDestructed`] error.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
#[doc(hidden)]
|
||||
fn add_async_method_once<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_method_once<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, T, A) -> MR + MaybeSend + 'static,
|
||||
@@ -398,7 +406,7 @@ pub trait UserDataMethods<T> {
|
||||
/// The first argument will be a [`AnyUserData`] of type `T` if the method is called with Lua
|
||||
/// method syntax: `my_userdata:my_method(arg1, arg2)`, or it is passed in as the first
|
||||
/// argument: `my_userdata.my_method(my_userdata, arg1, arg2)`.
|
||||
fn add_function<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_function<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -409,7 +417,7 @@ pub trait UserDataMethods<T> {
|
||||
/// This is a version of [`add_function`] that accepts a `FnMut` argument.
|
||||
///
|
||||
/// [`add_function`]: UserDataMethods::add_function
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -423,7 +431,7 @@ pub trait UserDataMethods<T> {
|
||||
/// [`add_function`]: UserDataMethods::add_function
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -438,7 +446,7 @@ pub trait UserDataMethods<T> {
|
||||
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
||||
///
|
||||
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -452,7 +460,7 @@ pub trait UserDataMethods<T> {
|
||||
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
||||
///
|
||||
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -468,7 +476,7 @@ pub trait UserDataMethods<T> {
|
||||
docsrs,
|
||||
doc(cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau")))))
|
||||
)]
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -484,7 +492,7 @@ pub trait UserDataMethods<T> {
|
||||
/// [`add_meta_method_mut`]: UserDataMethods::add_meta_method_mut
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -497,7 +505,7 @@ pub trait UserDataMethods<T> {
|
||||
/// Metamethods for binary operators can be triggered if either the left or right argument to
|
||||
/// the binary operator has a metatable, so the first argument here is not necessarily a
|
||||
/// userdata of type `T`.
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -508,7 +516,7 @@ pub trait UserDataMethods<T> {
|
||||
/// This is a version of [`add_meta_function`] that accepts a `FnMut` argument.
|
||||
///
|
||||
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -524,7 +532,7 @@ pub trait UserDataMethods<T> {
|
||||
docsrs,
|
||||
doc(cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau")))))
|
||||
)]
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -543,7 +551,7 @@ pub trait UserDataFields<T> {
|
||||
///
|
||||
/// If `add_meta_method` is used to set the `__index` metamethod, it will
|
||||
/// be used as a fall-back if no regular field or method are found.
|
||||
fn add_field<V>(&mut self, name: impl Into<StdString>, value: V)
|
||||
fn add_field<V>(&mut self, name: impl Into<String>, value: V)
|
||||
where
|
||||
V: IntoLua + 'static;
|
||||
|
||||
@@ -554,7 +562,7 @@ pub trait UserDataFields<T> {
|
||||
///
|
||||
/// If `add_meta_method` is used to set the `__index` metamethod, the `__index` metamethod will
|
||||
/// be used as a fall-back if no regular field or method are found.
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua;
|
||||
@@ -567,21 +575,21 @@ pub trait UserDataFields<T> {
|
||||
///
|
||||
/// If `add_meta_method` is used to set the `__newindex` metamethod, the `__newindex` metamethod
|
||||
/// will be used as a fall-back if no regular field is found.
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua;
|
||||
|
||||
/// Add a regular field getter as a function which accepts a generic [`AnyUserData`] of type `T`
|
||||
/// argument.
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua;
|
||||
|
||||
/// Add a regular field setter as a function which accepts a generic [`AnyUserData`] of type `T`
|
||||
/// first argument.
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua;
|
||||
@@ -594,7 +602,7 @@ pub trait UserDataFields<T> {
|
||||
///
|
||||
/// `mlua` will trigger an error on an attempt to define a protected metamethod,
|
||||
/// like `__gc` or `__metatable`.
|
||||
fn add_meta_field<V>(&mut self, name: impl Into<StdString>, value: V)
|
||||
fn add_meta_field<V>(&mut self, name: impl Into<String>, value: V)
|
||||
where
|
||||
V: IntoLua + 'static;
|
||||
|
||||
@@ -606,7 +614,7 @@ pub trait UserDataFields<T> {
|
||||
///
|
||||
/// `mlua` will trigger an error on an attempt to define a protected metamethod,
|
||||
/// like `__gc` or `__metatable`.
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl Into<StdString>, f: F)
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl Into<String>, f: F)
|
||||
where
|
||||
F: FnOnce(&Lua) -> Result<R> + 'static,
|
||||
R: IntoLua;
|
||||
@@ -706,7 +714,7 @@ pub trait UserData: Sized {
|
||||
///
|
||||
/// [`is`]: crate::AnyUserData::is
|
||||
/// [`borrow`]: crate::AnyUserData::borrow
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct AnyUserData(pub(crate) ValueRef);
|
||||
|
||||
impl AnyUserData {
|
||||
@@ -1021,8 +1029,8 @@ impl AnyUserData {
|
||||
|
||||
/// Returns a type name of this userdata (from a metatable field).
|
||||
///
|
||||
/// If no type name is set, returns `None`.
|
||||
pub fn type_name(&self) -> Result<Option<StdString>> {
|
||||
/// If no type name is set, returns `userdata`.
|
||||
pub fn type_name(&self) -> Result<LuaString> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
@@ -1039,8 +1047,8 @@ impl AnyUserData {
|
||||
ffi::luaL_getmetafield(state, -1, MetaMethod::Type.as_cstr().as_ptr())
|
||||
};
|
||||
match name_type {
|
||||
ffi::LUA_TSTRING => Ok(Some(String(lua.pop_ref()).to_str()?.to_owned())),
|
||||
_ => Ok(None),
|
||||
ffi::LUA_TSTRING => Ok(LuaString(lua.pop_ref())),
|
||||
_ => lua.create_string(b"userdata"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1076,6 +1084,48 @@ impl AnyUserData {
|
||||
};
|
||||
is_serializable().unwrap_or(false)
|
||||
}
|
||||
|
||||
unsafe fn invoke_tostring_dbg(&self) -> Result<Option<String>> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
let _guard = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
protect_lua!(state, 1, 1, fn(state) {
|
||||
// Try `__todebugstring` metamethod first, then `__tostring`
|
||||
#[allow(clippy::collapsible_if)]
|
||||
if ffi::luaL_callmeta(state, -1, cstr!("__todebugstring")) == 0 {
|
||||
if ffi::luaL_callmeta(state, -1, cstr!("__tostring")) == 0 {
|
||||
ffi::lua_pushnil(state);
|
||||
}
|
||||
}
|
||||
})?;
|
||||
Ok(lua.pop_value().as_string().map(|s| s.to_string_lossy()))
|
||||
}
|
||||
|
||||
pub(crate) fn fmt_pretty(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
// Try converting to a (debug) string first, with fallback to `__name/__type`
|
||||
match unsafe { self.invoke_tostring_dbg() } {
|
||||
Ok(Some(s)) => write!(fmt, "{s}"),
|
||||
_ => {
|
||||
let name = self.type_name().ok();
|
||||
let name = (name.as_ref())
|
||||
.map(|s| Either::Left(s.display()))
|
||||
.unwrap_or(Either::Right("userdata"));
|
||||
write!(fmt, "{name}: {:?}", self.to_pointer())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for AnyUserData {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
if fmt.alternate() {
|
||||
return self.fmt_pretty(fmt);
|
||||
}
|
||||
fmt.debug_tuple("AnyUserData").field(&self.0).finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle to a [`AnyUserData`] metatable.
|
||||
@@ -1126,13 +1176,13 @@ impl UserDataMetatable {
|
||||
/// It skips restricted metamethods, such as `__gc` or `__metatable`.
|
||||
///
|
||||
/// This struct is created by the [`UserDataMetatable::pairs`] method.
|
||||
pub struct UserDataMetatablePairs<'a, V>(TablePairs<'a, StdString, V>);
|
||||
pub struct UserDataMetatablePairs<'a, V>(TablePairs<'a, String, V>);
|
||||
|
||||
impl<V> Iterator for UserDataMetatablePairs<'_, V>
|
||||
where
|
||||
V: FromLua,
|
||||
{
|
||||
type Item = Result<(StdString, V)>;
|
||||
type Item = Result<(String, V)>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
@@ -1172,7 +1222,7 @@ impl AnyUserData {
|
||||
/// Wraps any Rust type, returning an opaque type that implements [`IntoLua`] trait.
|
||||
///
|
||||
/// This function uses [`Lua::create_any_userdata`] under the hood.
|
||||
pub fn wrap<T: MaybeSend + 'static>(data: T) -> impl IntoLua {
|
||||
pub fn wrap<T: MaybeSend + MaybeSync + 'static>(data: T) -> impl IntoLua {
|
||||
WrappedUserdata(move |lua| lua.create_any_userdata(data))
|
||||
}
|
||||
|
||||
@@ -1182,7 +1232,7 @@ impl AnyUserData {
|
||||
/// This function uses [`Lua::create_ser_any_userdata`] under the hood.
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
pub fn wrap_ser<T: Serialize + MaybeSend + 'static>(data: T) -> impl IntoLua {
|
||||
pub fn wrap_ser<T: Serialize + MaybeSend + MaybeSync + 'static>(data: T) -> impl IntoLua {
|
||||
WrappedUserdata(move |lua| lua.create_ser_any_userdata(data))
|
||||
}
|
||||
}
|
||||
|
||||
+33
-65
@@ -1,4 +1,4 @@
|
||||
use std::cell::{RefCell, UnsafeCell};
|
||||
use std::cell::RefCell;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
@@ -6,14 +6,14 @@ use serde::ser::{Serialize, Serializer};
|
||||
use crate::error::{Error, Result};
|
||||
use crate::types::XRc;
|
||||
|
||||
use super::lock::{RawLock, UserDataLock};
|
||||
use super::lock::{RawLock, RwLock, UserDataLock};
|
||||
use super::r#ref::{UserDataRef, UserDataRefMut};
|
||||
|
||||
#[cfg(all(feature = "serde", not(feature = "send")))]
|
||||
type DynSerialize = dyn erased_serde::Serialize;
|
||||
|
||||
#[cfg(all(feature = "serde", feature = "send"))]
|
||||
type DynSerialize = dyn erased_serde::Serialize + Send;
|
||||
type DynSerialize = dyn erased_serde::Serialize + Send + Sync;
|
||||
|
||||
pub(crate) enum UserDataStorage<T> {
|
||||
Owned(UserDataVariant<T>),
|
||||
@@ -23,9 +23,9 @@ pub(crate) enum UserDataStorage<T> {
|
||||
// A enum for storing userdata values.
|
||||
// It's stored inside a Lua VM and protected by the outer `ReentrantMutex`.
|
||||
pub(crate) enum UserDataVariant<T> {
|
||||
Default(XRc<UserDataCell<T>>),
|
||||
Default(XRc<RwLock<T>>),
|
||||
#[cfg(feature = "serde")]
|
||||
Serializable(XRc<UserDataCell<Box<DynSerialize>>>, bool), // bool is `is_sync`
|
||||
Serializable(XRc<RwLock<Box<DynSerialize>>>),
|
||||
}
|
||||
|
||||
impl<T> Clone for UserDataVariant<T> {
|
||||
@@ -34,7 +34,7 @@ impl<T> Clone for UserDataVariant<T> {
|
||||
match self {
|
||||
Self::Default(inner) => Self::Default(XRc::clone(inner)),
|
||||
#[cfg(feature = "serde")]
|
||||
Self::Serializable(inner, is_sync) => Self::Serializable(XRc::clone(inner), *is_sync),
|
||||
Self::Serializable(inner) => Self::Serializable(XRc::clone(inner)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,10 +42,12 @@ impl<T> Clone for UserDataVariant<T> {
|
||||
impl<T> UserDataVariant<T> {
|
||||
#[inline(always)]
|
||||
pub(super) fn try_borrow_scoped<R>(&self, f: impl FnOnce(&T) -> R) -> Result<R> {
|
||||
// We don't need to check for `T: Sync` because when this method is used (internally),
|
||||
// Lua mutex is already locked.
|
||||
// If non-`Sync` userdata is already borrowed by another thread (via `UserDataRef`), it will be
|
||||
// exclusively locked.
|
||||
// Shared (read) lock is always correct for in-place borrows:
|
||||
// - this method is called internally while the Lua mutex is held, ensuring exclusive Lua-level
|
||||
// access per call frame
|
||||
// - with `send` feature, all owned userdata satisfies `T: Sync`, so simultaneous shared references
|
||||
// from multiple threads are sound
|
||||
// - without `send` feature, single-threaded execution makes shared lock safe for any `T`
|
||||
let _guard = (self.raw_lock().try_lock_shared_guarded()).map_err(|_| Error::UserDataBorrowError)?;
|
||||
Ok(f(unsafe { &*self.as_ptr() }))
|
||||
}
|
||||
@@ -78,10 +80,12 @@ impl<T> UserDataVariant<T> {
|
||||
return Err(Error::UserDataBorrowMutError);
|
||||
}
|
||||
Ok(match self {
|
||||
Self::Default(inner) => XRc::into_inner(inner).unwrap().value.into_inner(),
|
||||
Self::Default(inner) => XRc::into_inner(inner).unwrap().into_inner(),
|
||||
#[cfg(feature = "serde")]
|
||||
Self::Serializable(inner, _) => unsafe {
|
||||
let raw = Box::into_raw(XRc::into_inner(inner).unwrap().value.into_inner());
|
||||
Self::Serializable(inner) => unsafe {
|
||||
// The serde variant erases `T` to `Box<DynSerialize>`, so we
|
||||
// must cast the raw pointer back to recover the concrete type.
|
||||
let raw = Box::into_raw(XRc::into_inner(inner).unwrap().into_inner());
|
||||
*Box::from_raw(raw as *mut T)
|
||||
},
|
||||
})
|
||||
@@ -92,25 +96,25 @@ impl<T> UserDataVariant<T> {
|
||||
match self {
|
||||
Self::Default(inner) => XRc::strong_count(inner),
|
||||
#[cfg(feature = "serde")]
|
||||
Self::Serializable(inner, _) => XRc::strong_count(inner),
|
||||
Self::Serializable(inner) => XRc::strong_count(inner),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn raw_lock(&self) -> &RawLock {
|
||||
match self {
|
||||
Self::Default(inner) => &inner.raw_lock,
|
||||
Self::Default(inner) => unsafe { inner.raw() },
|
||||
#[cfg(feature = "serde")]
|
||||
Self::Serializable(inner, _) => &inner.raw_lock,
|
||||
Self::Serializable(inner) => unsafe { inner.raw() },
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn as_ptr(&self) -> *mut T {
|
||||
match self {
|
||||
Self::Default(inner) => inner.value.get(),
|
||||
Self::Default(inner) => inner.data_ptr(),
|
||||
#[cfg(feature = "serde")]
|
||||
Self::Serializable(inner, _) => unsafe { &mut **(inner.value.get() as *mut Box<T>) },
|
||||
Self::Serializable(inner) => unsafe { (&mut **inner.data_ptr()) as *mut DynSerialize as *mut T },
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,51 +123,16 @@ impl<T> UserDataVariant<T> {
|
||||
impl Serialize for UserDataStorage<()> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
|
||||
match self {
|
||||
Self::Owned(variant @ UserDataVariant::Serializable(inner, is_sync)) => unsafe {
|
||||
#[cfg(feature = "send")]
|
||||
if *is_sync {
|
||||
let _guard = (variant.raw_lock().try_lock_shared_guarded())
|
||||
.map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?;
|
||||
(*inner.value.get()).serialize(serializer)
|
||||
} else {
|
||||
let _guard = (variant.raw_lock().try_lock_exclusive_guarded())
|
||||
.map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?;
|
||||
(*inner.value.get()).serialize(serializer)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
{
|
||||
let _ = is_sync;
|
||||
let _guard = (variant.raw_lock().try_lock_shared_guarded())
|
||||
.map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?;
|
||||
(*inner.value.get()).serialize(serializer)
|
||||
}
|
||||
Self::Owned(variant @ UserDataVariant::Serializable(inner)) => unsafe {
|
||||
let _guard = (variant.raw_lock().try_lock_shared_guarded())
|
||||
.map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?;
|
||||
(*inner.data_ptr()).serialize(serializer)
|
||||
},
|
||||
_ => Err(serde::ser::Error::custom("cannot serialize <userdata>")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A type that provides interior mutability for a userdata value (thread-safe).
|
||||
pub(crate) struct UserDataCell<T> {
|
||||
raw_lock: RawLock,
|
||||
value: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl<T: Send> Send for UserDataCell<T> {}
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl<T: Send> Sync for UserDataCell<T> {}
|
||||
|
||||
impl<T> UserDataCell<T> {
|
||||
#[inline(always)]
|
||||
fn new(value: T) -> Self {
|
||||
UserDataCell {
|
||||
raw_lock: RawLock::INIT,
|
||||
value: UnsafeCell::new(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum ScopedUserDataVariant<T> {
|
||||
Ref(*const T),
|
||||
RefMut(RefCell<*mut T>),
|
||||
@@ -173,10 +142,10 @@ pub(crate) enum ScopedUserDataVariant<T> {
|
||||
impl<T> Drop for ScopedUserDataVariant<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
if let Self::Boxed(value) = self {
|
||||
if let Ok(value) = value.try_borrow_mut() {
|
||||
unsafe { drop(Box::from_raw(*value)) };
|
||||
}
|
||||
if let Self::Boxed(value) = self
|
||||
&& let Ok(value) = value.try_borrow_mut()
|
||||
{
|
||||
unsafe { drop(Box::from_raw(*value)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -184,7 +153,7 @@ impl<T> Drop for ScopedUserDataVariant<T> {
|
||||
impl<T: 'static> UserDataStorage<T> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new(data: T) -> Self {
|
||||
Self::Owned(UserDataVariant::Default(XRc::new(UserDataCell::new(data))))
|
||||
Self::Owned(UserDataVariant::Default(XRc::new(RwLock::new(data))))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -201,11 +170,10 @@ impl<T: 'static> UserDataStorage<T> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new_ser(data: T) -> Self
|
||||
where
|
||||
T: Serialize + crate::types::MaybeSend,
|
||||
T: Serialize + crate::types::MaybeSend + crate::types::MaybeSync,
|
||||
{
|
||||
let data = Box::new(data) as Box<DynSerialize>;
|
||||
let is_sync = super::util::is_sync::<T>();
|
||||
let variant = UserDataVariant::Serializable(XRc::new(UserDataCell::new(data)), is_sync);
|
||||
let variant = UserDataVariant::Serializable(XRc::new(RwLock::new(data)));
|
||||
Self::Owned(variant)
|
||||
}
|
||||
|
||||
|
||||
+44
-19
@@ -1,6 +1,4 @@
|
||||
pub(crate) trait UserDataLock {
|
||||
const INIT: Self;
|
||||
|
||||
fn is_locked(&self) -> bool;
|
||||
fn try_lock_shared(&self) -> bool;
|
||||
fn try_lock_exclusive(&self) -> bool;
|
||||
@@ -48,12 +46,12 @@ impl<L: UserDataLock + ?Sized> Drop for LockGuard<'_, L> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use lock_impl::RawLock;
|
||||
pub(crate) use lock_impl::{RawLock, RwLock};
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
mod lock_impl {
|
||||
use std::cell::Cell;
|
||||
use std::cell::{Cell, UnsafeCell};
|
||||
|
||||
// Positive values represent the number of read references.
|
||||
// Negative values represent the number of write references (only one allowed).
|
||||
@@ -62,9 +60,6 @@ mod lock_impl {
|
||||
const UNUSED: isize = 0;
|
||||
|
||||
impl super::UserDataLock for RawLock {
|
||||
#[allow(clippy::declare_interior_mutable_const)]
|
||||
const INIT: Self = Cell::new(UNUSED);
|
||||
|
||||
#[inline(always)]
|
||||
fn is_locked(&self) -> bool {
|
||||
self.get() != UNUSED
|
||||
@@ -72,7 +67,7 @@ mod lock_impl {
|
||||
|
||||
#[inline(always)]
|
||||
fn try_lock_shared(&self) -> bool {
|
||||
let flag = self.get().wrapping_add(1);
|
||||
let flag = self.get().checked_add(1).expect("userdata lock count overflow");
|
||||
if flag <= UNUSED {
|
||||
return false;
|
||||
}
|
||||
@@ -104,41 +99,71 @@ mod lock_impl {
|
||||
self.set(flag + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// A cheap single-threaded read-write lock pairing a `parking_lot::RwLock` type.
|
||||
pub(crate) struct RwLock<T> {
|
||||
lock: RawLock,
|
||||
data: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
impl<T> RwLock<T> {
|
||||
/// Creates a new `RwLock` containing the given value.
|
||||
#[inline(always)]
|
||||
pub(crate) fn new(value: T) -> Self {
|
||||
RwLock {
|
||||
lock: RawLock::new(UNUSED),
|
||||
data: UnsafeCell::new(value),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying raw lock.
|
||||
#[inline(always)]
|
||||
pub(crate) unsafe fn raw(&self) -> &RawLock {
|
||||
&self.lock
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to the underlying data.
|
||||
#[inline(always)]
|
||||
pub(crate) fn data_ptr(&self) -> *mut T {
|
||||
self.data.get()
|
||||
}
|
||||
|
||||
/// Consumes this `RwLock`, returning the underlying data.
|
||||
#[inline(always)]
|
||||
pub(crate) fn into_inner(self) -> T {
|
||||
self.data.into_inner()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
mod lock_impl {
|
||||
use parking_lot::lock_api::RawRwLock;
|
||||
|
||||
pub(crate) type RawLock = parking_lot::RawRwLock;
|
||||
pub(crate) use parking_lot::{RawRwLock as RawLock, RwLock};
|
||||
|
||||
impl super::UserDataLock for RawLock {
|
||||
#[allow(clippy::declare_interior_mutable_const)]
|
||||
const INIT: Self = <Self as parking_lot::lock_api::RawRwLock>::INIT;
|
||||
|
||||
#[inline(always)]
|
||||
fn is_locked(&self) -> bool {
|
||||
RawRwLock::is_locked(self)
|
||||
parking_lot::lock_api::RawRwLock::is_locked(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn try_lock_shared(&self) -> bool {
|
||||
RawRwLock::try_lock_shared(self)
|
||||
parking_lot::lock_api::RawRwLock::try_lock_shared(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn try_lock_exclusive(&self) -> bool {
|
||||
RawRwLock::try_lock_exclusive(self)
|
||||
parking_lot::lock_api::RawRwLock::try_lock_exclusive(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn unlock_shared(&self) {
|
||||
RawRwLock::unlock_shared(self)
|
||||
parking_lot::lock_api::RawRwLock::unlock_shared(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn unlock_exclusive(&self) {
|
||||
RawRwLock::unlock_exclusive(self)
|
||||
parking_lot::lock_api::RawRwLock::unlock_exclusive(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::Function;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::WeakLua;
|
||||
use crate::table::Table;
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike};
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::value::Value;
|
||||
use crate::Function;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use crate::function::AsyncCallFuture;
|
||||
@@ -88,7 +86,7 @@ impl ObjectLike for AnyUserData {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_string(&self) -> Result<StdString> {
|
||||
fn to_string(&self) -> Result<String> {
|
||||
Value::UserData(self.clone()).to_string()
|
||||
}
|
||||
|
||||
|
||||
+75
-13
@@ -1,4 +1,4 @@
|
||||
use std::any::{type_name, TypeId};
|
||||
use std::any::{TypeId, type_name};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::c_int;
|
||||
use std::{fmt, mem};
|
||||
@@ -7,12 +7,11 @@ use crate::error::{Error, Result};
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::traits::FromLua;
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::util::get_userdata;
|
||||
use crate::util::{check_stack, get_userdata, take_userdata};
|
||||
use crate::value::Value;
|
||||
|
||||
use super::cell::{UserDataStorage, UserDataVariant};
|
||||
use super::lock::{LockGuard, RawLock, UserDataLock};
|
||||
use super::util::is_sync;
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
use {
|
||||
@@ -63,11 +62,10 @@ impl<T> TryFrom<UserDataVariant<T>> for UserDataRef<T> {
|
||||
|
||||
#[inline]
|
||||
fn try_from(variant: UserDataVariant<T>) -> Result<Self> {
|
||||
let guard = if cfg!(not(feature = "send")) || is_sync::<T>() {
|
||||
variant.raw_lock().try_lock_shared_guarded()
|
||||
} else {
|
||||
variant.raw_lock().try_lock_exclusive_guarded()
|
||||
};
|
||||
// Shared (read) lock is always correct:
|
||||
// - with `send` feature, `T: Sync` is guaranteed by the `MaybeSync` bound on userdata creation
|
||||
// - without `send` feature, single-threaded access makes shared lock safe for any `T`
|
||||
let guard = variant.raw_lock().try_lock_shared_guarded();
|
||||
let guard = guard.map_err(|_| Error::UserDataBorrowError)?;
|
||||
let guard = unsafe { mem::transmute::<LockGuard<_>, LockGuard<'static, _>>(guard) };
|
||||
Ok(UserDataRef::from_parts(UserDataRefInner::Default(variant), guard))
|
||||
@@ -442,15 +440,75 @@ impl<T> DerefMut for UserDataRefMutInner<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type that takes ownership of a userdata value.
|
||||
///
|
||||
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua by taking
|
||||
/// ownership of it.
|
||||
/// The original Lua userdata is marked as destructed and cannot be used further.
|
||||
pub struct UserDataOwned<T>(pub T);
|
||||
|
||||
impl<T> Deref for UserDataOwned<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for UserDataOwned<T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for UserDataOwned<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for UserDataOwned<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> FromLua for UserDataOwned<T> {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
try_value_to_userdata::<T>(value)?.take().map(UserDataOwned)
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let state = lua.state();
|
||||
let type_id = lua.get_userdata_type_id::<T>(state, idx)?;
|
||||
match type_id {
|
||||
Some(type_id) if type_id == TypeId::of::<T>() => {
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, idx);
|
||||
if (*ud).has_exclusive_access() {
|
||||
check_stack(state, 1)?;
|
||||
take_userdata::<UserDataStorage<T>>(state, idx)
|
||||
.into_inner()
|
||||
.map(UserDataOwned)
|
||||
} else {
|
||||
Err(Error::UserDataBorrowMutError)
|
||||
}
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_value_to_userdata<T>(value: Value) -> Result<AnyUserData> {
|
||||
match value {
|
||||
Value::UserData(ud) => Ok(ud),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "userdata".to_string(),
|
||||
message: Some(format!("expected userdata of type {}", type_name::<T>())),
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(
|
||||
value.type_name(),
|
||||
"userdata",
|
||||
format!("expected userdata of type {}", type_name::<T>()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,6 +524,10 @@ mod assertions {
|
||||
static_assertions::assert_impl_all!(UserDataRefMut<()>: Sync, Send);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_not_impl_all!(UserDataRefMut<std::rc::Rc<()>>: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(UserDataOwned<()>: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_not_impl_all!(UserDataOwned<std::rc::Rc<()>>: Send, Sync);
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_all!(UserDataRef<()>: Send, Sync);
|
||||
|
||||
+32
-27
@@ -4,15 +4,14 @@ use std::any::TypeId;
|
||||
use std::cell::RefCell;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_void;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::{Lua, LuaGuard};
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
use crate::types::{Callback, MaybeSend};
|
||||
use crate::userdata::{
|
||||
borrow_userdata_scoped, borrow_userdata_scoped_mut, AnyUserData, MetaMethod, TypeIdHints, UserData,
|
||||
UserDataFields, UserDataMethods, UserDataStorage,
|
||||
AnyUserData, MetaMethod, TypeIdHints, UserData, UserDataFields, UserDataMethods, UserDataStorage,
|
||||
borrow_userdata_scoped, borrow_userdata_scoped_mut,
|
||||
};
|
||||
use crate::util::short_type_name;
|
||||
use crate::value::Value;
|
||||
@@ -55,7 +54,7 @@ pub(crate) struct RawUserDataRegistry {
|
||||
|
||||
pub(crate) destructor: ffi::lua_CFunction,
|
||||
pub(crate) type_id: Option<TypeId>,
|
||||
pub(crate) type_name: StdString,
|
||||
pub(crate) type_name: String,
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
pub(crate) enable_namecall: bool,
|
||||
@@ -368,7 +367,7 @@ impl<T> UserDataRegistry<T> {
|
||||
method: name.to_string(),
|
||||
type_name: value.type_name(),
|
||||
message: Some("expected nil, table or function".to_string()),
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -382,12 +381,12 @@ impl<T> UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
// Returns function name for the type `T`, without the module path
|
||||
fn get_function_name<T>(name: &str) -> StdString {
|
||||
fn get_function_name<T>(name: &str) -> String {
|
||||
format!("{}.{name}", short_type_name::<T>())
|
||||
}
|
||||
|
||||
impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
fn add_field<V>(&mut self, name: impl Into<StdString>, value: V)
|
||||
fn add_field<V>(&mut self, name: impl Into<String>, value: V)
|
||||
where
|
||||
V: IntoLua + 'static,
|
||||
{
|
||||
@@ -395,7 +394,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
self.raw.fields.push((name, value.into_lua(self.lua.lua())));
|
||||
}
|
||||
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua,
|
||||
@@ -405,7 +404,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
self.raw.field_getters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua,
|
||||
@@ -415,7 +414,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
self.raw.field_setters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua,
|
||||
@@ -425,7 +424,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
self.raw.field_getters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl Into<StdString>, mut function: F)
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl Into<String>, mut function: F)
|
||||
where
|
||||
F: FnMut(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua,
|
||||
@@ -435,7 +434,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
self.raw.field_setters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_field<V>(&mut self, name: impl Into<StdString>, value: V)
|
||||
fn add_meta_field<V>(&mut self, name: impl Into<String>, value: V)
|
||||
where
|
||||
V: IntoLua + 'static,
|
||||
{
|
||||
@@ -445,7 +444,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
self.raw.meta_fields.push((name, field));
|
||||
}
|
||||
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl Into<StdString>, f: F)
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl Into<String>, f: F)
|
||||
where
|
||||
F: FnOnce(&Lua) -> Result<R> + 'static,
|
||||
R: IntoLua,
|
||||
@@ -458,7 +457,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
fn add_method<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_method<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -469,7 +468,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
self.raw.methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -481,7 +480,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -495,7 +494,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -508,7 +507,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
self.raw.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_function<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_function<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -519,7 +518,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
self.raw.methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -531,7 +530,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -543,7 +542,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
self.raw.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -554,7 +553,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
self.raw.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -566,7 +565,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -580,7 +579,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -593,7 +592,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
self.raw.async_meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -604,7 +603,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
self.raw.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -616,7 +615,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -655,6 +654,12 @@ macro_rules! lua_userdata_impl {
|
||||
// A special proxy object for UserData
|
||||
pub(crate) struct UserDataProxy<T>(pub(crate) PhantomData<T>);
|
||||
|
||||
// `UserDataProxy` holds no real `T` value, only a type marker, so it is always safe to send/share.
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl<T> Send for UserDataProxy<T> {}
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl<T> Sync for UserDataProxy<T> {}
|
||||
|
||||
lua_userdata_impl!(UserDataProxy<T>);
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use std::any::TypeId;
|
||||
use std::cell::Cell;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_int;
|
||||
use std::ptr;
|
||||
|
||||
@@ -11,35 +9,6 @@ use crate::error::{Error, Result};
|
||||
use crate::types::CallbackPtr;
|
||||
use crate::util::{get_userdata, rawget_field, rawset_field, take_userdata};
|
||||
|
||||
// This is a trick to check if a type is `Sync` or not.
|
||||
// It uses leaked specialization feature from stdlib.
|
||||
struct IsSync<'a, T> {
|
||||
is_sync: &'a Cell<bool>,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Clone for IsSync<'_, T> {
|
||||
fn clone(&self) -> Self {
|
||||
self.is_sync.set(false);
|
||||
IsSync {
|
||||
is_sync: self.is_sync,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Sync> Copy for IsSync<'_, T> {}
|
||||
|
||||
pub(crate) fn is_sync<T>() -> bool {
|
||||
let is_sync = Cell::new(true);
|
||||
let _ = [IsSync::<T> {
|
||||
is_sync: &is_sync,
|
||||
_marker: PhantomData,
|
||||
}]
|
||||
.clone();
|
||||
is_sync.get()
|
||||
}
|
||||
|
||||
// Userdata type hints, used to match types of wrapped userdata
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) struct TypeIdHints {
|
||||
|
||||
+4
-4
@@ -2,15 +2,15 @@ use std::any::Any;
|
||||
use std::fmt::Write as _;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
||||
use std::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::memory::MemoryState;
|
||||
use crate::util::{
|
||||
check_stack, get_internal_userdata, init_internal_metatable, push_internal_userdata, push_string,
|
||||
push_table, rawset_field, to_string, TypeKey, DESTRUCTED_USERDATA_METATABLE,
|
||||
DESTRUCTED_USERDATA_METATABLE, TypeKey, check_stack, get_internal_userdata, init_internal_metatable,
|
||||
push_internal_userdata, push_string, push_table, rawset_field, to_string,
|
||||
};
|
||||
|
||||
static WRAPPED_FAILURE_TYPE_KEY: u8 = 0;
|
||||
@@ -197,7 +197,7 @@ where
|
||||
F: FnOnce(*mut ffi::lua_State) -> R,
|
||||
R: Copy,
|
||||
{
|
||||
struct Params<F, R: Copy> {
|
||||
struct Params<F, R> {
|
||||
function: Option<F>,
|
||||
result: MaybeUninit<R>,
|
||||
nresults: c_int,
|
||||
|
||||
+6
-10
@@ -6,16 +6,16 @@ use std::{ptr, slice, str};
|
||||
use crate::error::{Error, Result};
|
||||
|
||||
pub(crate) use error::{
|
||||
error_traceback, error_traceback_thread, init_error_registry, pop_error, protect_lua_call,
|
||||
protect_lua_closure, WrappedFailure,
|
||||
WrappedFailure, error_traceback, error_traceback_thread, init_error_registry, pop_error,
|
||||
protect_lua_call, protect_lua_closure,
|
||||
};
|
||||
pub(crate) use path::parse_path as parse_lookup_path;
|
||||
pub(crate) use short_names::short_type_name;
|
||||
pub(crate) use types::TypeKey;
|
||||
pub(crate) use userdata::{
|
||||
get_destructed_userdata_metatable, get_internal_metatable, get_internal_userdata, get_userdata,
|
||||
init_internal_metatable, push_internal_userdata, push_userdata, take_userdata,
|
||||
DESTRUCTED_USERDATA_METATABLE,
|
||||
DESTRUCTED_USERDATA_METATABLE, get_destructed_userdata_metatable, get_internal_metatable,
|
||||
get_internal_userdata, get_userdata, init_internal_metatable, push_internal_userdata, push_userdata,
|
||||
take_userdata,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
@@ -264,11 +264,7 @@ pub(crate) unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut f
|
||||
// Check the current state first
|
||||
let is_main_state = ffi::lua_pushthread(state) == 1;
|
||||
ffi::lua_pop(state, 1);
|
||||
if is_main_state {
|
||||
Some(state)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
if is_main_state { Some(state) } else { None }
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
Some(ffi::lua_mainthread(state))
|
||||
|
||||
+1
-1
@@ -196,7 +196,7 @@ fn unquote_string<'a>(path: &'a str, chars: &mut Peekable<CharIndices<'a>>) -> R
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{parse_path, PathKey};
|
||||
use super::{PathKey, parse_path};
|
||||
|
||||
#[test]
|
||||
fn test_parse_path() {
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::{mem, ptr};
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::userdata::collect_userdata;
|
||||
use crate::util::{check_stack, get_metatable_ptr, push_table, rawset_field, TypeKey};
|
||||
use crate::util::{TypeKey, check_stack, get_metatable_ptr, push_table, rawset_field};
|
||||
|
||||
// Pushes the userdata and attaches a metatable with __gc method.
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
|
||||
+19
-28
@@ -1,19 +1,18 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashSet;
|
||||
use std::os::raw::c_void;
|
||||
use std::string::String as StdString;
|
||||
use std::{fmt, ptr, str};
|
||||
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::string::{BorrowedStr, String};
|
||||
use crate::string::{BorrowedStr, LuaString};
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::types::{Integer, LightUserData, Number, ValueRef};
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::util::{check_stack, StackGuard};
|
||||
use crate::util::{StackGuard, check_stack};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use {
|
||||
@@ -50,7 +49,7 @@ pub enum Value {
|
||||
/// An interned string, managed by Lua.
|
||||
///
|
||||
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
|
||||
String(String),
|
||||
String(LuaString),
|
||||
/// Reference to a Lua table.
|
||||
Table(Table),
|
||||
/// Reference to a Lua function (or closure).
|
||||
@@ -129,7 +128,7 @@ impl Value {
|
||||
#[inline]
|
||||
pub fn to_pointer(&self) -> *const c_void {
|
||||
match self {
|
||||
Value::String(String(vref)) => {
|
||||
Value::String(LuaString(vref)) => {
|
||||
// In Lua < 5.4 (excluding Luau), string pointers are NULL
|
||||
// Use alternative approach
|
||||
let lua = vref.lua.lock();
|
||||
@@ -151,8 +150,8 @@ impl Value {
|
||||
///
|
||||
/// This might invoke the `__tostring` metamethod for non-primitive types (eg. tables,
|
||||
/// functions).
|
||||
pub fn to_string(&self) -> Result<StdString> {
|
||||
unsafe fn invoke_to_string(vref: &ValueRef) -> Result<StdString> {
|
||||
pub fn to_string(&self) -> Result<String> {
|
||||
unsafe fn invoke_tostring(vref: &ValueRef) -> Result<String> {
|
||||
let lua = vref.lua.lock();
|
||||
let state = lua.state();
|
||||
let _guard = StackGuard::new(state);
|
||||
@@ -162,7 +161,7 @@ impl Value {
|
||||
protect_lua!(state, 1, 1, fn(state) {
|
||||
ffi::luaL_tolstring(state, -1, ptr::null_mut());
|
||||
})?;
|
||||
Ok(String(lua.pop_ref()).to_str()?.to_string())
|
||||
Ok(LuaString(lua.pop_ref()).to_str()?.to_string())
|
||||
}
|
||||
|
||||
match self {
|
||||
@@ -179,9 +178,9 @@ impl Value {
|
||||
| Value::Function(Function(vref))
|
||||
| Value::Thread(Thread(vref, ..))
|
||||
| Value::UserData(AnyUserData(vref))
|
||||
| Value::Other(vref) => unsafe { invoke_to_string(vref) },
|
||||
| Value::Other(vref) => unsafe { invoke_tostring(vref) },
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Buffer(crate::Buffer(vref)) => unsafe { invoke_to_string(vref) },
|
||||
Value::Buffer(crate::Buffer(vref)) => unsafe { invoke_tostring(vref) },
|
||||
Value::Error(err) => Ok(err.to_string()),
|
||||
}
|
||||
}
|
||||
@@ -336,17 +335,17 @@ impl Value {
|
||||
self.as_number()
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a Lua [`String`].
|
||||
/// Returns `true` if the value is a [`LuaString`].
|
||||
#[inline]
|
||||
pub fn is_string(&self) -> bool {
|
||||
self.as_string().is_some()
|
||||
}
|
||||
|
||||
/// Cast the value to Lua [`String`].
|
||||
/// Cast the value to a [`LuaString`].
|
||||
///
|
||||
/// If the value is a Lua [`String`], returns it or `None` otherwise.
|
||||
/// If the value is a [`LuaString`], returns it or `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_string(&self) -> Option<&String> {
|
||||
pub fn as_string(&self) -> Option<&LuaString> {
|
||||
match self {
|
||||
Value::String(s) => Some(s),
|
||||
_ => None,
|
||||
@@ -355,26 +354,26 @@ impl Value {
|
||||
|
||||
/// Cast the value to [`BorrowedStr`].
|
||||
///
|
||||
/// If the value is a Lua [`String`], try to convert it to [`BorrowedStr`] or return `None`
|
||||
/// If the value is a [`LuaString`], try to convert it to [`BorrowedStr`] or return `None`
|
||||
/// otherwise.
|
||||
#[deprecated(
|
||||
since = "0.11.0",
|
||||
note = "This method does not follow Rust naming convention. Use `as_string().and_then(|s| s.to_str().ok())` instead."
|
||||
)]
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> Option<BorrowedStr<'_>> {
|
||||
pub fn as_str(&self) -> Option<BorrowedStr> {
|
||||
self.as_string().and_then(|s| s.to_str().ok())
|
||||
}
|
||||
|
||||
/// Cast the value to [`StdString`].
|
||||
/// Cast the value to [`String`].
|
||||
///
|
||||
/// If the value is a Lua [`String`], converts it to [`StdString`] or returns `None` otherwise.
|
||||
/// If the value is a [`LuaString`], converts it to [`String`] or returns `None` otherwise.
|
||||
#[deprecated(
|
||||
since = "0.11.0",
|
||||
note = "This method does not follow Rust naming convention. Use `as_string().map(|s| s.to_string_lossy())` instead."
|
||||
)]
|
||||
#[inline]
|
||||
pub fn as_string_lossy(&self) -> Option<StdString> {
|
||||
pub fn as_string_lossy(&self) -> Option<String> {
|
||||
self.as_string().map(|s| s.to_string_lossy())
|
||||
}
|
||||
|
||||
@@ -562,15 +561,7 @@ impl Value {
|
||||
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::UserData(ud) => ud.fmt_pretty(fmt),
|
||||
#[cfg(feature = "luau")]
|
||||
buf @ Value::Buffer(_) => write!(fmt, "buffer: {:?}", buf.to_pointer()),
|
||||
Value::Error(e) if recursive => write!(fmt, "{e:?}"),
|
||||
|
||||
+5
-6
@@ -1,6 +1,5 @@
|
||||
#![cfg(feature = "async")]
|
||||
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -8,7 +7,7 @@ use futures_util::stream::TryStreamExt;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use mlua::{
|
||||
Error, Function, Lua, LuaOptions, MultiValue, ObjectLike, Result, StdLib, Table, ThreadStatus, UserData,
|
||||
Error, Function, Lua, LuaOptions, MultiValue, ObjectLike, Result, StdLib, Table, UserData,
|
||||
UserDataMethods, UserDataRef, Value,
|
||||
};
|
||||
|
||||
@@ -40,9 +39,9 @@ async fn test_async_function() -> Result<()> {
|
||||
async fn test_async_function_wrap() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = Function::wrap_async(|s: StdString| async move {
|
||||
let f = Function::wrap_async(|s: String| async move {
|
||||
tokio::task::yield_now().await;
|
||||
Ok(s)
|
||||
Ok::<_, Error>(s)
|
||||
});
|
||||
lua.globals().set("f", f)?;
|
||||
let res: String = lua.load(r#"f("hello")"#).eval_async().await?;
|
||||
@@ -68,7 +67,7 @@ async fn test_async_function_wrap() -> Result<()> {
|
||||
async fn test_async_function_wrap_raw() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = Function::wrap_raw_async(|s: StdString| async move {
|
||||
let f = Function::wrap_raw_async(|s: String| async move {
|
||||
tokio::task::yield_now().await;
|
||||
s
|
||||
});
|
||||
@@ -715,7 +714,7 @@ fn test_async_yield_with() -> Result<()> {
|
||||
assert_eq!(thread.resume::<(i32, i32)>((10, 11))?, (21, 110));
|
||||
assert_eq!(thread.resume::<(i32, i32)>((11, 12))?, (23, 132));
|
||||
assert_eq!(thread.resume::<(i32, i32)>((12, 13))?, (0, 0));
|
||||
assert_eq!(thread.status(), ThreadStatus::Finished);
|
||||
assert!(thread.is_finished());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
#[cfg(not(target_os = "wasi"))]
|
||||
use std::{fs, io};
|
||||
|
||||
use mlua::{Chunk, ChunkMode, Lua, Result};
|
||||
@@ -85,7 +86,7 @@ fn test_chunk_macro() -> Result<()> {
|
||||
data.raw_set("num", 1)?;
|
||||
|
||||
let ud = mlua::AnyUserData::wrap("hello");
|
||||
let f = mlua::Function::wrap(|| Ok(()));
|
||||
let f = mlua::Function::wrap(|| Ok::<_, mlua::Error>(()));
|
||||
|
||||
lua.globals().set("g", 123)?;
|
||||
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
error[E0277]: the type `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
error[E0277]: the type `UnsafeCell<mlua::state::RawLua>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::RawLua>` may contain interior mutability and a reference may not be safely transferable 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::state::raw::RawLua>`
|
||||
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::RawLua>`
|
||||
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::RawLua>`
|
||||
--> $CARGO/lock_api-$VERSION/src/remutex.rs
|
||||
|
|
||||
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>`
|
||||
note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::RawLua>>>`
|
||||
--> $RUST/core/src/marker.rs
|
||||
|
|
||||
| pub struct PhantomData<T: PointeeSized>;
|
||||
| ^^^^^^^^^^^
|
||||
note: required because it appears within the type `Arc<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
note: required because it appears within the type `Arc<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct Arc<
|
||||
@@ -63,22 +63,22 @@ note: required because it appears within the type `lock_api::remutex::RawReentra
|
||||
|
|
||||
| pub struct RawReentrantMutex<R, G> {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
|
||||
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::RawLua>`
|
||||
--> $CARGO/lock_api-$VERSION/src/remutex.rs
|
||||
|
|
||||
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>`
|
||||
note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::RawLua>>>`
|
||||
--> $RUST/core/src/marker.rs
|
||||
|
|
||||
| pub struct PhantomData<T: PointeeSized>;
|
||||
| ^^^^^^^^^^^
|
||||
note: required because it appears within the type `Arc<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
note: required because it appears within the type `Arc<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct Arc<
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
error[E0277]: the type `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
error[E0277]: the type `UnsafeCell<mlua::state::RawLua>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::RawLua>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: within `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::raw::RawLua>`
|
||||
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
|
||||
= help: within `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::RawLua>`
|
||||
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::RawLua>`
|
||||
--> $CARGO/lock_api-$VERSION/src/remutex.rs
|
||||
|
|
||||
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
= note: required for `NonNull<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `std::sync::Weak<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
= note: required for `NonNull<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::RawLua>>>` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `std::sync::Weak<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct Weak<
|
||||
@@ -57,7 +57,7 @@ error[E0277]: the type `UnsafeCell<usize>` may contain interior mutability and a
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: within `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<usize>`
|
||||
= help: within `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<usize>`
|
||||
note: required because it appears within the type `Cell<usize>`
|
||||
--> $RUST/core/src/cell.rs
|
||||
|
|
||||
@@ -68,18 +68,18 @@ note: required because it appears within the type `lock_api::remutex::RawReentra
|
||||
|
|
||||
| pub struct RawReentrantMutex<R, G> {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
|
||||
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::RawLua>`
|
||||
--> $CARGO/lock_api-$VERSION/src/remutex.rs
|
||||
|
|
||||
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
= note: required for `NonNull<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `std::sync::Weak<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
= note: required for `NonNull<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::RawLua>>>` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `std::sync::Weak<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct Weak<
|
||||
|
||||
+17
-13
@@ -49,7 +49,7 @@ fn test_string_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// From stack
|
||||
let f = lua.create_function(|_, s: mlua::String| Ok(s))?;
|
||||
let f = lua.create_function(|_, s: mlua::LuaString| Ok(s))?;
|
||||
let s = f.call::<String>("hello, world!")?;
|
||||
assert_eq!(s, "hello, world!");
|
||||
|
||||
@@ -708,9 +708,10 @@ fn test_either_from_lua() -> Result<()> {
|
||||
},
|
||||
err => panic!("expected `Error::BadArgument`, got {err:?}"),
|
||||
}
|
||||
assert!(err
|
||||
.to_string()
|
||||
.starts_with("bad argument #1: error converting Lua string to Either<i32, Table>"),);
|
||||
assert!(
|
||||
err.to_string()
|
||||
.starts_with("bad argument #1: error converting Lua string to Either<i32, Table>"),
|
||||
);
|
||||
}
|
||||
err => panic!("expected `Error::CallbackError`, got {err:?}"),
|
||||
}
|
||||
@@ -736,15 +737,18 @@ fn test_char_from_lua() -> Result<()> {
|
||||
assert_eq!(lua.convert::<char>("A")?, 'A');
|
||||
assert_eq!(lua.convert::<char>(65)?, 'A');
|
||||
assert_eq!(lua.convert::<char>(128175)?, '💯');
|
||||
assert!(lua
|
||||
.convert::<char>(5456324)
|
||||
.is_err_and(|e| e.to_string().contains("integer out of range")));
|
||||
assert!(lua
|
||||
.convert::<char>("hello")
|
||||
.is_err_and(|e| e.to_string().contains("expected string to have exactly one char")));
|
||||
assert!(lua
|
||||
.convert::<char>(HashMap::<String, String>::new())
|
||||
.is_err_and(|e| e.to_string().contains("expected string or integer")));
|
||||
assert!(
|
||||
lua.convert::<char>(5456324)
|
||||
.is_err_and(|e| e.to_string().contains("integer out of range"))
|
||||
);
|
||||
assert!(
|
||||
lua.convert::<char>("hello")
|
||||
.is_err_and(|e| e.to_string().contains("expected string to have exactly one char"))
|
||||
);
|
||||
assert!(
|
||||
lua.convert::<char>(HashMap::<String, String>::new())
|
||||
.is_err_and(|e| e.to_string().contains("expected string or integer"))
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -77,6 +77,19 @@ fn test_error_chain() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_external_error() {
|
||||
// `Error::external` should preserve `mlua::Error`
|
||||
let runtime_err = Error::runtime("test error");
|
||||
let converted = Error::external(runtime_err);
|
||||
assert!(matches!(converted, Error::RuntimeError(ref msg) if msg == "test error"));
|
||||
|
||||
// Other errors should become `ExternalError`
|
||||
let converted = Error::external(io::Error::other("other error"));
|
||||
assert!(matches!(converted, Error::ExternalError(_)));
|
||||
assert!(converted.downcast_ref::<io::Error>().is_some());
|
||||
}
|
||||
|
||||
#[cfg(feature = "anyhow")]
|
||||
#[test]
|
||||
fn test_error_anyhow() -> Result<()> {
|
||||
|
||||
+40
-8
@@ -1,4 +1,7 @@
|
||||
use mlua::{Error, Function, Lua, Result, String, Table, Variadic};
|
||||
use std::fmt;
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
use mlua::{Error, Function, Lua, LuaString, Result, Table, Variadic};
|
||||
|
||||
#[test]
|
||||
fn test_function_call() -> Result<()> {
|
||||
@@ -267,7 +270,7 @@ fn test_function_coverage() -> Result<()> {
|
||||
|
||||
assert_eq!(
|
||||
report[0],
|
||||
mlua::CoverageInfo {
|
||||
mlua::function::CoverageInfo {
|
||||
function: None,
|
||||
line_defined: 1,
|
||||
depth: 0,
|
||||
@@ -276,7 +279,7 @@ fn test_function_coverage() -> Result<()> {
|
||||
);
|
||||
assert_eq!(
|
||||
report[1],
|
||||
mlua::CoverageInfo {
|
||||
mlua::function::CoverageInfo {
|
||||
function: Some("abc".into()),
|
||||
line_defined: 4,
|
||||
depth: 1,
|
||||
@@ -285,7 +288,7 @@ fn test_function_coverage() -> Result<()> {
|
||||
);
|
||||
assert_eq!(
|
||||
report[2],
|
||||
mlua::CoverageInfo {
|
||||
mlua::function::CoverageInfo {
|
||||
function: None,
|
||||
line_defined: 12,
|
||||
depth: 1,
|
||||
@@ -294,7 +297,7 @@ fn test_function_coverage() -> Result<()> {
|
||||
);
|
||||
assert_eq!(
|
||||
report[3],
|
||||
mlua::CoverageInfo {
|
||||
mlua::function::CoverageInfo {
|
||||
function: None,
|
||||
line_defined: 13,
|
||||
depth: 2,
|
||||
@@ -343,7 +346,7 @@ fn test_function_deep_clone() -> Result<()> {
|
||||
fn test_function_wrap() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = Function::wrap(|s: String, n| Ok(s.to_str().unwrap().repeat(n)));
|
||||
let f = Function::wrap(|s: LuaString, n| Ok::<_, Error>(s.to_str().unwrap().repeat(n)));
|
||||
lua.globals().set("f", f)?;
|
||||
lua.load(r#"assert(f("hello", 2) == "hellohello")"#)
|
||||
.exec()
|
||||
@@ -361,11 +364,40 @@ fn test_function_wrap() -> Result<()> {
|
||||
.exec()
|
||||
.unwrap();
|
||||
|
||||
// Return external error
|
||||
#[derive(Debug)]
|
||||
struct MyError(String);
|
||||
impl fmt::Display for MyError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "MyError: {}", self.0)
|
||||
}
|
||||
}
|
||||
impl std::error::Error for MyError {}
|
||||
|
||||
let fext = Function::wrap(|s: String| -> StdResult<String, MyError> {
|
||||
if s == "bad" {
|
||||
return Err(MyError("bad input".into()));
|
||||
}
|
||||
Ok(format!("ok: {s}"))
|
||||
});
|
||||
lua.globals().set("fext", fext)?;
|
||||
lua.load(r#"assert(fext("hello") == "ok: hello")"#)
|
||||
.exec()
|
||||
.unwrap();
|
||||
lua.load(
|
||||
r#"
|
||||
local ok, err = pcall(fext, "bad")
|
||||
assert(not ok and tostring(err):find("MyError: bad input"))
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
.unwrap();
|
||||
|
||||
// Mutable callback
|
||||
let mut i = 0;
|
||||
let fmut = Function::wrap_mut(move || {
|
||||
i += 1;
|
||||
Ok(i)
|
||||
Ok::<_, Error>(i)
|
||||
});
|
||||
lua.globals().set("fmut", fmut)?;
|
||||
lua.load(r#"fmut(); fmut(); assert(fmut() == 3)"#).exec().unwrap();
|
||||
@@ -385,7 +417,7 @@ fn test_function_wrap() -> Result<()> {
|
||||
// Check recursive mut callback error
|
||||
let fmut = Function::wrap_mut(|f: Function| match f.call::<()>(&f) {
|
||||
Err(Error::CallbackError { cause, .. }) => match cause.as_ref() {
|
||||
Error::RecursiveMutCallback { .. } => Ok(()),
|
||||
Error::RecursiveMutCallback { .. } => Ok::<_, Error>(()),
|
||||
other => panic!("incorrect result: {other:?}"),
|
||||
},
|
||||
other => panic!("incorrect result: {other:?}"),
|
||||
|
||||
+5
-4
@@ -3,7 +3,8 @@
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use mlua::{DebugEvent, Error, HookTriggers, Lua, Result, ThreadStatus, Value, VmState};
|
||||
use mlua::debug::DebugEvent;
|
||||
use mlua::{Error, HookTriggers, Lua, Result, Value, VmState};
|
||||
|
||||
#[test]
|
||||
fn test_hook_triggers() {
|
||||
@@ -280,14 +281,14 @@ fn test_hook_yield() -> Result<()> {
|
||||
assert!(co.resume::<()>(()).is_ok());
|
||||
assert!(co.resume::<()>(()).is_ok());
|
||||
assert!(co.resume::<()>(()).is_ok());
|
||||
assert!(co.status() == ThreadStatus::Finished);
|
||||
assert!(co.is_finished());
|
||||
}
|
||||
#[cfg(any(feature = "lua51", feature = "lua52", feature = "luajit"))]
|
||||
{
|
||||
assert!(
|
||||
matches!(co.resume::<()>(()), Err(Error::RuntimeError(err)) if err.contains("attempt to yield from a hook"))
|
||||
);
|
||||
assert!(co.status() == ThreadStatus::Error);
|
||||
assert!(co.is_error());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -320,7 +321,7 @@ fn test_global_hook() -> Result<()> {
|
||||
thread.resume::<()>(()).unwrap();
|
||||
lua.remove_global_hook();
|
||||
thread.resume::<()>(()).unwrap();
|
||||
assert_eq!(thread.status(), ThreadStatus::Finished);
|
||||
assert!(thread.is_finished());
|
||||
assert_eq!(counter.load(Ordering::Relaxed), 3);
|
||||
|
||||
Ok(())
|
||||
|
||||
+22
-4
@@ -3,11 +3,11 @@
|
||||
use std::cell::Cell;
|
||||
use std::fmt::Debug;
|
||||
use std::os::raw::c_void;
|
||||
use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicU64, Ordering};
|
||||
|
||||
use mlua::{
|
||||
Compiler, Error, Function, Lua, LuaOptions, Result, StdLib, Table, ThreadStatus, Value, Vector, VmState,
|
||||
Compiler, Error, Function, Lua, LuaOptions, ObjectLike, Result, StdLib, Table, Value, Vector, VmState,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -324,11 +324,11 @@ fn test_interrupts() -> Result<()> {
|
||||
.into_function()?,
|
||||
)?;
|
||||
co.resume::<()>(())?;
|
||||
assert_eq!(co.status(), ThreadStatus::Resumable);
|
||||
assert!(co.is_resumable());
|
||||
let result: i32 = co.resume(())?;
|
||||
assert_eq!(result, 6);
|
||||
assert_eq!(yield_count.load(Ordering::Relaxed), 7);
|
||||
assert_eq!(co.status(), ThreadStatus::Finished);
|
||||
assert!(co.is_finished());
|
||||
|
||||
// Test no yielding at non-yieldable points
|
||||
yield_count.store(0, Ordering::Relaxed);
|
||||
@@ -535,5 +535,23 @@ fn test_heap_dump() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_integer64_type() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
_ = Lua::set_fflag("LuauIntegerType", true);
|
||||
|
||||
let integer_lib = lua.globals().get::<Table>("integer")?;
|
||||
let n = integer_lib.call_function::<i64>("create", 42)?;
|
||||
assert_eq!(n, 42);
|
||||
|
||||
let n: i64 = lua.load("return 42i").eval()?;
|
||||
assert_eq!(n, 42);
|
||||
let n: i64 = lua.load("return -42i").eval()?;
|
||||
assert_eq!(n, -42);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[path = "luau/require.rs"]
|
||||
mod require;
|
||||
|
||||
+16
-11
@@ -1,7 +1,8 @@
|
||||
use std::io::Result as IoResult;
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
use mlua::{Error, FromLua, IntoLua, Lua, MultiValue, NavigateError, Require, Result, TextRequirer, Value};
|
||||
use mlua::luau::{FsRequirer, NavigateError, Require};
|
||||
use mlua::{Error, FromLua, IntoLua, Lua, MultiValue, Result, Value};
|
||||
|
||||
fn run_require(lua: &Lua, path: impl IntoLua) -> Result<Value> {
|
||||
lua.load(r#"return require(...)"#).call(path)
|
||||
@@ -42,8 +43,10 @@ fn test_require_errors() {
|
||||
// Pass non-string to require
|
||||
let res = run_require(&lua, true);
|
||||
assert!(res.is_err());
|
||||
assert!((res.unwrap_err().to_string())
|
||||
.contains("bad argument #1 to 'require' (string expected, got boolean)"));
|
||||
assert!(
|
||||
(res.unwrap_err().to_string())
|
||||
.contains("bad argument #1 to 'require' (string expected, got boolean)")
|
||||
);
|
||||
|
||||
// Require from loadstring
|
||||
let res = lua
|
||||
@@ -63,7 +66,7 @@ fn test_require_errors() {
|
||||
assert!((res.unwrap_err().to_string()).contains("@ is not a valid alias"));
|
||||
|
||||
// Test throwing mlua::Error
|
||||
struct MyRequire(TextRequirer);
|
||||
struct MyRequire(FsRequirer);
|
||||
|
||||
impl Require for MyRequire {
|
||||
fn is_require_allowed(&self, chunk_name: &str) -> bool {
|
||||
@@ -107,9 +110,7 @@ fn test_require_errors() {
|
||||
}
|
||||
}
|
||||
|
||||
let require = lua
|
||||
.create_require_function(MyRequire(TextRequirer::new()))
|
||||
.unwrap();
|
||||
let require = lua.create_require_function(MyRequire(FsRequirer::new())).unwrap();
|
||||
lua.globals().set("require", require).unwrap();
|
||||
let res = lua.load(r#"return require('./a/relative/path')"#).exec();
|
||||
assert!((res.unwrap_err().to_string()).contains("test error"));
|
||||
@@ -169,8 +170,10 @@ fn test_require_without_config() {
|
||||
"./tests/luau/require/without_config/ambiguous_file_requirer",
|
||||
);
|
||||
assert!(res.is_err());
|
||||
assert!((res.unwrap_err().to_string())
|
||||
.contains("could not resolve child component \"dependency\" (ambiguous)"));
|
||||
assert!(
|
||||
(res.unwrap_err().to_string())
|
||||
.contains("could not resolve child component \"dependency\" (ambiguous)")
|
||||
);
|
||||
|
||||
// RequireWithDirectoryAmbiguity
|
||||
let res = run_require(
|
||||
@@ -178,8 +181,10 @@ fn test_require_without_config() {
|
||||
"./tests/luau/require/without_config/ambiguous_directory_requirer",
|
||||
);
|
||||
assert!(res.is_err());
|
||||
assert!((res.unwrap_err().to_string())
|
||||
.contains("could not resolve child component \"dependency\" (ambiguous)"));
|
||||
assert!(
|
||||
(res.unwrap_err().to_string())
|
||||
.contains("could not resolve child component \"dependency\" (ambiguous)")
|
||||
);
|
||||
|
||||
// CheckCachedResult
|
||||
let res = run_require(&lua, "./tests/luau/require/without_config/validate_cache").unwrap();
|
||||
|
||||
+24
-4
@@ -1,6 +1,10 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use mlua::{Error, GCMode, Lua, Result, UserData};
|
||||
use mlua::state::{GcIncParams, GcMode};
|
||||
use mlua::{Error, Lua, Result, UserData};
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua55"))]
|
||||
use mlua::state::GcGenParams;
|
||||
|
||||
#[test]
|
||||
fn test_memory_limit() -> Result<()> {
|
||||
@@ -74,8 +78,14 @@ fn test_gc_control() -> Result<()> {
|
||||
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
{
|
||||
assert_eq!(lua.gc_gen(0, 0), GCMode::Incremental);
|
||||
assert_eq!(lua.gc_inc(0, 0, 0), GCMode::Generational);
|
||||
assert!(matches!(
|
||||
lua.gc_set_mode(GcMode::Generational(GcGenParams::default())),
|
||||
GcMode::Incremental(_)
|
||||
));
|
||||
assert!(matches!(
|
||||
lua.gc_set_mode(GcMode::Incremental(GcIncParams::default())),
|
||||
GcMode::Generational(_)
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
@@ -93,7 +103,17 @@ fn test_gc_control() -> Result<()> {
|
||||
assert!(lua.gc_is_running());
|
||||
}
|
||||
|
||||
assert_eq!(lua.gc_inc(200, 100, 13), GCMode::Incremental);
|
||||
assert!(matches!(
|
||||
lua.gc_set_mode(GcMode::Incremental({
|
||||
let p = GcIncParams::default().step_multiplier(100);
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let p = p.pause(200);
|
||||
#[cfg(feature = "luau")]
|
||||
let p = p.goal(200);
|
||||
p
|
||||
})),
|
||||
GcMode::Incremental(_)
|
||||
));
|
||||
|
||||
struct MyUserdata(#[allow(unused)] Arc<()>);
|
||||
impl UserData for MyUserdata {}
|
||||
|
||||
+4
-2
@@ -1,4 +1,6 @@
|
||||
use mlua::{Error, ExternalError, Integer, IntoLuaMulti, Lua, MultiValue, Result, String, Value, Variadic};
|
||||
use mlua::{
|
||||
Error, ExternalError, Integer, IntoLuaMulti, Lua, LuaString, MultiValue, Result, Value, Variadic,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_result_conversions() -> Result<()> {
|
||||
@@ -81,7 +83,7 @@ fn test_multivalue_by_ref() -> Result<()> {
|
||||
Value::Boolean(true),
|
||||
]);
|
||||
|
||||
let f = lua.create_function(|_, (i, s, b): (i32, String, bool)| {
|
||||
let f = lua.create_function(|_, (i, s, b): (i32, LuaString, bool)| {
|
||||
assert_eq!(i, 3);
|
||||
assert_eq!(s.to_str()?, "hello");
|
||||
assert_eq!(b, true);
|
||||
|
||||
+7
-8
@@ -1,10 +1,9 @@
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
|
||||
use mlua::{
|
||||
AnyUserData, Error, Function, Lua, MetaMethod, ObjectLike, Result, String, UserData, UserDataFields,
|
||||
AnyUserData, Error, Function, Lua, LuaString, MetaMethod, ObjectLike, Result, UserData, UserDataFields,
|
||||
UserDataMethods, UserDataRegistry,
|
||||
};
|
||||
|
||||
@@ -437,15 +436,15 @@ fn test_scope_userdata_ref_mut() -> Result<()> {
|
||||
fn test_scope_any_userdata() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
fn register(reg: &mut UserDataRegistry<&mut StdString>) {
|
||||
reg.add_method_mut("push", |_, this, s: String| {
|
||||
fn register(reg: &mut UserDataRegistry<&mut String>) {
|
||||
reg.add_method_mut("push", |_, this, s: LuaString| {
|
||||
this.push_str(&s.to_str()?);
|
||||
Ok(())
|
||||
});
|
||||
reg.add_meta_method("__tostring", |_, data, ()| Ok((*data).clone()));
|
||||
}
|
||||
|
||||
let mut data = StdString::from("foo");
|
||||
let mut data = String::from("foo");
|
||||
lua.scope(|scope| {
|
||||
let ud = scope.create_any_userdata(&mut data, register)?;
|
||||
lua.globals().set("ud", ud)?;
|
||||
@@ -527,11 +526,11 @@ fn test_scope_any_userdata_ref_mut() -> Result<()> {
|
||||
fn test_scope_destructors() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.register_userdata_type::<Arc<StdString>>(|reg| {
|
||||
lua.register_userdata_type::<Arc<String>>(|reg| {
|
||||
reg.add_meta_method("__tostring", |_, data, ()| Ok(data.to_string()));
|
||||
})?;
|
||||
|
||||
let arc_str = Arc::new(StdString::from("foo"));
|
||||
let arc_str = Arc::new(String::from("foo"));
|
||||
|
||||
let ud = lua.create_any_userdata(arc_str.clone())?;
|
||||
lua.scope(|scope| {
|
||||
@@ -544,7 +543,7 @@ fn test_scope_destructors() -> Result<()> {
|
||||
|
||||
// Try destructing the userdata while it's borrowed
|
||||
let ud = lua.create_any_userdata(arc_str.clone())?;
|
||||
ud.borrow_scoped::<Arc<StdString>, _>(|arc_str| {
|
||||
ud.borrow_scoped::<Arc<String>, _>(|arc_str| {
|
||||
assert_eq!(arc_str.as_str(), "foo");
|
||||
lua.scope(|scope| {
|
||||
scope.add_destructor(|| {
|
||||
|
||||
+5
-51
@@ -1,58 +1,14 @@
|
||||
#![cfg(feature = "send")]
|
||||
|
||||
use std::cell::UnsafeCell;
|
||||
use std::marker::PhantomData;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use mlua::{AnyUserData, Error, Lua, ObjectLike, Result, UserData, UserDataMethods, UserDataRef};
|
||||
use static_assertions::{assert_impl_all, assert_not_impl_all};
|
||||
|
||||
#[test]
|
||||
fn test_userdata_multithread_access_send_only() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// This type is `Send` but not `Sync`.
|
||||
struct MyUserData(StdString, PhantomData<UnsafeCell<()>>);
|
||||
assert_impl_all!(MyUserData: Send);
|
||||
assert_not_impl_all!(MyUserData: Sync);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("method", |lua, this, ()| {
|
||||
let ud = lua.globals().get::<AnyUserData>("ud")?;
|
||||
assert_eq!(ud.call_method::<String>("method2", ())?, "method2");
|
||||
Ok(this.0.clone())
|
||||
});
|
||||
|
||||
methods.add_method("method2", |_, _, ()| Ok("method2"));
|
||||
}
|
||||
}
|
||||
|
||||
lua.globals()
|
||||
.set("ud", MyUserData("hello".to_string(), PhantomData))?;
|
||||
|
||||
// We acquired the exclusive reference.
|
||||
let ud = lua.globals().get::<UserDataRef<MyUserData>>("ud")?;
|
||||
|
||||
std::thread::scope(|s| {
|
||||
s.spawn(|| {
|
||||
let res = lua.globals().get::<UserDataRef<MyUserData>>("ud");
|
||||
assert!(matches!(res, Err(Error::UserDataBorrowError)));
|
||||
});
|
||||
});
|
||||
|
||||
drop(ud);
|
||||
lua.load("ud:method()").exec().unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
use mlua::{AnyUserData, Lua, ObjectLike, Result, UserData, UserDataMethods, UserDataRef};
|
||||
use static_assertions::assert_impl_all;
|
||||
|
||||
#[test]
|
||||
fn test_userdata_multithread_access_sync() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// This type is `Send` and `Sync`.
|
||||
struct MyUserData(StdString);
|
||||
struct MyUserData(String);
|
||||
assert_impl_all!(MyUserData: Send, Sync);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
@@ -75,13 +31,11 @@ fn test_userdata_multithread_access_sync() -> Result<()> {
|
||||
std::thread::scope(|s| {
|
||||
s.spawn(|| {
|
||||
// Getting another shared reference for `Sync` type is allowed.
|
||||
// FIXME: does not work due to https://github.com/rust-lang/rust/pull/135634
|
||||
// let _ = lua.globals().get::<UserDataRef<MyUserData>>("ud").unwrap();
|
||||
let _ = lua.globals().get::<UserDataRef<MyUserData>>("ud").unwrap();
|
||||
});
|
||||
});
|
||||
|
||||
// FIXME: does not work due to https://github.com/rust-lang/rust/pull/135634
|
||||
// lua.load("ud:method()").exec().unwrap();
|
||||
lua.load("ud:method()").exec().unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+10
-10
@@ -1,11 +1,11 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use mlua::{Lua, Result, String};
|
||||
use mlua::{Lua, LuaString, Result};
|
||||
|
||||
#[test]
|
||||
fn test_string_compare() {
|
||||
fn with_str<F: FnOnce(String)>(s: &str, f: F) {
|
||||
fn with_str<F: FnOnce(LuaString)>(s: &str, f: F) {
|
||||
f(Lua::new().create_string(s).unwrap());
|
||||
}
|
||||
|
||||
@@ -42,9 +42,9 @@ fn test_string_views() -> Result<()> {
|
||||
.exec()?;
|
||||
|
||||
let globals = lua.globals();
|
||||
let ok: String = globals.get("ok")?;
|
||||
let err: String = globals.get("err")?;
|
||||
let empty: String = globals.get("empty")?;
|
||||
let ok: LuaString = globals.get("ok")?;
|
||||
let err: LuaString = globals.get("err")?;
|
||||
let empty: LuaString = globals.get("empty")?;
|
||||
|
||||
assert_eq!(ok.to_str()?, "null bytes are valid utf-8, wh\0 knew?");
|
||||
assert_eq!(ok.to_string_lossy(), "null bytes are valid utf-8, wh\0 knew?");
|
||||
@@ -74,7 +74,7 @@ fn test_string_from_bytes() -> Result<()> {
|
||||
fn test_string_hash() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let set: HashSet<String> = lua.load(r#"{"hello", "world", "abc", 321}"#).eval()?;
|
||||
let set: HashSet<LuaString> = lua.load(r#"{"hello", "world", "abc", 321}"#).eval()?;
|
||||
assert_eq!(set.len(), 4);
|
||||
assert!(set.contains(&lua.create_string("hello")?));
|
||||
assert!(set.contains(&lua.create_string("world")?));
|
||||
@@ -133,13 +133,13 @@ fn test_string_display() -> Result<()> {
|
||||
fn test_string_wrap() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let s = String::wrap("hello, world");
|
||||
let s = LuaString::wrap("hello, world");
|
||||
lua.globals().set("s", s)?;
|
||||
assert_eq!(lua.globals().get::<String>("s")?, "hello, world");
|
||||
assert_eq!(lua.globals().get::<LuaString>("s")?, "hello, world");
|
||||
|
||||
let s2 = String::wrap("hello, world (owned)".to_string());
|
||||
let s2 = LuaString::wrap("hello, world (owned)".to_string());
|
||||
lua.globals().set("s2", s2)?;
|
||||
assert_eq!(lua.globals().get::<String>("s2")?, "hello, world (owned)");
|
||||
assert_eq!(lua.globals().get::<LuaString>("s2")?, "hello, world (owned)");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+31
-26
@@ -1,14 +1,13 @@
|
||||
use std::collections::HashMap;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use std::iter::FromIterator;
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
use std::string::String as StdString;
|
||||
use std::panic::{AssertUnwindSafe, catch_unwind};
|
||||
use std::sync::Arc;
|
||||
use std::{error, f32, f64, fmt};
|
||||
|
||||
use mlua::{
|
||||
ffi, ChunkMode, Error, ExternalError, Function, Lua, LuaOptions, Nil, Result, StdLib, String, Table,
|
||||
UserData, Value, Variadic,
|
||||
ChunkMode, Error, ExternalError, Function, Lua, LuaOptions, Nil, Result, StdLib, Table, UserData, Value,
|
||||
Variadic, ffi,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -155,7 +154,7 @@ fn test_replace_globals() -> Result<()> {
|
||||
globals.set("foo", "bar")?;
|
||||
|
||||
lua.set_globals(globals.clone())?;
|
||||
let val = lua.load("return foo").eval::<StdString>()?;
|
||||
let val = lua.load("return foo").eval::<String>()?;
|
||||
assert_eq!(val, "bar");
|
||||
|
||||
// Updating globals in sandboxed Lua state is not allowed
|
||||
@@ -398,7 +397,7 @@ fn test_error() -> Result<()> {
|
||||
fn test_panic() -> Result<()> {
|
||||
fn make_lua(options: LuaOptions) -> Result<Lua> {
|
||||
let lua = Lua::new_with(StdLib::ALL_SAFE, options)?;
|
||||
let rust_panic_function = lua.create_function(|_, msg: Option<StdString>| -> Result<()> {
|
||||
let rust_panic_function = lua.create_function(|_, msg: Option<String>| -> Result<()> {
|
||||
if let Some(msg) = msg {
|
||||
panic!("{}", msg)
|
||||
}
|
||||
@@ -496,7 +495,7 @@ fn test_panic() -> Result<()> {
|
||||
.exec()
|
||||
}) {
|
||||
Ok(r) => panic!("no panic was detected: {:?}", r),
|
||||
Err(p) => assert!(*p.downcast::<StdString>().unwrap() == "rust panic from lua"),
|
||||
Err(p) => assert!(*p.downcast::<String>().unwrap() == "rust panic from lua"),
|
||||
}
|
||||
|
||||
// Test disabling `catch_rust_panics` option / xpcall correctness
|
||||
@@ -520,7 +519,7 @@ fn test_panic() -> Result<()> {
|
||||
.exec()
|
||||
}) {
|
||||
Ok(r) => panic!("no panic was detected: {:?}", r),
|
||||
Err(p) => assert!(*p.downcast::<StdString>().unwrap() == "rust panic from lua"),
|
||||
Err(p) => assert!(*p.downcast::<String>().unwrap() == "rust panic from lua"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -684,10 +683,12 @@ fn test_pcall_xpcall() -> Result<()> {
|
||||
))]
|
||||
assert_eq!(globals.get::<std::string::String>("xpcall_error")?, "testerror");
|
||||
#[cfg(feature = "lua51")]
|
||||
assert!(globals
|
||||
.get::<String>("xpcall_error")?
|
||||
.to_str()?
|
||||
.ends_with(": testerror"));
|
||||
assert!(
|
||||
globals
|
||||
.get::<mlua::LuaString>("xpcall_error")?
|
||||
.to_str()?
|
||||
.ends_with(": testerror")
|
||||
);
|
||||
|
||||
// Make sure that weird xpcall error recursion at least doesn't cause unsafety or panics.
|
||||
lua.load(
|
||||
@@ -1070,10 +1071,11 @@ fn test_ref_stack_exhaustion() {
|
||||
Ok(())
|
||||
})) {
|
||||
Ok(_) => panic!("no panic was detected"),
|
||||
Err(p) => assert!(p
|
||||
.downcast::<StdString>()
|
||||
.unwrap()
|
||||
.starts_with("cannot create a Lua reference, out of auxiliary stack space")),
|
||||
Err(p) => assert!(
|
||||
p.downcast::<String>()
|
||||
.unwrap()
|
||||
.starts_with("cannot create a Lua reference, out of auxiliary stack space")
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1218,7 +1220,11 @@ fn test_context_thread_51() -> Result<()> {
|
||||
fn test_jit_version() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let jit: Table = lua.globals().get("jit")?;
|
||||
assert!(jit.get::<String>("version")?.to_str()?.contains("LuaJIT"));
|
||||
assert!(
|
||||
jit.get::<mlua::LuaString>("version")?
|
||||
.to_str()?
|
||||
.contains("LuaJIT")
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1318,7 +1324,7 @@ fn test_inspect_stack() -> Result<()> {
|
||||
// Not inside any function
|
||||
assert!(lua.inspect_stack(0, |_| ()).is_none());
|
||||
|
||||
let logline = lua.create_function(|lua, msg: StdString| {
|
||||
let logline = lua.create_function(|lua, msg: String| {
|
||||
let r = lua
|
||||
.inspect_stack(1, |debug| {
|
||||
let source = debug.source().short_src;
|
||||
@@ -1368,7 +1374,7 @@ fn test_inspect_stack() -> Result<()> {
|
||||
local function baz(a, b, c, ...)
|
||||
return stack_info()
|
||||
end
|
||||
assert(baz() == 'DebugStack { num_ups: 1, num_params: 3, is_vararg: true }')
|
||||
assert(baz() == 'DebugStack { num_upvalues: 1, num_params: 3, is_vararg: true }')
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
@@ -1381,7 +1387,7 @@ fn test_inspect_stack() -> Result<()> {
|
||||
local function baz(a, b, c, ...)
|
||||
return stack_info()
|
||||
end
|
||||
assert(baz() == 'DebugStack { num_ups: 1 }')
|
||||
assert(baz() == 'DebugStack { num_upvalues: 1 }')
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
@@ -1422,9 +1428,8 @@ fn test_traceback() -> Result<()> {
|
||||
assert!(traceback.contains("stack traceback:"));
|
||||
|
||||
// Test traceback inside a function
|
||||
let get_traceback = lua.create_function(|lua, (msg, level): (Option<StdString>, usize)| {
|
||||
lua.traceback(msg.as_deref(), level)
|
||||
})?;
|
||||
let get_traceback = lua
|
||||
.create_function(|lua, (msg, level): (Option<String>, usize)| lua.traceback(msg.as_deref(), level))?;
|
||||
lua.globals().set("get_traceback", get_traceback)?;
|
||||
|
||||
lua.load(
|
||||
@@ -1504,10 +1509,10 @@ fn test_multi_states() -> Result<()> {
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
fn test_warnings() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
lua.set_app_data::<Vec<(StdString, bool)>>(Vec::new());
|
||||
lua.set_app_data::<Vec<(String, bool)>>(Vec::new());
|
||||
|
||||
lua.set_warning_function(|lua, msg, incomplete| {
|
||||
lua.app_data_mut::<Vec<(StdString, bool)>>()
|
||||
lua.app_data_mut::<Vec<(String, bool)>>()
|
||||
.unwrap()
|
||||
.push((msg.to_string(), incomplete));
|
||||
Ok(())
|
||||
@@ -1521,7 +1526,7 @@ fn test_warnings() -> Result<()> {
|
||||
lua.remove_warning_function();
|
||||
lua.warning("one more warning", false);
|
||||
|
||||
let messages = lua.app_data_ref::<Vec<(StdString, bool)>>().unwrap();
|
||||
let messages = lua.app_data_ref::<Vec<(String, bool)>>().unwrap();
|
||||
assert_eq!(
|
||||
*messages,
|
||||
vec![
|
||||
|
||||
+18
-18
@@ -1,6 +1,6 @@
|
||||
use std::panic::catch_unwind;
|
||||
|
||||
use mlua::{Error, Function, IntoLua, Lua, Result, Thread, ThreadStatus, Value};
|
||||
use mlua::{Error, Function, IntoLua, Lua, Result, Thread, Value};
|
||||
|
||||
#[test]
|
||||
fn test_thread() -> Result<()> {
|
||||
@@ -21,17 +21,17 @@ fn test_thread() -> Result<()> {
|
||||
.eval()?,
|
||||
)?;
|
||||
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
assert!(thread.is_resumable());
|
||||
assert_eq!(thread.resume::<i64>(0)?, 0);
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
assert!(thread.is_resumable());
|
||||
assert_eq!(thread.resume::<i64>(1)?, 1);
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
assert!(thread.is_resumable());
|
||||
assert_eq!(thread.resume::<i64>(2)?, 3);
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
assert!(thread.is_resumable());
|
||||
assert_eq!(thread.resume::<i64>(3)?, 6);
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
assert!(thread.is_resumable());
|
||||
assert_eq!(thread.resume::<i64>(4)?, 10);
|
||||
assert_eq!(thread.status(), ThreadStatus::Finished);
|
||||
assert!(thread.is_finished());
|
||||
|
||||
let accumulate = lua.create_thread(
|
||||
lua.load(
|
||||
@@ -50,9 +50,9 @@ fn test_thread() -> Result<()> {
|
||||
accumulate.resume::<()>(i)?;
|
||||
}
|
||||
assert_eq!(accumulate.resume::<i64>(4)?, 10);
|
||||
assert_eq!(accumulate.status(), ThreadStatus::Resumable);
|
||||
assert!(accumulate.is_resumable());
|
||||
assert!(accumulate.resume::<()>("error").is_err());
|
||||
assert_eq!(accumulate.status(), ThreadStatus::Error);
|
||||
assert!(accumulate.is_error());
|
||||
|
||||
let thread = lua
|
||||
.load(
|
||||
@@ -65,7 +65,7 @@ fn test_thread() -> Result<()> {
|
||||
"#,
|
||||
)
|
||||
.eval::<Thread>()?;
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
assert!(thread.is_resumable());
|
||||
assert_eq!(thread.resume::<i64>(())?, 42);
|
||||
|
||||
let thread: Thread = lua
|
||||
@@ -92,7 +92,7 @@ fn test_thread() -> Result<()> {
|
||||
|
||||
// Already running thread must be unresumable
|
||||
let thread = lua.create_thread(lua.create_function(|lua, ()| {
|
||||
assert_eq!(lua.current_thread().status(), ThreadStatus::Running);
|
||||
assert!(lua.current_thread().is_running());
|
||||
let result = lua.current_thread().resume::<()>(());
|
||||
assert!(
|
||||
matches!(result, Err(Error::CoroutineUnresumable)),
|
||||
@@ -123,12 +123,12 @@ fn test_thread_reset() -> Result<()> {
|
||||
assert!(thread.reset(func.clone()).is_ok());
|
||||
|
||||
for _ in 0..2 {
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
assert!(thread.is_resumable());
|
||||
let _ = thread.resume::<AnyUserData>(MyUserData(arc.clone()))?;
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
assert!(thread.is_resumable());
|
||||
assert_eq!(Arc::strong_count(&arc), 2);
|
||||
thread.resume::<()>(())?;
|
||||
assert_eq!(thread.status(), ThreadStatus::Finished);
|
||||
assert!(thread.is_finished());
|
||||
thread.reset(func.clone())?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&arc), 1);
|
||||
@@ -138,21 +138,21 @@ fn test_thread_reset() -> Result<()> {
|
||||
let func: Function = lua.load(r#"function(ud) error("test error") end"#).eval()?;
|
||||
let thread = lua.create_thread(func.clone())?;
|
||||
let _ = thread.resume::<AnyUserData>(MyUserData(arc.clone()));
|
||||
assert_eq!(thread.status(), ThreadStatus::Error);
|
||||
assert!(thread.is_error());
|
||||
assert_eq!(Arc::strong_count(&arc), 2);
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
{
|
||||
assert!(thread.reset(func.clone()).is_err());
|
||||
// Reset behavior has changed in Lua v5.4.4
|
||||
// It's became possible to force reset thread by popping error object
|
||||
assert!(matches!(thread.status(), ThreadStatus::Finished));
|
||||
assert!(thread.is_finished());
|
||||
assert!(thread.reset(func.clone()).is_ok());
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
assert!(thread.is_resumable());
|
||||
}
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "luau"))]
|
||||
{
|
||||
assert!(thread.reset(func.clone()).is_ok());
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
assert!(thread.is_resumable());
|
||||
}
|
||||
|
||||
// Try reset running thread
|
||||
|
||||
+10
-7
@@ -1,6 +1,6 @@
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use mlua::{Function, LightUserData, Lua, Number, Result, String as LuaString, Thread};
|
||||
use mlua::{Error, Function, LightUserData, Lua, LuaString, Number, Result, Thread};
|
||||
|
||||
#[test]
|
||||
fn test_lightuserdata() -> Result<()> {
|
||||
@@ -30,7 +30,7 @@ fn test_boolean_type_metatable() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let mt = lua.create_table()?;
|
||||
mt.set("__add", Function::wrap(|a, b| Ok(a || b)))?;
|
||||
mt.set("__add", Function::wrap(|a, b| Ok::<_, mlua::Error>(a || b)))?;
|
||||
assert_eq!(lua.type_metatable::<bool>(), None);
|
||||
lua.set_type_metatable::<bool>(Some(mt.clone()));
|
||||
assert_eq!(lua.type_metatable::<bool>().unwrap(), mt);
|
||||
@@ -51,7 +51,7 @@ fn test_lightuserdata_type_metatable() -> Result<()> {
|
||||
mt.set(
|
||||
"__add",
|
||||
Function::wrap(|a: LightUserData, b: LightUserData| {
|
||||
Ok(LightUserData((a.0 as usize + b.0 as usize) as *mut c_void))
|
||||
Ok::<_, Error>(LightUserData((a.0 as usize + b.0 as usize) as *mut c_void))
|
||||
}),
|
||||
)?;
|
||||
lua.set_type_metatable::<LightUserData>(Some(mt.clone()));
|
||||
@@ -79,7 +79,10 @@ fn test_number_type_metatable() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let mt = lua.create_table()?;
|
||||
mt.set("__call", Function::wrap(|n1: f64, n2: f64| Ok(n1 * n2)))?;
|
||||
mt.set(
|
||||
"__call",
|
||||
Function::wrap(|n1: f64, n2: f64| Ok::<_, Error>(n1 * n2)),
|
||||
)?;
|
||||
lua.set_type_metatable::<Number>(Some(mt.clone()));
|
||||
assert_eq!(lua.type_metatable::<Number>().unwrap(), mt);
|
||||
|
||||
@@ -96,7 +99,7 @@ fn test_string_type_metatable() -> Result<()> {
|
||||
let mt = lua.create_table()?;
|
||||
mt.set(
|
||||
"__add",
|
||||
Function::wrap(|a: String, b: String| Ok(format!("{a}{b}"))),
|
||||
Function::wrap(|a: String, b: String| Ok::<_, Error>(format!("{a}{b}"))),
|
||||
)?;
|
||||
lua.set_type_metatable::<LuaString>(Some(mt.clone()));
|
||||
assert_eq!(lua.type_metatable::<LuaString>().unwrap(), mt);
|
||||
@@ -113,7 +116,7 @@ fn test_function_type_metatable() -> Result<()> {
|
||||
let mt = lua.create_table()?;
|
||||
mt.set(
|
||||
"__index",
|
||||
Function::wrap(|_: Function, key: String| Ok(format!("function.{key}"))),
|
||||
Function::wrap(|_: Function, key: String| Ok::<_, Error>(format!("function.{key}"))),
|
||||
)?;
|
||||
lua.set_type_metatable::<Function>(Some(mt.clone()));
|
||||
assert_eq!(lua.type_metatable::<Function>(), Some(mt));
|
||||
@@ -132,7 +135,7 @@ fn test_thread_type_metatable() -> Result<()> {
|
||||
let mt = lua.create_table()?;
|
||||
mt.set(
|
||||
"__index",
|
||||
Function::wrap(|_: Thread, key: String| Ok(format!("thread.{key}"))),
|
||||
Function::wrap(|_: Thread, key: String| Ok::<_, Error>(format!("thread.{key}"))),
|
||||
)?;
|
||||
lua.set_type_metatable::<Thread>(Some(mt.clone()));
|
||||
assert_eq!(lua.type_metatable::<Thread>(), Some(mt));
|
||||
|
||||
+112
-24
@@ -1,14 +1,13 @@
|
||||
use std::any::TypeId;
|
||||
use std::collections::HashMap;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
||||
|
||||
use mlua::{
|
||||
AnyUserData, Error, ExternalError, Function, Lua, MetaMethod, Nil, ObjectLike, Result, String, UserData,
|
||||
UserDataFields, UserDataMethods, UserDataRef, UserDataRegistry, Value, Variadic,
|
||||
AnyUserData, Error, ExternalError, Function, Lua, LuaString, MetaMethod, Nil, ObjectLike, Result,
|
||||
UserData, UserDataFields, UserDataMethods, UserDataOwned, UserDataRef, UserDataRegistry, Value, Variadic,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -131,7 +130,7 @@ fn test_metamethods() -> Result<()> {
|
||||
MetaMethod::Eq,
|
||||
|_, (lhs, rhs): (UserDataRef<Self>, UserDataRef<Self>)| Ok(lhs.0 == rhs.0),
|
||||
);
|
||||
methods.add_meta_method(MetaMethod::Index, |_, data, index: String| {
|
||||
methods.add_meta_method(MetaMethod::Index, |_, data, index: LuaString| {
|
||||
if index.to_str()? == "inner" {
|
||||
Ok(data.0)
|
||||
} else {
|
||||
@@ -291,8 +290,8 @@ fn test_gc_userdata() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
lua.globals().set("userdata", MyUserdata { id: 123 })?;
|
||||
|
||||
assert!(lua
|
||||
.load(
|
||||
assert!(
|
||||
lua.load(
|
||||
r#"
|
||||
local tbl = setmetatable({
|
||||
userdata = userdata
|
||||
@@ -308,7 +307,8 @@ fn test_gc_userdata() -> Result<()> {
|
||||
"#
|
||||
)
|
||||
.exec()
|
||||
.is_err());
|
||||
.is_err()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -491,8 +491,8 @@ fn test_user_values() -> Result<()> {
|
||||
ud.set_nth_user_value(1, "hello")?;
|
||||
ud.set_nth_user_value(2, "world")?;
|
||||
ud.set_nth_user_value(65535, 321)?;
|
||||
assert_eq!(ud.nth_user_value::<String>(1)?, "hello");
|
||||
assert_eq!(ud.nth_user_value::<String>(2)?, "world");
|
||||
assert_eq!(ud.nth_user_value::<LuaString>(1)?, "hello");
|
||||
assert_eq!(ud.nth_user_value::<LuaString>(2)?, "world");
|
||||
assert_eq!(ud.nth_user_value::<Value>(3)?, Value::Nil);
|
||||
assert_eq!(ud.nth_user_value::<i32>(65535)?, 321);
|
||||
|
||||
@@ -582,8 +582,8 @@ fn test_fields() -> Result<()> {
|
||||
});
|
||||
|
||||
// Use userdata "uservalue" storage
|
||||
fields.add_field_function_get("uval", |_, ud| ud.user_value::<Option<String>>());
|
||||
fields.add_field_function_set("uval", |_, ud, s: Option<String>| ud.set_user_value(s));
|
||||
fields.add_field_function_get("uval", |_, ud| ud.user_value::<Option<LuaString>>());
|
||||
fields.add_field_function_set("uval", |_, ud, s: Option<LuaString>| ud.set_user_value(s));
|
||||
|
||||
fields.add_meta_field(MetaMethod::Index, HashMap::from([("f", 321)]));
|
||||
fields.add_meta_field_with(MetaMethod::NewIndex, |lua| {
|
||||
@@ -630,9 +630,11 @@ fn test_fields() -> Result<()> {
|
||||
}
|
||||
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_meta_method(MetaMethod::Index, |_, _, name: StdString| match &*name {
|
||||
"y" => Ok(Some(-1)),
|
||||
_ => Ok(None),
|
||||
methods.add_meta_method(MetaMethod::Index, |_, _, name: LuaString| {
|
||||
match name.to_str()?.as_ref() {
|
||||
"y" => Ok(Some(-1)),
|
||||
_ => Ok(None),
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -659,7 +661,7 @@ fn test_metatable() -> Result<()> {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_function("my_type_name", |_, data: AnyUserData| {
|
||||
let metatable = data.metatable()?;
|
||||
metatable.get::<String>(MetaMethod::Type)
|
||||
metatable.get::<LuaString>(MetaMethod::Type)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -723,7 +725,47 @@ fn test_metatable() -> Result<()> {
|
||||
|
||||
let ud = lua.create_userdata(MyUserData3)?;
|
||||
let metatable = ud.metatable()?;
|
||||
assert_eq!(metatable.get::<String>(MetaMethod::Type)?.to_str()?, "CustomName");
|
||||
assert_eq!(
|
||||
metatable.get::<LuaString>(MetaMethod::Type)?.to_str()?,
|
||||
"CustomName"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_userdata_type_name() -> Result<()> {
|
||||
struct MyUserData;
|
||||
impl UserData for MyUserData {}
|
||||
|
||||
struct MyUserdataCustom;
|
||||
impl UserData for MyUserdataCustom {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
fields.add_meta_field_with(MetaMethod::Type, |_| Ok("MyCustomName"));
|
||||
}
|
||||
}
|
||||
|
||||
// mlua always sets __name/__type; override with a non-string to test the "userdata" fallback
|
||||
struct MyUserdataInvalid;
|
||||
impl UserData for MyUserdataInvalid {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
fields.add_meta_field_with(MetaMethod::Type, |_| Ok(42_i64));
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
|
||||
// Default is the Rust type name
|
||||
let ud = lua.create_userdata(MyUserData)?;
|
||||
assert_eq!(ud.type_name()?, "MyUserData");
|
||||
|
||||
// Custom name from metatable
|
||||
let ud = lua.create_userdata(MyUserdataCustom)?;
|
||||
assert_eq!(ud.type_name()?, "MyCustomName");
|
||||
|
||||
// Invalid type name should fallback to "userdata"
|
||||
let ud = lua.create_userdata(MyUserdataInvalid)?;
|
||||
assert_eq!(ud.type_name()?.to_str()?, "userdata");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -776,16 +818,16 @@ fn test_userdata_proxy() -> Result<()> {
|
||||
fn test_any_userdata() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.register_userdata_type::<StdString>(|reg| {
|
||||
lua.register_userdata_type::<String>(|reg| {
|
||||
reg.add_method("get", |_, this, ()| Ok(this.clone()));
|
||||
reg.add_method_mut("concat", |_, this, s: String| {
|
||||
reg.add_method_mut("concat", |_, this, s: LuaString| {
|
||||
this.push_str(&s.to_string_lossy());
|
||||
Ok(())
|
||||
});
|
||||
})?;
|
||||
|
||||
let ud = lua.create_any_userdata("hello".to_string())?;
|
||||
assert_eq!(&*ud.borrow::<StdString>()?, "hello");
|
||||
assert_eq!(&*ud.borrow::<String>()?, "hello");
|
||||
|
||||
lua.globals().set("ud", ud)?;
|
||||
lua.load(
|
||||
@@ -805,7 +847,7 @@ fn test_any_userdata() -> Result<()> {
|
||||
fn test_any_userdata_wrap() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.register_userdata_type::<StdString>(|reg| {
|
||||
lua.register_userdata_type::<String>(|reg| {
|
||||
reg.add_method("get", |_, this, ()| Ok(this.clone()));
|
||||
})?;
|
||||
|
||||
@@ -857,7 +899,7 @@ fn test_userdata_object_like() -> Result<()> {
|
||||
r => panic!("expected RuntimeError, got {r:?}"),
|
||||
}
|
||||
|
||||
assert_eq!(ud.call::<String>(())?, "called");
|
||||
assert_eq!(ud.call::<LuaString>(())?, "called");
|
||||
|
||||
ud.call_method::<()>("add", 2)?;
|
||||
assert_eq!(ud.get::<u32>("n")?, 323);
|
||||
@@ -1371,11 +1413,11 @@ fn test_userdata_namecall() -> Result<()> {
|
||||
struct MyUserData;
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn register(registry: &mut mlua::UserDataRegistry<Self>) {
|
||||
fn register(registry: &mut UserDataRegistry<Self>) {
|
||||
registry.add_method("method", |_, _, ()| Ok("method called"));
|
||||
registry.add_field_method_get("field", |_, _| Ok("field value"));
|
||||
|
||||
registry.add_meta_method(MetaMethod::Index, |_, _, key: StdString| Ok(key));
|
||||
registry.add_meta_method(MetaMethod::Index, |_, _, key: LuaString| Ok(key));
|
||||
|
||||
registry.enable_namecall();
|
||||
}
|
||||
@@ -1413,7 +1455,53 @@ fn test_userdata_get_path() -> Result<()> {
|
||||
}
|
||||
|
||||
let ud = lua.create_userdata(MyUd)?;
|
||||
assert_eq!(ud.get_path::<String>(".value")?, "userdata_value");
|
||||
assert_eq!(ud.get_path::<LuaString>(".value")?, "userdata_value");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_userdata_owned() -> Result<()> {
|
||||
#[derive(Debug)]
|
||||
struct MyUserdata(Arc<i64>);
|
||||
|
||||
impl UserData for MyUserdata {
|
||||
fn register(registry: &mut UserDataRegistry<Self>) {
|
||||
registry.add_method("num", |_, this, ()| Ok(*this.0));
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
let rc = Arc::new(42);
|
||||
|
||||
// It takes ownership and destructs the Lua userdata
|
||||
let ud = lua.create_userdata(MyUserdata(rc.clone()))?;
|
||||
assert_eq!(Arc::strong_count(&rc), 2);
|
||||
let owned: UserDataOwned<MyUserdata> = lua.convert(&ud)?;
|
||||
assert_eq!(*owned.0.0, 42);
|
||||
drop(owned);
|
||||
assert_eq!(Arc::strong_count(&rc), 1);
|
||||
match ud.borrow::<MyUserdata>() {
|
||||
Err(Error::UserDataDestructed) => {}
|
||||
r => panic!("expected UserDataDestructed, got {:?}", r),
|
||||
}
|
||||
|
||||
// Cannot take while borrowed
|
||||
let rc = Arc::new(7);
|
||||
let ud = lua.create_userdata(MyUserdata(rc.clone()))?;
|
||||
let borrowed = ud.borrow::<MyUserdata>()?;
|
||||
match lua.convert::<UserDataOwned<MyUserdata>>(&ud) {
|
||||
Err(Error::UserDataBorrowMutError) => {}
|
||||
r => panic!("expected UserDataBorrowMutError, got {:?}", r),
|
||||
}
|
||||
drop(borrowed);
|
||||
|
||||
// Works as a function parameter
|
||||
let f = lua.create_function(|_, owned: UserDataOwned<MyUserdata>| Ok(*owned.0.0))?;
|
||||
let rc = Arc::new(55);
|
||||
let ud = lua.create_userdata(MyUserdata(rc.clone()))?;
|
||||
assert_eq!(f.call::<i64>(ud)?, 55);
|
||||
assert_eq!(Arc::strong_count(&rc), 1); // dropped after call
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+37
-8
@@ -1,9 +1,11 @@
|
||||
use std::collections::HashMap;
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use mlua::{Error, LightUserData, Lua, MultiValue, Result, UserData, UserDataMethods, Value};
|
||||
use mlua::{
|
||||
AnyUserData, Error, LightUserData, Lua, MultiValue, Result, UserData, UserDataMethods, UserDataRegistry,
|
||||
Value,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_value_eq() -> Result<()> {
|
||||
@@ -178,7 +180,7 @@ fn test_value_to_string() -> Result<()> {
|
||||
assert!(thread.to_string()?.starts_with("thread:"));
|
||||
assert_eq!(thread.type_name(), "thread");
|
||||
|
||||
lua.register_userdata_type::<StdString>(|reg| {
|
||||
lua.register_userdata_type::<String>(|reg| {
|
||||
reg.add_meta_method("__tostring", |_, this, ()| Ok(this.clone()));
|
||||
})?;
|
||||
let ud: Value = Value::UserData(lua.create_any_userdata(String::from("string userdata"))?);
|
||||
@@ -213,12 +215,37 @@ fn test_value_to_string() -> Result<()> {
|
||||
fn test_debug_format() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.register_userdata_type::<HashMap<i32, StdString>>(|_| {})?;
|
||||
lua.register_userdata_type::<HashMap<i32, String>>(|_| {})?;
|
||||
let ud = lua
|
||||
.create_any_userdata::<HashMap<i32, StdString>>(HashMap::new())
|
||||
.create_any_userdata::<HashMap<i32, String>>(HashMap::new())
|
||||
.map(Value::UserData)?;
|
||||
assert!(format!("{ud:#?}").starts_with("HashMap<i32, String>:"));
|
||||
|
||||
struct ToDebugUserData;
|
||||
impl UserData for ToDebugUserData {
|
||||
fn register(registry: &mut UserDataRegistry<Self>) {
|
||||
registry.add_meta_method("__tostring", |_, _, ()| Ok("regular-string"));
|
||||
registry.add_meta_method("__todebugstring", |_, _, ()| Ok("debug-string"));
|
||||
}
|
||||
}
|
||||
let debug_ud = Value::UserData(lua.create_userdata(ToDebugUserData)?);
|
||||
assert_eq!(debug_ud.to_string()?, "regular-string");
|
||||
assert_eq!(format!("{debug_ud:#?}"), "debug-string");
|
||||
|
||||
struct ToStringUserData;
|
||||
impl UserData for ToStringUserData {
|
||||
fn register(registry: &mut UserDataRegistry<Self>) {
|
||||
registry.add_meta_method("__tostring", |_, _, ()| Ok("regular-string"));
|
||||
}
|
||||
}
|
||||
let tostring_only_ud = Value::UserData(lua.create_userdata(ToStringUserData)?);
|
||||
assert_eq!(format!("{tostring_only_ud:#?}"), "regular-string");
|
||||
|
||||
// Check that `AnyUsedata` pretty debug format is same as for `Value::UserData`
|
||||
let any_ud: AnyUserData = lua.create_userdata(ToDebugUserData)?;
|
||||
let value_ud = Value::UserData(any_ud.clone());
|
||||
assert_eq!(format!("{any_ud:#?}"), format!("{value_ud:#?}"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -259,9 +286,11 @@ fn test_value_conversions() -> Result<()> {
|
||||
assert!(Value::Table(lua.create_table()?).is_table());
|
||||
assert!(Value::Table(lua.create_table()?).as_table().is_some());
|
||||
assert!(Value::Function(lua.create_function(|_, ()| Ok(())).unwrap()).is_function());
|
||||
assert!(Value::Function(lua.create_function(|_, ()| Ok(())).unwrap())
|
||||
.as_function()
|
||||
.is_some());
|
||||
assert!(
|
||||
Value::Function(lua.create_function(|_, ()| Ok(())).unwrap())
|
||||
.as_function()
|
||||
.is_some()
|
||||
);
|
||||
assert!(Value::Thread(lua.create_thread(lua.load("function() end").eval()?)?).is_thread());
|
||||
assert!(
|
||||
Value::Thread(lua.create_thread(lua.load("function() end").eval()?)?)
|
||||
|
||||
Reference in New Issue
Block a user