Compare commits

..

26 Commits

Author SHA1 Message Date
Alex Orlenko 3253ae8f4a v0.9.0-beta.2 2023-04-27 23:13:32 +01:00
Alex Orlenko fdda0d3724 Update compile tests 2023-04-27 23:06:38 +01:00
Alex Orlenko a1d385c7b7 Add OwnedString 2023-04-26 23:17:27 +01:00
Alex Orlenko 21b834decc Update coverage calculation (exclude mlua-sys) 2023-04-26 22:22:11 +01:00
Alex Orlenko 4daa7de997 Various improvements for owned types, including:
- tests
- shortcuts for `OwnedFunction` and `OwnedAnyUserData`
2023-04-26 15:40:48 +01:00
Alex Orlenko bd68a155c0 Replace ManuallyDrop with MaybeUninit 2023-04-26 15:40:47 +01:00
Alex Orlenko 993aaf44c7 mlua-sys: v0.1.1 2023-04-24 11:25:22 +01:00
Alex Orlenko 54cbc62727 mlua-sys: use luau-src v0.5.6+luau573 2023-04-24 11:24:32 +01:00
Alex Orlenko 2d6a0fdf9c Check for reference leak detection only in mlua integratin tests.
This is not necessary an error and should not be enforced by default.
Fixes #268.
2023-04-23 10:56:26 +01:00
Alex Orlenko d0cbd32ad2 (clippy) mlua_derive: don't clone function name 2023-04-22 11:07:33 +01:00
Alex Orlenko be64706cff Missing doc for OwnedAnyUserData 2023-04-14 10:29:34 +01:00
Alex Orlenko c178bc0a55 cargo fmt 2023-04-14 10:29:12 +01:00
Alex Orlenko 0fccdfed5c Fix feature flags for owned types 2023-04-14 01:17:26 +01:00
Alex Orlenko aaf0a5e44a Remove usage of crate::ffi (clippy) 2023-04-14 00:36:31 +01:00
Alex Orlenko 98888883bc mlua-sys: clippy 2023-04-14 00:31:06 +01:00
Alex Orlenko 65e72f39ae Remove unstable feature flag from Function::wrap/wrap_mut/wrap_async 2023-04-14 00:21:18 +01:00
Alex Orlenko f9d1213c4b Don't set html_root_url (it's not recommended) 2023-04-13 22:44:40 +01:00
Alex Orlenko dc10c80e61 Add documentation url to mlua-sys 2023-04-13 22:32:27 +01:00
Alex Orlenko 23b9cefdca Add missing doc_cfg feature to mlua-sys 2023-04-13 21:55:37 +01:00
Alex Orlenko 15dc0e9f23 Move ffi module into mlua-sys crate 2023-04-12 23:23:34 +01:00
Alex Orlenko 0c53e09e30 Update tarpaulin settings 2023-04-12 01:03:33 +01:00
Alex Orlenko cdbf04f50c Add pretty-print to the Debug formatting to Value to Table.
This would allow dumping any Lua variable in human readable form.
2023-04-11 20:36:41 +01:00
Alex Orlenko ba324b4f54 Allow deserializing Lua null into unit(()) or unit struct. See #264 2023-04-10 01:49:51 +01:00
Alex Orlenko 3e83753466 Add Thread::set_hook() function 2023-04-08 23:53:48 +01:00
Alex Orlenko 288934c82c Refactor HookTriggers (make it const) 2023-04-05 01:02:27 +01:00
Alex Orlenko 483bc80fc4 Add must_use to AsyncThread 2023-04-05 00:23:13 +01:00
74 changed files with 1005 additions and 454 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
- name: Generate coverage report
run: |
cargo tarpaulin --verbose --features lua54,vendored,async,send,serialize,macros --out xml --exclude-files benches --exclude-files build --exclude-files mlua_derive --exclude-files src/ffi --exclude-files tests
cargo tarpaulin --out xml --tests --exclude-files benches/* --exclude-files mlua-sys/src/*/*
- name: Upload report to codecov.io
uses: codecov/codecov-action@v3
+7 -5
View File
@@ -118,11 +118,12 @@ jobs:
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v1
- uses: Swatinem/rust-cache@v2
- name: Run ${{ matrix.lua }} tests
run: |
cargo test --features "${{ matrix.lua }},vendored"
cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable"
cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,parking_lot,unstable"
shell: bash
- name: Run compile tests (macos lua54)
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua54' }}
@@ -149,12 +150,13 @@ jobs:
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v1
- uses: Swatinem/rust-cache@v2
- name: Run ${{ matrix.lua }} tests with address sanitizer
run: |
RUSTFLAGS="-Z sanitizer=address" \
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
shell: bash
env:
RUSTFLAGS: -Z sanitizer=address
test_modules:
name: Test modules
@@ -176,7 +178,7 @@ jobs:
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v1
- uses: Swatinem/rust-cache@v2
- name: Run ${{ matrix.lua }} module tests
run: |
(cd tests/module && cargo build --release --features "${{ matrix.lua }}")
+11
View File
@@ -1,3 +1,14 @@
## v0.9.0-beta.2
New features:
- Added `Thread::set_hook()` function to set hook on threads
- Added pretty print to the Debug formatting to Lua `Value` and `Table`
- ffi layer moved to `mlua-sys` crate
- Added OwnedString (unstable)
Breaking changes:
- Refactor `HookTriggers` (make it const)
## v0.9.0-beta.1
New features:
+12 -18
View File
@@ -1,6 +1,6 @@
[package]
name = "mlua"
version = "0.9.0-beta.1" # remember to update html_root_url and mlua_derive
version = "0.9.0-beta.2" # remember to update mlua_derive
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
edition = "2021"
repository = "https://github.com/khvzak/mlua"
@@ -9,8 +9,6 @@ readme = "README.md"
keywords = ["lua", "luajit", "luau", "async", "scripting"]
categories = ["api-bindings", "asynchronous"]
license = "MIT"
links = "lua"
build = "build/main.rs"
description = """
High level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox Luau
with async/await features and support of writing native Lua modules in Rust.
@@ -23,18 +21,19 @@ rustdoc-args = ["--cfg", "docsrs"]
[workspace]
members = [
"mlua_derive",
"mlua-sys",
]
[features]
lua54 = []
lua53 = []
lua52 = []
lua51 = []
luajit = []
luajit52 = ["luajit"]
luau = ["luau0-src"]
vendored = ["lua-src", "luajit-src"]
module = ["mlua_derive"]
lua54 = ["ffi/lua54"]
lua53 = ["ffi/lua53"]
lua52 = ["ffi/lua52"]
lua51 = ["ffi/lua51"]
luajit = ["ffi/luajit"]
luajit52 = ["luajit", "ffi/luajit52"]
luau = ["ffi/luau"]
vendored = ["ffi/vendored"]
module = ["mlua_derive", "ffi/module"]
async = ["futures-core", "futures-task", "futures-util"]
send = []
serialize = ["serde", "erased-serde", "serde-value"]
@@ -55,12 +54,7 @@ erased-serde = { version = "0.3", optional = true }
serde-value = { version = "0.7", optional = true }
parking_lot = { version = "0.12", optional = true }
[build-dependencies]
cc = { version = "1.0" }
pkg-config = { version = "0.3.17" }
lua-src = { version = ">= 544.0.0, < 550.0.0", optional = true }
luajit-src = { version = ">= 210.4.0, < 220.0.0", optional = true }
luau0-src = { version = "0.5.0", optional = true }
ffi = { package = "mlua-sys", version = "0.1.0", path = "mlua-sys" }
[dev-dependencies]
rustyline = "11.0"
-5
View File
@@ -1,5 +0,0 @@
use std::path::PathBuf;
pub fn probe_lua() -> Option<PathBuf> {
None
}
-115
View File
@@ -1,115 +0,0 @@
#[cfg_attr(
any(
feature = "luau",
all(
feature = "vendored",
any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit"
)
)
),
path = "find_vendored.rs"
)]
#[cfg_attr(
all(
not(feature = "vendored"),
any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit"
)
),
path = "find_normal.rs"
)]
#[cfg_attr(
not(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit",
feature = "luau"
)),
path = "find_dummy.rs"
)]
mod find;
fn main() {
#[cfg(not(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit",
feature = "luau"
)))]
compile_error!(
"You must enable one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
);
#[cfg(all(
feature = "lua54",
any(
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit",
feature = "luau"
)
))]
compile_error!(
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
);
#[cfg(all(
feature = "lua53",
any(
feature = "lua52",
feature = "lua51",
feature = "luajit",
feature = "luau"
)
))]
compile_error!(
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
);
#[cfg(all(
feature = "lua52",
any(feature = "lua51", feature = "luajit", feature = "luau")
))]
compile_error!(
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
);
#[cfg(all(feature = "lua51", any(feature = "luajit", feature = "luau")))]
compile_error!(
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
);
#[cfg(all(feature = "luajit", feature = "luau"))]
compile_error!(
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
);
// We don't support "vendored module" mode on windows
#[cfg(all(feature = "vendored", feature = "module", target_os = "windows"))]
compile_error!(
"Vendored (static) builds are not supported for modules on Windows.\n"
+ "Please, use `pkg-config` or custom mode to link to a Lua dll."
);
#[cfg(all(feature = "luau", feature = "module"))]
compile_error!("Luau does not support module mode");
#[cfg(any(not(feature = "module"), target_os = "windows"))]
find::probe_lua();
println!("cargo:rerun-if-changed=build");
}
+40
View File
@@ -0,0 +1,40 @@
[package]
name = "mlua-sys"
version = "0.1.1"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2021"
repository = "https://github.com/khvzak/mlua"
documentation = "https://docs.rs/mlua-sys"
readme = "README.md"
categories = ["external-ffi-bindings"]
license = "MIT"
links = "lua"
build = "build/main.rs"
description = """
Low level (FFI) bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox Luau
"""
[package.metadata.docs.rs]
features = ["lua54", "vendored"]
rustdoc-args = ["--cfg", "docsrs"]
[features]
lua54 = []
lua53 = []
lua52 = []
lua51 = []
luajit = []
luajit52 = ["luajit"]
luau = ["luau0-src"]
vendored = ["lua-src", "luajit-src"]
module = []
[dependencies]
[build-dependencies]
cc = "1.0"
cfg-if = "1.0"
pkg-config = "0.3.17"
lua-src = { version = ">= 544.0.0, < 550.0.0", optional = true }
luajit-src = { version = ">= 210.4.0, < 220.0.0", optional = true }
luau0-src = { version = "0.5.6", optional = true }
+8
View File
@@ -0,0 +1,8 @@
# mlua-sys
Low level (FFI) bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox [Luau].
Intended to be consumed by the [mlua] crate.
[Luau]: https://github.com/Roblox/luau
[mlua]: https://crates.io/crates/mlua
+19
View File
@@ -0,0 +1,19 @@
cfg_if::cfg_if! {
if #[cfg(all(feature = "lua54", not(any(feature = "lua53", feature = "lua52", feature = "lua51", feature = "luajit", feature = "luau"))))] {
include!("main_inner.rs");
} else if #[cfg(all(feature = "lua53", not(any(feature = "lua54", feature = "lua52", feature = "lua51", feature = "luajit", feature = "luau"))))] {
include!("main_inner.rs");
} else if #[cfg(all(feature = "lua52", not(any(feature = "lua54", feature = "lua53", feature = "lua51", feature = "luajit", feature = "luau"))))] {
include!("main_inner.rs");
} else if #[cfg(all(feature = "lua51", not(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit", feature = "luau"))))] {
include!("main_inner.rs");
} else if #[cfg(all(feature = "luajit", not(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "lua51", feature = "luau"))))] {
include!("main_inner.rs");
} else if #[cfg(all(feature = "luau", not(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "lua51", feature = "luajit"))))] {
include!("main_inner.rs");
} else {
fn main() {
compile_error!("You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau");
}
}
}
+26
View File
@@ -0,0 +1,26 @@
cfg_if::cfg_if! {
if #[cfg(any(feature = "luau", feature = "vendored"))] {
#[path = "find_vendored.rs"]
mod find;
} else {
#[path = "find_normal.rs"]
mod find;
}
}
fn main() {
// We don't support "vendored module" mode on windows
#[cfg(all(feature = "vendored", feature = "module", target_os = "windows"))]
compile_error!(
"Vendored (static) builds are not supported for modules on Windows.\n"
+ "Please, use `pkg-config` or custom mode to link to a Lua dll."
);
#[cfg(all(feature = "luau", feature = "module"))]
compile_error!("Luau does not support module mode");
#[cfg(any(not(feature = "module"), target_os = "windows"))]
find::probe_lua();
println!("cargo:rerun-if-changed=build");
}
+29 -29
View File
@@ -1,38 +1,46 @@
//! Low level bindings to Lua 5.4/5.3/5.2/5.1 including LuaJIT.
//! Low level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox Luau.
#![allow(non_camel_case_types, non_snake_case, dead_code)]
#![allow(clippy::missing_safety_doc)]
#![doc(test(attr(deny(warnings))))]
#![cfg_attr(docsrs, feature(doc_cfg))]
use std::os::raw::c_int;
#[cfg(feature = "lua54")]
#[cfg(any(feature = "lua54", doc))]
pub use lua54::*;
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua53", doc))]
pub use lua53::*;
#[cfg(feature = "lua52")]
#[cfg(any(feature = "lua52", doc))]
pub use lua52::*;
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[cfg(any(feature = "lua51", feature = "luajit", doc))]
pub use lua51::*;
#[cfg(feature = "luau")]
#[cfg(any(feature = "luau", doc))]
pub use luau::*;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[doc(hidden)]
pub const LUA_MAX_UPVALUES: c_int = 255;
#[cfg(any(feature = "lua51", all(feature = "luajit", not(feature = "vendored"))))]
#[doc(hidden)]
pub const LUA_MAX_UPVALUES: c_int = 60;
#[cfg(all(feature = "luajit", feature = "vendored"))]
#[doc(hidden)]
pub const LUA_MAX_UPVALUES: c_int = 120;
#[cfg(feature = "luau")]
#[doc(hidden)]
pub const LUA_MAX_UPVALUES: c_int = 200;
// I believe `luaL_traceback` < 5.4 requires this much free stack to not error.
// 5.4 uses `luaL_Buffer`
#[doc(hidden)]
pub const LUA_TRACEBACK_STACK: c_int = 11;
// The minimum alignment guaranteed by the architecture. This value is used to
@@ -51,6 +59,7 @@ pub const LUA_TRACEBACK_STACK: c_int = 11;
all(target_arch = "riscv32", not(target_os = "espidf")),
all(target_arch = "xtensa", not(target_os = "espidf")),
)))]
#[doc(hidden)]
pub const SYS_MIN_ALIGN: usize = 8;
#[cfg(all(any(
target_arch = "x86_64",
@@ -61,44 +70,35 @@ pub const SYS_MIN_ALIGN: usize = 8;
target_arch = "riscv64",
target_arch = "wasm64",
)))]
#[doc(hidden)]
pub const SYS_MIN_ALIGN: usize = 16;
// The allocator on the esp-idf platform guarentees 4 byte alignment.
#[cfg(all(any(
all(target_arch = "riscv32", target_os = "espidf"),
all(target_arch = "xtensa", target_os = "espidf"),
)))]
#[doc(hidden)]
pub const SYS_MIN_ALIGN: usize = 4;
// Hack to avoid stripping a few unused Lua symbols that could be imported
// by C modules in unsafe mode
#[cfg(not(feature = "luau"))]
pub(crate) fn keep_lua_symbols() {
let mut _symbols: Vec<*const extern "C" fn()> = vec![
lua_atpanic as _,
lua_isuserdata as _,
lua_tocfunction as _,
luaL_loadstring as _,
luaL_openlibs as _,
];
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
{
_symbols.push(lua_getglobal as _);
_symbols.push(lua_setglobal as _);
_symbols.push(luaL_setfuncs as _);
}
}
#[macro_use]
mod macros;
#[cfg(feature = "lua54")]
#[cfg(any(feature = "lua54", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
pub mod lua54;
#[cfg(feature = "lua53")]
#[cfg(any(feature = "lua53", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "lua53")))]
pub mod lua53;
#[cfg(feature = "lua52")]
#[cfg(any(feature = "lua52", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "lua52")))]
pub mod lua52;
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[cfg(any(feature = "lua51", feature = "luajit", doc))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua51", feature = "luajit"))))]
pub mod lua51;
#[cfg(feature = "luau")]
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub mod luau;
@@ -1,4 +1,4 @@
//! MLua compatibility layer for Lua 5.2
//! MLua compatibility layer for Lua 5.3
use std::os::raw::c_int;
@@ -74,6 +74,7 @@ pub type lua_Continuation = unsafe extern "C" fn(L: *mut lua_State, status: c_in
/// Type for userdata destructor functions.
pub type lua_Udestructor = unsafe extern "C" fn(*mut c_void);
pub type lua_Destructor = unsafe extern "C" fn(L: *mut lua_State, *mut c_void);
/// Type for memory-allocation functions.
pub type lua_Alloc = unsafe extern "C" fn(
@@ -265,11 +266,8 @@ extern "C" {
// TODO: lua_encodepointer
pub fn lua_clock() -> c_double;
pub fn lua_setuserdatatag(L: *mut lua_State, idx: c_int, tag: c_int);
pub fn lua_setuserdatadtor(
L: *mut lua_State,
tag: c_int,
dtor: Option<unsafe extern "C" fn(*mut lua_State, *mut c_void)>,
);
pub fn lua_setuserdatadtor(L: *mut lua_State, tag: c_int, dtor: Option<lua_Destructor>);
pub fn lua_getuserdatadtor(L: *mut lua_State, tag: c_int) -> Option<lua_Destructor>;
pub fn lua_clonefunction(L: *mut lua_State, idx: c_int);
pub fn lua_cleartable(L: *mut lua_State, idx: c_int);
}
+7
View File
@@ -0,0 +1,7 @@
#[allow(unused_macros)]
macro_rules! cstr {
($s:expr) => {
concat!($s, "\0") as *const str as *const [::std::os::raw::c_char]
as *const ::std::os::raw::c_char
};
}
+1 -1
View File
@@ -42,7 +42,7 @@ pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
}
let func = parse_macro_input!(item as ItemFn);
let func_name = func.sig.ident.clone();
let func_name = &func.sig.ident;
let module_name = args.name.unwrap_or_else(|| func_name.clone());
let ext_entrypoint_name = Ident::new(&format!("luaopen_{module_name}"), Span::call_site());
-1
View File
@@ -6,7 +6,6 @@ use std::path::{Path, PathBuf};
use std::string::String as StdString;
use crate::error::{Error, Result};
use crate::ffi;
use crate::function::Function;
use crate::lua::Lua;
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti, Value};
+17 -16
View File
@@ -9,7 +9,7 @@ use bstr::{BStr, BString};
use num_traits::cast;
use crate::error::{Error, Result};
use crate::function::Function;
use crate::function::{Function, WrappedFunction};
use crate::lua::Lua;
use crate::string::String;
use crate::table::Table;
@@ -18,14 +18,10 @@ use crate::types::{LightUserData, MaybeSend};
use crate::userdata::{AnyUserData, UserData, UserDataRef, UserDataRefMut};
use crate::value::{FromLua, IntoLua, Nil, Value};
#[cfg(feature = "unstable")]
use crate::{
function::{OwnedFunction, WrappedFunction},
table::OwnedTable,
userdata::OwnedAnyUserData,
};
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
use crate::{function::OwnedFunction, table::OwnedTable, userdata::OwnedAnyUserData};
#[cfg(all(feature = "async", feature = "unstable"))]
#[cfg(feature = "async")]
use crate::function::WrappedAsyncFunction;
impl<'lua> IntoLua<'lua> for Value<'lua> {
@@ -83,7 +79,8 @@ impl<'lua> FromLua<'lua> for Table<'lua> {
}
}
#[cfg(feature = "unstable")]
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
impl<'lua> IntoLua<'lua> for OwnedTable {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
@@ -91,7 +88,8 @@ impl<'lua> IntoLua<'lua> for OwnedTable {
}
}
#[cfg(feature = "unstable")]
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
impl<'lua> FromLua<'lua> for OwnedTable {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<OwnedTable> {
@@ -120,7 +118,8 @@ impl<'lua> FromLua<'lua> for Function<'lua> {
}
}
#[cfg(feature = "unstable")]
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
impl<'lua> IntoLua<'lua> for OwnedFunction {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
@@ -128,7 +127,8 @@ impl<'lua> IntoLua<'lua> for OwnedFunction {
}
}
#[cfg(feature = "unstable")]
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
impl<'lua> FromLua<'lua> for OwnedFunction {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<OwnedFunction> {
@@ -136,7 +136,6 @@ impl<'lua> FromLua<'lua> for OwnedFunction {
}
}
#[cfg(feature = "unstable")]
impl<'lua> IntoLua<'lua> for WrappedFunction<'lua> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
@@ -144,7 +143,7 @@ impl<'lua> IntoLua<'lua> for WrappedFunction<'lua> {
}
}
#[cfg(all(feature = "async", feature = "unstable"))]
#[cfg(feature = "async")]
impl<'lua> IntoLua<'lua> for WrappedAsyncFunction<'lua> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
@@ -194,7 +193,8 @@ impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
}
}
#[cfg(feature = "unstable")]
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
impl<'lua> IntoLua<'lua> for OwnedAnyUserData {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
@@ -202,7 +202,8 @@ impl<'lua> IntoLua<'lua> for OwnedAnyUserData {
}
}
#[cfg(feature = "unstable")]
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
impl<'lua> FromLua<'lua> for OwnedAnyUserData {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<OwnedAnyUserData> {
+58 -28
View File
@@ -1,36 +1,36 @@
use std::cell::RefCell;
use std::mem;
use std::os::raw::{c_int, c_void};
use std::ptr;
use std::slice;
use crate::error::{Error, Result};
use crate::ffi;
use crate::lua::Lua;
use crate::memory::MemoryState;
use crate::types::LuaRef;
use crate::types::{Callback, LuaRef, MaybeSend};
use crate::util::{
assert_stack, check_stack, error_traceback, pop_error, ptr_to_cstr_bytes, StackGuard,
};
use crate::value::{FromLuaMulti, IntoLuaMulti};
#[cfg(feature = "unstable")]
use {
crate::lua::Lua,
crate::types::{Callback, MaybeSend},
crate::value::IntoLua,
std::cell::RefCell,
};
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti};
#[cfg(feature = "async")]
use {futures_core::future::LocalBoxFuture, futures_util::future};
#[cfg(all(feature = "async", feature = "unstable"))]
use {crate::types::AsyncCallback, futures_core::Future, futures_util::TryFutureExt};
use {
crate::types::AsyncCallback,
futures_core::future::{Future, LocalBoxFuture},
futures_util::{future, TryFutureExt},
};
/// Handle to an internal Lua function.
#[derive(Clone, Debug)]
pub struct Function<'lua>(pub(crate) LuaRef<'lua>);
/// Owned handle to an internal Lua function.
///
/// The owned handle holds a *strong* reference to the current Lua instance.
/// Be warned, if you place it into a Lua type (eg. [`UserData`] or a Rust callback), it is *very easy*
/// to accidentally cause reference cycles that would prevent destroying Lua instance.
///
/// [`UserData`]: crate::UserData
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
#[derive(Clone, Debug)]
@@ -39,6 +39,7 @@ pub struct OwnedFunction(pub(crate) crate::types::LuaOwnedRef);
#[cfg(feature = "unstable")]
impl OwnedFunction {
/// Get borrowed handle to the underlying Lua function.
#[cfg_attr(feature = "send", allow(unused))]
pub const fn to_ref(&self) -> Function {
Function(self.0.to_ref())
}
@@ -406,8 +407,8 @@ impl<'lua> Function<'lua> {
}
/// Convert this handle to owned version.
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
#[inline]
pub fn into_owned(self) -> OwnedFunction {
OwnedFunction(self.0.into_owned())
@@ -420,22 +421,50 @@ impl<'lua> PartialEq for Function<'lua> {
}
}
// Additional shortcuts
#[cfg(feature = "unstable")]
impl OwnedFunction {
/// Calls the function, passing `args` as function arguments.
///
/// This is a shortcut for [`Function::call()`].
#[inline]
pub fn call<'lua, A, R>(&'lua self, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
self.to_ref().call(args)
}
/// Returns a future that, when polled, calls `self`, passing `args` as function arguments,
/// and drives the execution.
///
/// This is a shortcut for [`Function::call_async()`].
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
#[inline]
pub fn call_async<'lua, A, R>(&'lua self, args: A) -> LocalBoxFuture<'lua, Result<R>>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua,
{
self.to_ref().call_async(args)
}
}
pub(crate) struct WrappedFunction<'lua>(pub(crate) Callback<'lua, 'static>);
#[cfg(all(feature = "async", feature = "unstable"))]
#[cfg(feature = "async")]
pub(crate) struct WrappedAsyncFunction<'lua>(pub(crate) AsyncCallback<'lua, 'static>);
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
impl<'lua> Function<'lua> {
/// 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<'lua>
pub fn wrap<A, R, F>(func: F) -> impl IntoLua<'lua>
where
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
{
WrappedFunction(Box::new(move |lua, args| {
func(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
@@ -444,11 +473,11 @@ impl<'lua> Function<'lua> {
/// Wraps a Rust mutable closure, returning an opaque type that implements [`IntoLua`] trait.
#[inline]
pub fn wrap_mut<F, A, R>(func: F) -> impl IntoLua<'lua>
pub fn wrap_mut<A, R, F>(func: F) -> impl IntoLua<'lua>
where
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
{
let func = RefCell::new(func);
WrappedFunction(Box::new(move |lua, args| {
@@ -459,14 +488,15 @@ impl<'lua> Function<'lua> {
}))
}
/// Wraps a Rust async function or closure, returning an opaque type that implements [`IntoLua`] trait.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn wrap_async<F, A, FR, R>(func: F) -> impl IntoLua<'lua>
pub fn wrap_async<A, R, F, FR>(func: F) -> impl IntoLua<'lua>
where
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
FR: Future<Output = Result<R>> + 'lua,
R: IntoLuaMulti<'lua>,
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
FR: Future<Output = Result<R>> + 'lua,
{
WrappedAsyncFunction(Box::new(move |lua, args| {
let args = match A::from_lua_multi(args, lua) {
@@ -484,6 +514,6 @@ mod assertions {
static_assertions::assert_not_impl_any!(Function: Send);
#[cfg(feature = "unstable")]
#[cfg(all(feature = "unstable", not(feature = "send")))]
static_assertions::assert_not_impl_any!(OwnedFunction: Send);
}
+41 -28
View File
@@ -3,7 +3,8 @@ use std::cell::UnsafeCell;
use std::ops::{BitOr, BitOrAssign};
use std::os::raw::c_int;
use crate::ffi::{self, lua_Debug};
use ffi::lua_Debug;
use crate::lua::Lua;
use crate::util::ptr_to_cstr_bytes;
@@ -266,48 +267,59 @@ pub struct HookTriggers {
#[cfg(not(feature = "luau"))]
impl HookTriggers {
/// Returns a new instance of `HookTriggers` with [`on_calls`] trigger set.
/// An instance of `HookTriggers` with `on_calls` trigger set.
pub const ON_CALLS: Self = HookTriggers::new().on_calls();
/// An instance of `HookTriggers` with `on_returns` trigger set.
pub const ON_RETURNS: Self = HookTriggers::new().on_returns();
/// An instance of `HookTriggers` with `every_line` trigger set.
pub const EVERY_LINE: Self = HookTriggers::new().every_line();
/// Returns a new instance of `HookTriggers` with all triggers disabled.
pub const fn new() -> Self {
HookTriggers {
on_calls: false,
on_returns: false,
every_line: false,
every_nth_instruction: None,
}
}
/// Returns an instance of `HookTriggers` with [`on_calls`] trigger set.
///
/// [`on_calls`]: #structfield.on_calls
pub fn on_calls() -> Self {
HookTriggers {
on_calls: true,
..Default::default()
}
pub const fn on_calls(mut self) -> Self {
self.on_calls = true;
self
}
/// Returns a new instance of `HookTriggers` with [`on_returns`] trigger set.
/// Returns an instance of `HookTriggers` with [`on_returns`] trigger set.
///
/// [`on_returns`]: #structfield.on_returns
pub fn on_returns() -> Self {
HookTriggers {
on_returns: true,
..Default::default()
}
pub const fn on_returns(mut self) -> Self {
self.on_returns = true;
self
}
/// Returns a new instance of `HookTriggers` with [`every_line`] trigger set.
/// Returns an instance of `HookTriggers` with [`every_line`] trigger set.
///
/// [`every_line`]: #structfield.every_line
pub fn every_line() -> Self {
HookTriggers {
every_line: true,
..Default::default()
}
pub const fn every_line(mut self) -> Self {
self.every_line = true;
self
}
/// Returns a new instance of `HookTriggers` with [`every_nth_instruction`] trigger set.
/// Returns an instance of `HookTriggers` with [`every_nth_instruction`] trigger set.
///
/// [`every_nth_instruction`]: #structfield.every_nth_instruction
pub fn every_nth_instruction(n: u32) -> Self {
HookTriggers {
every_nth_instruction: Some(n),
..Default::default()
}
pub const fn every_nth_instruction(mut self, n: u32) -> Self {
self.every_nth_instruction = Some(n);
self
}
// Compute the mask to pass to `lua_sethook`.
pub(crate) fn mask(&self) -> c_int {
pub(crate) const fn mask(&self) -> c_int {
let mut mask: c_int = 0;
if self.on_calls {
mask |= ffi::LUA_MASKCALL
@@ -326,8 +338,9 @@ impl HookTriggers {
// Returns the `count` parameter to pass to `lua_sethook`, if applicable. Otherwise, zero is
// returned.
pub(crate) fn count(&self) -> c_int {
self.every_nth_instruction.unwrap_or(0) as c_int
pub(crate) const fn count(&self) -> c_int {
let Some(n) = self.every_nth_instruction else { return 0 };
n as c_int
}
}
+5 -6
View File
@@ -71,8 +71,6 @@
//! [`serde::Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html
//! [`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
// mlua types in rustdoc of other crates get linked to here.
#![doc(html_root_url = "https://docs.rs/mlua/0.9.0-beta.1")]
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
// warnings at all.
#![doc(test(attr(deny(warnings))))]
@@ -84,7 +82,6 @@ mod macros;
mod chunk;
mod conversion;
mod error;
mod ffi;
mod function;
mod hook;
mod lua;
@@ -106,7 +103,7 @@ mod value;
pub mod prelude;
pub use crate::{ffi::lua_CFunction, ffi::lua_State};
pub use ffi::{lua_CFunction, lua_State};
pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
pub use crate::error::{Error, ErrorContext, ExternalError, ExternalResult, Result};
@@ -154,8 +151,10 @@ pub mod serde;
extern crate mlua_derive;
// Unstable features
#[cfg(all(feature = "unstable", not(feature = "send")))]
pub use crate::{function::OwnedFunction, table::OwnedTable, userdata::OwnedAnyUserData};
#[cfg(feature = "unstable")]
pub use crate::{
function::OwnedFunction, string::OwnedString, table::OwnedTable, userdata::OwnedAnyUserData,
};
/// Create a type that implements [`AsChunk`] and can capture Rust variables.
///
+97 -54
View File
@@ -3,7 +3,7 @@ use std::cell::{Ref, RefCell, RefMut, UnsafeCell};
use std::ffi::{CStr, CString};
use std::fmt;
use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::mem::MaybeUninit;
use std::ops::Deref;
use std::os::raw::{c_char, c_int, c_void};
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe, Location};
@@ -16,7 +16,6 @@ use rustc_hash::FxHashMap;
use crate::chunk::{AsChunk, Chunk, ChunkMode};
use crate::error::{Error, Result};
use crate::ffi;
use crate::function::Function;
use crate::hook::Debug;
use crate::memory::{MemoryState, ALLOCATOR};
@@ -81,7 +80,7 @@ pub struct LuaInner {
// Data associated with the Lua.
pub(crate) struct ExtraData {
// Same layout as `Lua`
inner: Option<ManuallyDrop<Arc<LuaInner>>>,
inner: MaybeUninit<Arc<LuaInner>>,
registered_userdata: FxHashMap<TypeId, c_int>,
registered_userdata_mt: FxHashMap<*const c_void, Option<TypeId>>,
@@ -121,6 +120,8 @@ pub(crate) struct ExtraData {
#[cfg(not(feature = "luau"))]
hook_callback: Option<HookCallback>,
#[cfg(not(feature = "luau"))]
hook_thread: *mut ffi::lua_State,
#[cfg(feature = "lua54")]
warn_callback: Option<WarnCallback>,
#[cfg(feature = "luau")]
@@ -229,28 +230,21 @@ const MULTIVALUE_POOL_SIZE: usize = 64;
#[cfg_attr(docsrs, doc(cfg(feature = "send")))]
unsafe impl Send for Lua {}
#[cfg(not(feature = "module"))]
impl Drop for Lua {
fn drop(&mut self) {
let _ = self.gc_collect();
}
}
#[cfg(not(feature = "module"))]
impl Drop for LuaInner {
fn drop(&mut self) {
unsafe {
let extra = &mut *self.extra.get();
let drain_iter = extra.wrapped_failure_pool.drain(..);
#[cfg(feature = "async")]
let drain_iter = drain_iter.chain(extra.thread_pool.drain(..));
for index in drain_iter {
ffi::lua_pushnil(extra.ref_thread);
ffi::lua_replace(extra.ref_thread, index);
extra.ref_free.push(index);
}
#[cfg(feature = "luau")]
{
(*ffi::lua_callbacks(self.state())).userdata = ptr::null_mut();
}
mlua_debug_assert!(
ffi::lua_gettop(extra.ref_thread) == extra.ref_stack_top
&& extra.ref_stack_top as usize == extra.ref_free.len(),
"reference leak detected"
);
ffi::lua_close(self.main_state);
}
}
@@ -260,8 +254,8 @@ impl Drop for ExtraData {
fn drop(&mut self) {
#[cfg(feature = "module")]
unsafe {
ManuallyDrop::drop(&mut self.inner.take().unwrap())
};
self.inner.assume_init_drop();
}
*mlua_expect!(self.registry_unref_list.lock(), "unref list poisoned") = None;
if let Some(mem_state) = self.mem_state {
@@ -357,7 +351,24 @@ impl Lua {
/// [`StdLib`]: crate::StdLib
pub unsafe fn unsafe_new_with(libs: StdLib, options: LuaOptions) -> Lua {
#[cfg(not(feature = "luau"))]
ffi::keep_lua_symbols();
{
// Workaround to avoid stripping a few unused Lua symbols that could be imported
// by C modules in unsafe mode
let mut _symbols: Vec<*const extern "C" fn()> = vec![
ffi::lua_atpanic as _,
ffi::lua_isuserdata as _,
ffi::lua_tocfunction as _,
ffi::luaL_loadstring as _,
ffi::luaL_openlibs as _,
];
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
{
_symbols.push(ffi::lua_getglobal as _);
_symbols.push(ffi::lua_setglobal as _);
_symbols.push(ffi::luaL_setfuncs as _);
}
}
Self::inner_new(libs, options)
}
@@ -488,7 +499,7 @@ impl Lua {
// Create ExtraData
let extra = Arc::new(UnsafeCell::new(ExtraData {
inner: None,
inner: MaybeUninit::uninit(),
registered_userdata: FxHashMap::default(),
registered_userdata_mt: FxHashMap::default(),
last_checked_userdata_mt: (ptr::null(), None),
@@ -511,6 +522,8 @@ impl Lua {
waker: NonNull::from(noop_waker_ref()),
#[cfg(not(feature = "luau"))]
hook_callback: None,
#[cfg(not(feature = "luau"))]
hook_thread: ptr::null_mut(),
#[cfg(feature = "lua54")]
warn_callback: None,
#[cfg(feature = "luau")]
@@ -561,7 +574,7 @@ impl Lua {
extra: Arc::clone(&extra),
});
(*extra.get()).inner = Some(ManuallyDrop::new(Arc::clone(&inner)));
(*extra.get()).inner.write(Arc::clone(&inner));
#[cfg(not(feature = "module"))]
Arc::decrement_strong_count(Arc::as_ptr(&inner));
@@ -815,6 +828,11 @@ impl Lua {
/// limited form of execution limits by setting [`HookTriggers.every_nth_instruction`] and
/// erroring once an instruction limit has been reached.
///
/// This method sets a hook function for the main thread (if available) of this Lua instance.
/// If you want to set a hook function for a thread (coroutine), use [`Thread::set_hook()`] instead.
///
/// Please note you cannot have more than one hook function set at a time for this Lua instance.
///
/// # Example
///
/// Shows each line number of code being executed by the Lua interpreter.
@@ -823,7 +841,7 @@ impl Lua {
/// # use mlua::{Lua, HookTriggers, Result};
/// # fn main() -> Result<()> {
/// let lua = Lua::new();
/// lua.set_hook(HookTriggers::every_line(), |_lua, debug| {
/// lua.set_hook(HookTriggers::EVERY_LINE, |_lua, debug| {
/// println!("line {}", debug.curr_line());
/// Ok(())
/// })?;
@@ -842,47 +860,71 @@ impl Lua {
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F) -> Result<()>
where
F: 'static + MaybeSend + Fn(&Lua, Debug) -> Result<()>,
F: Fn(&Lua, Debug) -> Result<()> + MaybeSend + 'static,
{
unsafe {
let state = get_main_state(self.main_state).ok_or(Error::MainThreadNotAvailable)?;
self.set_thread_hook(state, triggers, callback);
}
Ok(())
}
/// Sets a 'hook' function for a thread (coroutine).
#[cfg(not(feature = "luau"))]
pub(crate) unsafe fn set_thread_hook<F>(
&self,
state: *mut ffi::lua_State,
triggers: HookTriggers,
callback: F,
) where
F: Fn(&Lua, Debug) -> Result<()> + MaybeSend + 'static,
{
unsafe extern "C" fn hook_proc(state: *mut ffi::lua_State, ar: *mut ffi::lua_Debug) {
let lua = match Lua::try_from_ptr(state) {
Some(lua) => lua,
None => return,
};
let extra = lua.extra.get();
let extra = extra_data(state);
if extra.is_null() {
return;
}
if (*extra).hook_thread != state {
// Hook was destined for a different thread, ignore
ffi::lua_sethook(state, None, 0, 0);
return;
}
callback_error_ext(state, extra, move |_| {
let debug = Debug::new(&lua, ar);
let hook_cb = (*extra).hook_callback.clone();
let hook_cb = mlua_expect!(hook_cb, "no hook callback set in hook_proc");
if Arc::strong_count(&hook_cb) > 2 {
return Ok(()); // Don't allow recursion
}
hook_cb(&lua, debug)
let lua: &Lua = mem::transmute((*extra).inner.assume_init_ref());
let _guard = StateGuard::new(&lua.0, state);
let debug = Debug::new(lua, ar);
hook_cb(lua, debug)
})
}
unsafe {
let state = get_main_state(self.main_state).ok_or(Error::MainThreadNotAvailable)?;
(*self.extra.get()).hook_callback = Some(Arc::new(callback));
ffi::lua_sethook(state, Some(hook_proc), triggers.mask(), triggers.count());
}
Ok(())
(*self.extra.get()).hook_callback = Some(Arc::new(callback));
(*self.extra.get()).hook_thread = state; // Mark for what thread the hook is set
ffi::lua_sethook(state, Some(hook_proc), triggers.mask(), triggers.count());
}
/// Removes any hook previously set by `set_hook`.
/// Removes any hook previously set by [`Lua::set_hook()`] or [`Thread::set_hook()`].
///
/// This function has no effect if a hook was not previously set.
#[cfg(not(feature = "luau"))]
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
pub fn remove_hook(&self) {
unsafe {
// If main_state is not available, then sethook wasn't called.
let state = match get_main_state(self.main_state) {
Some(state) => state,
None => return,
let state = self.state();
ffi::lua_sethook(state, None, 0, 0);
match get_main_state(self.main_state) {
Some(main_state) if !ptr::eq(state, main_state) => {
// If main_state is different from state, remove hook from it too
ffi::lua_sethook(main_state, None, 0, 0);
}
_ => {}
};
(*self.extra.get()).hook_callback = None;
ffi::lua_sethook(state, None, 0, 0);
(*self.extra.get()).hook_thread = ptr::null_mut();
}
}
@@ -950,7 +992,8 @@ impl Lua {
if Arc::strong_count(&interrupt_cb) > 2 {
return Ok(VmState::Continue); // Don't allow recursion
}
let lua: &Lua = mem::transmute((*extra).inner.as_ref().unwrap());
let lua: &Lua = mem::transmute((*extra).inner.assume_init_ref());
let _guard = StateGuard::new(&lua.0, state);
interrupt_cb(lua)
});
match result {
@@ -990,7 +1033,7 @@ impl Lua {
{
unsafe extern "C" fn warn_proc(ud: *mut c_void, msg: *const c_char, tocont: c_int) {
let extra = ud as *mut ExtraData;
let lua: &Lua = mem::transmute((*extra).inner.as_ref().unwrap());
let lua: &Lua = mem::transmute((*extra).inner.assume_init_ref());
callback_error_ext(lua.state(), extra, |_| {
let cb = mlua_expect!(
(*extra).warn_callback.as_ref(),
@@ -2422,15 +2465,15 @@ impl Lua {
}
}
#[cfg(feature = "unstable")]
#[cfg(all(feature = "unstable", not(feature = "send")))]
pub(crate) fn adopt_owned_ref(&self, loref: crate::types::LuaOwnedRef) -> LuaRef {
assert!(
Arc::ptr_eq(&loref.lua.0, &self.0),
Arc::ptr_eq(&loref.inner, &self.0),
"Lua instance passed Value created from a different main Lua state"
);
let index = loref.index;
unsafe {
ptr::read(&loref.lua);
ptr::read(&loref.inner);
mem::forget(loref);
}
LuaRef::new(self, index)
@@ -2615,7 +2658,7 @@ impl Lua {
check_stack(state, ffi::LUA_MINSTACK - nargs)?;
}
let lua: &Lua = mem::transmute((*extra).inner.as_ref().unwrap());
let lua: &Lua = mem::transmute((*extra).inner.assume_init_ref());
let _guard = StateGuard::new(&lua.0, state);
let mut args = MultiValue::new_or_pooled(lua);
@@ -2697,7 +2740,7 @@ impl Lua {
check_stack(state, ffi::LUA_MINSTACK - nargs)?;
}
let lua: &Lua = mem::transmute((*extra).inner.as_ref().unwrap());
let lua: &Lua = mem::transmute((*extra).inner.assume_init_ref());
let _guard = StateGuard::new(&lua.0, state);
let mut args = MultiValue::new_or_pooled(lua);
@@ -2742,7 +2785,7 @@ impl Lua {
check_stack(state, ffi::LUA_MINSTACK - nargs)?;
}
let lua: &Lua = mem::transmute((*extra).inner.as_ref().unwrap());
let lua: &Lua = mem::transmute((*extra).inner.assume_init_ref());
let _guard = StateGuard::new(&lua.0, state);
let fut = &mut (*upvalue).data;
@@ -2945,7 +2988,7 @@ impl Lua {
if extra.is_null() {
return None;
}
(*extra).inner.as_ref().map(|lua| Lua(Arc::clone(lua)))
Some(Lua(Arc::clone((*extra).inner.assume_init_ref())))
}
#[inline]
@@ -2959,8 +3002,8 @@ impl Lua {
#[cfg(feature = "unstable")]
#[inline]
pub(crate) fn clone(&self) -> Self {
Lua(Arc::clone(&self.0))
pub(crate) fn clone(&self) -> Arc<LuaInner> {
Arc::clone(&self.0)
}
}
-1
View File
@@ -4,7 +4,6 @@ use std::string::String as StdString;
use crate::chunk::ChunkMode;
use crate::error::{Error, Result};
use crate::ffi;
use crate::lua::Lua;
use crate::table::Table;
use crate::util::{check_stack, StackGuard};
-1
View File
@@ -2,7 +2,6 @@ use std::alloc::{self, Layout};
use std::os::raw::c_void;
use std::ptr;
use crate::ffi;
#[cfg(feature = "luau")]
use crate::lua::ExtraData;
+2 -2
View File
@@ -36,9 +36,9 @@ pub use crate::{
SerializeOptions as LuaSerializeOptions,
};
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[cfg(feature = "unstable")]
#[doc(no_inline)]
pub use crate::{
OwnedAnyUserData as LuaOwnedAnyUserData, OwnedFunction as LuaOwnedFunction,
OwnedTable as LuaOwnedTable,
OwnedString as LuaOwnedString, OwnedTable as LuaOwnedTable,
};
-1
View File
@@ -8,7 +8,6 @@ use std::os::raw::c_int;
use serde::Serialize;
use crate::error::{Error, Result};
use crate::ffi;
use crate::function::Function;
use crate::lua::Lua;
use crate::types::{Callback, CallbackUpvalue, LuaRef, MaybeSend};
+23 -1
View File
@@ -345,9 +345,31 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
}
}
#[inline]
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
match self.value {
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_unit(),
_ => self.deserialize_any(visitor),
}
}
#[inline]
fn deserialize_unit_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
match self.value {
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_unit(),
_ => self.deserialize_any(visitor),
}
}
serde::forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes
byte_buf unit unit_struct identifier ignored_any
byte_buf identifier ignored_any
}
}
-1
View File
@@ -6,7 +6,6 @@ use std::ptr;
use serde::{Deserialize, Serialize};
use crate::error::Result;
use crate::ffi;
use crate::lua::Lua;
use crate::private::Sealed;
use crate::table::Table;
-1
View File
@@ -4,7 +4,6 @@ use serde::{ser, Serialize};
use super::LuaSerdeExt;
use crate::error::{Error, Result};
use crate::ffi;
use crate::lua::Lua;
use crate::string::String;
use crate::table::Table;
+60 -1
View File
@@ -11,7 +11,6 @@ use {
};
use crate::error::{Error, Result};
use crate::ffi;
use crate::types::LuaRef;
/// Handle to an internal Lua string.
@@ -20,6 +19,27 @@ use crate::types::LuaRef;
#[derive(Clone)]
pub struct String<'lua>(pub(crate) LuaRef<'lua>);
/// Owned handle to an internal Lua string.
///
/// The owned handle holds a *strong* reference to the current Lua instance.
/// Be warned, if you place it into a Lua type (eg. [`UserData`] or a Rust callback), it is *very easy*
/// to accidentally cause reference cycles that would prevent destroying Lua instance.
///
/// [`UserData`]: crate::UserData
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
#[derive(Clone)]
pub struct OwnedString(pub(crate) crate::types::LuaOwnedRef);
#[cfg(feature = "unstable")]
impl OwnedString {
/// Get borrowed handle to the underlying Lua string.
#[cfg_attr(feature = "send", allow(unused))]
pub const fn to_ref(&self) -> String {
String(self.0.to_ref())
}
}
impl<'lua> String<'lua> {
/// Get a `&str` slice if the Lua string is valid UTF-8.
///
@@ -122,6 +142,14 @@ impl<'lua> String<'lua> {
let ref_thread = self.0.lua.ref_thread();
unsafe { ffi::lua_topointer(ref_thread, self.0.index) }
}
/// Convert this handle to owned version.
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
#[inline]
pub fn into_owned(self) -> OwnedString {
OwnedString(self.0.into_owned())
}
}
impl<'lua> fmt::Debug for String<'lua> {
@@ -203,6 +231,37 @@ impl<'lua> Serialize for String<'lua> {
}
}
// Additional shortcuts
#[cfg(feature = "unstable")]
impl OwnedString {
/// Get a `&str` slice if the Lua string is valid UTF-8.
///
/// This is a shortcut for [`String::to_str()`].
#[inline]
pub fn to_str(&self) -> Result<&str> {
let s = self.to_ref();
// Reattach lifetime to &self
unsafe { std::mem::transmute(s.to_str()) }
}
/// Get the bytes that make up this string.
///
/// This is a shortcut for [`String::as_bytes()`].
#[inline]
pub fn as_bytes(&self) -> &[u8] {
let s = self.to_ref();
// Reattach lifetime to &self
unsafe { std::mem::transmute(s.as_bytes()) }
}
}
#[cfg(feature = "unstable")]
impl fmt::Debug for OwnedString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.to_ref().fmt(f)
}
}
#[cfg(test)]
mod assertions {
use super::*;
+48 -4
View File
@@ -1,3 +1,5 @@
use std::collections::HashSet;
use std::fmt;
use std::marker::PhantomData;
use std::os::raw::c_void;
@@ -9,7 +11,6 @@ use {
};
use crate::error::{Error, Result};
use crate::ffi;
use crate::function::Function;
use crate::private::Sealed;
use crate::types::{Integer, LuaRef};
@@ -20,10 +21,16 @@ use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Nil, Value};
use {futures_core::future::LocalBoxFuture, futures_util::future};
/// Handle to an internal Lua table.
#[derive(Clone, Debug)]
#[derive(Clone)]
pub struct Table<'lua>(pub(crate) LuaRef<'lua>);
/// Owned handle to an internal Lua table.
///
/// The owned handle holds a *strong* reference to the current Lua instance.
/// Be warned, if you place it into a Lua type (eg. [`UserData`] or a Rust callback), it is *very easy*
/// to accidentally cause reference cycles that would prevent destroying Lua instance.
///
/// [`UserData`]: crate::UserData
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
#[derive(Clone, Debug)]
@@ -32,6 +39,7 @@ pub struct OwnedTable(pub(crate) crate::types::LuaOwnedRef);
#[cfg(feature = "unstable")]
impl OwnedTable {
/// Get borrowed handle to the underlying Lua table.
#[cfg_attr(feature = "send", allow(unused))]
pub const fn to_ref(&self) -> Table {
Table(self.0.to_ref())
}
@@ -583,8 +591,8 @@ impl<'lua> Table<'lua> {
}
/// Convert this handle to owned version.
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
#[inline]
pub fn into_owned(self) -> OwnedTable {
OwnedTable(self.0.into_owned())
@@ -738,6 +746,42 @@ impl<'lua> Table<'lua> {
}
Ok(())
}
pub(crate) fn fmt_pretty(
&self,
fmt: &mut fmt::Formatter,
ident: usize,
visited: &mut HashSet<*const c_void>,
) -> fmt::Result {
visited.insert(self.to_pointer());
let t = self.clone();
// Collect key/value pairs into a vector so we can sort them
let mut pairs = t.pairs::<Value, Value>().flatten().collect::<Vec<_>>();
// Sort keys
pairs.sort_by(|(a, _), (b, _)| a.cmp(b));
if pairs.is_empty() {
return write!(fmt, "{{}}");
}
writeln!(fmt, "{{")?;
for (key, value) in pairs {
write!(fmt, "{}[", " ".repeat(ident + 2))?;
key.fmt_pretty(fmt, false, ident + 2, visited)?;
write!(fmt, "] = ")?;
value.fmt_pretty(fmt, true, ident + 2, visited)?;
writeln!(fmt, ",")?;
}
write!(fmt, "{}}}", " ".repeat(ident))
}
}
impl fmt::Debug for Table<'_> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
if fmt.alternate() {
return self.fmt_pretty(fmt, 0, &mut HashSet::new());
}
fmt.write_fmt(format_args!("Table({:?})", self.0))
}
}
impl<'lua> PartialEq for Table<'lua> {
+27 -2
View File
@@ -2,7 +2,8 @@ use std::cmp;
use std::os::raw::c_int;
use crate::error::{Error, Result};
use crate::ffi;
#[allow(unused)]
use crate::lua::Lua;
use crate::types::LuaRef;
use crate::util::{check_stack, error_traceback_thread, pop_error, StackGuard};
use crate::value::{FromLuaMulti, IntoLuaMulti};
@@ -14,10 +15,16 @@ use crate::value::{FromLuaMulti, IntoLuaMulti};
))]
use crate::function::Function;
#[cfg(not(feature = "luau"))]
use crate::{
hook::{Debug, HookTriggers},
types::MaybeSend,
};
#[cfg(feature = "async")]
use {
crate::{
lua::{Lua, ASYNC_POLL_PENDING},
lua::ASYNC_POLL_PENDING,
value::{MultiValue, Value},
},
futures_core::{future::Future, stream::Stream},
@@ -56,6 +63,7 @@ pub struct Thread<'lua>(pub(crate) LuaRef<'lua>);
/// [`Stream`]: futures_core::stream::Stream
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct AsyncThread<'lua, R> {
thread: Thread<'lua>,
args0: Option<Result<MultiValue<'lua>>>,
@@ -177,6 +185,23 @@ impl<'lua> Thread<'lua> {
}
}
/// Sets a 'hook' function that will periodically be called as Lua code executes.
///
/// This function is similar or [`Lua::set_hook()`] except that it sets for the thread.
/// To remove a hook call [`Lua::remove_hook()`].
#[cfg(not(feature = "luau"))]
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F)
where
F: Fn(&Lua, Debug) -> Result<()> + MaybeSend + 'static,
{
let lua = self.0.lua;
unsafe {
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.0.index);
lua.set_thread_hook(thread_state, triggers, callback);
}
}
/// Resets a thread
///
/// In [Lua 5.4]: cleans its call stack and closes all pending to-be-closed variables.
+11 -16
View File
@@ -12,13 +12,15 @@ use std::ffi::CStr;
use futures_core::future::LocalBoxFuture;
use crate::error::Result;
use crate::ffi;
#[cfg(not(feature = "luau"))]
use crate::hook::Debug;
use crate::lua::{ExtraData, Lua};
use crate::util::{assert_stack, StackGuard};
use crate::value::MultiValue;
#[cfg(feature = "unstable")]
use {crate::lua::LuaInner, std::marker::PhantomData};
/// Type of Lua integer numbers.
pub type Integer = ffi::lua_Integer;
/// Type of Lua floating point numbers.
@@ -238,9 +240,9 @@ impl<'lua> PartialEq for LuaRef<'lua> {
#[cfg(feature = "unstable")]
pub(crate) struct LuaOwnedRef {
pub(crate) lua: Lua,
pub(crate) inner: Arc<LuaInner>,
pub(crate) index: c_int,
_non_send: std::marker::PhantomData<*const ()>,
_non_send: PhantomData<*const ()>,
}
#[cfg(feature = "unstable")]
@@ -260,31 +262,24 @@ impl Clone for LuaOwnedRef {
#[cfg(feature = "unstable")]
impl Drop for LuaOwnedRef {
fn drop(&mut self) {
self.lua.drop_ref_index(self.index);
let lua: &Lua = unsafe { mem::transmute(&self.inner) };
lua.drop_ref_index(self.index);
}
}
#[cfg(feature = "unstable")]
impl LuaOwnedRef {
pub(crate) const fn new(lua: Lua, index: c_int) -> Self {
#[cfg(feature = "send")]
{
let _lua = lua;
let _index = index;
panic!("mlua must be compiled without \"send\" feature to use Owned types");
}
#[cfg(not(feature = "send"))]
pub(crate) const fn new(inner: Arc<LuaInner>, index: c_int) -> Self {
LuaOwnedRef {
lua,
inner,
index,
_non_send: std::marker::PhantomData,
_non_send: PhantomData,
}
}
pub(crate) const fn to_ref(&self) -> LuaRef {
LuaRef {
lua: &self.lua,
lua: unsafe { mem::transmute(&self.inner) },
index: self.index,
drop: false,
}
+37 -4
View File
@@ -17,7 +17,6 @@ use {
};
use crate::error::{Error, Result};
use crate::ffi;
use crate::function::Function;
use crate::lua::Lua;
use crate::table::{Table, TablePairs};
@@ -740,6 +739,11 @@ impl Serialize for UserDataSerializeError {
#[derive(Clone, Debug)]
pub struct AnyUserData<'lua>(pub(crate) LuaRef<'lua>);
/// Owned handle to an internal Lua userdata.
///
/// The owned handle holds a *strong* reference to the current Lua instance.
/// Be warned, if you place it into a Lua type (eg. [`UserData`] or a Rust callback), it is *very easy*
/// to accidentally cause reference cycles that would prevent destroying Lua instance.
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
#[derive(Clone, Debug)]
@@ -747,6 +751,8 @@ pub struct OwnedAnyUserData(pub(crate) crate::types::LuaOwnedRef);
#[cfg(feature = "unstable")]
impl OwnedAnyUserData {
/// Get borrowed handle to the underlying Lua userdata.
#[cfg_attr(feature = "send", allow(unused))]
pub const fn to_ref(&self) -> AnyUserData {
AnyUserData(self.0.to_ref())
}
@@ -1030,8 +1036,9 @@ impl<'lua> AnyUserData<'lua> {
}
}
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
/// Convert this handle to owned version.
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
#[inline]
pub fn into_owned(self) -> OwnedAnyUserData {
OwnedAnyUserData(self.0.into_owned())
@@ -1121,6 +1128,32 @@ unsafe fn getuservalue_table(state: *mut ffi::lua_State, idx: c_int) -> c_int {
return ffi::lua_getuservalue(state, idx);
}
// Additional shortcuts
#[cfg(feature = "unstable")]
impl OwnedAnyUserData {
/// Borrow this userdata immutably if it is of type `T`.
///
/// This is a shortcut for [`AnyUserData::borrow()`]
#[inline]
pub fn borrow<T: 'static>(&self) -> Result<Ref<T>> {
let ud = self.to_ref();
let t = ud.borrow::<T>()?;
// Reattach lifetime to &self
Ok(unsafe { mem::transmute::<Ref<T>, Ref<T>>(t) })
}
/// Borrow this userdata mutably if it is of type `T`.
///
/// This is a shortcut for [`AnyUserData::borrow_mut()`]
#[inline]
pub fn borrow_mut<T: 'static>(&self) -> Result<RefMut<T>> {
let ud = self.to_ref();
let t = ud.borrow_mut::<T>()?;
// Reattach lifetime to &self
Ok(unsafe { mem::transmute::<RefMut<T>, RefMut<T>>(t) })
}
}
/// Handle to a `UserData` metatable.
#[derive(Clone, Debug)]
pub struct UserDataMetatable<'lua>(pub(crate) Table<'lua>);
@@ -1286,6 +1319,6 @@ mod assertions {
static_assertions::assert_not_impl_any!(AnyUserData: Send);
#[cfg(feature = "unstable")]
#[cfg(all(feature = "unstable", not(feature = "send")))]
static_assertions::assert_not_impl_any!(OwnedAnyUserData: Send);
}
-1
View File
@@ -5,7 +5,6 @@ use std::string::String as StdString;
use std::sync::{Arc, Mutex, RwLock};
use crate::error::{Error, Result};
use crate::ffi;
use crate::lua::Lua;
use crate::types::{Callback, MaybeSend};
use crate::userdata::{
-1
View File
@@ -11,7 +11,6 @@ use once_cell::sync::Lazy;
use rustc_hash::FxHashMap;
use crate::error::{Error, Result};
use crate::ffi;
use crate::memory::MemoryState;
static METATABLE_CACHE: Lazy<FxHashMap<TypeId, u8>> = Lazy::new(|| {
+97 -3
View File
@@ -1,8 +1,10 @@
use std::cmp::Ordering;
use std::collections::HashSet;
use std::iter::{self, FromIterator};
use std::ops::Index;
use std::os::raw::c_void;
use std::sync::Arc;
use std::{ptr, slice, str, vec};
use std::{fmt, ptr, slice, str, vec};
#[cfg(feature = "serialize")]
use {
@@ -12,7 +14,6 @@ use {
};
use crate::error::{Error, Result};
use crate::ffi;
use crate::function::Function;
use crate::lua::Lua;
use crate::string::String;
@@ -24,7 +25,7 @@ use crate::userdata::AnyUserData;
/// A dynamically typed Lua value. The `String`, `Table`, `Function`, `Thread`, and `UserData`
/// variants contain handle types into the internal Lua state. It is a logic error to mix handle
/// types between separate `Lua` instances, and doing so will result in a panic.
#[derive(Debug, Clone)]
#[derive(Clone)]
pub enum Value<'lua> {
/// The Lua value `nil`.
Nil,
@@ -121,6 +122,99 @@ impl<'lua> Value<'lua> {
}
}
}
// Compares two values.
// Used to sort values for Debug printing.
pub(crate) fn cmp(&self, other: &Self) -> Ordering {
fn cmp_num(a: Number, b: Number) -> Ordering {
match (a, b) {
_ if a < b => Ordering::Less,
_ if a > b => Ordering::Greater,
_ => Ordering::Equal,
}
}
match (self, other) {
// Nil
(Value::Nil, Value::Nil) => Ordering::Equal,
(Value::Nil, _) => Ordering::Less,
(_, Value::Nil) => Ordering::Greater,
// Null (a special case)
(Value::LightUserData(ud1), Value::LightUserData(ud2)) if ud1 == ud2 => Ordering::Equal,
(Value::LightUserData(ud1), _) if ud1.0.is_null() => Ordering::Less,
(_, Value::LightUserData(ud2)) if ud2.0.is_null() => Ordering::Greater,
// Boolean
(Value::Boolean(a), Value::Boolean(b)) => a.cmp(b),
(Value::Boolean(_), _) => Ordering::Less,
(_, Value::Boolean(_)) => Ordering::Greater,
// Integer && Number
(Value::Integer(a), Value::Integer(b)) => a.cmp(b),
(&Value::Integer(a), &Value::Number(b)) => cmp_num(a as Number, b),
(&Value::Number(a), &Value::Integer(b)) => cmp_num(a, b as Number),
(&Value::Number(a), &Value::Number(b)) => cmp_num(a, b),
(Value::Integer(_) | Value::Number(_), _) => Ordering::Less,
(_, Value::Integer(_) | Value::Number(_)) => Ordering::Greater,
// String
(Value::String(a), Value::String(b)) => a.as_bytes().cmp(b.as_bytes()),
(Value::String(_), _) => Ordering::Less,
(_, Value::String(_)) => Ordering::Greater,
// Other variants can be randomly ordered
(a, b) => a.to_pointer().cmp(&b.to_pointer()),
}
}
pub(crate) fn fmt_pretty(
&self,
fmt: &mut fmt::Formatter,
recursive: bool,
ident: usize,
visited: &mut HashSet<*const c_void>,
) -> fmt::Result {
match self {
Value::Nil => write!(fmt, "nil"),
Value::Boolean(b) => write!(fmt, "{b}"),
Value::LightUserData(ud) if ud.0.is_null() => write!(fmt, "null"),
Value::LightUserData(ud) => write!(fmt, "<lightuserdata {:?}>", ud.0),
Value::Integer(i) => write!(fmt, "{i}"),
Value::Number(n) => write!(fmt, "{n}"),
#[cfg(feature = "luau")]
Value::Vector(x, y, z) => write!(fmt, "vector({x}, {y}, {z})"),
Value::String(s) => write!(fmt, "{s:?}"),
Value::Table(t) if recursive && !visited.contains(&t.to_pointer()) => {
t.fmt_pretty(fmt, ident, visited)
}
t @ Value::Table(_) => write!(fmt, "<table {:?}>", t.to_pointer()),
f @ Value::Function(_) => write!(fmt, "<function {:?}>", f.to_pointer()),
t @ Value::Thread(_) => write!(fmt, "<thread {:?}>", t.to_pointer()),
// TODO: Show type name for registered userdata
u @ Value::UserData(_) => write!(fmt, "<userdata {:?}>", u.to_pointer()),
Value::Error(e) if recursive => write!(fmt, "{e:?}"),
Value::Error(_) => write!(fmt, "<error>"),
}
}
}
impl fmt::Debug for Value<'_> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
if fmt.alternate() {
return self.fmt_pretty(fmt, true, 0, &mut HashSet::new());
}
match self {
Value::Nil => write!(fmt, "Nil"),
Value::Boolean(b) => write!(fmt, "Boolean({b})"),
Value::LightUserData(ud) => write!(fmt, "{ud:?}"),
Value::Integer(i) => write!(fmt, "Integer({i})"),
Value::Number(n) => write!(fmt, "Number({n})"),
#[cfg(feature = "luau")]
Value::Vector(x, y, z) => write!(fmt, "Vector({x}, {y}, {z})"),
Value::String(s) => write!(fmt, "String({s:?})"),
Value::Table(t) => write!(fmt, "{t:?}"),
Value::Function(f) => write!(fmt, "{f:?}"),
Value::Thread(t) => write!(fmt, "{t:?}"),
Value::UserData(ud) => write!(fmt, "{ud:?}"),
Value::Error(e) => write!(fmt, "Error({e:?})"),
}
}
}
impl<'lua> PartialEq for Value<'lua> {
+8
View File
@@ -0,0 +1,8 @@
[lua54_coverage]
features = "lua54,vendored,async,serialize,macros,unstable"
[lua51_coverage]
features = "lua51,vendored,async,serialize,macros,unstable"
[luau_coverage]
features = "luau,async,serialize,macros,unstable"
+37 -2
View File
@@ -9,7 +9,7 @@ use futures_util::stream::TryStreamExt;
use mlua::{
AnyUserDataExt, Error, Function, Lua, LuaOptions, Result, StdLib, Table, TableExt, UserData,
UserDataMethods,
UserDataMethods, Value,
};
#[tokio::test]
@@ -26,7 +26,6 @@ async fn test_async_function() -> Result<()> {
Ok(())
}
#[cfg(feature = "unstable")]
#[tokio::test]
async fn test_async_function_wrap() -> Result<()> {
let lua = Lua::new();
@@ -271,6 +270,24 @@ async fn test_async_thread() -> Result<()> {
Ok(())
}
#[test]
fn test_async_thread_capture() -> Result<()> {
let lua = Lua::new();
let f = lua.create_async_function(move |_lua, v: Value| async move {
tokio::task::yield_now().await;
drop(v);
Ok(())
})?;
let thread = lua.create_thread(f)?;
// After first resume, `v: Value` is captured in the coroutine
thread.resume::<_, ()>("abc").unwrap();
drop(thread);
Ok(())
}
#[tokio::test]
async fn test_async_table() -> Result<()> {
let options = LuaOptions::new().thread_pool_size(4);
@@ -467,3 +484,21 @@ async fn test_async_thread_error() -> Result<()> {
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[tokio::test]
async fn test_owned_async_call() -> Result<()> {
let lua = Lua::new();
let hello = lua
.create_async_function(|_, name: String| async move {
Delay::new(Duration::from_millis(10)).await;
Ok(format!("hello, {}!", name))
})?
.into_owned();
drop(lua);
assert_eq!(hello.call_async::<_, String>("alex").await?, "hello, alex!");
Ok(())
}
+26 -20
View File
@@ -1,22 +1,28 @@
error[E0277]: the type `UnsafeCell<mlua::lua::LuaInner>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> tests/compile/lua_norefunwindsafe.rs:7:5
|
7 | catch_unwind(|| lua.create_table().unwrap());
| ^^^^^^^^^^^^ `UnsafeCell<mlua::lua::LuaInner>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::LuaInner>`
= note: required because it appears within the type `alloc::sync::ArcInner<UnsafeCell<mlua::lua::LuaInner>>`
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<UnsafeCell<mlua::lua::LuaInner>>>`
= note: required because it appears within the type `Arc<UnsafeCell<mlua::lua::LuaInner>>`
= note: required because it appears within the type `Lua`
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
error[E0277]: the type `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
7 | catch_unwind(|| lua.create_table().unwrap());
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
| |
| required by a bound introduced by this call
|
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::ExtraData>`
= note: required because it appears within the type `ArcInner<UnsafeCell<ExtraData>>`
= note: required because it appears within the type `PhantomData<ArcInner<UnsafeCell<ExtraData>>>`
= note: required because it appears within the type `Arc<UnsafeCell<ExtraData>>`
= note: required because it appears within the type `LuaInner`
= note: required because it appears within the type `ArcInner<LuaInner>`
= note: required because it appears within the type `PhantomData<ArcInner<LuaInner>>`
= note: required because it appears within the type `Arc<LuaInner>`
= note: required because it appears within the type `Lua`
= note: required for `&Lua` to implement `UnwindSafe`
note: required because it's used within this closure
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
7 | catch_unwind(|| lua.create_table().unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
7 | catch_unwind(|| lua.create_table().unwrap());
| ^^
note: required by a bound in `catch_unwind`
--> $RUST/std/src/panic.rs
|
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
| ^^^^^^^^^^ required by this bound in `catch_unwind`
--> $RUST/std/src/panic.rs
|
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
| ^^^^^^^^^^ required by this bound in `catch_unwind`
+22 -23
View File
@@ -1,26 +1,25 @@
error[E0277]: `Rc<Cell<i32>>` cannot be sent between threads safely
--> tests/compile/non_send.rs:11:9
|
11 | lua.create_function(move |_, ()| {
| _________^^^^^^^^^^^^^^^_-
| | |
| | `Rc<Cell<i32>>` cannot be sent between threads safely
12 | | Ok(data.get())
13 | | })?
| |_____- within this `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`
|
= help: within `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`, the trait `Send` is not implemented for `Rc<Cell<i32>>`
--> tests/compile/non_send.rs:11:25
|
11 | lua.create_function(move |_, ()| {
| --------------- ^-----------
| | |
| _________|_______________within this `[closure@$DIR/tests/compile/non_send.rs:11:25: 11:37]`
| | |
| | required by a bound introduced by this call
12 | | Ok(data.get())
13 | | })?
| |_____^ `Rc<Cell<i32>>` cannot be sent between threads safely
|
= help: within `[closure@$DIR/tests/compile/non_send.rs:11:25: 11:37]`, the trait `Send` is not implemented for `Rc<Cell<i32>>`
note: required because it's used within this closure
--> tests/compile/non_send.rs:11:25
|
11 | lua.create_function(move |_, ()| {
| _________________________^
12 | | Ok(data.get())
13 | | })?
| |_____^
= note: required because of the requirements on the impl of `mlua::types::MaybeSend` for `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`
--> tests/compile/non_send.rs:11:25
|
11 | lua.create_function(move |_, ()| {
| ^^^^^^^^^^^^
= note: required for `[closure@$DIR/tests/compile/non_send.rs:11:25: 11:37]` to implement `mlua::types::MaybeSend`
note: required by a bound in `Lua::create_function`
--> src/lua.rs
|
| F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
| ^^^^^^^^^ required by this bound in `Lua::create_function`
--> src/lua.rs
|
| F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
| ^^^^^^^^^ required by this bound in `Lua::create_function`
+28 -22
View File
@@ -1,24 +1,30 @@
error[E0277]: the type `UnsafeCell<mlua::lua::LuaInner>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> tests/compile/ref_nounwindsafe.rs:8:5
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ^^^^^^^^^^^^ `UnsafeCell<mlua::lua::LuaInner>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::LuaInner>`
= note: required because it appears within the type `alloc::sync::ArcInner<UnsafeCell<mlua::lua::LuaInner>>`
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<UnsafeCell<mlua::lua::LuaInner>>>`
= note: required because it appears within the type `Arc<UnsafeCell<mlua::lua::LuaInner>>`
= note: required because it appears within the type `Lua`
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
= note: required because it appears within the type `LuaTable<'_>`
error[E0277]: the type `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> tests/compile/ref_nounwindsafe.rs:8:18
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
| |
| required by a bound introduced by this call
|
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::ExtraData>`
= note: required because it appears within the type `ArcInner<UnsafeCell<ExtraData>>`
= note: required because it appears within the type `PhantomData<ArcInner<UnsafeCell<ExtraData>>>`
= note: required because it appears within the type `Arc<UnsafeCell<ExtraData>>`
= note: required because it appears within the type `LuaInner`
= note: required because it appears within the type `ArcInner<LuaInner>`
= note: required because it appears within the type `PhantomData<ArcInner<LuaInner>>`
= note: required because it appears within the type `Arc<LuaInner>`
= note: required because it appears within the type `Lua`
= note: required for `&Lua` to implement `UnwindSafe`
= note: required because it appears within the type `LuaRef<'_>`
= note: required because it appears within the type `Table<'_>`
note: required because it's used within this closure
--> tests/compile/ref_nounwindsafe.rs:8:18
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
--> tests/compile/ref_nounwindsafe.rs:8:18
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ^^^^^^^
note: required by a bound in `catch_unwind`
--> $RUST/std/src/panic.rs
|
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
| ^^^^^^^^^^ required by this bound in `catch_unwind`
--> $RUST/std/src/panic.rs
|
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
| ^^^^^^^^^^ required by this bound in `catch_unwind`
+16 -12
View File
@@ -1,15 +1,17 @@
error[E0597]: `lua` does not live long enough
--> tests/compile/static_callback_args.rs:12:5
|
12 | / lua.create_function(|_, table: Table| {
13 | | BAD_TIME.with(|bt| {
| |_________-
10 | let lua = Lua::new();
| --- binding `lua` declared here
11 |
12 | / lua.create_function(|_, table: Table| {
13 | |/ BAD_TIME.with(|bt| {
14 | || *bt.borrow_mut() = Some(table);
15 | || });
| ||__________- argument requires that `lua` is borrowed for `'static`
16 | | Ok(())
17 | | })?
| |______^ borrowed value does not live long enough
16 | | Ok(())
17 | | })?
| |_______^ borrowed value does not live long enough
...
32 | }
| - `lua` dropped here while still borrowed
@@ -17,15 +19,17 @@ error[E0597]: `lua` does not live long enough
error[E0505]: cannot move out of `lua` because it is borrowed
--> tests/compile/static_callback_args.rs:22:10
|
12 | / lua.create_function(|_, table: Table| {
13 | | BAD_TIME.with(|bt| {
| |_________-
10 | let lua = Lua::new();
| --- binding `lua` declared here
11 |
12 | / lua.create_function(|_, table: Table| {
13 | |/ BAD_TIME.with(|bt| {
14 | || *bt.borrow_mut() = Some(table);
15 | || });
| ||__________- argument requires that `lua` is borrowed for `'static`
16 | | Ok(())
17 | | })?
| |______- borrow of `lua` occurs here
16 | | Ok(())
17 | | })?
| |_______- borrow of `lua` occurs here
...
22 | drop(lua);
| ^^^ move out of `lua` occurs here
+15
View File
@@ -0,0 +1,15 @@
use mlua::{Lua, Result};
#[test]
fn test_debug_format() -> Result<()> {
let lua = Lua::new();
// Globals
let globals = lua.globals();
let dump = format!("{globals:#?}");
assert!(dump.starts_with("{\n [\"_G\"] = <table"));
// TODO: Other cases
Ok(())
}
+42 -1
View File
@@ -168,7 +168,6 @@ fn test_function_info() -> Result<()> {
Ok(())
}
#[cfg(feature = "unstable")]
#[test]
fn test_function_wrap() -> Result<()> {
use mlua::Error;
@@ -200,3 +199,45 @@ fn test_function_wrap() -> Result<()> {
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[test]
fn test_owned_function() -> Result<()> {
let lua = Lua::new();
let f = lua
.create_function(|_, ()| Ok("hello, world!"))?
.into_owned();
drop(lua);
// We still should be able to call the function despite Lua is dropped
let s = f.call::<_, String>(())?;
assert_eq!(s.to_string_lossy(), "hello, world!");
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[test]
fn test_owned_function_drop() -> Result<()> {
let rc = std::sync::Arc::new(());
{
let lua = Lua::new();
lua.set_app_data(rc.clone());
let f1 = lua
.create_function(|_, ()| Ok("hello, world!"))?
.into_owned();
let f2 =
lua.create_function(move |_, ()| f1.to_ref().call::<_, std::string::String>(()))?;
assert_eq!(f2.call::<_, String>(())?.to_string_lossy(), "hello, world!");
}
// Check that Lua is properly destroyed
// It works because we collect garbage when Lua goes out of scope
assert_eq!(std::sync::Arc::strong_count(&rc), 1);
Ok(())
}
+53 -16
View File
@@ -9,11 +9,9 @@ use std::sync::{Arc, Mutex};
use mlua::{DebugEvent, Error, HookTriggers, Lua, Result, Value};
#[test]
fn test_hook_triggers_bitor() {
let trigger = HookTriggers::on_calls()
| HookTriggers::on_returns()
| HookTriggers::every_line()
| HookTriggers::every_nth_instruction(5);
fn test_hook_triggers() {
let trigger = HookTriggers::new().on_calls().on_returns()
| HookTriggers::new().every_line().every_nth_instruction(5);
assert!(trigger.on_calls);
assert!(trigger.on_returns);
@@ -27,7 +25,7 @@ fn test_line_counts() -> Result<()> {
let hook_output = output.clone();
let lua = Lua::new();
lua.set_hook(HookTriggers::every_line(), move |_lua, debug| {
lua.set_hook(HookTriggers::EVERY_LINE, move |_lua, debug| {
assert_eq!(debug.event(), DebugEvent::Line);
hook_output.lock().unwrap().push(debug.curr_line());
Ok(())
@@ -59,7 +57,7 @@ fn test_function_calls() -> Result<()> {
let hook_output = output.clone();
let lua = Lua::new();
lua.set_hook(HookTriggers::on_calls(), move |_lua, debug| {
lua.set_hook(HookTriggers::ON_CALLS, move |_lua, debug| {
assert_eq!(debug.event(), DebugEvent::Call);
let names = debug.names();
let source = debug.source();
@@ -104,7 +102,7 @@ fn test_function_calls() -> Result<()> {
fn test_error_within_hook() -> Result<()> {
let lua = Lua::new();
lua.set_hook(HookTriggers::every_line(), |_lua, _debug| {
lua.set_hook(HookTriggers::EVERY_LINE, |_lua, _debug| {
Err(Error::RuntimeError(
"Something happened in there!".to_string(),
))
@@ -136,7 +134,7 @@ fn test_limit_execution_instructions() -> Result<()> {
let max_instructions = AtomicI64::new(10000);
lua.set_hook(
HookTriggers::every_nth_instruction(30),
HookTriggers::new().every_nth_instruction(30),
move |_lua, debug| {
assert_eq!(debug.event(), DebugEvent::Count);
if max_instructions.fetch_sub(30, Ordering::Relaxed) <= 30 {
@@ -166,11 +164,14 @@ fn test_limit_execution_instructions() -> Result<()> {
fn test_hook_removal() -> Result<()> {
let lua = Lua::new();
lua.set_hook(HookTriggers::every_nth_instruction(1), |_lua, _debug| {
Err(Error::RuntimeError(
"this hook should've been removed by this time".to_string(),
))
})?;
lua.set_hook(
HookTriggers::new().every_nth_instruction(1),
|_lua, _debug| {
Err(Error::RuntimeError(
"this hook should've been removed by this time".to_string(),
))
},
)?;
assert!(lua.load("local x = 1").exec().is_err());
lua.remove_hook();
@@ -193,11 +194,11 @@ fn test_hook_swap_within_hook() -> Result<()> {
tl.borrow()
.as_ref()
.unwrap()
.set_hook(HookTriggers::every_line(), move |lua, _debug| {
.set_hook(HookTriggers::EVERY_LINE, move |lua, _debug| {
lua.globals().set("ok", 1i64)?;
TL_LUA.with(|tl| {
tl.borrow().as_ref().unwrap().set_hook(
HookTriggers::every_line(),
HookTriggers::EVERY_LINE,
move |lua, _debug| {
lua.load(
r#"
@@ -233,3 +234,39 @@ fn test_hook_swap_within_hook() -> Result<()> {
Ok(())
})
}
#[test]
fn test_hook_threads() -> Result<()> {
let lua = Lua::new();
let func = lua
.load(
r#"
local x = 2 + 3
local y = x * 63
local z = string.len(x..", "..y)
"#,
)
.into_function()?;
let co = lua.create_thread(func)?;
let output = Arc::new(Mutex::new(Vec::new()));
let hook_output = output.clone();
co.set_hook(HookTriggers::EVERY_LINE, move |_lua, debug| {
assert_eq!(debug.event(), DebugEvent::Line);
hook_output.lock().unwrap().push(debug.curr_line());
Ok(())
});
co.resume(())?;
lua.remove_hook();
let output = output.lock().unwrap();
if cfg!(feature = "luajit") && lua.load("jit.version_num").eval::<i64>()? >= 20100 {
assert_eq!(*output, vec![2, 3, 4, 0, 4]);
} else {
assert_eq!(*output, vec![2, 3, 4]);
}
Ok(())
}
+18 -5
View File
@@ -392,31 +392,44 @@ fn test_from_value_newtype_struct() -> Result<(), Box<dyn StdError>> {
#[test]
fn test_from_value_enum() -> Result<(), Box<dyn StdError>> {
let lua = Lua::new();
lua.globals().set("null", lua.null())?;
#[derive(Deserialize, PartialEq, Debug)]
enum E {
struct UnitStruct;
#[derive(Deserialize, PartialEq, Debug)]
enum E<T = ()> {
Unit,
Integer(u32),
Tuple(u32, u32),
Struct { a: u32 },
Wrap(T),
}
let value = lua.load(r#""Unit""#).eval()?;
let got = lua.from_value(value)?;
let got: E = lua.from_value(value)?;
assert_eq!(E::Unit, got);
let value = lua.load(r#"{Integer = 1}"#).eval()?;
let got = lua.from_value(value)?;
let got: E = lua.from_value(value)?;
assert_eq!(E::Integer(1), got);
let value = lua.load(r#"{Tuple = {1, 2}}"#).eval()?;
let got = lua.from_value(value)?;
let got: E = lua.from_value(value)?;
assert_eq!(E::Tuple(1, 2), got);
let value = lua.load(r#"{Struct = {a = 3}}"#).eval()?;
let got = lua.from_value(value)?;
let got: E = lua.from_value(value)?;
assert_eq!(E::Struct { a: 3 }, got);
let value = lua.load(r#"{Wrap = null}"#).eval()?;
let got = lua.from_value(value)?;
assert_eq!(E::Wrap(UnitStruct), got);
let value = lua.load(r#"{Wrap = null}"#).eval()?;
let got = lua.from_value(value)?;
assert_eq!(E::Wrap(()), got);
Ok(())
}
+19
View File
@@ -98,3 +98,22 @@ fn test_string_debug() -> Result<()> {
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[test]
fn test_owned_string() -> Result<()> {
let lua = Lua::new();
let s = lua.create_string("hello, world!")?.into_owned();
drop(lua);
// Shortcuts
assert_eq!(s.as_bytes(), b"hello, world!");
assert_eq!(s.to_str()?, "hello, world!");
assert_eq!(format!("{s:?}"), "\"hello, world!\"");
// Access via reference
assert_eq!(s.to_ref().to_string_lossy(), "hello, world!");
Ok(())
}
+14
View File
@@ -392,3 +392,17 @@ fn test_table_call() -> Result<()> {
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[test]
fn test_owned_table() -> Result<()> {
let lua = Lua::new();
let table = lua.create_table()?.into_owned();
drop(lua);
table.to_ref().set("abc", 123)?;
assert_eq!(table.to_ref().get::<_, i64>("abc")?, 123);
Ok(())
}
+19
View File
@@ -820,3 +820,22 @@ fn test_userdata_method_errors() -> Result<()> {
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[test]
fn test_owned_userdata() -> Result<()> {
let lua = Lua::new();
let ud = lua.create_any_userdata("abc")?.into_owned();
drop(lua);
assert_eq!(*ud.borrow::<&str>()?, "abc");
*ud.borrow_mut()? = "cba";
assert_eq!(*ud.to_ref().borrow::<&str>()?, "cba");
assert!(matches!(
ud.borrow::<i64>(),
Err(Error::UserDataTypeMismatch)
));
Ok(())
}