mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
56 Commits
dev
..
v0.12.0-rc.1
| 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 |
@@ -1,8 +1,8 @@
|
||||
name: Documentation (dev)
|
||||
name: Documentation (main)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [dev]
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||
|
||||
@@ -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)
|
||||
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.12.0-dev.1" # 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.88"
|
||||
edition = "2024"
|
||||
@@ -63,7 +63,7 @@ parking_lot = { version = "0.12", features = ["arc_lock"] }
|
||||
anyhow = { version = "1.0", optional = true }
|
||||
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"
|
||||
@@ -77,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,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
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua-sys"
|
||||
version = "0.10.0"
|
||||
version = "0.11.0-rc.1"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
rust-version = "1.88"
|
||||
edition = "2024"
|
||||
@@ -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);
|
||||
|
||||
+1
-1
@@ -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)
|
||||
}
|
||||
|
||||
+8
-1
@@ -1,3 +1,10 @@
|
||||
//! 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;
|
||||
@@ -153,6 +160,7 @@ 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,
|
||||
@@ -779,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 {
|
||||
|
||||
+36
-35
@@ -4,7 +4,7 @@ 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::{mem, slice, str};
|
||||
use std::{slice, str};
|
||||
|
||||
use bstr::{BStr, BString, ByteVec};
|
||||
use num_traits::cast;
|
||||
@@ -16,7 +16,7 @@ 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};
|
||||
|
||||
@@ -86,91 +86,79 @@ impl FromLua for LuaString {
|
||||
}
|
||||
}
|
||||
|
||||
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 = LuaString::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 })
|
||||
BorrowedStr::try_from(&s)
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let s = LuaString::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 })
|
||||
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 = LuaString::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 })
|
||||
Ok(BorrowedBytes::from(&s))
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let s = LuaString::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 })
|
||||
Ok(BorrowedBytes::from(&s))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,7 +282,7 @@ impl FromLua for AnyUserData {
|
||||
}
|
||||
}
|
||||
|
||||
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)?))
|
||||
@@ -535,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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -597,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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -818,6 +812,13 @@ macro_rules! lua_convert_int {
|
||||
});
|
||||
}
|
||||
}
|
||||
#[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())
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
//! 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 [`Debug`] for accessing debug information and
|
||||
//! [`HookTriggers`] for configuring debug hooks.
|
||||
//! 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;
|
||||
|
||||
+12
-5
@@ -1,3 +1,8 @@
|
||||
//! 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;
|
||||
@@ -340,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.
|
||||
@@ -550,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);
|
||||
}
|
||||
|
||||
+107
-19
@@ -3,12 +3,6 @@
|
||||
//! This module provides types for working with Lua functions from Rust, including
|
||||
//! both Lua-defined functions and native Rust callbacks.
|
||||
//!
|
||||
//! # Main Types
|
||||
//!
|
||||
//! - [`Function`] - A handle to a Lua function that can be called from Rust.
|
||||
//! - [`FunctionInfo`] - Debug information about a function (name, source, line numbers, etc.).
|
||||
//! - [`CoverageInfo`] - Code coverage data for Luau functions (requires `luau` feature).
|
||||
//!
|
||||
//! # Calling Functions
|
||||
//!
|
||||
//! Use [`Function::call`] to invoke a Lua function synchronously:
|
||||
@@ -81,12 +75,13 @@
|
||||
|
||||
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::{
|
||||
StackGuard, assert_stack, check_stack, linenumber_to_usize, pop_error, ptr_to_lossy_str, ptr_to_str,
|
||||
@@ -96,7 +91,6 @@ 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},
|
||||
@@ -246,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>
|
||||
@@ -636,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)
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -672,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 {
|
||||
@@ -688,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);
|
||||
@@ -702,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) {
|
||||
@@ -715,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()) })
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -729,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 {
|
||||
@@ -788,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::*;
|
||||
|
||||
+56
-39
@@ -61,6 +61,7 @@
|
||||
//! [`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.
|
||||
@@ -72,77 +73,93 @@
|
||||
mod macros;
|
||||
|
||||
mod buffer;
|
||||
mod chunk;
|
||||
mod conversion;
|
||||
mod error;
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
mod luau;
|
||||
mod memory;
|
||||
mod multi;
|
||||
mod scope;
|
||||
mod state;
|
||||
mod stdlib;
|
||||
mod string;
|
||||
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::error::{Error, ErrorContext, ExternalError, ExternalResult, Result};
|
||||
#[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, LuaString, LuaString as String};
|
||||
#[doc(inline)]
|
||||
pub use crate::string::{BorrowedBytes, BorrowedStr, LuaString};
|
||||
#[doc(inline)]
|
||||
pub use crate::table::Table;
|
||||
pub use crate::thread::{Thread, ThreadStatus};
|
||||
pub use crate::traits::{
|
||||
FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, LuaNativeFn, LuaNativeFnMut, ObjectLike,
|
||||
};
|
||||
#[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},
|
||||
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::{LuaSerdeExt, de::Options as DeserializeOptions, ser::Options as SerializeOptions},
|
||||
value::SerializableValue,
|
||||
};
|
||||
pub use crate::{serde::LuaSerdeExt, value::SerializableValue};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
@@ -243,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)]
|
||||
|
||||
+9
-2
@@ -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;
|
||||
@@ -10,7 +17,7 @@ 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(())
|
||||
|
||||
+1
-2
@@ -12,8 +12,7 @@ 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)]
|
||||
|
||||
@@ -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,7 +36,7 @@ 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()
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
+24
-14
@@ -3,34 +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, GCMode as LuaGCMode, Integer as LuaInteger, IntoLua, IntoLuaMulti,
|
||||
LightUserData as LuaLightUserData, Lua, LuaNativeFn, LuaNativeFnMut, LuaOptions, LuaString,
|
||||
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,
|
||||
Table as LuaTable, 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, function::FunctionInfo as LuaFunctionInfo,
|
||||
table::TablePairs as LuaTablePairs, table::TableSequence as LuaTableSequence,
|
||||
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, NavigateError as LuaNavigateError, Require as LuaRequire,
|
||||
TextRequirer as LuaTextRequirer, Vector as LuaVector,
|
||||
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)]
|
||||
|
||||
+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};
|
||||
|
||||
+246
-187
@@ -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;
|
||||
@@ -20,8 +25,8 @@ 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::{StackGuard, assert_stack, check_stack, protect_lua_closure, push_string, rawset_field};
|
||||
@@ -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
|
||||
@@ -909,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> {
|
||||
@@ -998,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.
|
||||
@@ -1023,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
|
||||
|
||||
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!(),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1456,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>
|
||||
@@ -1492,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)) }
|
||||
}
|
||||
@@ -1503,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)) }
|
||||
}
|
||||
@@ -1518,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)) }
|
||||
}
|
||||
@@ -1531,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)) }
|
||||
}
|
||||
|
||||
+38
-12
@@ -16,7 +16,7 @@ use crate::stdlib::StdLib;
|
||||
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,
|
||||
@@ -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
|
||||
@@ -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);
|
||||
@@ -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<()> {
|
||||
@@ -1584,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)?;
|
||||
}
|
||||
|
||||
+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))]
|
||||
|
||||
+51
-41
@@ -1,8 +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::{cmp, fmt, slice, str};
|
||||
use std::{cmp, fmt, mem, slice, str};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::Lua;
|
||||
@@ -25,6 +29,9 @@ pub struct LuaString(pub(crate) ValueRef);
|
||||
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
|
||||
///
|
||||
/// ```
|
||||
@@ -42,7 +49,7 @@ impl LuaString {
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_str(&self) -> Result<BorrowedStr<'_>> {
|
||||
pub fn to_str(&self) -> Result<BorrowedStr> {
|
||||
BorrowedStr::try_from(self)
|
||||
}
|
||||
|
||||
@@ -85,8 +92,9 @@ impl LuaString {
|
||||
|
||||
/// 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
|
||||
///
|
||||
@@ -101,16 +109,16 @@ impl LuaString {
|
||||
/// # }
|
||||
/// ```
|
||||
#[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
|
||||
@@ -233,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, LuaString>,
|
||||
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)]
|
||||
@@ -249,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>,
|
||||
{
|
||||
@@ -284,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>,
|
||||
{
|
||||
@@ -295,33 +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 LuaString> for BorrowedStr<'a> {
|
||||
impl TryFrom<&LuaString> for BorrowedStr {
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn try_from(value: &'a LuaString) -> Result<Self> {
|
||||
let BorrowedBytes { buf, borrow, _lua } = BorrowedBytes::from(value);
|
||||
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, borrow, _lua })
|
||||
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, LuaString>,
|
||||
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)]
|
||||
@@ -330,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]>,
|
||||
{
|
||||
@@ -359,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]>,
|
||||
{
|
||||
@@ -370,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>;
|
||||
|
||||
@@ -385,12 +393,14 @@ impl<'a> IntoIterator for &'a BorrowedBytes<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a LuaString> for BorrowedBytes<'a> {
|
||||
impl From<&LuaString> for BorrowedBytes {
|
||||
#[inline]
|
||||
fn from(value: &'a LuaString) -> 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 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-11
@@ -3,12 +3,6 @@
|
||||
//! 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.
|
||||
//!
|
||||
//! # Main Types
|
||||
//!
|
||||
//! - [`Table`] - A handle to a Lua table.
|
||||
//! - [`TablePairs`] - An iterator over key-value pairs in a table.
|
||||
//! - [`TableSequence`] - An iterator over the array (sequence) portion of a table.
|
||||
//!
|
||||
//! # Basic Operations
|
||||
//!
|
||||
//! Tables support key-value access similar to Rust's `HashMap`:
|
||||
@@ -784,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(())
|
||||
@@ -860,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(())
|
||||
|
||||
+66
-9
@@ -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};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+6
-93
@@ -1,3 +1,8 @@
|
||||
//! 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::sync::Arc;
|
||||
|
||||
@@ -5,12 +10,11 @@ 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 {
|
||||
@@ -245,97 +249,6 @@ 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() -> String {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+62
-11
@@ -1,16 +1,21 @@
|
||||
//! 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 crate::Either;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::Lua;
|
||||
use crate::string::LuaString;
|
||||
use crate::table::{Table, TablePairs};
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
use crate::types::{MaybeSend, ValueRef};
|
||||
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;
|
||||
|
||||
@@ -25,7 +30,7 @@ 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::{
|
||||
@@ -123,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.
|
||||
@@ -232,6 +242,7 @@ impl MetaMethod {
|
||||
MetaMethod::NewIndex => "__newindex",
|
||||
MetaMethod::Call => "__call",
|
||||
MetaMethod::ToString => "__tostring",
|
||||
MetaMethod::ToDebugString => "__todebugstring",
|
||||
|
||||
#[cfg(any(
|
||||
feature = "lua55",
|
||||
@@ -318,7 +329,6 @@ 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<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
@@ -373,7 +383,6 @@ 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<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
@@ -705,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 {
|
||||
@@ -1020,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<String>> {
|
||||
/// 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 {
|
||||
@@ -1038,8 +1047,8 @@ impl AnyUserData {
|
||||
ffi::luaL_getmetafield(state, -1, MetaMethod::Type.as_cstr().as_ptr())
|
||||
};
|
||||
match name_type {
|
||||
ffi::LUA_TSTRING => Ok(Some(LuaString(lua.pop_ref()).to_str()?.to_owned())),
|
||||
_ => Ok(None),
|
||||
ffi::LUA_TSTRING => Ok(LuaString(lua.pop_ref())),
|
||||
_ => lua.create_string(b"userdata"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1075,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.
|
||||
@@ -1171,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))
|
||||
}
|
||||
|
||||
@@ -1181,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))
|
||||
}
|
||||
}
|
||||
|
||||
+29
-61
@@ -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>),
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+69
-7
@@ -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,6 +440,66 @@ 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 {
|
||||
@@ -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);
|
||||
|
||||
@@ -654,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 {
|
||||
|
||||
+1
-1
@@ -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,
|
||||
|
||||
+5
-13
@@ -151,7 +151,7 @@ impl Value {
|
||||
/// This might invoke the `__tostring` metamethod for non-primitive types (eg. tables,
|
||||
/// functions).
|
||||
pub fn to_string(&self) -> Result<String> {
|
||||
unsafe fn invoke_to_string(vref: &ValueRef) -> Result<String> {
|
||||
unsafe fn invoke_tostring(vref: &ValueRef) -> Result<String> {
|
||||
let lua = vref.lua.lock();
|
||||
let state = lua.state();
|
||||
let _guard = StackGuard::new(state);
|
||||
@@ -178,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()),
|
||||
}
|
||||
}
|
||||
@@ -361,7 +361,7 @@ impl Value {
|
||||
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())
|
||||
}
|
||||
|
||||
@@ -561,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:?}"),
|
||||
|
||||
+3
-3
@@ -7,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,
|
||||
};
|
||||
|
||||
@@ -41,7 +41,7 @@ async fn test_async_function_wrap() -> Result<()> {
|
||||
|
||||
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?;
|
||||
@@ -714,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<
|
||||
|
||||
@@ -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<()> {
|
||||
|
||||
+35
-3
@@ -1,3 +1,6 @@
|
||||
use std::fmt;
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
use mlua::{Error, Function, Lua, LuaString, Result, Table, Variadic};
|
||||
|
||||
#[test]
|
||||
@@ -343,7 +346,7 @@ fn test_function_deep_clone() -> Result<()> {
|
||||
fn test_function_wrap() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = Function::wrap(|s: LuaString, 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(())
|
||||
|
||||
+21
-3
@@ -7,7 +7,7 @@ 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;
|
||||
|
||||
@@ -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)
|
||||
@@ -65,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 {
|
||||
@@ -109,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"));
|
||||
|
||||
+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
-49
@@ -1,50 +1,7 @@
|
||||
#![cfg(feature = "send")]
|
||||
|
||||
use std::cell::UnsafeCell;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
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(String, 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<()> {
|
||||
@@ -74,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(())
|
||||
}
|
||||
|
||||
+2
-2
@@ -1374,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()?;
|
||||
@@ -1387,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()?;
|
||||
|
||||
+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, LuaString, Number, Result, 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));
|
||||
|
||||
+85
-2
@@ -7,7 +7,7 @@ use std::sync::atomic::{AtomicI64, Ordering};
|
||||
|
||||
use mlua::{
|
||||
AnyUserData, Error, ExternalError, Function, Lua, LuaString, MetaMethod, Nil, ObjectLike, Result,
|
||||
UserData, UserDataFields, UserDataMethods, UserDataRef, UserDataRegistry, Value, Variadic,
|
||||
UserData, UserDataFields, UserDataMethods, UserDataOwned, UserDataRef, UserDataRegistry, Value, Variadic,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -733,6 +733,43 @@ fn test_metatable() -> Result<()> {
|
||||
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(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_userdata_proxy() -> Result<()> {
|
||||
struct MyUserData(i64);
|
||||
@@ -1376,7 +1413,7 @@ 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"));
|
||||
|
||||
@@ -1422,3 +1459,49 @@ fn test_userdata_get_path() -> Result<()> {
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
+29
-1
@@ -2,7 +2,10 @@ use std::collections::HashMap;
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
|
||||
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<()> {
|
||||
@@ -218,6 +221,31 @@ fn test_debug_format() -> Result<()> {
|
||||
.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(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user