Compare commits

..

45 Commits

Author SHA1 Message Date
Alex Orlenko 355a0606c3 v0.9.0-rc.1 2023-07-11 22:15:31 +01:00
Alex Orlenko 2277ee4860 Rename UserDataRegistrar to LuaUserDataRegistry 2023-07-11 22:15:15 +01:00
Alex Orlenko 8a4977e8e7 Use pretty format in the repl example 2023-07-11 21:39:05 +01:00
Alex Orlenko 128c357e07 Use __tostring if __name is not available when pretty printing userdata values 2023-07-11 21:36:02 +01:00
Alex Orlenko 9f0fc27c52 Make Lua::push_value() and Lua::pop_value() public (but hidden from the docs).
Can be useful for low-level intergration with mlua values.
Also closes #215.
2023-07-11 20:03:41 +01:00
Alex Orlenko bfd1c29c0a Add back Table::raw_sequence_values() with deprecation notice 2023-07-11 16:02:28 +01:00
Alex Orlenko 5fca2db6d3 Revert Add error-send feature 2023-07-11 15:50:15 +01:00
Alex Orlenko 8ecbf5b76a v0.9.0-rc.1 2023-07-11 01:09:45 +01:00
Alex Orlenko d037c31b4d mlua-sys: v0.2.1 2023-07-11 01:07:37 +01:00
Alex Orlenko 4fe89c9d45 mlua_derive: v0.9.0-rc.1 2023-07-11 01:07:07 +01:00
Alex Orlenko 389526bb80 Some doc improvements 2023-07-11 00:28:24 +01:00
Alex Orlenko 4adc3116f9 Add error-send feature 2023-07-11 00:20:52 +01:00
Alex Orlenko 8e0bdc9934 Remove clippy::let_and_return ignore 2023-07-10 22:58:15 +01:00
Alex Orlenko 7dc6e4c132 Add Error::runtime() helper 2023-07-10 22:41:03 +01:00
Alex Orlenko 44b8c8b7a6 Add AnyUserData::wrap() to more easy way of creating _any_ userdata in Lua.
This is similar to `Function::wrap()`.
2023-07-10 16:36:38 +01:00
Alex Orlenko 6b8b79266f Drop futures-timer dev-dependency 2023-07-10 01:16:27 +01:00
Alex Orlenko 08ab685d8d Update itertools dev-dependency 2023-07-10 01:16:20 +01:00
Alex Orlenko 057deb0169 Update rustyline dependency to 12.0 2023-07-10 01:05:40 +01:00
Alex Orlenko 44533d2c9d Add new module attribule skip_memory_check to improve performance
in module mode (by skipping memory allocations check) with extra risks.
2023-07-10 00:43:19 +01:00
Alex Orlenko 54c7a2d191 Update compile tests 2023-07-10 00:10:20 +01:00
Alex Orlenko c38a1f060b Various cosmetic changes 2023-07-10 00:04:40 +01:00
Alex Orlenko 5dca743b0c Improve performance AnyUserData::{is_serializable/inspect/serialize} 2023-07-10 00:01:26 +01:00
Alex Orlenko 01c1952c9f Rename AnyUserData::get_*_user_value to AnyUserData::*_user_value.
To be more consistent with function like `Lua::named_registry_value`, `Lua::add_data_ref` and os on.
2023-07-09 23:50:44 +01:00
Alex Orlenko dbc3dd95d4 Fix Luau vector4 formatting when throwing it in exception 2023-07-09 23:14:49 +01:00
Alex Orlenko a9b0cdfc03 Take &str as function name in TableExt and AnyUserDataExt traits 2023-07-09 22:49:16 +01:00
Alex Orlenko 1c20494158 Use usize instead of c_int for table capacity (Lua::create_table_with_capacity) 2023-07-09 22:11:26 +01:00
Alex Orlenko c9294ad642 Add #[must_use] hint and const to some Luau Compiler functions 2023-07-09 14:13:06 +01:00
Alex Orlenko 3a71bfb8a0 Refactor Lua 5.4 warnings to use &str instead of CStr 2023-07-09 14:13:05 +01:00
Alex Orlenko 24e14c4874 Always set hook on a current Lua context (state) and remove MainThreadNotAvailable as no longer needed 2023-07-09 12:33:21 +01:00
Alex Orlenko 20826a69ae Re-export ffi crate (mlua-sys) 2023-07-09 11:31:59 +01:00
Alex Orlenko 5127903c38 Remove generic type parameter from init_userdata_metatable 2023-07-08 17:00:46 +01:00
Alex Orlenko 541139b944 Change Lua ref types Debug print from Ref{index} to Ref{pointer}
The index is always uniq per instance, but pointer can exactly tell when
several values reference to the same Lua internal value.
2023-07-08 12:52:14 +01:00
Alex Orlenko 925a2816cc clippy 2023-07-06 00:59:46 +01:00
Alex Orlenko b3b8d79446 Make Debug interface more user friendly
- use String instead of Vec<u8>
- update docs
- unify fields between lua5.x/luau
- line numbers are `usize`
2023-07-06 00:38:33 +01:00
Alex Orlenko 85f17a269d Add Table:is_empty() function 2023-06-26 10:50:18 +01:00
Alex Orlenko b169031d4e Don't use any metamethods in Table::sequence_values() iterator.
This is matches with `Table::pair()` iterator.
Remove `Table::raw_sequence_values()` iterator.
2023-06-26 10:46:59 +01:00
Alex Orlenko 399e469328 Update "async userdata method" benchmark 2023-06-21 22:23:43 +01:00
Alex Orlenko 1367a033d7 Don't clone function when doing call_async() 2023-06-21 13:14:17 +01:00
Alex Orlenko c1168d3ec1 Refactor call_async() functions to use static dispatch outside of traits 2023-06-21 12:44:24 +01:00
Alex Orlenko b05698d55b impl UserData for Rc<T> and Arc<T> where T: UserData 2023-06-21 01:30:09 +01:00
Alex Orlenko aeacf6cacc Remove allow(dead_code) from mlua-sys build scripts 2023-06-20 23:49:48 +01:00
Alex Orlenko 1f0e81c9a1 Add a dedicated type for Luau vector.
Refactor existing implementation and add 4-dimensional vectors support.
2023-06-20 13:30:42 +01:00
Alex Orlenko c2bfc9ec52 Implement PartialEq<[T]> for tables 2023-06-19 23:28:08 +01:00
Alex Orlenko 9fdba541e9 Update UserDataMethods::add_async_method() functions to take &T as second argument instead of cloning T.
New functions: `UserDataMethods::add_async_method_mut()`, `UserDataMethods::add_async_meta_method_mut()`.
2023-06-15 00:34:41 +01:00
Alex Orlenko cf0524aa23 Use lua_iscfunction instead of lua_getinfo in Function::environment() 2023-06-08 09:33:48 +01:00
54 changed files with 1774 additions and 1021 deletions
+4 -4
View File
@@ -9,7 +9,7 @@ jobs:
matrix:
os: [ubuntu-22.04, macos-latest, windows-latest]
rust: [stable]
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit]
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
include:
- os: ubuntu-22.04
target: x86_64-unknown-linux-gnu
@@ -104,7 +104,7 @@ jobs:
matrix:
os: [ubuntu-22.04, macos-latest, windows-latest]
rust: [stable, nightly]
lua: [lua54, lua53, lua52, lua51, luajit, luajit52, luau, luau-jit]
lua: [lua54, lua53, lua52, lua51, luajit, luajit52, luau, luau-jit, luau-vector4]
include:
- os: ubuntu-22.04
target: x86_64-unknown-linux-gnu
@@ -140,7 +140,7 @@ jobs:
matrix:
os: [ubuntu-22.04]
rust: [nightly]
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit]
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
include:
- os: ubuntu-22.04
target: x86_64-unknown-linux-gnu
@@ -222,7 +222,7 @@ jobs:
runs-on: ubuntu-22.04
strategy:
matrix:
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit]
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
+14
View File
@@ -1,3 +1,17 @@
## v0.9.0-rc.1
- `UserDataMethods::add_async_method()` takes `&T` instead of cloning `T`
- Implemented `PartialEq<[T]>` for tables
- Added Luau 4-dimensional vectors support (`luau-vector4` feature)
- `Table::sequence_values()` iterator no longer uses any metamethods (`Table::raw_sequence_values()` is deprecated)
- Added `Table:is_empty()` function that checks both hash and array parts
- Refactored Debug interface
- Re-exported `ffi` (`mlua-sys`) crate for easier writing of unsafe code
- Refactored Lua 5.4 warnings interface
- Take `&str` as function name in `TableExt` and `AnyUserDataExt` traits
- Added module attribule `skip_memory_check` to improve performance
- Added `AnyUserData::wrap()` to provide more easy way of creating _any_ userdata in Lua
## v0.9.0-beta.3
- Added `OwnedAnyUserData::take()`
+5 -5
View File
@@ -1,6 +1,6 @@
[package]
name = "mlua"
version = "0.9.0-beta.3" # remember to update mlua_derive
version = "0.9.0-rc.1" # remember to update mlua_derive
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
edition = "2021"
repository = "https://github.com/khvzak/mlua"
@@ -33,6 +33,7 @@ luajit = ["ffi/luajit"]
luajit52 = ["luajit", "ffi/luajit52"]
luau = ["ffi/luau"]
luau-jit = ["luau", "ffi/luau-codegen"]
luau-vector4 = ["luau", "ffi/luau-vector4"]
vendored = ["ffi/vendored"]
module = ["mlua_derive", "ffi/module"]
async = ["futures-util"]
@@ -42,7 +43,7 @@ macros = ["mlua_derive/macros"]
unstable = []
[dependencies]
mlua_derive = { version = "=0.9.0-beta.2", optional = true, path = "mlua_derive" }
mlua_derive = { version = "=0.9.0-rc.1", optional = true, path = "mlua_derive" }
bstr = { version = "1.0", features = ["std"], default_features = false }
once_cell = { version = "1.0" }
num-traits = { version = "0.2.14" }
@@ -53,17 +54,16 @@ erased-serde = { version = "0.3", optional = true }
serde-value = { version = "0.7", optional = true }
parking_lot = { version = "0.12", optional = true }
ffi = { package = "mlua-sys", version = "0.2.0", path = "mlua-sys" }
ffi = { package = "mlua-sys", version = "0.2.1", path = "mlua-sys" }
[dev-dependencies]
rustyline = "11.0"
rustyline = "12.0"
criterion = { version = "0.5", features = ["async_tokio"] }
trybuild = "1.0"
futures = "0.3.5"
hyper = { version = "0.14", features = ["client", "server"] }
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1.0", features = ["full"] }
futures-timer = "3.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
maplit = "1.0"
+1
View File
@@ -46,6 +46,7 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
* `luajit52`: activate [LuaJIT] support with partial compatibility with Lua 5.2
* `luau`: activate [Luau] support (auto vendored mode)
* `luau-jit`: activate [Luau] support with experimental jit backend. This is unstable feature and not recommended to use.
* `luau-vector4`: activate [Luau] support with 4-dimensional vector.
* `vendored`: build static Lua(JIT) library from sources during `mlua` compilation using [lua-src] or [luajit-src] crates
* `module`: enable module mode (building loadable `cdylib` library for Lua)
* `async`: enable async/await support (any executor can be used, eg. [tokio] or [async-std])
+6 -4
View File
@@ -264,17 +264,19 @@ fn call_userdata_method(c: &mut Criterion) {
}
fn call_async_userdata_method(c: &mut Criterion) {
#[derive(Clone, Copy)]
struct UserData(i64);
struct UserData(String);
impl LuaUserData for UserData {
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("method", |_, this, ()| async move { Ok(this.0) });
methods.add_async_method("method", |_, this, ()| async move { Ok(this.0.clone()) });
}
}
let options = LuaOptions::new().thread_pool_size(1024);
let lua = Lua::new_with(LuaStdLib::ALL_SAFE, options).unwrap();
lua.globals().set("userdata", UserData(10)).unwrap();
lua.globals()
.set("userdata", UserData("hello".to_string()))
.unwrap();
c.bench_function("call async [userdata method] 10", |b| {
let rt = Runtime::new().unwrap();
+2 -3
View File
@@ -3,14 +3,13 @@ use std::collections::HashMap;
use hyper::body::{Body as HyperBody, HttpBody as _};
use hyper::Client as HyperClient;
use mlua::{chunk, AnyUserData, ExternalResult, Lua, Result, UserData, UserDataMethods};
use mlua::{chunk, ExternalResult, Lua, Result, UserData, UserDataMethods};
struct BodyReader(HyperBody);
impl UserData for BodyReader {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_function("read", |lua, reader: AnyUserData| async move {
let mut reader = reader.borrow_mut::<Self>()?;
methods.add_async_method_mut("read", |lua, reader, ()| async move {
if let Some(bytes) = reader.0.data().await {
let bytes = bytes.into_lua_err()?;
return Some(lua.create_string(&bytes)).transpose();
+12 -23
View File
@@ -6,9 +6,7 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener, TcpStream};
use tokio::task;
use mlua::{
chunk, AnyUserData, Function, Lua, RegistryKey, String as LuaString, UserData, UserDataMethods,
};
use mlua::{chunk, Function, Lua, RegistryKey, String as LuaString, UserData, UserDataMethods};
struct LuaTcpStream(TcpStream);
@@ -18,28 +16,19 @@ impl UserData for LuaTcpStream {
Ok(this.0.peer_addr()?.to_string())
});
methods.add_async_function(
"read",
|lua, (this, size): (AnyUserData, usize)| async move {
let mut this = this.borrow_mut::<Self>()?;
let mut buf = vec![0; size];
let n = this.0.read(&mut buf).await?;
buf.truncate(n);
lua.create_string(&buf)
},
);
methods.add_async_method_mut("read", |lua, this, size| async move {
let mut buf = vec![0; size];
let n = this.0.read(&mut buf).await?;
buf.truncate(n);
lua.create_string(&buf)
});
methods.add_async_function(
"write",
|_, (this, data): (AnyUserData, LuaString)| async move {
let mut this = this.borrow_mut::<Self>()?;
let n = this.0.write(&data.as_bytes()).await?;
Ok(n)
},
);
methods.add_async_method_mut("write", |_, this, data: LuaString| async move {
let n = this.0.write(&data.as_bytes()).await?;
Ok(n)
});
methods.add_async_function("close", |_, this: AnyUserData| async move {
let mut this = this.borrow_mut::<Self>()?;
methods.add_async_method_mut("close", |_, this, ()| async move {
this.0.shutdown().await?;
Ok(())
});
+3 -3
View File
@@ -1,11 +1,11 @@
//! This example shows a simple read-evaluate-print-loop (REPL).
use mlua::{Error, Lua, MultiValue};
use rustyline::Editor;
use rustyline::DefaultEditor;
fn main() {
let lua = Lua::new();
let mut editor = Editor::<(), _>::new().expect("Failed to make rustyline editor");
let mut editor = DefaultEditor::new().expect("Failed to create editor");
loop {
let mut prompt = "> ";
@@ -24,7 +24,7 @@ fn main() {
"{}",
values
.iter()
.map(|value| format!("{:?}", value))
.map(|value| format!("{:#?}", value))
.collect::<Vec<_>>()
.join("\t")
);
+4 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "mlua-sys"
version = "0.2.0"
version = "0.2.1"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2021"
repository = "https://github.com/khvzak/mlua"
@@ -27,6 +27,7 @@ luajit = []
luajit52 = ["luajit"]
luau = ["luau0-src"]
luau-codegen = ["luau"]
luau-vector4 = ["luau"]
vendored = ["lua-src", "luajit-src"]
module = []
@@ -37,5 +38,5 @@ cc = "1.0"
cfg-if = "1.0"
pkg-config = "0.3.17"
lua-src = { version = ">= 546.0.0, < 550.0.0", optional = true }
luajit-src = { version = ">= 210.4.0, < 220.0.0", optional = true }
luau0-src = { version = "0.5.8", optional = true }
luajit-src = { version = ">= 210.4.5, < 220.0.0", optional = true }
luau0-src = { version = "0.5.11", optional = true }
-2
View File
@@ -1,5 +1,3 @@
#![allow(dead_code)]
use std::env;
use std::ops::Bound;
use std::path::PathBuf;
+1 -2
View File
@@ -1,5 +1,3 @@
#![allow(dead_code)]
use std::path::PathBuf;
pub fn probe_lua() -> Option<PathBuf> {
@@ -23,6 +21,7 @@ pub fn probe_lua() -> Option<PathBuf> {
#[cfg(feature = "luau")]
let artifacts = luau0_src::Build::new()
.enable_codegen(cfg!(feature = "luau-codegen"))
.set_vector_size(if cfg!(feature = "luau-vector4") { 4 } else { 3 })
.build();
artifacts.print_cargo_metadata();
+3
View File
@@ -156,7 +156,10 @@ extern "C" {
pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number);
pub fn lua_pushinteger(L: *mut lua_State, n: lua_Integer);
pub fn lua_pushunsigned(L: *mut lua_State, n: lua_Unsigned);
#[cfg(not(feature = "luau-vector4"))]
pub fn lua_pushvector(L: *mut lua_State, x: c_float, y: c_float, z: c_float);
#[cfg(feature = "luau-vector4")]
pub fn lua_pushvector(L: *mut lua_State, x: c_float, y: c_float, z: c_float, w: c_float);
#[link_name = "lua_pushlstring"]
pub fn lua_pushlstring_(L: *mut lua_State, s: *const c_char, l: usize);
#[link_name = "lua_pushstring"]
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "mlua_derive"
version = "0.9.0-beta.2"
version = "0.9.0-rc.1"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2021"
description = "Procedural macros for the mlua crate."
@@ -19,6 +19,6 @@ quote = "1.0"
proc-macro2 = { version = "1.0", features = ["span-locations"] }
proc-macro-error = { version = "1.0", optional = true }
syn = { version = "2.0", features = ["full"] }
itertools = { version = "0.10", optional = true }
itertools = { version = "0.11", optional = true }
regex = { version = "1.4", optional = true }
once_cell = { version = "1.0", optional = true }
+12 -1
View File
@@ -13,6 +13,7 @@ use {
#[derive(Default)]
struct ModuleAttributes {
name: Option<Ident>,
skip_memory_check: bool,
}
impl ModuleAttributes {
@@ -26,6 +27,11 @@ impl ModuleAttributes {
return Err(meta.error("`name` attribute must have a value"));
}
}
} else if meta.path.is_ident("skip_memory_check") {
if meta.value().is_ok() {
return Err(meta.error("`skip_memory_check` attribute have no values"));
}
self.skip_memory_check = true;
} else {
return Err(meta.error("unsupported module attribute"));
}
@@ -45,6 +51,7 @@ pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
let func_name = &func.sig.ident;
let module_name = args.name.unwrap_or_else(|| func_name.clone());
let ext_entrypoint_name = Ident::new(&format!("luaopen_{module_name}"), Span::call_site());
let skip_memory_check = args.skip_memory_check;
let wrapped = quote! {
::mlua::require_module_feature!();
@@ -53,7 +60,11 @@ pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
#[no_mangle]
unsafe extern "C" fn #ext_entrypoint_name(state: *mut ::mlua::lua_State) -> ::std::os::raw::c_int {
::mlua::Lua::init_from_ptr(state)
let lua = ::mlua::Lua::init_from_ptr(state);
if #skip_memory_check {
lua.skip_memory_check(true);
}
lua
.entrypoint1(#func_name)
.expect("cannot initialize module")
}
+29 -31
View File
@@ -11,9 +11,6 @@ use crate::lua::Lua;
use crate::table::Table;
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti};
#[cfg(feature = "async")]
use futures_util::future::{self, LocalBoxFuture};
/// Trait for types [loadable by Lua] and convertible to a [`Chunk`]
///
/// [loadable by Lua]: https://www.lua.org/manual/5.4/manual.html#3.3.2
@@ -134,6 +131,14 @@ pub struct Compiler {
#[cfg(any(feature = "luau", doc))]
impl Default for Compiler {
fn default() -> Self {
Self::new()
}
}
#[cfg(any(feature = "luau", doc))]
impl Compiler {
/// Creates Luau compiler instance with default options
pub const fn new() -> Self {
// Defaults are taken from luacode.h
Compiler {
optimization_level: 1,
@@ -144,14 +149,6 @@ impl Default for Compiler {
mutable_globals: Vec::new(),
}
}
}
#[cfg(any(feature = "luau", doc))]
impl Compiler {
/// Creates Luau compiler instance with default options
pub fn new() -> Self {
Compiler::default()
}
/// Sets Luau compiler optimization level.
///
@@ -159,7 +156,8 @@ impl Compiler {
/// * 0 - no optimization
/// * 1 - baseline optimization level that doesn't prevent debuggability (default)
/// * 2 - includes optimizations that harm debuggability such as inlining
pub fn set_optimization_level(mut self, level: u8) -> Self {
#[must_use]
pub const fn set_optimization_level(mut self, level: u8) -> Self {
self.optimization_level = level;
self
}
@@ -170,7 +168,8 @@ impl Compiler {
/// * 0 - no debugging support
/// * 1 - line info & function names only; sufficient for backtraces (default)
/// * 2 - full debug info with local & upvalue names; necessary for debugger
pub fn set_debug_level(mut self, level: u8) -> Self {
#[must_use]
pub const fn set_debug_level(mut self, level: u8) -> Self {
self.debug_level = level;
self
}
@@ -181,18 +180,21 @@ impl Compiler {
/// * 0 - no code coverage support (default)
/// * 1 - statement coverage
/// * 2 - statement and expression coverage (verbose)
pub fn set_coverage_level(mut self, level: u8) -> Self {
#[must_use]
pub const fn set_coverage_level(mut self, level: u8) -> Self {
self.coverage_level = level;
self
}
#[doc(hidden)]
#[must_use]
pub fn set_vector_lib(mut self, lib: Option<String>) -> Self {
self.vector_lib = lib;
self
}
#[doc(hidden)]
#[must_use]
pub fn set_vector_ctor(mut self, ctor: Option<String>) -> Self {
self.vector_ctor = ctor;
self
@@ -201,6 +203,7 @@ impl Compiler {
/// Sets a list of globals that are mutable.
///
/// It disables the import optimization for fields accessed through these.
#[must_use]
pub fn set_mutable_globals(mut self, globals: Vec<String>) -> Self {
self.mutable_globals = globals;
self
@@ -312,8 +315,8 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
/// [`exec`]: #method.exec
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn exec_async(self) -> LocalBoxFuture<'lua, Result<()>> {
self.call_async(())
pub async fn exec_async(self) -> Result<()> {
self.call_async(()).await
}
/// Evaluate the chunk as either an expression or block.
@@ -344,17 +347,16 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
/// [`eval`]: #method.eval
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn eval_async<'fut, R>(self) -> LocalBoxFuture<'fut, Result<R>>
pub async fn eval_async<R>(self) -> Result<R>
where
'lua: 'fut,
R: FromLuaMulti<'lua> + 'fut,
R: FromLuaMulti<'lua> + 'lua,
{
if self.detect_mode() == ChunkMode::Binary {
self.call_async(())
self.call_async(()).await
} else if let Ok(function) = self.to_expression() {
function.call_async(())
function.call_async(()).await
} else {
self.call_async(())
self.call_async(()).await
}
}
@@ -374,16 +376,12 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
/// [`call`]: #method.call
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn call_async<'fut, A, R>(self, args: A) -> LocalBoxFuture<'fut, Result<R>>
pub async fn call_async<A, R>(self, args: A) -> Result<R>
where
'lua: 'fut,
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
R: FromLuaMulti<'lua> + 'lua,
{
match self.into_function() {
Ok(func) => func.call_async(args),
Err(e) => Box::pin(future::err(e)),
}
self.into_function()?.call_async(args).await
}
/// Load this chunk into a regular `Function`.
@@ -470,7 +468,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
fn to_expression(&self) -> Result<Function<'lua>> {
// We assume that mode is Text
let source = self.source.as_ref();
let source = source.map_err(|err| Error::RuntimeError(err.to_string()))?;
let source = source.map_err(Error::runtime)?;
let source = Self::expression_source(source);
// We don't need to compile source if no compiler options set
#[cfg(feature = "luau")]
@@ -504,7 +502,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
}
fn convert_name(name: String) -> Result<CString> {
CString::new(name).map_err(|err| Error::RuntimeError(format!("invalid name: {err}")))
CString::new(name).map_err(|err| Error::runtime(format!("invalid name: {err}")))
}
fn expression_source(source: &[u8]) -> Vec<u8> {
+42 -24
View File
@@ -52,7 +52,7 @@ impl<'lua> FromLua<'lua> for String<'lua> {
lua.coerce_string(value)?
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: "String",
to: "string",
message: Some("expected string or number".to_string()),
})
}
@@ -211,7 +211,7 @@ impl<'lua> FromLua<'lua> for OwnedAnyUserData {
}
}
impl<'lua, T: 'static + MaybeSend + UserData> IntoLua<'lua> for T {
impl<'lua, T: UserData + MaybeSend + 'static> IntoLua<'lua> for T {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::UserData(lua.create_userdata(self)?))
@@ -244,7 +244,7 @@ impl<'lua> FromLua<'lua> for Error {
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Error> {
match value {
Value::Error(err) => Ok(err),
val => Ok(Error::RuntimeError(
val => Ok(Error::runtime(
lua.coerce_string(val)?
.and_then(|s| Some(s.to_str().ok()?.to_owned()))
.unwrap_or_else(|| "<unprintable error>".to_owned()),
@@ -292,6 +292,29 @@ impl<'lua> FromLua<'lua> for LightUserData {
}
}
#[cfg(feature = "luau")]
impl<'lua> IntoLua<'lua> for crate::types::Vector {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Vector(self))
}
}
#[cfg(feature = "luau")]
impl<'lua> FromLua<'lua> for crate::types::Vector {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match value {
Value::Vector(v) => Ok(v),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "vector",
message: None,
}),
}
}
}
impl<'lua> IntoLua<'lua> for StdString {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
@@ -412,7 +435,7 @@ impl<'lua> FromLua<'lua> for BString {
lua.coerce_string(value)?
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: "String",
to: "BString",
message: Some("expected string or number".to_string()),
})?
.as_bytes()
@@ -533,7 +556,7 @@ lua_convert_float!(f64);
impl<'lua, T> IntoLua<'lua> for &[T]
where
T: Clone + IntoLua<'lua>,
T: IntoLua<'lua> + Clone,
{
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
@@ -561,21 +584,22 @@ where
fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self> {
match value {
#[cfg(feature = "luau")]
Value::Vector(x, y, z) if N == 3 => Ok(mlua_expect!(
vec![
T::from_lua(Value::Number(x as _), _lua)?,
T::from_lua(Value::Number(y as _), _lua)?,
T::from_lua(Value::Number(z as _), _lua)?,
]
.try_into()
.map_err(|_| ()),
"cannot convert vector to array"
)),
#[rustfmt::skip]
Value::Vector(v) if N == crate::types::Vector::SIZE => unsafe {
use std::{mem, ptr};
let mut arr: [mem::MaybeUninit<T>; N] = mem::MaybeUninit::uninit().assume_init();
ptr::write(arr[0].as_mut_ptr() , T::from_lua(Value::Number(v.x() as _), _lua)?);
ptr::write(arr[1].as_mut_ptr(), T::from_lua(Value::Number(v.y() as _), _lua)?);
ptr::write(arr[2].as_mut_ptr(), T::from_lua(Value::Number(v.z() as _), _lua)?);
#[cfg(feature = "luau-vector4")]
ptr::write(arr[3].as_mut_ptr(), T::from_lua(Value::Number(v.w() as _), _lua)?);
Ok(mem::transmute_copy(&arr))
},
Value::Table(table) => {
let vec = table.sequence_values().collect::<Result<Vec<_>>>()?;
vec.try_into()
.map_err(|vec: Vec<T>| Error::FromLuaConversionError {
from: "Table",
from: "table",
to: "Array",
message: Some(format!("expected table of length {}, got {}", N, vec.len())),
})
@@ -614,12 +638,6 @@ impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Vec<T> {
#[inline]
fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self> {
match value {
#[cfg(feature = "luau")]
Value::Vector(x, y, z) => Ok(vec![
T::from_lua(Value::Number(x as _), _lua)?,
T::from_lua(Value::Number(y as _), _lua)?,
T::from_lua(Value::Number(z as _), _lua)?,
]),
Value::Table(table) => table.sequence_values().collect(),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
@@ -691,7 +709,7 @@ impl<'lua, T: Eq + Hash + FromLua<'lua>, S: BuildHasher + Default> FromLua<'lua>
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match value {
Value::Table(table) if table.len()? > 0 => table.sequence_values().collect(),
Value::Table(table) if table.raw_len() > 0 => table.sequence_values().collect(),
Value::Table(table) => table
.pairs::<T, Value<'lua>>()
.map(|res| res.map(|(k, _)| k))
@@ -718,7 +736,7 @@ impl<'lua, T: Ord + FromLua<'lua>> FromLua<'lua> for BTreeSet<T> {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match value {
Value::Table(table) if table.len()? > 0 => table.sequence_values().collect(),
Value::Table(table) if table.raw_len() > 0 => table.sequence_values().collect(),
Value::Table(table) => table
.pairs::<T, Value<'lua>>()
.map(|res| res.map(|(k, _)| k))
+14 -13
View File
@@ -47,11 +47,6 @@ pub enum Error {
/// This error can only happen when Lua state was not created by us and does not have the
/// custom allocator attached.
MemoryLimitNotAvailable,
/// Main thread is not available.
///
/// This error can only happen in Lua5.1/LuaJIT module mode, when module loaded within a coroutine.
/// These Lua versions does not have `LUA_RIDX_MAINTHREAD` registry key.
MainThreadNotAvailable,
/// A mutable callback has triggered Lua code that has called the same mutable callback again.
///
/// This is an error because a mutable callback can only be borrowed mutably once.
@@ -69,7 +64,7 @@ pub enum Error {
/// called with a huge number of arguments, or a rust callback returns a huge number of return
/// values.
StackError,
/// Too many arguments to `Function::bind`
/// Too many arguments to `Function::bind`.
BindError,
/// Bad argument received from Lua (usually when calling a function).
///
@@ -130,7 +125,7 @@ pub enum Error {
///
/// [`AnyUserData`]: crate::AnyUserData
UserDataDestructed,
/// An [`AnyUserData`] immutable borrow failed because it is already borrowed mutably.
/// An [`AnyUserData`] immutable borrow failed.
///
/// This error can occur when a method on a [`UserData`] type calls back into Lua, which then
/// tries to call a method on the same [`UserData`] type. Consider restructuring your API to
@@ -139,7 +134,7 @@ pub enum Error {
/// [`AnyUserData`]: crate::AnyUserData
/// [`UserData`]: crate::UserData
UserDataBorrowError,
/// An [`AnyUserData`] mutable borrow failed because it is already borrowed.
/// An [`AnyUserData`] mutable borrow failed.
///
/// This error can occur when a method on a [`UserData`] type calls back into Lua, which then
/// tries to call a method on the same [`UserData`] type. Consider restructuring your API to
@@ -226,9 +221,6 @@ impl fmt::Display for Error {
Error::MemoryLimitNotAvailable => {
write!(fmt, "setting memory limit is not available")
}
Error::MainThreadNotAvailable => {
write!(fmt, "main thread is not available in Lua 5.1")
}
Error::RecursiveMutCallback => write!(fmt, "mutable callback called recursively"),
Error::CallbackDestructed => write!(
fmt,
@@ -270,8 +262,8 @@ impl fmt::Display for Error {
Error::CoroutineInactive => write!(fmt, "cannot resume inactive coroutine"),
Error::UserDataTypeMismatch => write!(fmt, "userdata is not expected type"),
Error::UserDataDestructed => write!(fmt, "userdata has been destructed"),
Error::UserDataBorrowError => write!(fmt, "userdata already mutably borrowed"),
Error::UserDataBorrowMutError => write!(fmt, "userdata already borrowed"),
Error::UserDataBorrowError => write!(fmt, "error borrowing userdata"),
Error::UserDataBorrowMutError => write!(fmt, "error mutably borrowing userdata"),
Error::MetaMethodRestricted(ref method) => write!(fmt, "metamethod {method} is restricted"),
Error::MetaMethodTypeError { ref method, type_name, ref message } => {
write!(fmt, "metamethod {method} has unsupported type {type_name}")?;
@@ -345,7 +337,14 @@ impl StdError for Error {
}
impl Error {
/// Creates a new `RuntimeError` with the given message.
#[inline]
pub fn runtime<S: fmt::Display>(message: S) -> Self {
Error::RuntimeError(message.to_string())
}
/// Wraps an external error object.
#[inline]
pub fn external<T: Into<Box<dyn StdError + Send + Sync>>>(err: T) -> Self {
Error::ExternalError(err.into().into())
}
@@ -387,6 +386,7 @@ impl Error {
}
}
/// Trait for converting [`std::error::Error`] into Lua [`Error`].
pub trait ExternalError {
fn into_lua_err(self) -> Error;
}
@@ -397,6 +397,7 @@ impl<E: Into<Box<dyn StdError + Send + Sync>>> ExternalError for E {
}
}
/// Trait for converting [`std::result::Result`] into Lua [`Result`].
pub trait ExternalResult<T> {
fn into_lua_err(self) -> Result<T>;
}
+44 -66
View File
@@ -10,14 +10,15 @@ use crate::memory::MemoryState;
use crate::table::Table;
use crate::types::{Callback, LuaRef, MaybeSend};
use crate::util::{
assert_stack, check_stack, error_traceback, pop_error, ptr_to_cstr_bytes, StackGuard,
assert_stack, check_stack, error_traceback, linenumber_to_usize, pop_error, ptr_to_lossy_str,
ptr_to_str, StackGuard,
};
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti};
#[cfg(feature = "async")]
use {
crate::types::AsyncCallback,
futures_util::future::{self, Future, LocalBoxFuture, TryFutureExt},
futures_util::future::{self, Future},
};
/// Handle to an internal Lua function.
@@ -52,24 +53,22 @@ impl OwnedFunction {
/// [`Lua Debug Interface`]: https://www.lua.org/manual/5.4/manual.html#4.7
#[derive(Clone, Debug)]
pub struct FunctionInfo {
/// A (reasonable) name of the function.
/// A (reasonable) name of the function (`None` if the name cannot be found).
pub name: Option<String>,
/// Explains the `name` field ("global", "local", "method", "field", "upvalue", or "").
/// Explains the `name` field (can be `global`/`local`/`method`/`field`/`upvalue`/etc).
///
/// Always `None` for Luau.
pub name_what: Option<String>,
/// A string "Lua" if the function is a Lua function, "C" if it is a C function, "main" if it is the main part of a chunk.
pub what: Option<String>,
/// The source of the chunk that created the function.
pub source: Option<Vec<u8>>,
/// A "printable" version of source, to be used in error messages.
pub short_src: Option<Vec<u8>>,
pub name_what: Option<&'static str>,
/// A string `Lua` if the function is a Lua function, `C` if it is a C function, `main` if it is the main part of a chunk.
pub what: &'static str,
/// Source of the chunk that created the function.
pub source: Option<String>,
/// A "printable" version of `source`, to be used in error messages.
pub short_src: Option<String>,
/// The line number where the definition of the function starts.
pub line_defined: i32,
/// The line number where the definition of the function ends.
///
/// Always `-1` for Luau.
pub last_line_defined: i32,
pub line_defined: Option<usize>,
/// The line number where the definition of the function ends (not set by Luau).
pub last_line_defined: Option<usize>,
}
/// Luau function coverage snapshot.
@@ -77,7 +76,7 @@ pub struct FunctionInfo {
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CoverageInfo {
pub function: Option<std::string::String>,
pub function: Option<String>,
pub line_defined: i32,
pub depth: i32,
pub hits: Vec<i32>,
@@ -168,14 +167,13 @@ impl<'lua> Function<'lua> {
///
/// ```
/// use std::time::Duration;
/// use futures_timer::Delay;
/// # use mlua::{Lua, Result};
/// # #[tokio::main]
/// # async fn main() -> Result<()> {
/// # let lua = Lua::new();
///
/// let sleep = lua.create_async_function(move |_lua, n: u64| async move {
/// Delay::new(Duration::from_millis(n)).await;
/// tokio::time::sleep(Duration::from_millis(n)).await;
/// Ok(())
/// })?;
///
@@ -188,21 +186,18 @@ impl<'lua> Function<'lua> {
/// [`AsyncThread`]: crate::AsyncThread
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
pub fn call_async<A, R>(&self, args: A) -> impl Future<Output = Result<R>> + 'lua
where
'lua: 'fut,
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
R: FromLuaMulti<'lua> + 'lua,
{
let lua = self.0.lua;
match lua.create_recycled_thread(self) {
Ok(t) => {
let mut t = t.into_async(args);
t.set_recyclable(true);
Box::pin(t)
}
Err(e) => Box::pin(future::err(e)),
}
let thread_res = lua.create_recycled_thread(self).map(|th| {
let mut th = th.into_async(args);
th.set_recyclable(true);
th
});
async move { thread_res?.await }
}
/// Returns a function that, when called, calls `self`, passing `args` as the first set of
@@ -303,17 +298,7 @@ impl<'lua> Function<'lua> {
assert_stack(state, 1);
lua.push_ref(&self.0);
let mut ar: ffi::lua_Debug = mem::zeroed();
#[cfg(not(feature = "luau"))]
{
ffi::lua_pushvalue(state, -1);
ffi::lua_getinfo(state, cstr!(">S"), &mut ar);
}
#[cfg(feature = "luau")]
ffi::lua_getinfo(state, -1, cstr!("s"), &mut ar);
if ptr_to_cstr_bytes(ar.what) == Some(b"C") {
if ffi::lua_iscfunction(state, -1) != 0 {
return None;
}
@@ -350,17 +335,7 @@ impl<'lua> Function<'lua> {
check_stack(state, 2)?;
lua.push_ref(&self.0);
let mut ar: ffi::lua_Debug = mem::zeroed();
#[cfg(not(feature = "luau"))]
{
ffi::lua_pushvalue(state, -1);
ffi::lua_getinfo(state, cstr!(">S"), &mut ar);
}
#[cfg(feature = "luau")]
ffi::lua_getinfo(state, -1, cstr!("s"), &mut ar);
if ptr_to_cstr_bytes(ar.what) == Some(b"C") {
if ffi::lua_iscfunction(state, -1) != 0 {
return Ok(false);
}
@@ -414,23 +389,25 @@ impl<'lua> Function<'lua> {
mlua_assert!(res != 0, "lua_getinfo failed with `>Sn`");
FunctionInfo {
name: ptr_to_cstr_bytes(ar.name).map(|s| String::from_utf8_lossy(s).into_owned()),
name: ptr_to_lossy_str(ar.name).map(|s| s.into_owned()),
#[cfg(not(feature = "luau"))]
name_what: ptr_to_cstr_bytes(ar.namewhat)
.map(|s| String::from_utf8_lossy(s).into_owned()),
name_what: match ptr_to_str(ar.namewhat) {
Some("") => None,
val => val,
},
#[cfg(feature = "luau")]
name_what: None,
what: ptr_to_cstr_bytes(ar.what).map(|s| String::from_utf8_lossy(s).into_owned()),
source: ptr_to_cstr_bytes(ar.source).map(|s| s.to_vec()),
what: ptr_to_str(ar.what).unwrap_or("main"),
source: ptr_to_lossy_str(ar.source).map(|s| s.into_owned()),
#[cfg(not(feature = "luau"))]
short_src: ptr_to_cstr_bytes(ar.short_src.as_ptr()).map(|s| s.to_vec()),
short_src: ptr_to_lossy_str(ar.short_src.as_ptr()).map(|s| s.into_owned()),
#[cfg(feature = "luau")]
short_src: ptr_to_cstr_bytes(ar.short_src).map(|s| s.to_vec()),
line_defined: ar.linedefined,
short_src: ptr_to_lossy_str(ar.short_src).map(|s| s.into_owned()),
line_defined: linenumber_to_usize(ar.linedefined),
#[cfg(not(feature = "luau"))]
last_line_defined: ar.lastlinedefined,
last_line_defined: linenumber_to_usize(ar.lastlinedefined),
#[cfg(feature = "luau")]
last_line_defined: -1,
last_line_defined: None,
}
}
}
@@ -484,7 +461,7 @@ impl<'lua> Function<'lua> {
/// Requires `feature = "luau"`
///
/// [`Compiler::set_coverage_level`]: crate::chunk::Compiler::set_coverage_level
#[cfg(any(feature = "luau", docsrs))]
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub fn coverage<F>(&self, mut func: F)
where
@@ -564,12 +541,12 @@ impl OwnedFunction {
#[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>>
pub async fn call_async<'lua, A, R>(&'lua self, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua,
{
self.to_ref().call_async(args)
self.to_ref().call_async(args).await
}
}
@@ -624,7 +601,8 @@ impl<'lua> Function<'lua> {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
Box::pin(func(lua, args).and_then(move |ret| future::ready(ret.into_lua_multi(lua))))
let fut = func(lua, args);
Box::pin(async move { fut.await?.into_lua_multi(lua) })
}))
}
}
+31 -17
View File
@@ -1,3 +1,4 @@
use std::borrow::Cow;
use std::cell::UnsafeCell;
#[cfg(not(feature = "luau"))]
use std::ops::{BitOr, BitOrAssign};
@@ -6,7 +7,7 @@ use std::os::raw::c_int;
use ffi::lua_Debug;
use crate::lua::Lua;
use crate::util::ptr_to_cstr_bytes;
use crate::util::{linenumber_to_usize, ptr_to_lossy_str, ptr_to_str};
/// Contains information about currently executing Lua code.
///
@@ -78,9 +79,12 @@ impl<'lua> Debug<'lua> {
);
DebugNames {
name: ptr_to_cstr_bytes((*self.ar.get()).name),
name: ptr_to_lossy_str((*self.ar.get()).name),
#[cfg(not(feature = "luau"))]
name_what: ptr_to_cstr_bytes((*self.ar.get()).namewhat),
name_what: match ptr_to_str((*self.ar.get()).namewhat) {
Some("") => None,
val => val,
},
#[cfg(feature = "luau")]
name_what: None,
}
@@ -102,15 +106,17 @@ impl<'lua> Debug<'lua> {
);
DebugSource {
source: ptr_to_cstr_bytes((*self.ar.get()).source),
source: ptr_to_lossy_str((*self.ar.get()).source),
#[cfg(not(feature = "luau"))]
short_src: ptr_to_cstr_bytes((*self.ar.get()).short_src.as_ptr()),
short_src: ptr_to_lossy_str((*self.ar.get()).short_src.as_ptr()),
#[cfg(feature = "luau")]
short_src: ptr_to_cstr_bytes((*self.ar.get()).short_src),
line_defined: (*self.ar.get()).linedefined,
short_src: ptr_to_lossy_str((*self.ar.get()).short_src),
line_defined: linenumber_to_usize((*self.ar.get()).linedefined),
#[cfg(not(feature = "luau"))]
last_line_defined: (*self.ar.get()).lastlinedefined,
what: ptr_to_cstr_bytes((*self.ar.get()).what),
last_line_defined: linenumber_to_usize((*self.ar.get()).lastlinedefined),
#[cfg(feature = "luau")]
last_line_defined: None,
what: ptr_to_str((*self.ar.get()).what).unwrap_or("main"),
}
}
}
@@ -210,18 +216,26 @@ pub enum DebugEvent {
#[derive(Clone, Debug)]
pub struct DebugNames<'a> {
pub name: Option<&'a [u8]>,
pub name_what: Option<&'a [u8]>,
/// A (reasonable) name of the function (`None` if the name cannot be found).
pub name: Option<Cow<'a, str>>,
/// Explains the `name` field (can be `global`/`local`/`method`/`field`/`upvalue`/etc).
///
/// Always `None` for Luau.
pub name_what: Option<&'static str>,
}
#[derive(Clone, Debug)]
pub struct DebugSource<'a> {
pub source: Option<&'a [u8]>,
pub short_src: Option<&'a [u8]>,
pub line_defined: i32,
#[cfg(not(feature = "luau"))]
pub last_line_defined: i32,
pub what: Option<&'a [u8]>,
/// Source of the chunk that created the function.
pub source: Option<Cow<'a, str>>,
/// A "printable" version of `source`, to be used in error messages.
pub short_src: Option<Cow<'a, str>>,
/// The line number where the definition of the function starts.
pub line_defined: Option<usize>,
/// The line number where the definition of the function ends (not set by Luau).
pub last_line_defined: Option<usize>,
/// A string `Lua` if the function is a Lua function, `C` if it is a C function, `main` if it is the main part of a chunk.
pub what: &'static str,
}
#[derive(Copy, Clone, Debug)]
+22 -4
View File
@@ -103,7 +103,7 @@ mod value;
pub mod prelude;
pub use ffi::{lua_CFunction, lua_State};
pub use ffi::{self, lua_CFunction, lua_State};
pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
pub use crate::error::{Error, ErrorContext, ExternalError, ExternalResult, Result};
@@ -122,7 +122,7 @@ pub use crate::userdata::{
UserDataRef, UserDataRefMut,
};
pub use crate::userdata_ext::AnyUserDataExt;
pub use crate::userdata_impl::UserDataRegistrar;
pub use crate::userdata_impl::UserDataRegistry;
pub use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil, Value};
#[cfg(not(feature = "luau"))]
@@ -130,7 +130,11 @@ pub use crate::hook::HookTriggers;
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub use crate::{chunk::Compiler, function::CoverageInfo, types::VmState};
pub use crate::{
chunk::Compiler,
function::CoverageInfo,
types::{Vector, VmState},
};
#[cfg(feature = "async")]
pub use crate::thread::AsyncThread;
@@ -231,7 +235,7 @@ pub use mlua_derive::chunk;
///
/// You can also pass options to the attribute:
///
/// name - name of the module, defaults to the name of the function
/// * name - name of the module, defaults to the name of the function
///
/// ```ignore
/// #[mlua::lua_module(name = "alt_module")]
@@ -240,6 +244,20 @@ pub use mlua_derive::chunk;
/// }
/// ```
///
/// * 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 mode will improve performance of such operations
/// with risk of having uncaught exceptions and memory leaks.
///
/// ```ignore
/// #[mlua::lua_module(skip_memory_check)]
/// fn my_module(lua: &Lua) -> Result<Table> {
/// ...
/// }
/// ```
///
#[cfg(any(feature = "module", docsrs))]
#[cfg_attr(docsrs, doc(cfg(feature = "module")))]
pub use mlua_derive::lua_module;
+128 -69
View File
@@ -30,7 +30,7 @@ use crate::types::{
LightUserData, LuaRef, MaybeSend, Number, RegistryKey,
};
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataCell};
use crate::userdata_impl::{UserDataProxy, UserDataRegistrar};
use crate::userdata_impl::{UserDataProxy, UserDataRegistry};
use crate::util::{
self, assert_stack, check_stack, get_destructed_userdata_metatable, get_gc_metatable,
get_gc_userdata, get_main_state, get_userdata, init_error_registry, init_gc_metatable,
@@ -50,12 +50,15 @@ use crate::{hook::HookTriggers, types::HookCallback};
#[cfg(feature = "luau")]
use crate::types::InterruptCallback;
#[cfg(any(feature = "luau", doc))]
use crate::{chunk::Compiler, types::VmState};
use crate::{
chunk::Compiler,
types::{Vector, VmState},
};
#[cfg(feature = "async")]
use {
crate::types::{AsyncCallback, AsyncCallbackUpvalue, AsyncPollUpvalue},
futures_util::future::{self, Future, TryFutureExt},
futures_util::future::{self, Future},
futures_util::task::{noop_waker_ref, Context, Poll, Waker},
};
@@ -92,7 +95,10 @@ pub(crate) struct ExtraData {
safe: bool,
libs: StdLib,
mem_state: Option<NonNull<MemoryState>>,
#[cfg(feature = "module")]
skip_memory_check: bool,
// Auxiliary thread to store references
ref_thread: *mut ffi::lua_State,
ref_stack_size: c_int,
ref_stack_top: c_int,
@@ -445,7 +451,7 @@ impl Lua {
///
/// Once called, a returned Lua state is cached in the registry and can be retrieved
/// by calling this function again.
#[allow(clippy::missing_safety_doc)]
#[allow(clippy::missing_safety_doc, clippy::arc_with_non_send_sync)]
pub unsafe fn init_from_ptr(state: *mut ffi::lua_State) -> Lua {
assert!(!state.is_null(), "Lua state is NULL");
if let Some(lua) = Lua::try_from_ptr(state) {
@@ -511,6 +517,8 @@ impl Lua {
safe: false,
libs: StdLib::NONE,
mem_state: None,
#[cfg(feature = "module")]
skip_memory_check: false,
ref_thread,
// We need 1 extra stack space to move values in and out of the ref stack.
ref_stack_size: ffi::LUA_MINSTACK - 1,
@@ -694,8 +702,6 @@ impl Lua {
///
/// This function is useful when the `Lua` object is supposed to live for the remainder
/// of the program's life.
/// In particular in asynchronous context this will allow to spawn Lua tasks to execute
/// in background.
///
/// Dropping the returned reference will cause a memory leak. If this is not acceptable,
/// the reference should first be wrapped with the [`Lua::from_static`] function producing a `Lua`.
@@ -724,7 +730,7 @@ impl Lua {
where
A: FromLuaMulti<'lua>,
R: IntoLua<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
{
let entrypoint_inner = |lua: &'lua Lua, func: F| {
let state = lua.state();
@@ -766,11 +772,18 @@ impl Lua {
pub unsafe fn entrypoint1<'lua, R, F>(self, func: F) -> Result<c_int>
where
R: IntoLua<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua) -> Result<R>,
F: Fn(&'lua Lua) -> Result<R> + MaybeSend + 'static,
{
self.entrypoint(move |lua, _: ()| func(lua))
}
/// Skips memory checks for some operations.
#[doc(hidden)]
#[cfg(feature = "module")]
pub fn skip_memory_check(&self, skip: bool) {
unsafe { (*self.extra.get()).skip_memory_check = skip };
}
/// Enables (or disables) sandbox mode on this Lua instance.
///
/// This method, in particular:
@@ -833,8 +846,8 @@ 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.
/// This method sets a hook function for the current thread of this Lua instance.
/// If you want to set a hook function for another 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.
///
@@ -849,7 +862,7 @@ impl Lua {
/// lua.set_hook(HookTriggers::EVERY_LINE, |_lua, debug| {
/// println!("line {}", debug.curr_line());
/// Ok(())
/// })?;
/// });
///
/// lua.load(r#"
/// local x = 2 + 3
@@ -863,15 +876,11 @@ impl Lua {
/// [`HookTriggers.every_nth_instruction`]: crate::HookTriggers::every_nth_instruction
#[cfg(not(feature = "luau"))]
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F) -> Result<()>
pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F)
where
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(())
unsafe { self.set_thread_hook(self.state(), triggers, callback) };
}
/// Sets a 'hook' function for a thread (coroutine).
@@ -1028,7 +1037,7 @@ impl Lua {
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
pub fn set_warning_function<F>(&self, callback: F)
where
F: 'static + MaybeSend + Fn(&Lua, &CStr, bool) -> Result<()>,
F: Fn(&Lua, &str, bool) -> Result<()> + MaybeSend + 'static,
{
unsafe extern "C" fn warn_proc(ud: *mut c_void, msg: *const c_char, tocont: c_int) {
let extra = ud as *mut ExtraData;
@@ -1038,8 +1047,8 @@ impl Lua {
(*extra).warn_callback.as_ref(),
"no warning callback set in warn_proc"
);
let msg = CStr::from_ptr(msg);
cb(lua, msg, tocont != 0)
let msg = std::string::String::from_utf8_lossy(CStr::from_ptr(msg).to_bytes());
cb(lua, &msg, tocont != 0)
});
}
@@ -1066,15 +1075,25 @@ impl Lua {
/// Emits a warning with the given message.
///
/// A message in a call with `tocont` set to `true` should be continued in another call to this function.
/// A message in a call with `incomplete` set to `true` should be continued in
/// another call to this function.
///
/// Requires `feature = "lua54"`
#[cfg(feature = "lua54")]
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
pub fn warning<S: Into<Vec<u8>>>(&self, msg: S, tocont: bool) -> Result<()> {
let msg = CString::new(msg).map_err(|err| Error::RuntimeError(err.to_string()))?;
unsafe { ffi::lua_warning(self.state(), msg.as_ptr(), tocont as c_int) };
Ok(())
pub fn warning(&self, msg: impl AsRef<str>, incomplete: bool) {
let msg = msg.as_ref();
let mut bytes = vec![0; msg.len() + 1];
bytes[..msg.len()].copy_from_slice(msg.as_bytes());
let real_len = bytes.iter().position(|&c| c == 0).unwrap();
bytes.truncate(real_len);
unsafe {
ffi::lua_warning(
self.state(),
bytes.as_ptr() as *const c_char,
incomplete as c_int,
);
}
}
/// Gets information about the interpreter runtime stack.
@@ -1121,7 +1140,7 @@ impl Lua {
/// a `Error::MemoryError` is generated instead.
/// Returns previous limit (zero means no limit).
///
/// Does not work on module mode where Lua state is managed externally.
/// Does not work in module mode where Lua state is managed externally.
pub fn set_memory_limit(&self, limit: usize) -> Result<usize> {
unsafe {
match (*self.extra.get()).mem_state.map(|mut x| x.as_mut()) {
@@ -1300,7 +1319,7 @@ impl Lua {
///
/// By default JIT is enabled. Changing this option does not have any effect on
/// already loaded functions.
#[cfg(any(feature = "luau-jit", docsrs))]
#[cfg(any(feature = "luau-jit", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau-jit")))]
pub fn enable_jit(&self, enable: bool) {
unsafe { (*self.extra.get()).enable_jit = enable };
@@ -1400,7 +1419,7 @@ impl Lua {
/// `narr` is a hint for how many elements the table will have as a sequence;
/// `nrec` is a hint for how many other elements the table will have.
/// Lua may use these hints to preallocate memory for the new table.
pub fn create_table_with_capacity(&self, narr: c_int, nrec: c_int) -> Result<Table> {
pub fn create_table_with_capacity(&self, narr: usize, nrec: usize) -> Result<Table> {
let state = self.state();
unsafe {
if self.unlikely_memory_error() {
@@ -1430,7 +1449,7 @@ impl Lua {
let iter = iter.into_iter();
let lower_bound = iter.size_hint().0;
let protect = !self.unlikely_memory_error();
push_table(state, 0, lower_bound as c_int, protect)?;
push_table(state, 0, lower_bound, protect)?;
for (k, v) in iter {
self.push_value(k.into_lua(self)?)?;
self.push_value(v.into_lua(self)?)?;
@@ -1459,7 +1478,7 @@ impl Lua {
let iter = iter.into_iter();
let lower_bound = iter.size_hint().0;
let protect = !self.unlikely_memory_error();
push_table(state, lower_bound as c_int, 0, protect)?;
push_table(state, lower_bound, 0, protect)?;
for (i, v) in iter.enumerate() {
self.push_value(v.into_lua(self)?)?;
if protect {
@@ -1524,7 +1543,7 @@ impl Lua {
where
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
{
self.create_callback(Box::new(move |lua, args| {
func(lua, A::from_lua_multi_args(args, 1, None, lua)?)?.into_lua_multi(lua)
@@ -1541,7 +1560,7 @@ impl Lua {
where
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
{
let func = RefCell::new(func);
self.create_function(move |lua, args| {
@@ -1582,11 +1601,10 @@ impl Lua {
///
/// ```
/// use std::time::Duration;
/// use futures_timer::Delay;
/// use mlua::{Lua, Result};
///
/// async fn sleep(_lua: &Lua, n: u64) -> Result<&'static str> {
/// Delay::new(Duration::from_millis(n)).await;
/// tokio::time::sleep(Duration::from_millis(n)).await;
/// Ok("done")
/// }
///
@@ -1608,15 +1626,16 @@ impl Lua {
where
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
FR: 'lua + Future<Output = Result<R>>,
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
FR: Future<Output = Result<R>> + 'lua,
{
self.create_async_callback(Box::new(move |lua, args| {
let args = match A::from_lua_multi_args(args, 1, None, lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
Box::pin(func(lua, args).and_then(move |ret| future::ready(ret.into_lua_multi(lua))))
let fut = func(lua, args);
Box::pin(async move { fut.await?.into_lua_multi(lua) })
}))
}
@@ -1624,6 +1643,13 @@ impl Lua {
///
/// Equivalent to `coroutine.create`.
pub fn create_thread<'lua>(&'lua self, func: Function) -> Result<Thread<'lua>> {
self.create_thread_inner(&func)
}
/// Wraps a Lua function into a new thread (or coroutine).
///
/// Takes function by reference.
fn create_thread_inner<'lua>(&'lua self, func: &Function) -> Result<Thread<'lua>> {
let state = self.state();
unsafe {
let _sg = StackGuard::new(state);
@@ -1672,7 +1698,7 @@ impl Lua {
return Ok(Thread(LuaRef::new(self, index)));
}
};
self.create_thread(func.clone())
self.create_thread_inner(func)
}
/// Resets thread (coroutine) and returns to the pool for later use.
@@ -1750,9 +1776,9 @@ impl Lua {
/// This methods provides a way to add fields or methods to userdata objects of a type `T`.
pub fn register_userdata_type<T: 'static>(
&self,
f: impl FnOnce(&mut UserDataRegistrar<T>),
f: impl FnOnce(&mut UserDataRegistry<T>),
) -> Result<()> {
let mut registry = UserDataRegistrar::new();
let mut registry = UserDataRegistry::new();
f(&mut registry);
unsafe {
@@ -1803,7 +1829,7 @@ impl Lua {
#[inline]
pub fn create_proxy<T>(&self) -> Result<AnyUserData>
where
T: 'static + UserData,
T: UserData + 'static,
{
unsafe { self.make_userdata(UserDataCell::new(UserDataProxy::<T>(PhantomData))) }
}
@@ -2134,8 +2160,7 @@ impl Lua {
return Ok(());
} else if t != Value::Nil && key.registry_id == ffi::LUA_REFNIL {
// We cannot update `LUA_REFNIL` slot
let err = "cannot replace nil value with non-nil".to_string();
return Err(Error::RuntimeError(err));
return Err(Error::runtime("cannot replace nil value with non-nil"));
}
let state = self.state();
@@ -2270,8 +2295,11 @@ impl Lua {
extra.app_data.remove()
}
// Uses 2 stack spaces, does not call checkstack
pub(crate) unsafe fn push_value(&self, value: Value) -> Result<()> {
/// Pushes a value onto the Lua stack.
///
/// Uses 2 stack spaces, does not call checkstack.
#[doc(hidden)]
pub unsafe fn push_value(&self, value: Value) -> Result<()> {
let state = self.state();
match value {
Value::Nil => {
@@ -2295,8 +2323,11 @@ impl Lua {
}
#[cfg(feature = "luau")]
Value::Vector(x, y, z) => {
ffi::lua_pushvector(state, x, y, z);
Value::Vector(v) => {
#[cfg(not(feature = "luau-vector4"))]
ffi::lua_pushvector(state, v.x(), v.y(), v.z());
#[cfg(feature = "luau-vector4")]
ffi::lua_pushvector(state, v.x(), v.y(), v.z(), v.w());
}
Value::String(s) => {
@@ -2328,8 +2359,11 @@ impl Lua {
Ok(())
}
// Uses 2 stack spaces, does not call checkstack
pub(crate) unsafe fn pop_value(&self) -> Value {
/// Pops a value from the Lua stack.
///
/// Uses 2 stack spaces, does not call checkstack.
#[doc(hidden)]
pub unsafe fn pop_value(&self) -> Value {
let state = self.state();
match ffi::lua_type(state, -1) {
ffi::LUA_TNIL => {
@@ -2379,7 +2413,10 @@ impl Lua {
ffi::LUA_TVECTOR => {
let v = ffi::lua_tovector(state, -1);
mlua_debug_assert!(!v.is_null(), "vector is null");
let vec = Value::Vector(*v, *v.add(1), *v.add(2));
#[cfg(not(feature = "luau-vector4"))]
let vec = Value::Vector(Vector([*v, *v.add(1), *v.add(2)]));
#[cfg(feature = "luau-vector4")]
let vec = Value::Vector(Vector([*v, *v.add(1), *v.add(2), *v.add(3)]));
ffi::lua_pop(state, 1);
vec
}
@@ -2490,7 +2527,7 @@ impl Lua {
unsafe fn register_userdata_metatable<'lua, T: 'static>(
&'lua self,
mut registry: UserDataRegistrar<'lua, T>,
mut registry: UserDataRegistry<'lua, T>,
) -> Result<Integer> {
let state = self.state();
let _sg = StackGuard::new(state);
@@ -2500,7 +2537,7 @@ impl Lua {
let metatable_nrec = registry.meta_methods.len() + registry.meta_fields.len();
#[cfg(feature = "async")]
let metatable_nrec = metatable_nrec + registry.async_meta_methods.len();
push_table(state, 0, metatable_nrec as c_int, true)?;
push_table(state, 0, metatable_nrec, true)?;
for (k, m) in registry.meta_methods {
self.push_value(Value::Function(self.create_callback(m)?))?;
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
@@ -2535,7 +2572,7 @@ impl Lua {
if index_type == ffi::LUA_TNIL {
// Create a new table
ffi::lua_pop(state, 1);
push_table(state, 0, fields_nrec as c_int, true)?;
push_table(state, 0, fields_nrec, true)?;
}
for (k, f) in registry.fields {
self.push_value(f(self, MultiValue::new())?.pop_front().unwrap())?;
@@ -2555,7 +2592,7 @@ impl Lua {
let mut field_getters_index = None;
let field_getters_nrec = registry.field_getters.len();
if field_getters_nrec > 0 {
push_table(state, 0, field_getters_nrec as c_int, true)?;
push_table(state, 0, field_getters_nrec, true)?;
for (k, m) in registry.field_getters {
self.push_value(Value::Function(self.create_callback(m)?))?;
rawset_field(state, -2, &k)?;
@@ -2567,7 +2604,7 @@ impl Lua {
let mut field_setters_index = None;
let field_setters_nrec = registry.field_setters.len();
if field_setters_nrec > 0 {
push_table(state, 0, field_setters_nrec as c_int, true)?;
push_table(state, 0, field_setters_nrec, true)?;
for (k, m) in registry.field_setters {
self.push_value(Value::Function(self.create_callback(m)?))?;
rawset_field(state, -2, &k)?;
@@ -2588,7 +2625,7 @@ impl Lua {
_ => {
// Create a new table
ffi::lua_pop(state, 1);
push_table(state, 0, methods_nrec as c_int, true)?;
push_table(state, 0, methods_nrec, true)?;
}
}
for (k, m) in registry.methods {
@@ -2614,12 +2651,21 @@ impl Lua {
}
}
init_userdata_metatable::<UserDataCell<T>>(
#[cfg(feature = "luau")]
let extra_init = None;
#[cfg(not(feature = "luau"))]
let extra_init: Option<fn(*mut ffi::lua_State) -> Result<()>> = Some(|state| {
ffi::lua_pushcfunction(state, util::userdata_destructor::<UserDataCell<T>>);
rawset_field(state, -2, "__gc")
});
init_userdata_metatable(
state,
metatable_index,
field_getters_index,
field_setters_index,
methods_index,
extra_init,
)?;
// Pop extra tables to get metatable on top of the stack
@@ -2658,18 +2704,17 @@ impl Lua {
}
}
// Pushes a LuaRef value onto the stack, checking that it's a registered
// Returns `TypeId` for the LuaRef, checking that it's a registered
// and not destructed UserData.
// Uses 2 stack spaces, does not call checkstack.
pub(crate) unsafe fn push_userdata_ref(&self, lref: &LuaRef) -> Result<Option<TypeId>> {
let state = self.state();
self.push_ref(lref);
if ffi::lua_getmetatable(state, -1) == 0 {
ffi::lua_pop(state, 1);
//
// Returns `None` if the userdata is registered but non-static.
pub(crate) unsafe fn get_userdata_type_id(&self, lref: &LuaRef) -> Result<Option<TypeId>> {
let ref_thread = self.ref_thread();
if ffi::lua_getmetatable(ref_thread, lref.index) == 0 {
return Err(Error::UserDataTypeMismatch);
}
let mt_ptr = ffi::lua_topointer(state, -1);
ffi::lua_pop(state, 1);
let mt_ptr = ffi::lua_topointer(ref_thread, -1);
ffi::lua_pop(ref_thread, 1);
// Fast path to skip looking up the metatable in the map
let (last_mt, last_type_id) = (*self.extra.get()).last_checked_userdata_mt;
@@ -2689,6 +2734,14 @@ impl Lua {
}
}
// Pushes a LuaRef (userdata) value onto the stack, returning their `TypeId`.
// Uses 1 stack space, does not call checkstack.
pub(crate) unsafe fn push_userdata_ref(&self, lref: &LuaRef) -> Result<Option<TypeId>> {
let type_id = self.get_userdata_type_id(lref)?;
self.push_ref(lref);
Ok(type_id)
}
// Creates a Function out of a Callback containing a 'static Fn. This is safe ONLY because the
// Fn is 'static, otherwise it could capture 'lua arguments improperly. Without ATCs, we
// cannot easily deal with the "correct" callback type of:
@@ -2940,7 +2993,7 @@ impl Lua {
}
// Create new metatable from UserData definition
let mut registry = UserDataRegistrar::new();
let mut registry = UserDataRegistry::new();
T::add_fields(&mut registry);
T::add_methods(&mut registry);
@@ -2960,7 +3013,7 @@ impl Lua {
}
// Create empty metatable
let registry = UserDataRegistrar::new();
let registry = UserDataRegistry::new();
self.register_userdata_metatable::<T>(registry)
})
}
@@ -3041,7 +3094,13 @@ impl Lua {
(*self.extra.get())
.mem_state
.map(|x| x.as_ref().memory_limit() == 0)
.unwrap_or_default()
.unwrap_or_else(|| {
// Alternatively, check the special flag (only for module mode)
#[cfg(feature = "module")]
return (*self.extra.get()).skip_memory_check;
#[cfg(not(feature = "module"))]
return false;
})
}
#[cfg(feature = "unstable")]
+8 -2
View File
@@ -70,7 +70,7 @@ unsafe extern "C" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_int {
}
fn lua_require(lua: &Lua, name: Option<StdString>) -> Result<Value> {
let name = name.ok_or_else(|| Error::RuntimeError("invalid module name".into()))?;
let name = name.ok_or_else(|| Error::runtime("invalid module name"))?;
// Find module in the cache
let state = lua.state();
@@ -101,7 +101,7 @@ fn lua_require(lua: &Lua, name: Option<StdString>) -> Result<Value> {
break;
}
}
let source = source.ok_or_else(|| Error::RuntimeError(format!("cannot find '{name}'")))?;
let source = source.ok_or_else(|| Error::runtime(format!("cannot find '{name}'")))?;
let value = lua
.load(&source)
@@ -126,6 +126,12 @@ unsafe extern "C" fn lua_vector(state: *mut ffi::lua_State) -> c_int {
let x = ffi::luaL_checknumber(state, 1) as c_float;
let y = ffi::luaL_checknumber(state, 2) as c_float;
let z = ffi::luaL_checknumber(state, 3) as c_float;
#[cfg(feature = "luau-vector4")]
let w = ffi::luaL_checknumber(state, 4) as c_float;
#[cfg(not(feature = "luau-vector4"))]
ffi::lua_pushvector(state, x, y, z);
#[cfg(feature = "luau-vector4")]
ffi::lua_pushvector(state, x, y, z, w);
1
}
+2 -2
View File
@@ -13,7 +13,7 @@ pub use crate::{
Thread as LuaThread, ThreadStatus as LuaThreadStatus, UserData as LuaUserData,
UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable,
UserDataMethods as LuaUserDataMethods, UserDataRef as LuaUserDataRef,
UserDataRefMut as LuaUserDataRefMut, UserDataRegistrar as LuaUserDataRegistrar,
UserDataRefMut as LuaUserDataRefMut, UserDataRegistry as LuaUserDataRegistry,
Value as LuaValue,
};
@@ -23,7 +23,7 @@ pub use crate::HookTriggers as LuaHookTriggers;
#[cfg(feature = "luau")]
#[doc(no_inline)]
pub use crate::{CoverageInfo as LuaCoverageInfo, VmState as LuaVmState};
pub use crate::{CoverageInfo as LuaCoverageInfo, Vector as LuaVector, VmState as LuaVmState};
#[cfg(feature = "async")]
#[doc(no_inline)]
+62 -23
View File
@@ -2,7 +2,6 @@ use std::any::Any;
use std::cell::{Cell, RefCell};
use std::marker::PhantomData;
use std::mem;
use std::os::raw::c_int;
#[cfg(feature = "serialize")]
use serde::Serialize;
@@ -14,10 +13,10 @@ use crate::types::{Callback, CallbackUpvalue, LuaRef, MaybeSend};
use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
};
use crate::userdata_impl::UserDataRegistrar;
use crate::userdata_impl::UserDataRegistry;
use crate::util::{
assert_stack, check_stack, get_userdata, init_userdata_metatable, push_table, rawset_field,
take_userdata, StackGuard,
self, assert_stack, check_stack, get_userdata, init_userdata_metatable, push_table,
rawset_field, take_userdata, StackGuard,
};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Value};
@@ -240,7 +239,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
#[cfg(feature = "lua54")]
for i in 1..=USER_VALUE_MAXSLOT {
ffi::lua_pushnil(state);
ffi::lua_setiuservalue(state, -2, i as c_int);
ffi::lua_setiuservalue(state, -2, i as _);
}
#[cfg(any(feature = "lua53", feature = "lua52", feature = "luau"))]
{
@@ -369,7 +368,6 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
check_stack(state, 13)?;
#[cfg(not(feature = "luau"))]
#[allow(clippy::let_and_return)]
let ud_ptr = protect_lua!(state, 0, 1, |state| {
let ud = ffi::lua_newuserdata(state, mem::size_of::<UserDataCell<T>>());
@@ -384,13 +382,13 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
})?;
#[cfg(feature = "luau")]
let ud_ptr = {
crate::util::push_userdata(state, UserDataCell::new(data), true)?;
util::push_userdata(state, UserDataCell::new(data), true)?;
ffi::lua_touserdata(state, -1) as *const UserDataCell<T>
};
// Prepare metatable, add meta methods first and then meta fields
let meta_methods_nrec = ud_methods.meta_methods.len() + ud_fields.meta_fields.len() + 1;
push_table(state, 0, meta_methods_nrec as c_int, true)?;
push_table(state, 0, meta_methods_nrec, true)?;
for (k, m) in ud_methods.meta_methods {
lua.push_value(Value::Function(wrap_method(self, ud_ptr, m)?))?;
@@ -405,7 +403,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let mut field_getters_index = None;
let field_getters_nrec = ud_fields.field_getters.len();
if field_getters_nrec > 0 {
push_table(state, 0, field_getters_nrec as c_int, true)?;
push_table(state, 0, field_getters_nrec, true)?;
for (k, m) in ud_fields.field_getters {
lua.push_value(Value::Function(wrap_method(self, ud_ptr, m)?))?;
rawset_field(state, -2, &k)?;
@@ -416,7 +414,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let mut field_setters_index = None;
let field_setters_nrec = ud_fields.field_setters.len();
if field_setters_nrec > 0 {
push_table(state, 0, field_setters_nrec as c_int, true)?;
push_table(state, 0, field_setters_nrec, true)?;
for (k, m) in ud_fields.field_setters {
lua.push_value(Value::Function(wrap_method(self, ud_ptr, m)?))?;
rawset_field(state, -2, &k)?;
@@ -428,7 +426,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let methods_nrec = ud_methods.methods.len();
if methods_nrec > 0 {
// Create table used for methods lookup
push_table(state, 0, methods_nrec as c_int, true)?;
push_table(state, 0, methods_nrec, true)?;
for (k, m) in ud_methods.methods {
lua.push_value(Value::Function(wrap_method(self, ud_ptr, m)?))?;
rawset_field(state, -2, &k)?;
@@ -436,12 +434,21 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
methods_index = Some(ffi::lua_absindex(state, -1));
}
init_userdata_metatable::<UserDataCell<T>>(
#[cfg(feature = "luau")]
let extra_init = None;
#[cfg(not(feature = "luau"))]
let extra_init: Option<fn(*mut ffi::lua_State) -> Result<()>> = Some(|state| {
ffi::lua_pushcfunction(state, util::userdata_destructor::<UserDataCell<T>>);
rawset_field(state, -2, "__gc")
});
init_userdata_metatable(
state,
metatable_index,
field_getters_index,
field_setters_index,
methods_index,
extra_init,
)?;
let count = field_getters_index.map(|_| 1).unwrap_or(0)
@@ -479,7 +486,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
#[cfg(feature = "lua54")]
for i in 1..=USER_VALUE_MAXSLOT {
ffi::lua_pushnil(state);
ffi::lua_setiuservalue(state, -2, i as c_int);
ffi::lua_setiuservalue(state, -2, i as _);
}
#[cfg(any(feature = "lua53", feature = "lua52", feature = "luau"))]
{
@@ -611,12 +618,28 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
}
#[cfg(feature = "async")]
fn add_async_method<M, A, MR, R>(&mut self, _name: impl AsRef<str>, _method: M)
fn add_async_method<'s, M, A, MR, R>(&mut self, _name: impl AsRef<str>, _method: M)
where
T: Clone,
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static,
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 'lua,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
{
// The panic should never happen as async non-static code wouldn't compile
// Non-static lifetime must be bounded to 'lua lifetime
panic!("asynchronous methods are not supported for non-static userdata")
}
#[cfg(feature = "async")]
fn add_async_method_mut<'s, M, A, MR, R>(&mut self, _name: impl AsRef<str>, _method: M)
where
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
{
// The panic should never happen as async non-static code wouldn't compile
@@ -686,12 +709,28 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
}
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_method<M, A, MR, R>(&mut self, _name: impl AsRef<str>, _method: M)
fn add_async_meta_method<'s, M, A, MR, R>(&mut self, _name: impl AsRef<str>, _method: M)
where
T: Clone,
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static,
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 'lua,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
{
// The panic should never happen as async non-static code wouldn't compile
// Non-static lifetime must be bounded to 'lua lifetime
panic!("asynchronous meta methods are not supported for non-static userdata")
}
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_method_mut<'s, M, A, MR, R>(&mut self, _name: impl AsRef<str>, _method: M)
where
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
{
// The panic should never happen as async non-static code wouldn't compile
@@ -821,7 +860,7 @@ impl<'lua, T: UserData> UserDataFields<'lua, T> for NonStaticUserDataFields<'lua
self.meta_fields.push((
name,
Box::new(move |lua, _| {
UserDataRegistrar::<()>::check_meta_field(lua, &name2, value.clone())
UserDataRegistry::<()>::check_meta_field(lua, &name2, value.clone())
}),
));
}
@@ -835,7 +874,7 @@ impl<'lua, T: UserData> UserDataFields<'lua, T> for NonStaticUserDataFields<'lua
let name2 = name.clone();
self.meta_fields.push((
name,
Box::new(move |lua, _| UserDataRegistrar::<()>::check_meta_field(lua, &name2, f(lua)?)),
Box::new(move |lua, _| UserDataRegistry::<()>::check_meta_field(lua, &name2, f(lua)?)),
));
}
}
+7 -7
View File
@@ -124,7 +124,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
#[allow(clippy::useless_conversion)]
Value::Number(n) => visitor.visit_f64(n.into()),
#[cfg(feature = "luau")]
Value::Vector(_, _, _) => self.deserialize_seq(visitor),
Value::Vector(_) => self.deserialize_seq(visitor),
Value::String(s) => match s.to_str() {
Ok(s) => visitor.visit_str(s),
Err(_) => visitor.visit_bytes(s.as_bytes()),
@@ -223,9 +223,9 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
{
match self.value {
#[cfg(feature = "luau")]
Value::Vector(x, y, z) => {
Value::Vector(vec) => {
let mut deserializer = VecDeserializer {
vec: [x, y, z],
vec,
next: 0,
options: self.options,
visited: self.visited,
@@ -237,7 +237,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
let len = t.raw_len() as usize;
let mut deserializer = SeqDeserializer {
seq: t.raw_sequence_values(),
seq: t.sequence_values(),
options: self.options,
visited: self.visited,
};
@@ -412,7 +412,7 @@ impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
#[cfg(feature = "luau")]
struct VecDeserializer {
vec: [f32; 3],
vec: crate::types::Vector,
next: usize,
options: Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
@@ -426,7 +426,7 @@ impl<'de> de::SeqAccess<'de> for VecDeserializer {
where
T: de::DeserializeSeed<'de>,
{
match self.vec.get(self.next) {
match self.vec.0.get(self.next) {
Some(&n) => {
self.next += 1;
let visited = Rc::clone(&self.visited);
@@ -439,7 +439,7 @@ impl<'de> de::SeqAccess<'de> for VecDeserializer {
}
fn size_hint(&self) -> Option<usize> {
Some(3)
Some(crate::types::Vector::SIZE)
}
}
+65 -45
View File
@@ -1,5 +1,3 @@
use std::os::raw::c_int;
use serde::{ser, Serialize};
use super::LuaSerdeExt;
@@ -7,8 +5,6 @@ use crate::error::{Error, Result};
use crate::lua::Lua;
use crate::string::String;
use crate::table::Table;
use crate::types::Integer;
use crate::util::{check_stack, StackGuard};
use crate::value::{IntoLua, Value};
/// A struct for serializing Rust values into Lua values.
@@ -120,9 +116,9 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
// Associated types for keeping track of additional state while serializing
// compound data structures like sequences and maps.
type SerializeSeq = SerializeVec<'lua>;
type SerializeTuple = SerializeVec<'lua>;
type SerializeTupleStruct = SerializeVec<'lua>;
type SerializeSeq = SerializeSeq<'lua>;
type SerializeTuple = SerializeSeq<'lua>;
type SerializeTupleStruct = SerializeSeq<'lua>;
type SerializeTupleVariant = SerializeTupleVariant<'lua>;
type SerializeMap = SerializeMap<'lua>;
type SerializeStruct = SerializeMap<'lua>;
@@ -235,13 +231,11 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
#[inline]
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
let len = len.unwrap_or(0) as c_int;
let table = self.lua.create_table_with_capacity(len, 0)?;
let table = self.lua.create_table_with_capacity(len.unwrap_or(0), 0)?;
if self.options.set_array_metatable {
table.set_metatable(Some(self.lua.array_metatable()));
}
let options = self.options;
Ok(SerializeVec { table, options })
Ok(SerializeSeq::new(table, self.options))
}
#[inline]
@@ -252,9 +246,14 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
#[inline]
fn serialize_tuple_struct(
self,
_name: &'static str,
name: &'static str,
len: usize,
) -> Result<Self::SerializeTupleStruct> {
#[cfg(feature = "luau")]
if name == "Vector" && len == crate::types::Vector::SIZE {
return Ok(SerializeSeq::new_vector(self.lua, self.options));
}
_ = name;
self.serialize_seq(Some(len))
}
@@ -275,10 +274,9 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
#[inline]
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
let len = len.unwrap_or(0) as c_int;
Ok(SerializeMap {
key: None,
table: self.lua.create_table_with_capacity(0, len)?,
table: self.lua.create_table_with_capacity(0, len.unwrap_or(0))?,
options: self.options,
})
}
@@ -298,19 +296,47 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
) -> Result<Self::SerializeStructVariant> {
Ok(SerializeStructVariant {
name: self.lua.create_string(variant)?,
table: self.lua.create_table_with_capacity(0, len as c_int)?,
table: self.lua.create_table_with_capacity(0, len)?,
options: self.options,
})
}
}
#[doc(hidden)]
pub struct SerializeVec<'lua> {
table: Table<'lua>,
pub struct SerializeSeq<'lua> {
lua: &'lua Lua,
#[cfg(feature = "luau")]
vector: Option<crate::types::Vector>,
table: Option<Table<'lua>>,
next: usize,
options: Options,
}
impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
impl<'lua> SerializeSeq<'lua> {
const fn new(table: Table<'lua>, options: Options) -> Self {
Self {
lua: table.0.lua,
#[cfg(feature = "luau")]
vector: None,
table: Some(table),
next: 0,
options,
}
}
#[cfg(feature = "luau")]
const fn new_vector(lua: &'lua Lua, options: Options) -> Self {
Self {
lua,
vector: Some(crate::types::Vector::zero()),
table: None,
next: 0,
options,
}
}
}
impl<'lua> ser::SerializeSeq for SerializeSeq<'lua> {
type Ok = Value<'lua>;
type Error = Error;
@@ -318,35 +344,19 @@ impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
where
T: Serialize + ?Sized,
{
let lua = self.table.0.lua;
let state = lua.state();
let value = lua.to_value_with(value, self.options)?;
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 4)?;
lua.push_ref(&self.table.0);
lua.push_value(value)?;
if lua.unlikely_memory_error() {
let len = ffi::lua_rawlen(state, -2) as Integer;
ffi::lua_rawseti(state, -2, len + 1);
ffi::lua_pop(state, 1);
Ok(())
} else {
protect_lua!(state, 2, 0, fn(state) {
let len = ffi::lua_rawlen(state, -2) as Integer;
ffi::lua_rawseti(state, -2, len + 1);
})
}
}
let value = self.lua.to_value_with(value, self.options)?;
let table = self.table.as_ref().unwrap();
table.raw_seti(self.next + 1, value)?;
self.next += 1;
Ok(())
}
fn end(self) -> Result<Value<'lua>> {
Ok(Value::Table(self.table))
Ok(Value::Table(self.table.unwrap()))
}
}
impl<'lua> ser::SerializeTuple for SerializeVec<'lua> {
impl<'lua> ser::SerializeTuple for SerializeSeq<'lua> {
type Ok = Value<'lua>;
type Error = Error;
@@ -362,7 +372,7 @@ impl<'lua> ser::SerializeTuple for SerializeVec<'lua> {
}
}
impl<'lua> ser::SerializeTupleStruct for SerializeVec<'lua> {
impl<'lua> ser::SerializeTupleStruct for SerializeSeq<'lua> {
type Ok = Value<'lua>;
type Error = Error;
@@ -370,10 +380,22 @@ impl<'lua> ser::SerializeTupleStruct for SerializeVec<'lua> {
where
T: Serialize + ?Sized,
{
#[cfg(feature = "luau")]
if let Some(vector) = self.vector.as_mut() {
let value = self.lua.to_value_with(value, self.options)?;
let value = self.lua.unpack(value)?;
vector.0[self.next] = value;
self.next += 1;
return Ok(());
}
ser::SerializeSeq::serialize_element(self, value)
}
fn end(self) -> Result<Value<'lua>> {
#[cfg(feature = "luau")]
if let Some(vector) = self.vector {
return Ok(Value::Vector(vector));
}
ser::SerializeSeq::end(self)
}
}
@@ -394,9 +416,7 @@ impl<'lua> ser::SerializeTupleVariant for SerializeTupleVariant<'lua> {
T: Serialize + ?Sized,
{
let lua = self.table.0.lua;
let idx = self.table.raw_len() + 1;
self.table
.raw_insert(idx, lua.to_value_with(value, self.options)?)
self.table.raw_push(lua.to_value_with(value, self.options)?)
}
fn end(self) -> Result<Value<'lua>> {
+1 -2
View File
@@ -139,8 +139,7 @@ impl<'lua> String<'lua> {
/// Typically this function is used only for hashing and debug information.
#[inline]
pub fn to_pointer(&self) -> *const c_void {
let ref_thread = self.0.lua.ref_thread();
unsafe { ffi::lua_topointer(ref_thread, self.0.index) }
self.0.to_pointer()
}
/// Convert this handle to owned version.
+155 -77
View File
@@ -45,7 +45,6 @@ impl OwnedTable {
}
}
#[allow(clippy::len_without_is_empty)]
impl<'lua> Table<'lua> {
/// Sets a key-value pair in the table.
///
@@ -150,11 +149,15 @@ impl<'lua> Table<'lua> {
}
/// Checks whether the table contains a non-nil value for `key`.
///
/// This might invoke the `__index` metamethod.
pub fn contains_key<K: IntoLua<'lua>>(&self, key: K) -> Result<bool> {
Ok(self.get::<_, Value>(key)? != Value::Nil)
}
/// Appends a value to the back of the table.
///
/// This might invoke the `__len` and `__newindex` metamethods.
pub fn push<V: IntoLua<'lua>>(&self, value: V) -> Result<()> {
// Fast track
if !self.has_metatable() {
@@ -179,6 +182,8 @@ impl<'lua> Table<'lua> {
}
/// Removes the last element from the table and returns it.
///
/// This might invoke the `__len` and `__newindex` metamethods.
pub fn pop<V: FromLua<'lua>>(&self) -> Result<V> {
// Fast track
if !self.has_metatable() {
@@ -314,7 +319,7 @@ impl<'lua> Table<'lua> {
let size = self.raw_len();
if idx < 1 || idx > size + 1 {
return Err(Error::RuntimeError("index out of bounds".to_string()));
return Err(Error::runtime("index out of bounds"));
}
let value = value.into_lua(lua)?;
@@ -402,7 +407,7 @@ impl<'lua> Table<'lua> {
Value::Integer(idx) => {
let size = self.raw_len();
if idx < 1 || idx > size {
return Err(Error::RuntimeError("index out of bounds".to_string()));
return Err(Error::runtime("index out of bounds"));
}
unsafe {
let _sg = StackGuard::new(state);
@@ -492,6 +497,32 @@ impl<'lua> Table<'lua> {
unsafe { ffi::lua_rawlen(ref_thread, self.0.index) as Integer }
}
/// Returns `true` if the table is empty, without invoking metamethods.
///
/// It checks both the array part and the hash part.
pub fn is_empty(&self) -> bool {
// Check array part
if self.raw_len() != 0 {
return false;
}
// Check hash part
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 4);
lua.push_ref(&self.0);
ffi::lua_pushnil(state);
if ffi::lua_next(state, -2) != 0 {
return false;
}
}
true
}
/// Returns a reference to the metatable of this table, or `None` if no metatable is set.
///
/// Unlike the `getmetatable` Lua function, this method ignores the `__metatable` field.
@@ -586,8 +617,7 @@ impl<'lua> Table<'lua> {
/// Typically this function is used only for hashing and debug information.
#[inline]
pub fn to_pointer(&self) -> *const c_void {
let ref_thread = self.0.lua.ref_thread();
unsafe { ffi::lua_topointer(ref_thread, self.0.index) }
self.0.to_pointer()
}
/// Convert this handle to owned version.
@@ -641,12 +671,9 @@ impl<'lua> Table<'lua> {
/// Consume this table and return an iterator over all values in the sequence part of the table.
///
/// The iterator will yield all values `t[1]`, `t[2]`, and so on, until a `nil` value is
/// encountered. This mirrors the behavior of Lua's `ipairs` function and will invoke the
/// `__index` metamethod according to the usual rules. However, the deprecated `__ipairs`
/// metatable will not be called.
///
/// Just like [`pairs`], the values are wrapped in a [`Result`].
/// The iterator will yield all values `t[1]`, `t[2]` and so on, until a `nil` value is
/// encountered. This mirrors the behavior of Lua's `ipairs` function but does not invoke
/// any metamethods.
///
/// # Note
///
@@ -685,28 +712,18 @@ impl<'lua> Table<'lua> {
table: self.0,
index: Some(1),
len: None,
raw: false,
_phantom: PhantomData,
}
}
/// Consume this table and return an iterator over all values in the sequence part of the table.
///
/// Unlike the `sequence_values`, does not invoke `__index` metamethod when iterating.
///
/// [`sequence_values`]: #method.sequence_values
#[doc(hidden)]
#[deprecated(since = "0.9.0", note = "use `sequence_values` instead")]
pub fn raw_sequence_values<V: FromLua<'lua>>(self) -> TableSequence<'lua, V> {
TableSequence {
table: self.0,
index: Some(1),
len: None,
raw: true,
_phantom: PhantomData,
}
self.sequence_values()
}
#[cfg(feature = "serialize")]
pub(crate) fn raw_sequence_values_by_len<V: FromLua<'lua>>(
pub(crate) fn sequence_values_by_len<V: FromLua<'lua>>(
self,
len: Option<Integer>,
) -> TableSequence<'lua, V> {
@@ -715,11 +732,37 @@ impl<'lua> Table<'lua> {
table: self.0,
index: Some(1),
len: Some(len),
raw: true,
_phantom: PhantomData,
}
}
/// Sets element value at position `idx` without invoking metamethods.
#[allow(dead_code)]
pub(crate) fn raw_seti<V: IntoLua<'lua>>(&self, idx: usize, value: V) -> Result<()> {
#[cfg(feature = "luau")]
self.check_readonly_write()?;
let lua = self.0.lua;
let state = lua.state();
let value = value.into_lua(lua)?;
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 5)?;
lua.push_ref(&self.0);
lua.push_value(value)?;
let idx = idx.try_into().unwrap();
if lua.unlikely_memory_error() {
ffi::lua_rawseti(state, -2, idx);
} else {
protect_lua!(state, 2, 0, |state| ffi::lua_rawseti(state, -2, idx))?;
}
Ok(())
}
}
#[cfg(feature = "serialize")]
pub(crate) fn is_array(&self) -> bool {
let lua = self.0.lua;
@@ -741,8 +784,7 @@ impl<'lua> Table<'lua> {
#[inline(always)]
pub(crate) fn check_readonly_write(&self) -> Result<()> {
if self.is_readonly() {
let err = "attempt to modify a readonly table".to_string();
return Err(Error::RuntimeError(err));
return Err(Error::runtime("attempt to modify a readonly table"));
}
Ok(())
}
@@ -797,6 +839,56 @@ impl<'lua> AsRef<Table<'lua>> for Table<'lua> {
}
}
impl<'lua, T> PartialEq<[T]> for Table<'lua>
where
T: IntoLua<'lua> + Clone,
{
fn eq(&self, other: &[T]) -> bool {
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 4);
lua.push_ref(&self.0);
let len = ffi::lua_rawlen(state, -1);
for i in 0..len {
ffi::lua_rawgeti(state, -1, (i + 1) as _);
let val = lua.pop_value();
if val == Nil {
return i == other.len();
}
match other.get(i).map(|v| v.clone().into_lua(lua)) {
Some(Ok(other_val)) if val == other_val => continue,
_ => return false,
}
}
}
true
}
}
impl<'lua, T> PartialEq<&[T]> for Table<'lua>
where
T: IntoLua<'lua> + Clone,
{
#[inline]
fn eq(&self, other: &&[T]) -> bool {
self == *other
}
}
impl<'lua, T, const N: usize> PartialEq<[T; N]> for Table<'lua>
where
T: IntoLua<'lua> + Clone,
{
#[inline]
fn eq(&self, other: &[T; N]) -> bool {
self == &other[..]
}
}
/// An extension trait for `Table`s that provides a variety of convenient functionality.
pub trait TableExt<'lua>: Sealed {
/// Calls the table as function assuming it has `__call` metamethod.
@@ -812,11 +904,10 @@ pub trait TableExt<'lua>: Sealed {
/// The metamethod is called with the table as its first argument, followed by the passed arguments.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
fn call_async<A, R>(&self, args: A) -> LocalBoxFuture<'lua, Result<R>>
where
'lua: 'fut,
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut;
R: FromLuaMulti<'lua> + 'lua;
/// Gets the function associated to `key` from the table and executes it,
/// passing the table itself along with `args` as function arguments.
@@ -825,9 +916,8 @@ pub trait TableExt<'lua>: Sealed {
/// `table.get::<_, Function>(key)?.call((table.clone(), arg1, ..., argN))`
///
/// This might invoke the `__index` metamethod.
fn call_method<K, A, R>(&self, key: K, args: A) -> Result<R>
fn call_method<A, R>(&self, name: &str, args: A) -> Result<R>
where
K: IntoLua<'lua>,
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>;
@@ -838,9 +928,8 @@ pub trait TableExt<'lua>: Sealed {
/// `table.get::<_, Function>(key)?.call(args)`
///
/// This might invoke the `__index` metamethod.
fn call_function<K, A, R>(&self, key: K, args: A) -> Result<R>
fn call_function<A, R>(&self, name: &str, args: A) -> Result<R>
where
K: IntoLua<'lua>,
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>;
@@ -852,12 +941,10 @@ pub trait TableExt<'lua>: Sealed {
/// This might invoke the `__index` metamethod.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async_method<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>>
fn call_async_method<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
where
'lua: 'fut,
K: IntoLua<'lua>,
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut;
R: FromLuaMulti<'lua> + 'lua;
/// Gets the function associated to `key` from the table and asynchronously executes it,
/// passing `args` as function arguments and returning Future.
@@ -867,16 +954,10 @@ pub trait TableExt<'lua>: Sealed {
/// This might invoke the `__index` metamethod.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async_function<'fut, K, A, R>(
&self,
key: K,
args: A,
) -> LocalBoxFuture<'fut, Result<R>>
fn call_async_function<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
where
'lua: 'fut,
K: IntoLua<'lua>,
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut;
R: FromLuaMulti<'lua> + 'lua;
}
impl<'lua> TableExt<'lua> for Table<'lua> {
@@ -890,43 +971,43 @@ impl<'lua> TableExt<'lua> for Table<'lua> {
}
#[cfg(feature = "async")]
fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
fn call_async<A, R>(&self, args: A) -> LocalBoxFuture<'lua, Result<R>>
where
'lua: 'fut,
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
R: FromLuaMulti<'lua> + 'lua,
{
Function(self.0.clone()).call_async(args)
let args = match args.into_lua_multi(self.0.lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
let func = Function(self.0.clone());
Box::pin(async move { func.call_async(args).await })
}
fn call_method<K, A, R>(&self, key: K, args: A) -> Result<R>
fn call_method<A, R>(&self, name: &str, args: A) -> Result<R>
where
K: IntoLua<'lua>,
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
let lua = self.0.lua;
let mut args = args.into_lua_multi(lua)?;
args.push_front(Value::Table(self.clone()));
self.get::<_, Function>(key)?.call(args)
self.get::<_, Function>(name)?.call(args)
}
fn call_function<K, A, R>(&self, key: K, args: A) -> Result<R>
fn call_function<A, R>(&self, name: &str, args: A) -> Result<R>
where
K: IntoLua<'lua>,
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
self.get::<_, Function>(key)?.call(args)
self.get::<_, Function>(name)?.call(args)
}
#[cfg(feature = "async")]
fn call_async_method<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>>
fn call_async_method<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
where
'lua: 'fut,
K: IntoLua<'lua>,
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
R: FromLuaMulti<'lua> + 'lua,
{
let lua = self.0.lua;
let mut args = match args.into_lua_multi(lua) {
@@ -934,19 +1015,22 @@ impl<'lua> TableExt<'lua> for Table<'lua> {
Err(e) => return Box::pin(future::err(e)),
};
args.push_front(Value::Table(self.clone()));
self.call_async_function(key, args)
self.call_async_function(name, args)
}
#[cfg(feature = "async")]
fn call_async_function<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>>
fn call_async_function<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
where
'lua: 'fut,
K: IntoLua<'lua>,
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
R: FromLuaMulti<'lua> + 'lua,
{
match self.get::<_, Function>(key) {
Ok(func) => func.call_async(args),
let lua = self.0.lua;
let args = match args.into_lua_multi(lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
match self.get::<_, Function>(name) {
Ok(func) => Box::pin(async move { func.call_async(args).await }),
Err(e) => Box::pin(future::err(e)),
}
}
@@ -975,7 +1059,7 @@ impl<'lua> Serialize for Table<'lua> {
let len = self.raw_len() as usize;
if len > 0 || self.is_array() {
let mut seq = serializer.serialize_seq(Some(len))?;
for v in self.clone().raw_sequence_values_by_len::<Value>(None) {
for v in self.clone().sequence_values_by_len::<Value>(None) {
let v = v.map_err(serde::ser::Error::custom)?;
seq.serialize_element(&v)?;
}
@@ -1065,7 +1149,6 @@ pub struct TableSequence<'lua, V> {
table: LuaRef<'lua>,
index: Option<Integer>,
len: Option<Integer>,
raw: bool,
_phantom: PhantomData<V>,
}
@@ -1082,15 +1165,10 @@ where
let res = (|| unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 1 + if self.raw { 0 } else { 3 })?;
check_stack(state, 1)?;
lua.push_ref(&self.table);
let res = if self.raw {
ffi::lua_rawgeti(state, -1, index)
} else {
protect_lua!(state, 1, 1, |state| ffi::lua_geti(state, -1, index))?
};
match res {
match ffi::lua_rawgeti(state, -1, index) {
ffi::LUA_TNIL if index > self.len.unwrap_or(0) => Ok(None),
_ => Ok(Some((index, lua.pop_value()))),
}
+9 -16
View File
@@ -8,13 +8,6 @@ use crate::types::LuaRef;
use crate::util::{check_stack, error_traceback_thread, pop_error, StackGuard};
use crate::value::{FromLuaMulti, IntoLuaMulti};
#[cfg(any(
feature = "lua54",
all(feature = "luajit", feature = "vendored"),
feature = "luau",
))]
use crate::function::Function;
#[cfg(not(feature = "luau"))]
use crate::{
hook::{Debug, HookTriggers},
@@ -60,14 +53,14 @@ pub struct Thread<'lua>(pub(crate) LuaRef<'lua>);
///
/// Requires `feature = "async"`
///
/// [`Future`]: futures_core::future::Future
/// [`Stream`]: futures_core::stream::Stream
/// [`Future`]: std::future::Future
/// [`Stream`]: futures_util::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>>>,
init_args: Option<Result<MultiValue<'lua>>>,
ret: PhantomData<R>,
recycle: bool,
}
@@ -223,7 +216,7 @@ impl<'lua> Thread<'lua> {
all(feature = "luajit", feature = "vendored"),
feature = "luau",
))]
pub fn reset(&self, func: Function<'lua>) -> Result<()> {
pub fn reset(&self, func: crate::function::Function<'lua>) -> Result<()> {
let lua = self.0.lua;
let state = lua.state();
unsafe {
@@ -272,8 +265,8 @@ impl<'lua> Thread<'lua> {
///
/// Requires `feature = "async"`
///
/// [`Future`]: futures_core::future::Future
/// [`Stream`]: futures_core::stream::Stream
/// [`Future`]: std::future::Future
/// [`Stream`]: futures_util::stream::Stream
/// [`resume()`]: https://www.lua.org/manual/5.4/manual.html#lua_resume
///
/// # Examples
@@ -315,7 +308,7 @@ impl<'lua> Thread<'lua> {
let args = args.into_lua_multi(self.0.lua);
AsyncThread {
thread: self,
args0: Some(args),
init_args: Some(args),
ret: PhantomData,
recycle: false,
}
@@ -427,7 +420,7 @@ where
// This is safe as we are not moving the whole struct
let this = unsafe { self.get_unchecked_mut() };
let ret: MultiValue = if let Some(args) = this.args0.take() {
let ret: MultiValue = if let Some(args) = this.init_args.take() {
this.thread.resume(args?)?
} else {
this.thread.resume(())?
@@ -461,7 +454,7 @@ where
// This is safe as we are not moving the whole struct
let this = unsafe { self.get_unchecked_mut() };
let ret: MultiValue = if let Some(args) = this.args0.take() {
let ret: MultiValue = if let Some(args) = this.init_args.take() {
this.thread.resume(args?)?
} else {
this.thread.resume(())?
+106 -19
View File
@@ -8,9 +8,6 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::{fmt, mem, ptr};
#[cfg(feature = "lua54")]
use std::ffi::CStr;
use rustc_hash::FxHashMap;
#[cfg(feature = "async")]
@@ -20,12 +17,14 @@ use crate::error::Result;
#[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};
#[cfg(all(feature = "luau", feature = "serialize"))]
use serde::ser::{Serialize, SerializeTupleStruct, Serializer};
/// Type of Lua integer numbers.
pub type Integer = ffi::lua_Integer;
/// Type of Lua floating point numbers.
@@ -76,10 +75,10 @@ pub(crate) type InterruptCallback = Arc<dyn Fn(&Lua) -> Result<VmState> + Send>;
pub(crate) type InterruptCallback = Arc<dyn Fn(&Lua) -> Result<VmState>>;
#[cfg(all(feature = "send", feature = "lua54"))]
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &CStr, bool) -> Result<()> + Send>;
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &str, bool) -> Result<()> + Send>;
#[cfg(all(not(feature = "send"), feature = "lua54"))]
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &CStr, bool) -> Result<()>>;
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &str, bool) -> Result<()>>;
#[cfg(feature = "send")]
pub trait MaybeSend: Send {}
@@ -91,6 +90,92 @@ pub trait MaybeSend {}
#[cfg(not(feature = "send"))]
impl<T> MaybeSend for T {}
/// A Luau vector type.
///
/// By default vectors are 3-dimensional, but can be 4-dimensional
/// if the `luau-vector4` feature is enabled.
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[derive(Debug, Default, Clone, Copy, PartialEq)]
pub struct Vector(pub(crate) [f32; Self::SIZE]);
#[cfg(any(feature = "luau", doc))]
impl fmt::Display for Vector {
#[rustfmt::skip]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(not(feature = "luau-vector4"))]
return write!(f, "vector({}, {}, {})", self.x(), self.y(), self.z());
#[cfg(feature = "luau-vector4")]
return write!(f, "vector({}, {}, {}, {})", self.x(), self.y(), self.z(), self.w());
}
}
#[cfg(any(feature = "luau", doc))]
impl Vector {
pub(crate) const SIZE: usize = if cfg!(feature = "luau-vector4") { 4 } else { 3 };
/// Creates a new vector.
#[cfg(not(feature = "luau-vector4"))]
pub const fn new(x: f32, y: f32, z: f32) -> Self {
Self([x, y, z])
}
/// Creates a new vector.
#[cfg(feature = "luau-vector4")]
pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
Self([x, y, z, w])
}
/// Creates a new vector with all components set to `0.0`.
#[doc(hidden)]
pub const fn zero() -> Self {
Self([0.0; Self::SIZE])
}
/// Returns 1st component of the vector.
pub const fn x(&self) -> f32 {
self.0[0]
}
/// Returns 2nd component of the vector.
pub const fn y(&self) -> f32 {
self.0[1]
}
/// Returns 3rd component of the vector.
pub const fn z(&self) -> f32 {
self.0[2]
}
/// Returns 4th component of the vector.
#[cfg(any(feature = "luau-vector4", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau-vector4")))]
pub const fn w(&self) -> f32 {
self.0[3]
}
}
#[cfg(all(feature = "luau", feature = "serialize"))]
impl Serialize for Vector {
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
let mut ts = serializer.serialize_tuple_struct("Vector", Self::SIZE)?;
ts.serialize_field(&self.x())?;
ts.serialize_field(&self.y())?;
ts.serialize_field(&self.z())?;
#[cfg(feature = "luau-vector4")]
ts.serialize_field(&self.w())?;
ts.end()
}
}
#[cfg(any(feature = "luau", doc))]
impl PartialEq<[f32; Self::SIZE]> for Vector {
#[inline]
fn eq(&self, other: &[f32; Self::SIZE]) -> bool {
self.0 == *other
}
}
pub(crate) struct DestructedUserdata;
/// An auto generated key into the Lua registry.
@@ -102,14 +187,14 @@ pub(crate) struct DestructedUserdata;
/// Be warned, If you place this into Lua via a [`UserData`] type or a rust callback, it is *very
/// easy* to accidentally cause reference cycles that the Lua garbage collector cannot resolve.
/// Instead of placing a [`RegistryKey`] into a [`UserData`] type, prefer instead to use
/// [`AnyUserData::set_user_value`] / [`AnyUserData::get_user_value`].
/// [`AnyUserData::set_user_value`] / [`AnyUserData::user_value`].
///
/// [`UserData`]: crate::UserData
/// [`RegistryKey`]: crate::RegistryKey
/// [`Lua::remove_registry_value`]: crate::Lua::remove_registry_value
/// [`Lua::expire_registry_values`]: crate::Lua::expire_registry_values
/// [`AnyUserData::set_user_value`]: crate::AnyUserData::set_user_value
/// [`AnyUserData::get_user_value`]: crate::AnyUserData::get_user_value
/// [`AnyUserData::user_value`]: crate::AnyUserData::user_value
pub struct RegistryKey {
pub(crate) registry_id: c_int,
pub(crate) is_nil: AtomicBool,
@@ -199,6 +284,11 @@ impl<'lua> LuaRef<'lua> {
}
}
#[inline]
pub(crate) fn to_pointer(&self) -> *const c_void {
unsafe { ffi::lua_topointer(self.lua.ref_thread(), self.index) }
}
#[cfg(feature = "unstable")]
#[inline]
pub(crate) fn into_owned(self) -> LuaOwnedRef {
@@ -211,7 +301,7 @@ impl<'lua> LuaRef<'lua> {
impl<'lua> fmt::Debug for LuaRef<'lua> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Ref({})", self.index)
write!(f, "Ref({:p})", self.to_pointer())
}
}
@@ -231,15 +321,12 @@ impl<'lua> Drop for LuaRef<'lua> {
impl<'lua> PartialEq for LuaRef<'lua> {
fn eq(&self, other: &Self) -> bool {
let lua = self.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 2);
lua.push_ref(self);
lua.push_ref(other);
ffi::lua_rawequal(state, -1, -2) == 1
}
let ref_thread = self.lua.ref_thread();
assert!(
ref_thread == other.lua.ref_thread(),
"Lua instance passed Value created from a different main Lua state"
);
unsafe { ffi::lua_rawequal(ref_thread, self.index, other.index) == 1 }
}
}
@@ -253,7 +340,7 @@ pub(crate) struct LuaOwnedRef {
#[cfg(feature = "unstable")]
impl fmt::Debug for LuaOwnedRef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "OwnedRef({})", self.index)
write!(f, "OwnedRef({:p})", self.to_ref().to_pointer())
}
}
+119 -73
View File
@@ -24,7 +24,7 @@ use crate::table::{Table, TablePairs};
use crate::types::{LuaRef, MaybeSend};
use crate::util::{check_stack, get_userdata, take_userdata, StackGuard};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
use crate::UserDataRegistrar;
use crate::UserDataRegistry;
#[cfg(feature = "lua54")]
pub(crate) const USER_VALUE_MAXSLOT: usize = 8;
@@ -259,8 +259,7 @@ pub trait UserDataMethods<'lua, T> {
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>;
/// Add an async method which accepts a `T` as the first parameter and returns Future.
/// The passed `T` is cloned from the original value.
/// Add an async method which accepts a `&T` as the first parameter and returns Future.
///
/// Refer to [`add_method`] for more information about the implementation.
///
@@ -269,12 +268,31 @@ pub trait UserDataMethods<'lua, T> {
/// [`add_method`]: #method.add_method
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn add_async_method<M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
fn add_async_method<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where
T: Clone,
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static,
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 'lua,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>;
/// Add an async method which accepts a `&mut T` as the first parameter and returns Future.
///
/// Refer to [`add_method`] for more information about the implementation.
///
/// Requires `feature = "async"`
///
/// [`add_method`]: #method.add_method
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn add_async_method_mut<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>;
/// Add a regular method as a function which accepts generic arguments, the first argument will
@@ -349,8 +367,7 @@ pub trait UserDataMethods<'lua, T> {
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>;
/// Add an async metamethod which accepts a `T` as the first parameter and returns Future.
/// The passed `T` is cloned from the original value.
/// Add an async metamethod which accepts a `&T` as the first parameter and returns Future.
///
/// This is an async version of [`add_meta_method`].
///
@@ -359,12 +376,31 @@ pub trait UserDataMethods<'lua, T> {
/// [`add_meta_method`]: #method.add_meta_method
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
fn add_async_meta_method<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where
T: Clone,
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static,
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 'lua,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>;
/// Add an async metamethod which accepts a `&mut T` as the first parameter and returns Future.
///
/// This is an async version of [`add_meta_method_mut`].
///
/// Requires `feature = "async"`
///
/// [`add_meta_method_mut`]: #method.add_meta_method_mut
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn add_async_meta_method_mut<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>;
/// Add a metamethod which accepts generic arguments.
@@ -410,7 +446,7 @@ pub trait UserDataMethods<'lua, T> {
//
#[doc(hidden)]
fn append_methods_from<S>(&mut self, _other: UserDataRegistrar<'lua, S>) {}
fn append_methods_from<S>(&mut self, _other: UserDataRegistry<'lua, S>) {}
}
/// Field registry for [`UserData`] implementors.
@@ -508,7 +544,7 @@ pub trait UserDataFields<'lua, T> {
//
#[doc(hidden)]
fn append_fields_from<S>(&mut self, _other: UserDataRegistrar<'lua, S>) {}
fn append_fields_from<S>(&mut self, _other: UserDataRegistry<'lua, S>) {}
}
/// Trait for custom userdata types.
@@ -718,19 +754,6 @@ impl<T> Deref for UserDataVariant<T> {
}
}
#[cfg(feature = "serialize")]
struct UserDataSerializeError;
#[cfg(feature = "serialize")]
impl Serialize for UserDataSerializeError {
fn serialize<S>(&self, _serializer: S) -> StdResult<S::Ok, S::Error>
where
S: Serializer,
{
Err(ser::Error::custom("cannot serialize <userdata>"))
}
}
/// Handle to an internal Lua userdata for any type that implements [`UserData`].
///
/// Similar to `std::any::Any`, this provides an interface for dynamic type checking via the [`is`]
@@ -826,11 +849,11 @@ impl<'lua> AnyUserData<'lua> {
/// Sets an associated value to this `AnyUserData`.
///
/// The value may be any Lua value whatsoever, and can be retrieved with [`get_user_value`].
/// The value may be any Lua value whatsoever, and can be retrieved with [`user_value`].
///
/// This is the same as calling [`set_nth_user_value`] with `n` set to 1.
///
/// [`get_user_value`]: #method.get_user_value
/// [`user_value`]: #method.user_value
/// [`set_nth_user_value`]: #method.set_nth_user_value
#[inline]
pub fn set_user_value<V: IntoLua<'lua>>(&self, v: V) -> Result<()> {
@@ -839,30 +862,34 @@ impl<'lua> AnyUserData<'lua> {
/// Returns an associated value set by [`set_user_value`].
///
/// This is the same as calling [`get_nth_user_value`] with `n` set to 1.
/// This is the same as calling [`nth_user_value`] with `n` set to 1.
///
/// [`set_user_value`]: #method.set_user_value
/// [`get_nth_user_value`]: #method.get_nth_user_value
/// [`nth_user_value`]: #method.nth_user_value
#[inline]
pub fn user_value<V: FromLua<'lua>>(&self) -> Result<V> {
self.nth_user_value(1)
}
#[doc(hidden)]
#[deprecated(since = "0.9.0", note = "please use `user_value` instead")]
pub fn get_user_value<V: FromLua<'lua>>(&self) -> Result<V> {
self.get_nth_user_value(1)
self.nth_user_value(1)
}
/// Sets an associated `n`th value to this `AnyUserData`.
///
/// The value may be any Lua value whatsoever, and can be retrieved with [`get_nth_user_value`].
/// The value may be any Lua value whatsoever, and can be retrieved with [`nth_user_value`].
/// `n` starts from 1 and can be up to 65535.
///
/// This is supported for all Lua versions.
/// In Lua 5.4 first 7 elements are stored in a most efficient way.
/// For other Lua versions this functionality is provided using a wrapping table.
///
/// [`get_nth_user_value`]: #method.get_nth_user_value
/// [`nth_user_value`]: #method.nth_user_value
pub fn set_nth_user_value<V: IntoLua<'lua>>(&self, n: usize, v: V) -> Result<()> {
if n < 1 || n > u16::MAX as usize {
return Err(Error::RuntimeError(
"user value index out of bounds".to_string(),
));
return Err(Error::runtime("user value index out of bounds"));
}
let lua = self.0.lua;
@@ -913,11 +940,9 @@ impl<'lua> AnyUserData<'lua> {
/// For other Lua versions this functionality is provided using a wrapping table.
///
/// [`set_nth_user_value`]: #method.set_nth_user_value
pub fn get_nth_user_value<V: FromLua<'lua>>(&self, n: usize) -> Result<V> {
pub fn nth_user_value<V: FromLua<'lua>>(&self, n: usize) -> Result<V> {
if n < 1 || n > u16::MAX as usize {
return Err(Error::RuntimeError(
"user value index out of bounds".to_string(),
));
return Err(Error::runtime("user value index out of bounds"));
}
let lua = self.0.lua;
@@ -950,18 +975,20 @@ impl<'lua> AnyUserData<'lua> {
}
}
#[doc(hidden)]
#[deprecated(since = "0.9.0", note = "please use `nth_user_value` instead")]
pub fn get_nth_user_value<V: FromLua<'lua>>(&self, n: usize) -> Result<V> {
self.nth_user_value(n)
}
/// Sets an associated value to this `AnyUserData` by name.
///
/// The value can be retrieved with [`get_named_user_value`].
/// The value can be retrieved with [`named_user_value`].
///
/// [`get_named_user_value`]: #method.get_named_user_value
pub fn set_named_user_value<V>(&self, name: impl AsRef<str>, v: V) -> Result<()>
where
V: IntoLua<'lua>,
{
/// [`named_user_value`]: #method.named_user_value
pub fn set_named_user_value<V: IntoLua<'lua>>(&self, name: &str, v: V) -> Result<()> {
let lua = self.0.lua;
let state = lua.state();
let name = name.as_ref();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 5)?;
@@ -994,13 +1021,9 @@ impl<'lua> AnyUserData<'lua> {
/// Returns an associated value by name set by [`set_named_user_value`].
///
/// [`set_named_user_value`]: #method.set_named_user_value
pub fn get_named_user_value<V>(&self, name: impl AsRef<str>) -> Result<V>
where
V: FromLua<'lua>,
{
pub fn named_user_value<V: FromLua<'lua>>(&self, name: &str) -> Result<V> {
let lua = self.0.lua;
let state = lua.state();
let name = name.as_ref();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 4)?;
@@ -1021,6 +1044,12 @@ impl<'lua> AnyUserData<'lua> {
}
}
#[doc(hidden)]
#[deprecated(since = "0.9.0", note = "please use `named_user_value` instead")]
pub fn get_named_user_value<V: FromLua<'lua>>(&self, name: &str) -> Result<V> {
self.named_user_value(name)
}
/// Returns a metatable of this `UserData`.
///
/// Returned [`UserDataMetatable`] object wraps the original metatable and
@@ -1055,6 +1084,11 @@ impl<'lua> AnyUserData<'lua> {
OwnedAnyUserData(self.0.into_owned())
}
#[inline(always)]
pub(crate) fn type_id(&self) -> Result<Option<TypeId>> {
unsafe { self.0.lua.get_userdata_type_id(&self.0) }
}
/// Returns a type name of this `UserData` (from `__name` metatable field).
pub(crate) fn type_name(&self) -> Result<Option<StdString>> {
let lua = self.0.lua;
@@ -1104,15 +1138,11 @@ impl<'lua> AnyUserData<'lua> {
#[cfg(feature = "serialize")]
pub(crate) fn is_serializable(&self) -> bool {
let lua = self.0.lua;
let state = lua.state();
let is_serializable = || unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 2)?;
// Userdata can be unregistered or destructed
lua.push_userdata_ref(&self.0)?;
let _ = lua.get_userdata_type_id(&self.0)?;
let ud = &*get_userdata::<UserDataCell<()>>(state, -1);
let ud = &*get_userdata::<UserDataCell<()>>(lua.ref_thread(), self.0.index);
match &*ud.0.try_borrow().map_err(|_| Error::UserDataBorrowError)? {
UserDataVariant::Serializable(_) => Result::Ok(true),
_ => Result::Ok(false),
@@ -1127,15 +1157,12 @@ impl<'lua> AnyUserData<'lua> {
F: FnOnce(&'a UserDataCell<T>) -> Result<R>,
{
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 2)?;
let type_id = lua.push_userdata_ref(&self.0)?;
let type_id = lua.get_userdata_type_id(&self.0)?;
match type_id {
Some(type_id) if type_id == TypeId::of::<T>() => {
func(&*get_userdata::<UserDataCell<T>>(state, -1))
let ref_thread = lua.ref_thread();
func(&*get_userdata::<UserDataCell<T>>(ref_thread, self.0.index))
}
_ => Err(Error::UserDataTypeMismatch),
}
@@ -1278,19 +1305,17 @@ impl<'lua> Serialize for AnyUserData<'lua> {
S: Serializer,
{
let lua = self.0.lua;
let state = lua.state();
let data = unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 3).map_err(ser::Error::custom)?;
lua.push_userdata_ref(&self.0).map_err(ser::Error::custom)?;
let ud = &*get_userdata::<UserDataCell<()>>(state, -1);
let _ = lua
.get_userdata_type_id(&self.0)
.map_err(ser::Error::custom)?;
let ud = &*get_userdata::<UserDataCell<()>>(lua.ref_thread(), self.0.index);
ud.0.try_borrow()
.map_err(|_| ser::Error::custom(Error::UserDataBorrowError))?
};
match &*data {
UserDataVariant::Serializable(ser) => ser.serialize(serializer),
_ => UserDataSerializeError.serialize(serializer),
_ => Err(ser::Error::custom("cannot serialize <userdata>")),
}
}
}
@@ -1345,6 +1370,27 @@ impl<'lua, T: 'static> UserDataRefMut<'lua, T> {
}
}
pub(crate) struct WrappedUserdata<F: for<'lua> FnOnce(&'lua Lua) -> Result<AnyUserData<'lua>>>(F);
impl<'lua> AnyUserData<'lua> {
/// 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<'lua> {
WrappedUserdata(move |lua| lua.create_any_userdata(data))
}
}
impl<'lua, F> IntoLua<'lua> for WrappedUserdata<F>
where
F: for<'l> FnOnce(&'l Lua) -> Result<AnyUserData<'l>>,
{
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
(self.0)(lua).map(Value::UserData)
}
}
#[inline]
fn try_value_to_userdata<T>(value: Value) -> Result<AnyUserData> {
match value {
Value::UserData(ud) => Ok(ud),
+46 -61
View File
@@ -27,15 +27,14 @@ pub trait AnyUserDataExt<'lua>: Sealed {
/// The metamethod is called with the userdata as its first argument, followed by the passed arguments.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
fn call_async<A, R>(&self, args: A) -> LocalBoxFuture<'lua, Result<R>>
where
'lua: 'fut,
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut;
R: FromLuaMulti<'lua> + 'lua;
/// Calls the userdata method, assuming it has `__index` metamethod
/// and a function associated to `name`.
fn call_method<A, R>(&self, name: impl AsRef<str>, args: A) -> Result<R>
fn call_method<A, R>(&self, name: &str, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>;
@@ -48,15 +47,10 @@ pub trait AnyUserDataExt<'lua>: Sealed {
/// This might invoke the `__index` metamethod.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async_method<'fut, A, R>(
&self,
name: impl AsRef<str>,
args: A,
) -> LocalBoxFuture<'fut, Result<R>>
fn call_async_method<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
where
'lua: 'fut,
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut;
R: FromLuaMulti<'lua> + 'lua;
/// Gets the function associated to `key` from the table and executes it,
/// passing `args` as function arguments.
@@ -65,7 +59,7 @@ pub trait AnyUserDataExt<'lua>: Sealed {
/// `table.get::<_, Function>(key)?.call(args)`
///
/// This might invoke the `__index` metamethod.
fn call_function<A, R>(&self, name: impl AsRef<str>, args: A) -> Result<R>
fn call_function<A, R>(&self, name: &str, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>;
@@ -78,15 +72,10 @@ pub trait AnyUserDataExt<'lua>: Sealed {
/// This might invoke the `__index` metamethod.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async_function<'fut, A, R>(
&self,
name: impl AsRef<str>,
args: A,
) -> LocalBoxFuture<'fut, Result<R>>
fn call_async_function<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
where
'lua: 'fut,
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut;
R: FromLuaMulti<'lua> + 'lua;
}
impl<'lua> AnyUserDataExt<'lua> for AnyUserData<'lua> {
@@ -95,9 +84,7 @@ impl<'lua> AnyUserDataExt<'lua> for AnyUserData<'lua> {
match metatable.get::<Value>(MetaMethod::Index)? {
Value::Table(table) => table.raw_get(key),
Value::Function(func) => func.call((self.clone(), key)),
_ => Err(Error::RuntimeError(
"attempt to index a userdata value".to_string(),
)),
_ => Err(Error::runtime("attempt to index a userdata value")),
}
}
@@ -106,9 +93,7 @@ impl<'lua> AnyUserDataExt<'lua> for AnyUserData<'lua> {
match metatable.get::<Value>(MetaMethod::NewIndex)? {
Value::Table(table) => table.raw_set(key, value),
Value::Function(func) => func.call((self.clone(), key, value)),
_ => Err(Error::RuntimeError(
"attempt to index a userdata value".to_string(),
)),
_ => Err(Error::runtime("attempt to index a userdata value")),
}
}
@@ -120,33 +105,37 @@ impl<'lua> AnyUserDataExt<'lua> for AnyUserData<'lua> {
let metatable = self.get_metatable()?;
match metatable.get::<Value>(MetaMethod::Call)? {
Value::Function(func) => func.call((self.clone(), args)),
_ => Err(Error::RuntimeError(
"attempt to call a userdata value".to_string(),
)),
_ => Err(Error::runtime("attempt to call a userdata value")),
}
}
#[cfg(feature = "async")]
fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
fn call_async<A, R>(&self, args: A) -> LocalBoxFuture<'lua, Result<R>>
where
'lua: 'fut,
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
R: FromLuaMulti<'lua> + 'lua,
{
let metatable = match self.get_metatable() {
Ok(metatable) => metatable,
Err(err) => return Box::pin(future::err(err)),
};
match metatable.get::<Value>(MetaMethod::Call) {
Ok(Value::Function(func)) => func.call_async((self.clone(), args)),
Ok(_) => Box::pin(future::err(Error::RuntimeError(
"attempt to call a userdata value".to_string(),
Ok(Value::Function(func)) => {
let mut args = match args.into_lua_multi(self.0.lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
args.push_front(Value::UserData(self.clone()));
Box::pin(async move { func.call_async(args).await })
}
Ok(_) => Box::pin(future::err(Error::runtime(
"attempt to call a userdata value",
))),
Err(err) => Box::pin(future::err(err)),
}
}
fn call_method<A, R>(&self, name: impl AsRef<str>, args: A) -> Result<R>
fn call_method<A, R>(&self, name: &str, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
@@ -155,50 +144,46 @@ impl<'lua> AnyUserDataExt<'lua> for AnyUserData<'lua> {
}
#[cfg(feature = "async")]
fn call_async_method<'fut, A, R>(
&self,
name: impl AsRef<str>,
args: A,
) -> LocalBoxFuture<'fut, Result<R>>
fn call_async_method<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
where
'lua: 'fut,
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
R: FromLuaMulti<'lua> + 'lua,
{
self.call_async_function(name, (self.clone(), args))
}
fn call_function<A, R>(&self, name: impl AsRef<str>, args: A) -> Result<R>
fn call_function<A, R>(&self, name: &str, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
match self.get(name.as_ref())? {
match self.get(name)? {
Value::Function(func) => func.call(args),
val => Err(Error::RuntimeError(format!(
"attempt to call a {} value",
val.type_name()
))),
val => {
let msg = format!("attempt to call a {} value", val.type_name());
Err(Error::runtime(msg))
}
}
}
#[cfg(feature = "async")]
fn call_async_function<'fut, A, R>(
&self,
name: impl AsRef<str>,
args: A,
) -> LocalBoxFuture<'fut, Result<R>>
fn call_async_function<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
where
'lua: 'fut,
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
R: FromLuaMulti<'lua> + 'lua,
{
match self.get(name.as_ref()) {
Ok(Value::Function(func)) => func.call_async(args),
Ok(val) => Box::pin(future::err(Error::RuntimeError(format!(
"attempt to call a {} value",
val.type_name()
)))),
match self.get(name) {
Ok(Value::Function(func)) => {
let args = match args.into_lua_multi(self.0.lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
Box::pin(async move { func.call_async(args).await })
}
Ok(val) => {
let msg = format!("attempt to call a {} value", val.type_name());
Box::pin(future::err(Error::runtime(msg)))
}
Err(err) => Box::pin(future::err(err)),
}
}
+349 -205
View File
@@ -1,6 +1,9 @@
#![allow(clippy::await_holding_refcell_ref, clippy::await_holding_lock)]
use std::any::TypeId;
use std::cell::{Ref, RefCell, RefMut};
use std::marker::PhantomData;
use std::os::raw::c_int;
use std::string::String as StdString;
use std::sync::{Arc, Mutex, RwLock};
@@ -10,7 +13,7 @@ use crate::types::{Callback, MaybeSend};
use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
};
use crate::util::{check_stack, get_userdata, short_type_name, StackGuard};
use crate::util::{get_userdata, short_type_name};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Value};
#[cfg(not(feature = "send"))]
@@ -23,7 +26,8 @@ use {
std::future::Future,
};
pub struct UserDataRegistrar<'lua, T: 'static> {
/// Handle to registry for userdata methods and metamethods.
pub struct UserDataRegistry<'lua, T: 'static> {
// Fields
pub(crate) fields: Vec<(String, Callback<'lua, 'static>)>,
pub(crate) field_getters: Vec<(String, Callback<'lua, 'static>)>,
@@ -41,9 +45,9 @@ pub struct UserDataRegistrar<'lua, T: 'static> {
_type: PhantomData<T>,
}
impl<'lua, T: 'static> UserDataRegistrar<'lua, T> {
impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
pub(crate) const fn new() -> Self {
UserDataRegistrar {
UserDataRegistry {
fields: Vec::new(),
field_getters: Vec::new(),
field_setters: Vec::new(),
@@ -75,62 +79,63 @@ impl<'lua, T: 'static> UserDataRegistrar<'lua, T> {
}
Box::new(move |lua, mut args| {
let front = args.pop_front();
let front = args
.pop_front()
.ok_or_else(|| Error::from_lua_conversion("missing argument", "userdata", None));
let front = try_self_arg!(front);
let call = |ud| {
// Self was at index 1, so we pass 2 here
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args)?.into_lua_multi(lua)
};
if let Some(front) = front {
let state = lua.state();
let userdata = try_self_arg!(AnyUserData::from_lua(front, lua));
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 2)?;
let type_id = try_self_arg!(lua.push_userdata_ref(&userdata.0));
match type_id {
Some(id) if id == TypeId::of::<T>() => {
let ud = try_self_arg!(get_userdata_ref::<T>(state));
call(&ud)
}
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
let ud = try_self_arg!(get_userdata_ref::<Rc<RefCell<T>>>(state));
let ud = try_self_arg!(ud.try_borrow(), Error::UserDataBorrowError);
call(&ud)
}
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
let ud = try_self_arg!(get_userdata_ref::<Arc<Mutex<T>>>(state));
let ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowError);
call(&ud)
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(state);
let ud = try_self_arg!(ud);
let ud = try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowError));
call(&ud)
}
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
let ud = try_self_arg!(get_userdata_ref::<Arc<RwLock<T>>>(state));
let ud = try_self_arg!(ud.try_read(), Error::UserDataBorrowError);
call(&ud)
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(state);
let ud = try_self_arg!(ud);
let ud = try_self_arg!(ud.try_read().ok_or(Error::UserDataBorrowError));
call(&ud)
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
}
} else {
let err = Error::from_lua_conversion("missing argument", "userdata", None);
Err(Error::bad_self_argument(&name, err))
let userdata = try_self_arg!(AnyUserData::from_lua(front, lua));
let (ref_thread, index) = (lua.ref_thread(), userdata.0.index);
match try_self_arg!(userdata.type_id()) {
Some(id) if id == TypeId::of::<T>() => unsafe {
let ud = try_self_arg!(get_userdata_ref::<T>(ref_thread, index));
call(&ud)
},
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<T>>() => unsafe {
let ud = try_self_arg!(get_userdata_ref::<Rc<T>>(ref_thread, index));
call(&ud)
},
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => unsafe {
let ud = try_self_arg!(get_userdata_ref::<Rc<RefCell<T>>>(ref_thread, index));
let ud = try_self_arg!(ud.try_borrow(), Error::UserDataBorrowError);
call(&ud)
},
Some(id) if id == TypeId::of::<Arc<T>>() => unsafe {
let ud = try_self_arg!(get_userdata_ref::<Arc<T>>(ref_thread, index));
call(&ud)
},
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => unsafe {
let ud = try_self_arg!(get_userdata_ref::<Arc<Mutex<T>>>(ref_thread, index));
let ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowError);
call(&ud)
},
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => unsafe {
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(ref_thread, index);
let ud = try_self_arg!(ud);
let ud = try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowError));
call(&ud)
},
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => unsafe {
let ud = try_self_arg!(get_userdata_ref::<Arc<RwLock<T>>>(ref_thread, index));
let ud = try_self_arg!(ud.try_read(), Error::UserDataBorrowError);
call(&ud)
},
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => unsafe {
let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(ref_thread, index);
let ud = try_self_arg!(ud);
let ud = try_self_arg!(ud.try_read().ok_or(Error::UserDataBorrowError));
call(&ud)
},
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
})
}
@@ -156,158 +161,258 @@ impl<'lua, T: 'static> UserDataRegistrar<'lua, T> {
let mut method = method
.try_borrow_mut()
.map_err(|_| Error::RecursiveMutCallback)?;
let front = args.pop_front();
let front = args
.pop_front()
.ok_or_else(|| Error::from_lua_conversion("missing argument", "userdata", None));
let front = try_self_arg!(front);
let call = |ud| {
// Self was at index 1, so we pass 2 here
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args)?.into_lua_multi(lua)
};
if let Some(front) = front {
let state = lua.state();
let userdata = try_self_arg!(AnyUserData::from_lua(front, lua));
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 2)?;
let type_id = try_self_arg!(lua.push_userdata_ref(&userdata.0));
match type_id {
Some(id) if id == TypeId::of::<T>() => {
let mut ud = try_self_arg!(get_userdata_mut::<T>(state));
call(&mut ud)
}
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
let ud = try_self_arg!(get_userdata_mut::<Rc<RefCell<T>>>(state));
let mut ud =
try_self_arg!(ud.try_borrow_mut(), Error::UserDataBorrowMutError);
call(&mut ud)
}
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
let ud = try_self_arg!(get_userdata_mut::<Arc<Mutex<T>>>(state));
let mut ud =
try_self_arg!(ud.try_lock(), Error::UserDataBorrowMutError);
call(&mut ud)
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
let ud = get_userdata_mut::<Arc<parking_lot::Mutex<T>>>(state);
let ud = try_self_arg!(ud);
let mut ud =
try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowMutError));
call(&mut ud)
}
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
let ud = try_self_arg!(get_userdata_mut::<Arc<RwLock<T>>>(state));
let mut ud =
try_self_arg!(ud.try_write(), Error::UserDataBorrowMutError);
call(&mut ud)
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
let ud = get_userdata_mut::<Arc<parking_lot::RwLock<T>>>(state);
let ud = try_self_arg!(ud);
let mut ud =
try_self_arg!(ud.try_write().ok_or(Error::UserDataBorrowMutError));
call(&mut ud)
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
}
} else {
let err = Error::from_lua_conversion("missing argument", "userdata", None);
Err(Error::bad_self_argument(&name, err))
let userdata = try_self_arg!(AnyUserData::from_lua(front, lua));
let (ref_thread, index) = (lua.ref_thread(), userdata.0.index);
match try_self_arg!(userdata.type_id()) {
Some(id) if id == TypeId::of::<T>() => unsafe {
let mut ud = try_self_arg!(get_userdata_mut::<T>(ref_thread, index));
call(&mut ud)
},
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<T>>() => Err(Error::UserDataBorrowMutError),
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => unsafe {
let ud = try_self_arg!(get_userdata_mut::<Rc<RefCell<T>>>(ref_thread, index));
let mut ud = try_self_arg!(ud.try_borrow_mut(), Error::UserDataBorrowMutError);
call(&mut ud)
},
Some(id) if id == TypeId::of::<Arc<T>>() => Err(Error::UserDataBorrowMutError),
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => unsafe {
let ud = try_self_arg!(get_userdata_mut::<Arc<Mutex<T>>>(ref_thread, index));
let mut ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowMutError);
call(&mut ud)
},
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => unsafe {
let ud = get_userdata_mut::<Arc<parking_lot::Mutex<T>>>(ref_thread, index);
let ud = try_self_arg!(ud);
let mut ud = try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowMutError));
call(&mut ud)
},
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => unsafe {
let ud = try_self_arg!(get_userdata_mut::<Arc<RwLock<T>>>(ref_thread, index));
let mut ud = try_self_arg!(ud.try_write(), Error::UserDataBorrowMutError);
call(&mut ud)
},
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => unsafe {
let ud = get_userdata_mut::<Arc<parking_lot::RwLock<T>>>(ref_thread, index);
let ud = try_self_arg!(ud);
let mut ud = try_self_arg!(ud.try_write().ok_or(Error::UserDataBorrowMutError));
call(&mut ud)
},
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
})
}
#[cfg(feature = "async")]
fn box_async_method<M, A, MR, R>(name: &str, method: M) -> AsyncCallback<'lua, 'static>
fn box_async_method<'s, M, A, MR, R>(name: &str, method: M) -> AsyncCallback<'lua, 'static>
where
T: Clone,
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static,
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 'lua,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
{
let name = get_function_name::<T>(name);
macro_rules! try_self_arg {
($res:expr) => {
$res.map_err(|err| Error::bad_self_argument(&name, err))?
};
($res:expr, $err:expr) => {
$res.map_err(|_| Error::bad_self_argument(&name, $err))?
};
}
let method = Arc::new(method);
Box::new(move |lua, mut args| {
let front = args.pop_front();
let call = |ud| {
// Self was at index 1, so we pass 2 here
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
Ok(method(lua, ud, args))
};
let fut_res = || {
if let Some(front) = front {
let state = lua.state();
let userdata = AnyUserData::from_lua(front, lua)?;
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 2)?;
let type_id = try_self_arg!(lua.push_userdata_ref(&userdata.0));
match type_id {
Some(id) if id == TypeId::of::<T>() => {
let ud = get_userdata_ref::<T>(state)?;
call(ud.clone())
}
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
let ud = try_self_arg!(get_userdata_ref::<Rc<RefCell<T>>>(state));
let ud = try_self_arg!(ud.try_borrow(), Error::UserDataBorrowError);
call(ud.clone())
}
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
let ud = try_self_arg!(get_userdata_ref::<Arc<Mutex<T>>>(state));
let ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowError);
call(ud.clone())
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(state);
let ud = try_self_arg!(ud);
let ud =
try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowError));
call(ud.clone())
}
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
let ud = try_self_arg!(get_userdata_ref::<Arc<RwLock<T>>>(state));
let ud = try_self_arg!(ud.try_read(), Error::UserDataBorrowError);
call(ud.clone())
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(state);
let ud = try_self_arg!(ud);
let ud =
try_self_arg!(ud.try_read().ok_or(Error::UserDataBorrowError));
call(ud.clone())
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
}
} else {
let err = Error::from_lua_conversion("missing argument", "userdata", None);
Err(Error::bad_self_argument(&name, err))
}
};
match fut_res() {
Ok(fut) => {
Box::pin(fut.and_then(move |ret| future::ready(ret.into_lua_multi(lua))))
}
Err(e) => Box::pin(future::err(e)),
let name = name.clone();
let method = method.clone();
macro_rules! try_self_arg {
($res:expr) => {
$res.map_err(|err| Error::bad_self_argument(&name, err))?
};
($res:expr, $err:expr) => {
$res.map_err(|_| Error::bad_self_argument(&name, $err))?
};
}
Box::pin(async move {
let front = args.pop_front().ok_or_else(|| {
Error::from_lua_conversion("missing argument", "userdata", None)
});
let front = try_self_arg!(front);
let userdata: AnyUserData = try_self_arg!(AnyUserData::from_lua(front, lua));
let (ref_thread, index) = (lua.ref_thread(), userdata.0.index);
match try_self_arg!(userdata.type_id()) {
Some(id) if id == TypeId::of::<T>() => unsafe {
let ud = try_self_arg!(get_userdata_ref::<T>(ref_thread, index));
let ud = std::mem::transmute::<&T, &T>(&ud);
// Self was at index 1, so we pass 2 here
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<T>>() => unsafe {
let ud = try_self_arg!(get_userdata_ref::<Rc<T>>(ref_thread, index));
let ud = std::mem::transmute::<&T, &T>(&ud);
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => unsafe {
let ud =
try_self_arg!(get_userdata_ref::<Rc<RefCell<T>>>(ref_thread, index));
let ud = try_self_arg!(ud.try_borrow(), Error::UserDataBorrowError);
let ud = std::mem::transmute::<&T, &T>(&ud);
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
Some(id) if id == TypeId::of::<Arc<T>>() => unsafe {
let ud = try_self_arg!(get_userdata_ref::<Arc<T>>(ref_thread, index));
let ud = std::mem::transmute::<&T, &T>(&ud);
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => unsafe {
let ud =
try_self_arg!(get_userdata_ref::<Arc<Mutex<T>>>(ref_thread, index));
let ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowError);
let ud = std::mem::transmute::<&T, &T>(&ud);
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => unsafe {
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(ref_thread, index);
let ud = try_self_arg!(ud);
let ud = try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowError));
let ud = std::mem::transmute::<&T, &T>(&ud);
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => unsafe {
let ud =
try_self_arg!(get_userdata_ref::<Arc<RwLock<T>>>(ref_thread, index));
let ud = try_self_arg!(ud.try_read(), Error::UserDataBorrowError);
let ud = std::mem::transmute::<&T, &T>(&ud);
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => unsafe {
let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(ref_thread, index);
let ud = try_self_arg!(ud);
let ud = try_self_arg!(ud.try_read().ok_or(Error::UserDataBorrowError));
let ud = std::mem::transmute::<&T, &T>(&ud);
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
})
})
}
#[cfg(feature = "async")]
fn box_async_method_mut<'s, M, A, MR, R>(name: &str, method: M) -> AsyncCallback<'lua, 'static>
where
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
{
let name = get_function_name::<T>(name);
let method = Arc::new(method);
Box::new(move |lua, mut args| {
let name = name.clone();
let method = method.clone();
macro_rules! try_self_arg {
($res:expr) => {
$res.map_err(|err| Error::bad_self_argument(&name, err))?
};
($res:expr, $err:expr) => {
$res.map_err(|_| Error::bad_self_argument(&name, $err))?
};
}
Box::pin(async move {
let front = args.pop_front().ok_or_else(|| {
Error::from_lua_conversion("missing argument", "userdata", None)
});
let front = try_self_arg!(front);
let userdata: AnyUserData = try_self_arg!(AnyUserData::from_lua(front, lua));
let (ref_thread, index) = (lua.ref_thread(), userdata.0.index);
match try_self_arg!(userdata.type_id()) {
Some(id) if id == TypeId::of::<T>() => unsafe {
let mut ud = try_self_arg!(get_userdata_mut::<T>(ref_thread, index));
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
// Self was at index 1, so we pass 2 here
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
Err(Error::UserDataBorrowMutError)
}
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => unsafe {
let ud =
try_self_arg!(get_userdata_mut::<Rc<RefCell<T>>>(ref_thread, index));
let mut ud =
try_self_arg!(ud.try_borrow_mut(), Error::UserDataBorrowMutError);
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Arc<T>>() => Err(Error::UserDataBorrowMutError),
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => unsafe {
let ud =
try_self_arg!(get_userdata_mut::<Arc<Mutex<T>>>(ref_thread, index));
let mut ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowMutError);
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => unsafe {
let ud = get_userdata_mut::<Arc<parking_lot::Mutex<T>>>(ref_thread, index);
let ud = try_self_arg!(ud);
let mut ud =
try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowMutError));
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => unsafe {
let ud =
try_self_arg!(get_userdata_mut::<Arc<RwLock<T>>>(ref_thread, index));
let mut ud = try_self_arg!(ud.try_write(), Error::UserDataBorrowMutError);
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => unsafe {
let ud = get_userdata_mut::<Arc<parking_lot::RwLock<T>>>(ref_thread, index);
let ud = try_self_arg!(ud);
let mut ud =
try_self_arg!(ud.try_write().ok_or(Error::UserDataBorrowMutError));
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
})
})
}
@@ -389,7 +494,7 @@ fn get_function_name<T>(name: &str) -> StdString {
format!("{}.{name}", short_type_name::<T>())
}
impl<'lua, T: 'static> UserDataFields<'lua, T> for UserDataRegistrar<'lua, T> {
impl<'lua, T: 'static> UserDataFields<'lua, T> for UserDataRegistry<'lua, T> {
fn add_field<V>(&mut self, name: impl AsRef<str>, value: V)
where
V: IntoLua<'lua> + Clone + 'static,
@@ -468,7 +573,7 @@ impl<'lua, T: 'static> UserDataFields<'lua, T> for UserDataRegistrar<'lua, T> {
// Below are internal methods
fn append_fields_from<S>(&mut self, other: UserDataRegistrar<'lua, S>) {
fn append_fields_from<S>(&mut self, other: UserDataRegistry<'lua, S>) {
self.fields.extend(other.fields);
self.field_getters.extend(other.field_getters);
self.field_setters.extend(other.field_setters);
@@ -476,7 +581,7 @@ impl<'lua, T: 'static> UserDataFields<'lua, T> for UserDataRegistrar<'lua, T> {
}
}
impl<'lua, T: 'static> UserDataMethods<'lua, T> for UserDataRegistrar<'lua, T> {
impl<'lua, T: 'static> UserDataMethods<'lua, T> for UserDataRegistry<'lua, T> {
fn add_method<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
where
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
@@ -500,12 +605,13 @@ impl<'lua, T: 'static> UserDataMethods<'lua, T> for UserDataRegistrar<'lua, T> {
}
#[cfg(feature = "async")]
fn add_async_method<M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
fn add_async_method<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where
T: Clone,
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static,
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 'lua,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
{
let name = name.as_ref();
@@ -513,6 +619,21 @@ impl<'lua, T: 'static> UserDataMethods<'lua, T> for UserDataRegistrar<'lua, T> {
.push((name.into(), Self::box_async_method(name, method)));
}
#[cfg(feature = "async")]
fn add_async_method_mut<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
{
let name = name.as_ref();
self.async_methods
.push((name.into(), Self::box_async_method_mut(name, method)));
}
fn add_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
where
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
@@ -571,12 +692,13 @@ impl<'lua, T: 'static> UserDataMethods<'lua, T> for UserDataRegistrar<'lua, T> {
}
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
fn add_async_meta_method<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where
T: Clone,
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static,
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 'lua,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
{
let name = name.as_ref();
@@ -584,6 +706,21 @@ impl<'lua, T: 'static> UserDataMethods<'lua, T> for UserDataRegistrar<'lua, T> {
.push((name.into(), Self::box_async_method(name, method)));
}
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_method_mut<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
{
let name = name.as_ref();
self.async_meta_methods
.push((name.into(), Self::box_async_method_mut(name, method)));
}
fn add_meta_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
where
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
@@ -621,7 +758,7 @@ impl<'lua, T: 'static> UserDataMethods<'lua, T> for UserDataRegistrar<'lua, T> {
// Below are internal methods used in generated code
fn append_methods_from<S>(&mut self, other: UserDataRegistrar<'lua, S>) {
fn append_methods_from<S>(&mut self, other: UserDataRegistry<'lua, S>) {
self.methods.extend(other.methods);
#[cfg(feature = "async")]
self.async_methods.extend(other.async_methods);
@@ -632,26 +769,29 @@ impl<'lua, T: 'static> UserDataMethods<'lua, T> for UserDataRegistrar<'lua, T> {
}
#[inline]
unsafe fn get_userdata_ref<'a, T>(state: *mut ffi::lua_State) -> Result<Ref<'a, T>> {
(*get_userdata::<UserDataCell<T>>(state, -1)).try_borrow()
unsafe fn get_userdata_ref<'a, T>(state: *mut ffi::lua_State, index: c_int) -> Result<Ref<'a, T>> {
(*get_userdata::<UserDataCell<T>>(state, index)).try_borrow()
}
#[inline]
unsafe fn get_userdata_mut<'a, T>(state: *mut ffi::lua_State) -> Result<RefMut<'a, T>> {
(*get_userdata::<UserDataCell<T>>(state, -1)).try_borrow_mut()
unsafe fn get_userdata_mut<'a, T>(
state: *mut ffi::lua_State,
index: c_int,
) -> Result<RefMut<'a, T>> {
(*get_userdata::<UserDataCell<T>>(state, index)).try_borrow_mut()
}
macro_rules! lua_userdata_impl {
($type:ty) => {
impl<T: UserData + 'static> UserData for $type {
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
let mut orig_fields = UserDataRegistrar::new();
let mut orig_fields = UserDataRegistry::new();
T::add_fields(&mut orig_fields);
fields.append_fields_from(orig_fields);
}
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
let mut orig_methods = UserDataRegistrar::new();
let mut orig_methods = UserDataRegistry::new();
T::add_methods(&mut orig_methods);
methods.append_methods_from(orig_methods);
}
@@ -659,8 +799,12 @@ macro_rules! lua_userdata_impl {
};
}
#[cfg(not(feature = "send"))]
lua_userdata_impl!(Rc<T>);
#[cfg(not(feature = "send"))]
lua_userdata_impl!(Rc<RefCell<T>>);
lua_userdata_impl!(Arc<T>);
lua_userdata_impl!(Arc<Mutex<T>>);
lua_userdata_impl!(Arc<RwLock<T>>);
#[cfg(feature = "parking_lot")]
+34 -18
View File
@@ -1,11 +1,12 @@
use std::any::{Any, TypeId};
use std::borrow::Cow;
use std::ffi::CStr;
use std::fmt::Write;
use std::mem::MaybeUninit;
use std::os::raw::{c_char, c_int, c_void};
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
use std::sync::Arc;
use std::{mem, ptr, slice};
use std::{mem, ptr, slice, str};
use once_cell::sync::Lazy;
use rustc_hash::FxHashMap;
@@ -251,10 +252,12 @@ pub unsafe fn push_string(state: *mut ffi::lua_State, s: &[u8], protect: bool) -
#[inline]
pub unsafe fn push_table(
state: *mut ffi::lua_State,
narr: c_int,
nrec: c_int,
narr: usize,
nrec: usize,
protect: bool,
) -> Result<()> {
let narr: c_int = narr.try_into().unwrap_or(c_int::MAX);
let nrec: c_int = nrec.try_into().unwrap_or(c_int::MAX);
if protect {
protect_lua!(state, 0, 1, |state| ffi::lua_createtable(state, narr, nrec))
} else {
@@ -543,12 +546,13 @@ pub unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Re
// captured `__index` if no matches found.
// The same is also applicable for `__newindex` metamethod and `field_setters` table.
// Internally uses 9 stack spaces and does not call checkstack.
pub unsafe fn init_userdata_metatable<T>(
pub unsafe fn init_userdata_metatable(
state: *mut ffi::lua_State,
metatable: c_int,
field_getters: Option<c_int>,
field_setters: Option<c_int>,
methods: Option<c_int>,
extra_init: Option<fn(*mut ffi::lua_State) -> Result<()>>,
) -> Result<()> {
ffi::lua_pushvalue(state, metatable);
@@ -595,10 +599,9 @@ pub unsafe fn init_userdata_metatable<T>(
rawset_field(state, -2, "__newindex")?;
}
#[cfg(not(feature = "luau"))]
{
ffi::lua_pushcfunction(state, userdata_destructor::<T>);
rawset_field(state, -2, "__gc")?;
// Additional initialization
if let Some(extra_init) = extra_init {
extra_init(state)?;
}
ffi::lua_pushboolean(state, 0);
@@ -655,8 +658,6 @@ where
Ok(Err(err)) => {
ffi::lua_settop(state, 1);
let wrapped_error = ud as *mut WrappedFailure;
// Build `CallbackError` with traceback
let traceback = if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
ffi::luaL_traceback(state, state, ptr::null(), 0);
@@ -667,10 +668,8 @@ where
"<not enough stack space for traceback>".to_string()
};
let cause = Arc::new(err);
ptr::write(
wrapped_error,
WrappedFailure::Error(Error::CallbackError { traceback, cause }),
);
let wrapped_error = WrappedFailure::Error(Error::CallbackError { traceback, cause });
ptr::write(ud, wrapped_error);
get_gc_metatable::<WrappedFailure>(state);
ffi::lua_setmetatable(state, -2);
@@ -678,7 +677,7 @@ where
}
Err(p) => {
ffi::lua_settop(state, 1);
ptr::write(ud as *mut WrappedFailure, WrappedFailure::Panic(Some(p)));
ptr::write(ud, WrappedFailure::Panic(Some(p)));
get_gc_metatable::<WrappedFailure>(state);
ffi::lua_setmetatable(state, -2);
ffi::lua_error(state)
@@ -1044,7 +1043,10 @@ pub(crate) unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> Stri
let v = ffi::lua_tovector(state, index);
mlua_debug_assert!(!v.is_null(), "vector is null");
let (x, y, z) = (*v, *v.add(1), *v.add(2));
format!("vector({x},{y},{z})")
#[cfg(not(feature = "luau-vector4"))]
return format!("vector({x}, {y}, {z})");
#[cfg(feature = "luau-vector4")]
return format!("vector({x}, {y}, {z}, {w})", w = *v.add(3));
}
ffi::LUA_TSTRING => {
let mut size = 0;
@@ -1066,11 +1068,25 @@ pub(crate) unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_Stat
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, key);
}
pub(crate) unsafe fn ptr_to_cstr_bytes<'a>(input: *const c_char) -> Option<&'a [u8]> {
pub(crate) unsafe fn ptr_to_str<'a>(input: *const c_char) -> Option<&'a str> {
if input.is_null() {
return None;
}
Some(CStr::from_ptr(input).to_bytes())
str::from_utf8(CStr::from_ptr(input).to_bytes()).ok()
}
pub(crate) unsafe fn ptr_to_lossy_str<'a>(input: *const c_char) -> Option<Cow<'a, str>> {
if input.is_null() {
return None;
}
Some(String::from_utf8_lossy(CStr::from_ptr(input).to_bytes()))
}
pub(crate) fn linenumber_to_usize(n: c_int) -> Option<usize> {
match n {
n if n < 0 => None,
n => Some(n as usize),
}
}
static DESTRUCTED_USERDATA_METATABLE: u8 = 0;
+3 -2
View File
@@ -1,5 +1,6 @@
//! Mostly copied from bevy_utils
//! https://github.com/bevyengine/bevy/blob/main/crates/bevy_utils/src/short_names.rs
//! Mostly copied from [bevy_utils]
//!
//! [bevy_utils]: https://github.com/bevyengine/bevy/blob/main/crates/bevy_utils/src/short_names.rs
use std::any::type_name;
+21 -21
View File
@@ -44,7 +44,7 @@ pub enum Value<'lua> {
/// A Luau vector.
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
Vector(f32, f32, f32),
Vector(crate::types::Vector),
/// An interned string, managed by Lua.
///
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
@@ -79,7 +79,7 @@ impl<'lua> Value<'lua> {
Value::Integer(_) => "integer",
Value::Number(_) => "number",
#[cfg(feature = "luau")]
Value::Vector(_, _, _) => "vector",
Value::Vector(_) => "vector",
Value::String(_) => "string",
Value::Table(_) => "table",
Value::Function(_) => "function",
@@ -116,18 +116,14 @@ impl<'lua> Value<'lua> {
/// Typically this function is used only for hashing and debug information.
#[inline]
pub fn to_pointer(&self) -> *const c_void {
unsafe {
match self {
Value::LightUserData(ud) => ud.0,
Value::Table(t) => t.to_pointer(),
Value::String(s) => s.to_pointer(),
Value::Function(Function(r))
| Value::Thread(Thread(r))
| Value::UserData(AnyUserData(r)) => {
ffi::lua_topointer(r.lua.ref_thread(), r.index)
}
_ => ptr::null(),
}
match self {
Value::LightUserData(ud) => ud.0,
Value::String(String(r))
| Value::Table(Table(r))
| Value::Function(Function(r))
| Value::Thread(Thread(r))
| Value::UserData(AnyUserData(r)) => r.to_pointer(),
_ => ptr::null(),
}
}
@@ -143,7 +139,7 @@ impl<'lua> Value<'lua> {
Value::Integer(i) => Ok(i.to_string()),
Value::Number(n) => Ok(n.to_string()),
#[cfg(feature = "luau")]
Value::Vector(x, y, z) => Ok(format!("vector({x}, {y}, {z})")),
Value::Vector(v) => Ok(v.to_string()),
Value::String(s) => Ok(s.to_str()?.to_string()),
Value::Table(Table(r))
| Value::Function(Function(r))
@@ -218,7 +214,7 @@ impl<'lua> Value<'lua> {
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::Vector(v) => write!(fmt, "{v}"),
Value::String(s) => write!(fmt, "{s:?}"),
Value::Table(t) if recursive && !visited.contains(&t.to_pointer()) => {
t.fmt_pretty(fmt, ident, visited)
@@ -227,9 +223,13 @@ impl<'lua> Value<'lua> {
f @ Value::Function(_) => write!(fmt, "function: {:?}", f.to_pointer()),
t @ Value::Thread(_) => write!(fmt, "thread: {:?}", t.to_pointer()),
u @ Value::UserData(ud) => {
// Try `__name` first then `__tostring`
let name = ud.type_name().ok().flatten();
let name = name.unwrap_or_else(|| "userdata".to_string());
write!(fmt, "{name}: {:?}", u.to_pointer())
let s = name
.map(|name| format!("{name}: {:?}", u.to_pointer()))
.or_else(|| u.to_string().ok())
.unwrap_or_else(|| format!("userdata: {:?}", u.to_pointer()));
write!(fmt, "{s}")
}
Value::Error(e) if recursive => write!(fmt, "{e:?}"),
Value::Error(_) => write!(fmt, "error"),
@@ -249,7 +249,7 @@ impl fmt::Debug for Value<'_> {
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::Vector(v) => write!(fmt, "{v:?}"),
Value::String(s) => write!(fmt, "String({s:?})"),
Value::Table(t) => write!(fmt, "{t:?}"),
Value::Function(f) => write!(fmt, "{f:?}"),
@@ -271,7 +271,7 @@ impl<'lua> PartialEq for Value<'lua> {
(Value::Number(a), Value::Integer(b)) => *a == *b as Number,
(Value::Number(a), Value::Number(b)) => *a == *b,
#[cfg(feature = "luau")]
(Value::Vector(x1, y1, z1), Value::Vector(x2, y2, z2)) => (x1, y1, z1) == (x2, y2, z2),
(Value::Vector(v1), Value::Vector(v2)) => v1 == v2,
(Value::String(a), Value::String(b)) => a == b,
(Value::Table(a), Value::Table(b)) => a == b,
(Value::Function(a), Value::Function(b)) => a == b,
@@ -303,7 +303,7 @@ impl<'lua> Serialize for Value<'lua> {
.serialize_i64((*i).try_into().expect("cannot convert Lua Integer to i64")),
Value::Number(n) => serializer.serialize_f64(*n),
#[cfg(feature = "luau")]
Value::Vector(x, y, z) => (x, y, z).serialize(serializer),
Value::Vector(v) => v.serialize(serializer),
Value::String(s) => s.serialize(serializer),
Value::Table(t) => t.serialize(serializer),
Value::UserData(ud) => ud.serialize(serializer),
+40 -44
View File
@@ -1,10 +1,8 @@
#![cfg(feature = "async")]
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, Mutex};
use std::time::Duration;
use futures_timer::Delay;
use futures_util::stream::TryStreamExt;
use mlua::{
@@ -12,6 +10,10 @@ use mlua::{
UserDataMethods, Value,
};
async fn sleep_ms(ms: u64) {
tokio::time::sleep(Duration::from_millis(ms)).await;
}
#[tokio::test]
async fn test_async_function() -> Result<()> {
let lua = Lua::new();
@@ -44,7 +46,7 @@ async fn test_async_sleep() -> Result<()> {
let lua = Lua::new();
let sleep = lua.create_async_function(move |_lua, n: u64| async move {
Delay::new(Duration::from_millis(n)).await;
sleep_ms(n).await;
Ok(format!("elapsed:{}ms", n))
})?;
lua.globals().set("sleep", sleep)?;
@@ -60,7 +62,7 @@ async fn test_async_call() -> Result<()> {
let lua = Lua::new();
let hello = lua.create_async_function(|_lua, name: String| async move {
Delay::new(Duration::from_millis(10)).await;
sleep_ms(10).await;
Ok(format!("hello, {}!", name))
})?;
@@ -103,7 +105,7 @@ async fn test_async_handle_yield() -> Result<()> {
let lua = Lua::new();
let sum = lua.create_async_function(|_lua, (a, b): (i64, i64)| async move {
Delay::new(Duration::from_millis(10)).await;
sleep_ms(10).await;
Ok(a + b)
})?;
@@ -161,10 +163,10 @@ async fn test_async_return_async_closure() -> Result<()> {
let lua = Lua::new();
let f = lua.create_async_function(|lua, a: i64| async move {
Delay::new(Duration::from_millis(10)).await;
sleep_ms(10).await;
let g = lua.create_async_function(move |_, b: i64| async move {
Delay::new(Duration::from_millis(10)).await;
sleep_ms(10).await;
return Ok(a + b);
})?;
@@ -254,7 +256,7 @@ async fn test_async_thread() -> Result<()> {
let f = lua.create_async_function(move |_lua, ()| {
let cnt3 = cnt2.clone();
async move {
Delay::new(Duration::from_millis(*cnt3.as_ref())).await;
sleep_ms(*cnt3.as_ref()).await;
Ok("done")
}
})?;
@@ -297,40 +299,34 @@ async fn test_async_table() -> Result<()> {
table.set("val", 10)?;
let get_value = lua.create_async_function(|_, table: Table| async move {
Delay::new(Duration::from_millis(10)).await;
sleep_ms(10).await;
table.get::<_, i64>("val")
})?;
table.set("get_value", get_value)?;
let set_value = lua.create_async_function(|_, (table, n): (Table, i64)| async move {
Delay::new(Duration::from_millis(10)).await;
sleep_ms(10).await;
table.set("val", n)
})?;
table.set("set_value", set_value)?;
let sleep = lua.create_async_function(|_, n| async move {
Delay::new(Duration::from_millis(n)).await;
sleep_ms(n).await;
Ok(format!("elapsed:{}ms", n))
})?;
table.set("sleep", sleep)?;
assert_eq!(
table
.call_async_method::<_, _, i64>("get_value", ())
.await?,
table.call_async_method::<_, i64>("get_value", ()).await?,
10
);
table.call_async_method("set_value", 15).await?;
assert_eq!(
table
.call_async_method::<_, _, i64>("get_value", ())
.await?,
table.call_async_method::<_, i64>("get_value", ()).await?,
15
);
assert_eq!(
table
.call_async_function::<_, _, String>("sleep", 7)
.await?,
table.call_async_function::<_, String>("sleep", 7).await?,
"elapsed:7ms"
);
@@ -343,12 +339,12 @@ async fn test_async_thread_pool() -> Result<()> {
let lua = Lua::new_with(StdLib::ALL_SAFE, options)?;
let error_f = lua.create_async_function(|_, ()| async move {
Delay::new(Duration::from_millis(10)).await;
Err::<(), _>(Error::RuntimeError("test".to_string()))
sleep_ms(10).await;
Err::<(), _>(Error::runtime("test"))
})?;
let sleep = lua.create_async_function(|_, n| async move {
Delay::new(Duration::from_millis(n)).await;
sleep_ms(n).await;
Ok(format!("elapsed:{}ms", n))
})?;
@@ -361,31 +357,30 @@ async fn test_async_thread_pool() -> Result<()> {
#[tokio::test]
async fn test_async_userdata() -> Result<()> {
#[derive(Clone)]
struct MyUserData(Arc<AtomicU64>);
struct MyUserData(u64);
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("get_value", |_, data, ()| async move {
Delay::new(Duration::from_millis(10)).await;
Ok(data.0.load(Ordering::Relaxed))
sleep_ms(10).await;
Ok(data.0)
});
methods.add_async_method("set_value", |_, data, n| async move {
Delay::new(Duration::from_millis(10)).await;
data.0.store(n, Ordering::Relaxed);
methods.add_async_method_mut("set_value", |_, data, n| async move {
sleep_ms(10).await;
data.0 = n;
Ok(())
});
methods.add_async_function("sleep", |_, n| async move {
Delay::new(Duration::from_millis(n)).await;
sleep_ms(n).await;
Ok(format!("elapsed:{}ms", n))
});
#[cfg(not(any(feature = "lua51", feature = "luau")))]
methods.add_async_meta_method(mlua::MetaMethod::Call, |_, data, ()| async move {
let n = data.0.load(Ordering::Relaxed);
Delay::new(Duration::from_millis(n)).await;
let n = data.0;
sleep_ms(n).await;
Ok(format!("elapsed:{}ms", n))
});
@@ -393,25 +388,26 @@ async fn test_async_userdata() -> Result<()> {
methods.add_async_meta_method(
mlua::MetaMethod::Index,
|_, data, key: String| async move {
Delay::new(Duration::from_millis(10)).await;
sleep_ms(10).await;
match key.as_str() {
"ms" => Ok(Some(data.0.load(Ordering::Relaxed) as f64)),
"s" => Ok(Some((data.0.load(Ordering::Relaxed) as f64) / 1000.0)),
"ms" => Ok(Some(data.0 as f64)),
"s" => Ok(Some((data.0 as f64) / 1000.0)),
_ => Ok(None),
}
},
);
#[cfg(not(any(feature = "lua51", feature = "luau")))]
methods.add_async_meta_method(
methods.add_async_meta_method_mut(
mlua::MetaMethod::NewIndex,
|_, data, (key, value): (String, f64)| async move {
Delay::new(Duration::from_millis(10)).await;
sleep_ms(10).await;
match key.as_str() {
"ms" => Ok(data.0.store(value as u64, Ordering::Relaxed)),
"s" => Ok(data.0.store((value * 1000.0) as u64, Ordering::Relaxed)),
_ => Err(Error::external(format!("key '{}' not found", key))),
"ms" => data.0 = value as u64,
"s" => data.0 = (value * 1000.0) as u64,
_ => return Err(Error::external(format!("key '{}' not found", key))),
}
Ok(())
},
);
}
@@ -420,7 +416,7 @@ async fn test_async_userdata() -> Result<()> {
let lua = Lua::new();
let globals = lua.globals();
let userdata = lua.create_userdata(MyUserData(Arc::new(AtomicU64::new(11))))?;
let userdata = lua.create_userdata(MyUserData(11))?;
globals.set("userdata", userdata.clone())?;
lua.load(
@@ -492,7 +488,7 @@ async fn test_owned_async_call() -> Result<()> {
let hello = lua
.create_async_function(|_, name: String| async move {
Delay::new(Duration::from_millis(10)).await;
sleep_ms(10).await;
Ok(format!("hello, {}!", name))
})?
.into_owned();
@@ -513,7 +509,7 @@ async fn test_async_terminate() -> Result<()> {
let mutex = mutex2.clone();
async move {
let _guard = mutex.lock();
Delay::new(Duration::from_millis(100)).await;
sleep_ms(100).await;
Ok(())
}
})?;
+5 -1
View File
@@ -15,7 +15,11 @@ fn test_compilation() {
t.compile_fail("tests/compile/static_callback_args.rs");
#[cfg(feature = "async")]
t.compile_fail("tests/compile/async_nonstatic_userdata.rs");
{
t.compile_fail("tests/compile/async_any_userdata_method.rs");
t.compile_fail("tests/compile/async_nonstatic_userdata.rs");
t.compile_fail("tests/compile/async_userdata_method.rs");
}
#[cfg(feature = "send")]
t.compile_fail("tests/compile/non_send.rs");
@@ -0,0 +1,14 @@
use mlua::{UserDataMethods, Lua};
fn main() {
let lua = Lua::new();
lua.register_userdata_type::<String>(|reg| {
let s = String::new();
let mut s = &s;
reg.add_async_method("t", |_, this: &String, ()| async {
s = this;
Ok(())
});
}).unwrap();
}
@@ -0,0 +1,81 @@
error: lifetime may not live long enough
--> tests/compile/async_any_userdata_method.rs:9:58
|
9 | reg.add_async_method("t", |_, this: &String, ()| async {
| ___________________________________----------------------_^
| | | |
| | | return type of closure `[async block@$DIR/tests/compile/async_any_userdata_method.rs:9:58: 12:10]` contains a lifetime `'2`
| | lifetime `'1` represents this closure's body
10 | | s = this;
11 | | Ok(())
12 | | });
| |_________^ returning this value requires that `'1` must outlive `'2`
|
= note: closure implements `Fn`, so references to captured variables can't escape the closure
error[E0596]: cannot borrow `s` as mutable, as it is a captured variable in a `Fn` closure
--> tests/compile/async_any_userdata_method.rs:9:58
|
9 | reg.add_async_method("t", |_, this: &String, ()| async {
| __________________________________________________________^
10 | | s = this;
| | - mutable borrow occurs due to use of `s` in closure
11 | | Ok(())
12 | | });
| |_________^ cannot borrow as mutable
error[E0597]: `s` does not live long enough
--> tests/compile/async_any_userdata_method.rs:8:21
|
8 | let mut s = &s;
| ^^ borrowed value does not live long enough
9 | / reg.add_async_method("t", |_, this: &String, ()| async {
10 | | s = this;
11 | | Ok(())
12 | | });
| |__________- argument requires that `s` is borrowed for `'static`
13 | }).unwrap();
| - `s` dropped here while still borrowed
error[E0521]: borrowed data escapes outside of closure
--> tests/compile/async_any_userdata_method.rs:9:9
|
6 | lua.register_userdata_type::<String>(|reg| {
| ---
| |
| `reg` is a reference that is only valid in the closure body
| has type `&mut LuaUserDataRegistry<'1, std::string::String>`
...
9 | / reg.add_async_method("t", |_, this: &String, ()| async {
10 | | s = this;
11 | | Ok(())
12 | | });
| | ^
| | |
| |__________`reg` escapes the closure body here
| argument requires that `'1` must outlive `'static`
|
= note: requirement occurs because of a mutable reference to `LuaUserDataRegistry<'_, std::string::String>`
= note: mutable references are invariant over their type parameter
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error[E0373]: closure may outlive the current function, but it borrows `s`, which is owned by the current function
--> tests/compile/async_any_userdata_method.rs:9:35
|
9 | reg.add_async_method("t", |_, this: &String, ()| async {
| ^^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `s`
10 | s = this;
| - `s` is borrowed here
|
note: function requires argument type to outlive `'static`
--> tests/compile/async_any_userdata_method.rs:9:9
|
9 | / reg.add_async_method("t", |_, this: &String, ()| async {
10 | | s = this;
11 | | Ok(())
12 | | });
| |__________^
help: to force the closure to take ownership of `s` (and any other referenced variables), use the `move` keyword
|
9 | reg.add_async_method("t", move |_, this: &String, ()| async {
| ++++
@@ -4,11 +4,8 @@ error: lifetime may not live long enough
7 | impl<'a> UserData for MyUserData<'a> {
| -- lifetime `'a` defined here
8 | fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
| ---- lifetime `'lua` defined here
9 | / methods.add_async_method("print", |_, data, ()| async move {
10 | | println!("{}", data.0);
11 | | Ok(())
12 | | });
| |______________^ argument requires that `'a` must outlive `'lua`
|
= help: consider adding the following bound: `'a: 'lua`
| |______________^ requires that `'a` must outlive `'static`
+14
View File
@@ -0,0 +1,14 @@
use mlua::{UserData, UserDataMethods};
struct MyUserData;
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("method", |_, this: &'static Self, ()| async {
Ok(())
});
// ^ lifetime may not live long enough
}
}
fn main() {}
@@ -0,0 +1,17 @@
warning: unused variable: `this`
--> tests/compile/async_userdata_method.rs:7:48
|
7 | methods.add_async_method("method", |_, this: &'static Self, ()| async {
| ^^^^ help: if this is intentional, prefix it with an underscore: `_this`
|
= note: `#[warn(unused_variables)]` on by default
error: lifetime may not live long enough
--> tests/compile/async_userdata_method.rs:7:9
|
6 | fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
| ---- lifetime `'lua` defined here
7 | / methods.add_async_method("method", |_, this: &'static Self, ()| async {
8 | | Ok(())
9 | | });
| |__________^ argument requires that `'lua` must outlive `'static`
+2 -2
View File
@@ -21,5 +21,5 @@ note: required because it's used within this closure
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`
| F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
| ^^^^^^^^^ required by this bound in `Lua::create_function`
+1 -1
View File
@@ -7,7 +7,7 @@ fn test_error_context() -> Result<()> {
let lua = Lua::new();
let func = lua.create_function(|_, ()| {
Err::<(), _>(Error::RuntimeError("runtime error".into())).context("some context")
Err::<(), _>(Error::runtime("runtime error")).context("some context")
})?;
lua.globals().set("func", func)?;
+17 -17
View File
@@ -196,37 +196,37 @@ fn test_function_info() -> Result<()> {
let function1_info = function1.info();
#[cfg(feature = "luau")]
assert_eq!(function1_info.name.as_deref(), Some("function1"));
assert_eq!(function1_info.source.as_deref(), Some(b"source1".as_ref()));
assert_eq!(function1_info.line_defined, 2);
assert_eq!(function1_info.source.as_deref(), Some("source1"));
assert_eq!(function1_info.line_defined, Some(2));
#[cfg(not(feature = "luau"))]
assert_eq!(function1_info.last_line_defined, 4);
assert_eq!(function1_info.last_line_defined, Some(4));
#[cfg(feature = "luau")]
assert_eq!(function1_info.last_line_defined, -1);
assert_eq!(function1_info.what.as_deref(), Some("Lua"));
assert_eq!(function1_info.last_line_defined, None);
assert_eq!(function1_info.what, "Lua");
let function2_info = function2.info();
assert_eq!(function2_info.name, None);
assert_eq!(function2_info.source.as_deref(), Some(b"source1".as_ref()));
assert_eq!(function2_info.line_defined, 3);
assert_eq!(function2_info.source.as_deref(), Some("source1"));
assert_eq!(function2_info.line_defined, Some(3));
#[cfg(not(feature = "luau"))]
assert_eq!(function2_info.last_line_defined, 3);
assert_eq!(function2_info.last_line_defined, Some(3));
#[cfg(feature = "luau")]
assert_eq!(function2_info.last_line_defined, -1);
assert_eq!(function2_info.what.as_deref(), Some("Lua"));
assert_eq!(function2_info.last_line_defined, None);
assert_eq!(function2_info.what, "Lua");
let function3_info = function3.info();
assert_eq!(function3_info.name, None);
assert_eq!(function3_info.source.as_deref(), Some(b"=[C]".as_ref()));
assert_eq!(function3_info.line_defined, -1);
assert_eq!(function3_info.last_line_defined, -1);
assert_eq!(function3_info.what.as_deref(), Some("C"));
assert_eq!(function3_info.source.as_deref(), Some("=[C]"));
assert_eq!(function3_info.line_defined, None);
assert_eq!(function3_info.last_line_defined, None);
assert_eq!(function3_info.what, "C");
let print_info = globals.get::<_, Function>("print")?.info();
#[cfg(feature = "luau")]
assert_eq!(print_info.name.as_deref(), Some("print"));
assert_eq!(print_info.source.as_deref(), Some(b"=[C]".as_ref()));
assert_eq!(print_info.what.as_deref(), Some("C"));
assert_eq!(print_info.line_defined, -1);
assert_eq!(print_info.source.as_deref(), Some("=[C]"));
assert_eq!(print_info.what, "C");
assert_eq!(print_info.line_defined, None);
Ok(())
}
+16 -25
View File
@@ -2,7 +2,6 @@
use std::cell::RefCell;
use std::ops::Deref;
use std::str;
use std::sync::atomic::{AtomicI64, Ordering};
use std::sync::{Arc, Mutex};
@@ -29,7 +28,7 @@ fn test_line_counts() -> Result<()> {
assert_eq!(debug.event(), DebugEvent::Line);
hook_output.lock().unwrap().push(debug.curr_line());
Ok(())
})?;
});
lua.load(
r#"
local x = 2 + 3
@@ -61,11 +60,10 @@ fn test_function_calls() -> Result<()> {
assert_eq!(debug.event(), DebugEvent::Call);
let names = debug.names();
let source = debug.source();
let name = names.name.map(|s| str::from_utf8(s).unwrap().to_owned());
let what = source.what.map(|s| str::from_utf8(s).unwrap().to_owned());
hook_output.lock().unwrap().push((name, what));
let name = names.name.map(|s| s.into_owned());
hook_output.lock().unwrap().push((name, source.what));
Ok(())
})?;
});
lua.load(
r#"
@@ -80,18 +78,12 @@ fn test_function_calls() -> Result<()> {
if cfg!(feature = "luajit") && lua.load("jit.version_num").eval::<i64>()? >= 20100 {
assert_eq!(
*output,
vec![
(None, Some("main".to_string())),
(Some("len".to_string()), Some("Lua".to_string()))
]
vec![(None, "main"), (Some("len".to_string()), "Lua")]
);
} else {
assert_eq!(
*output,
vec![
(None, Some("main".to_string())),
(Some("len".to_string()), Some("C".to_string()))
]
vec![(None, "main"), (Some("len".to_string()), "C")]
);
}
@@ -103,10 +95,8 @@ fn test_error_within_hook() -> Result<()> {
let lua = Lua::new();
lua.set_hook(HookTriggers::EVERY_LINE, |_lua, _debug| {
Err(Error::RuntimeError(
"Something happened in there!".to_string(),
))
})?;
Err(Error::runtime("Something happened in there!"))
});
let err = lua
.load("x = 1")
@@ -138,12 +128,12 @@ fn test_limit_execution_instructions() -> Result<()> {
move |_lua, debug| {
assert_eq!(debug.event(), DebugEvent::Count);
if max_instructions.fetch_sub(30, Ordering::Relaxed) <= 30 {
Err(Error::RuntimeError("time's up".to_string()))
Err(Error::runtime("time's up"))
} else {
Ok(())
}
},
)?;
);
lua.globals().set("x", Value::Integer(0))?;
let _ = lua
@@ -167,11 +157,11 @@ fn test_hook_removal() -> Result<()> {
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(),
Err(Error::runtime(
"this hook should've been removed by this time",
))
},
)?;
);
assert!(lua.load("local x = 1").exec().is_err());
lua.remove_hook();
@@ -215,9 +205,10 @@ fn test_hook_swap_within_hook() -> Result<()> {
Ok(())
},
)
})
});
Ok(())
})
})?;
});
TL_LUA.with(|tl| {
let tl = tl.borrow();
+52 -5
View File
@@ -7,7 +7,9 @@ use std::panic::{catch_unwind, AssertUnwindSafe};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use mlua::{Compiler, CoverageInfo, Error, Lua, Result, Table, ThreadStatus, Value, VmState};
use mlua::{
Compiler, CoverageInfo, Error, Lua, Result, Table, ThreadStatus, Value, Vector, VmState,
};
#[test]
fn test_version() -> Result<()> {
@@ -50,13 +52,18 @@ fn test_require() -> Result<()> {
.exec()
}
#[cfg(not(feature = "luau-vector4"))]
#[test]
fn test_vectors() -> Result<()> {
let lua = Lua::new();
let v: [f32; 3] = lua.load("vector(1, 2, 3) + vector(3, 2, 1)").eval()?;
let v: Vector = lua.load("vector(1, 2, 3) + vector(3, 2, 1)").eval()?;
assert_eq!(v, [4.0, 4.0, 4.0]);
// Test conversion into Rust array
let v: [f64; 3] = lua.load("vector(1, 2, 3)").eval()?;
assert!(v == [1.0, 2.0, 3.0]);
// Test vector methods
lua.load(
r#"
@@ -83,6 +90,46 @@ fn test_vectors() -> Result<()> {
Ok(())
}
#[cfg(feature = "luau-vector4")]
#[test]
fn test_vectors() -> Result<()> {
let lua = Lua::new();
let v: Vector = lua.load("vector(1, 2, 3, 4) + vector(4, 3, 2, 1)").eval()?;
assert_eq!(v, [5.0, 5.0, 5.0, 5.0]);
// Test conversion into Rust array
let v: [f64; 4] = lua.load("vector(1, 2, 3, 4)").eval()?;
assert!(v == [1.0, 2.0, 3.0, 4.0]);
// Test vector methods
lua.load(
r#"
local v = vector(1, 2, 3, 4)
assert(v.x == 1)
assert(v.y == 2)
assert(v.z == 3)
assert(v.w == 4)
"#,
)
.exec()?;
// Test vector methods (fastcall)
lua.load(
r#"
local v = vector(1, 2, 3, 4)
assert(v.x == 1)
assert(v.y == 2)
assert(v.z == 3)
assert(v.w == 4)
"#,
)
.set_compiler(Compiler::new().set_vector_ctor(Some("vector".to_string())))
.exec()?;
Ok(())
}
#[test]
fn test_readonly_table() -> Result<()> {
let lua = Lua::new();
@@ -232,7 +279,7 @@ fn test_interrupts() -> Result<()> {
//
// Test errors in interrupts
//
lua.set_interrupt(|_| Err(Error::RuntimeError("error from interrupt".into())));
lua.set_interrupt(|_| Err(Error::runtime("error from interrupt")));
match f.call::<_, ()>(()) {
Err(Error::CallbackError { cause, .. }) => match *cause {
Error::RuntimeError(ref m) if m == "error from interrupt" => {}
@@ -254,8 +301,8 @@ fn test_coverage() -> Result<()> {
let f = lua
.load(
r#"local v = vector(1, 2, 3)
assert(v.x == 1 and v.y == 2 and v.z == 3)
r#"local s = "abc"
assert(#s == 3)
function abc(i)
if i < 5 then
+1 -1
View File
@@ -26,7 +26,7 @@ struct MyUserData(i32);
impl LuaUserData for MyUserData {}
#[mlua::lua_module(name = "test_module_second")]
#[mlua::lua_module(name = "test_module_second", skip_memory_check)]
fn test_module2(lua: &Lua) -> LuaResult<LuaTable> {
let exports = lua.create_table()?;
exports.set("userdata", MyUserData(123))?;
+25 -2
View File
@@ -145,7 +145,7 @@ fn test_serialize_failure() -> Result<(), Box<dyn StdError>> {
Ok(())
}
#[cfg(feature = "luau")]
#[cfg(all(feature = "luau", not(feature = "luau-vector4")))]
#[test]
fn test_serialize_vector() -> Result<(), Box<dyn StdError>> {
let lua = Lua::new();
@@ -153,7 +153,7 @@ fn test_serialize_vector() -> Result<(), Box<dyn StdError>> {
let globals = lua.globals();
globals.set(
"vector",
lua.create_function(|_, (x, y, z)| Ok(Value::Vector(x, y, z)))?,
lua.create_function(|_, (x, y, z)| Ok(mlua::Vector::new(x, y, z)))?,
)?;
let val = lua.load("{_vector = vector(1, 2, 3)}").eval::<Value>()?;
@@ -168,6 +168,29 @@ fn test_serialize_vector() -> Result<(), Box<dyn StdError>> {
Ok(())
}
#[cfg(feature = "luau-vector4")]
#[test]
fn test_serialize_vector() -> Result<(), Box<dyn StdError>> {
let lua = Lua::new();
let globals = lua.globals();
globals.set(
"vector",
lua.create_function(|_, (x, y, z, w)| Ok(mlua::Vector::new(x, y, z, w)))?,
)?;
let val = lua.load("{_vector = vector(1, 2, 3, 4)}").eval::<Value>()?;
let json = serde_json::json!({
"_vector": [1.0, 2.0, 3.0, 4.0],
});
assert_eq!(serde_json::to_value(&val)?, json);
let expected_json = lua.from_value::<serde_json::Value>(val)?;
assert_eq!(expected_json, json);
Ok(())
}
#[test]
fn test_to_value_struct() -> LuaResult<()> {
let lua = Lua::new();
+22 -40
View File
@@ -1,7 +1,7 @@
use mlua::{Error, Lua, Nil, Result, Table, TableExt, Value};
#[test]
fn test_set_get() -> Result<()> {
fn test_globals_set_get() -> Result<()> {
let lua = Lua::new();
let globals = lua.globals();
@@ -43,6 +43,7 @@ fn test_table() -> Result<()> {
let table3 = globals.get::<_, Table>("table3")?;
assert_eq!(table1.len()?, 5);
assert!(!table1.is_empty());
assert_eq!(
table1
.clone()
@@ -57,8 +58,10 @@ fn test_table() -> Result<()> {
.collect::<Result<Vec<i64>>>()?,
vec![1, 2, 3, 4, 5]
);
assert_eq!(table1, [1, 2, 3, 4, 5]);
assert_eq!(table2.len()?, 0);
assert!(table2.is_empty());
assert_eq!(
table2
.clone()
@@ -66,12 +69,10 @@ fn test_table() -> Result<()> {
.collect::<Result<Vec<(i64, i64)>>>()?,
vec![]
);
assert_eq!(
table2.sequence_values().collect::<Result<Vec<i64>>>()?,
vec![]
);
assert_eq!(table2, [0; 0]);
// sequence_values should only iterate until the first border
assert_eq!(table3, [1, 2]);
assert_eq!(
table3.sequence_values().collect::<Result<Vec<i64>>>()?,
vec![1, 2]
@@ -116,17 +117,12 @@ fn test_table_push_pop() -> Result<()> {
// Test raw access
let table1 = lua.create_sequence_from(vec![123])?;
table1.raw_push(321)?;
assert_eq!(
table1
.clone()
.raw_sequence_values::<i64>()
.collect::<Result<Vec<_>>>()?,
vec![123, 321]
);
assert_eq!(table1, [123, 321]);
assert_eq!(table1.raw_pop::<i64>()?, 321);
assert_eq!(table1.raw_pop::<i64>()?, 123);
assert_eq!(table1.raw_pop::<Value>()?, Value::Nil); // An extra pop should do nothing
assert_eq!(table1.raw_len(), 0);
assert_eq!(table1, [0; 0]);
// Test access through metamethods
let table2 = lua
@@ -144,6 +140,13 @@ fn test_table_push_pop() -> Result<()> {
.eval::<Table>()?;
table2.push(345)?;
assert_eq!(table2.len()?, 2);
assert_eq!(
table2
.clone()
.sequence_values::<i64>()
.collect::<Result<Vec<_>>>()?,
vec![]
);
assert_eq!(table2.pop::<i64>()?, 345);
assert_eq!(table2.pop::<i64>()?, 234);
assert_eq!(table2.pop::<Value>()?, Value::Nil);
@@ -191,8 +194,10 @@ fn test_table_clear() -> Result<()> {
)
.eval::<Table>()?;
assert_eq!(t2.raw_len(), 3);
assert!(!t2.is_empty());
t2.clear()?;
assert_eq!(t2.raw_len(), 0);
assert!(t2.is_empty());
assert_eq!(t2.raw_get::<_, Value>("a")?, Value::Nil);
assert_ne!(t2.get_metatable(), None);
@@ -205,29 +210,9 @@ fn test_table_sequence_from() -> Result<()> {
let get_table = lua.create_function(|_, t: Table| Ok(t))?;
assert_eq!(
get_table
.call::<_, Table>(vec![1, 2, 3])?
.sequence_values()
.collect::<Result<Vec<i64>>>()?,
vec![1, 2, 3]
);
assert_eq!(
get_table
.call::<_, Table>([1, 2, 3].as_ref())?
.sequence_values()
.collect::<Result<Vec<i64>>>()?,
vec![1, 2, 3]
);
assert_eq!(
get_table
.call::<_, Table>([1, 2, 3])?
.sequence_values()
.collect::<Result<Vec<i64>>>()?,
vec![1, 2, 3]
);
assert_eq!(get_table.call::<_, Table>(vec![1, 2, 3])?, [1, 2, 3]);
assert_eq!(get_table.call::<_, Table>([4, 5, 6])?, [4, 5, 6]);
assert_eq!(get_table.call::<_, Table>([7, 8, 9].as_slice())?, [7, 8, 9]);
Ok(())
}
@@ -377,11 +362,8 @@ fn test_table_call() -> Result<()> {
let table: Table = lua.globals().get("table")?;
assert_eq!(table.call::<_, String>("b")?, "call_2");
assert_eq!(table.call_function::<_, _, String>("func", "a")?, "func_a");
assert_eq!(
table.call_method::<_, _, String>("method", "a")?,
"method_1"
);
assert_eq!(table.call_function::<_, String>("func", "a")?, "func_a");
assert_eq!(table.call_method::<_, String>("method", "a")?, "method_1");
// Test calling non-callable table
let table2 = lua.create_table()?;
+10 -9
View File
@@ -1226,8 +1226,8 @@ fn test_inspect_stack() -> Result<()> {
let logline = lua.create_function(|lua, msg: StdString| {
let debug = lua.inspect_stack(1).unwrap(); // caller
let source = debug.source().short_src.map(core::str::from_utf8);
let source = source.transpose().unwrap().unwrap_or("?");
let source = debug.source().short_src;
let source = source.as_deref().unwrap_or("?");
let line = debug.curr_line();
Ok(format!("{}:{} {}", source, line, msg))
})?;
@@ -1278,20 +1278,20 @@ fn test_warnings() -> Result<()> {
let lua = Lua::new();
lua.set_app_data::<Vec<(StdString, bool)>>(Vec::new());
lua.set_warning_function(|lua, msg, tocont| {
let msg = msg.to_string_lossy().to_string();
lua.set_warning_function(|lua, msg, incomplete| {
lua.app_data_mut::<Vec<(StdString, bool)>>()
.unwrap()
.push((msg, tocont));
.push((msg.to_string(), incomplete));
Ok(())
});
lua.warning("native warning ...", true)?;
lua.warning("finish", false)?;
lua.warning("native warning ...", true);
lua.warning("finish", false);
lua.warning("\0", false);
lua.load(r#"warn("lua warning", "continue")"#).exec()?;
lua.remove_warning_function();
lua.warning("one more warning", false)?;
lua.warning("one more warning", false);
let messages = lua.app_data_ref::<Vec<(StdString, bool)>>().unwrap();
assert_eq!(
@@ -1299,13 +1299,14 @@ fn test_warnings() -> Result<()> {
vec![
("native warning ...".to_string(), true),
("finish".to_string(), false),
("".to_string(), false),
("lua warning".to_string(), true),
("continue".to_string(), false),
]
);
// Trigger error inside warning
lua.set_warning_function(|_, _, _| Err(Error::RuntimeError("warning error".to_string())));
lua.set_warning_function(|_, _, _| Err(Error::runtime("warning error")));
assert!(matches!(
lua.load(r#"warn("test")"#).exec(),
Err(Error::CallbackError { cause, .. })
+82 -18
View File
@@ -411,21 +411,21 @@ fn test_user_values() -> Result<()> {
ud.set_nth_user_value(1, "hello")?;
ud.set_nth_user_value(2, "world")?;
ud.set_nth_user_value(65535, 321)?;
assert_eq!(ud.get_nth_user_value::<String>(1)?, "hello");
assert_eq!(ud.get_nth_user_value::<String>(2)?, "world");
assert_eq!(ud.get_nth_user_value::<Value>(3)?, Value::Nil);
assert_eq!(ud.get_nth_user_value::<i32>(65535)?, 321);
assert_eq!(ud.nth_user_value::<String>(1)?, "hello");
assert_eq!(ud.nth_user_value::<String>(2)?, "world");
assert_eq!(ud.nth_user_value::<Value>(3)?, Value::Nil);
assert_eq!(ud.nth_user_value::<i32>(65535)?, 321);
assert!(ud.get_nth_user_value::<Value>(0).is_err());
assert!(ud.get_nth_user_value::<Value>(65536).is_err());
assert!(ud.nth_user_value::<Value>(0).is_err());
assert!(ud.nth_user_value::<Value>(65536).is_err());
// Named user values
ud.set_named_user_value("name", "alex")?;
ud.set_named_user_value("age", 10)?;
assert_eq!(ud.get_named_user_value::<String>("name")?, "alex");
assert_eq!(ud.get_named_user_value::<i32>("age")?, 10);
assert_eq!(ud.get_named_user_value::<Value>("nonexist")?, Value::Nil);
assert_eq!(ud.named_user_value::<String>("name")?, "alex");
assert_eq!(ud.named_user_value::<i32>("age")?, 10);
assert_eq!(ud.named_user_value::<Value>("nonexist")?, Value::Nil);
Ok(())
}
@@ -495,7 +495,7 @@ fn test_fields() -> Result<()> {
});
// Use userdata "uservalue" storage
fields.add_field_function_get("uval", |_, ud| ud.get_user_value::<Option<String>>());
fields.add_field_function_get("uval", |_, ud| ud.user_value::<Option<String>>());
fields
.add_field_function_set("uval", |_, ud, s| ud.set_user_value::<Option<String>>(s));
@@ -625,10 +625,33 @@ fn test_userdata_wrapped() -> Result<()> {
let lua = Lua::new();
let globals = lua.globals();
// Rc<T>
#[cfg(not(feature = "send"))]
{
let ud1 = Rc::new(RefCell::new(MyUserData(1)));
globals.set("rc_refcell_ud", ud1.clone())?;
let ud = Rc::new(MyUserData(1));
globals.set("rc_ud", ud.clone())?;
lua.load(
r#"
assert(rc_ud.static == "constant")
local ok, err = pcall(function() rc_ud.data = 2 end)
assert(
tostring(err):sub(1, 32) == "error mutably borrowing userdata",
"expected error mutably borrowing userdata, got " .. tostring(err)
)
assert(rc_ud.data == 1)
"#,
)
.exec()?;
globals.set("rc_ud", Nil)?;
lua.gc_collect()?;
assert_eq!(Rc::strong_count(&ud), 1);
}
// Rc<RefCell<T>>
#[cfg(not(feature = "send"))]
{
let ud = Rc::new(RefCell::new(MyUserData(1)));
globals.set("rc_refcell_ud", ud.clone())?;
lua.load(
r#"
assert(rc_refcell_ud.static == "constant")
@@ -637,12 +660,32 @@ fn test_userdata_wrapped() -> Result<()> {
"#,
)
.exec()?;
assert_eq!(ud1.borrow().0, 2);
assert_eq!(ud.borrow().0, 2);
globals.set("rc_refcell_ud", Nil)?;
lua.gc_collect()?;
assert_eq!(Rc::strong_count(&ud1), 1);
assert_eq!(Rc::strong_count(&ud), 1);
}
// Arc<T>
let ud1 = Arc::new(MyUserData(2));
globals.set("arc_ud", ud1.clone())?;
lua.load(
r#"
assert(arc_ud.static == "constant")
local ok, err = pcall(function() arc_ud.data = 3 end)
assert(
tostring(err):sub(1, 32) == "error mutably borrowing userdata",
"expected error mutably borrowing userdata, got " .. tostring(err)
)
assert(arc_ud.data == 2)
"#,
)
.exec()?;
globals.set("arc_ud", Nil)?;
lua.gc_collect()?;
assert_eq!(Arc::strong_count(&ud1), 1);
// Arc<Mutex<T>>
let ud2 = Arc::new(Mutex::new(MyUserData(2)));
globals.set("arc_mutex_ud", ud2.clone())?;
lua.load(
@@ -657,7 +700,11 @@ fn test_userdata_wrapped() -> Result<()> {
assert_eq!(ud2.lock().unwrap().0, 3);
#[cfg(feature = "parking_lot")]
assert_eq!(ud2.lock().0, 3);
globals.set("arc_mutex_ud", Nil)?;
lua.gc_collect()?;
assert_eq!(Arc::strong_count(&ud2), 1);
// Arc<RwLock<T>>
let ud3 = Arc::new(RwLock::new(MyUserData(3)));
globals.set("arc_rwlock_ud", ud3.clone())?;
lua.load(
@@ -672,12 +719,8 @@ fn test_userdata_wrapped() -> Result<()> {
assert_eq!(ud3.read().unwrap().0, 4);
#[cfg(feature = "parking_lot")]
assert_eq!(ud3.read().0, 4);
// Test drop
globals.set("arc_mutex_ud", Nil)?;
globals.set("arc_rwlock_ud", Nil)?;
lua.gc_collect()?;
assert_eq!(Arc::strong_count(&ud2), 1);
assert_eq!(Arc::strong_count(&ud3), 1);
Ok(())
@@ -753,6 +796,27 @@ fn test_any_userdata() -> Result<()> {
Ok(())
}
#[test]
fn test_any_userdata_wrap() -> Result<()> {
let lua = Lua::new();
lua.register_userdata_type::<StdString>(|reg| {
reg.add_method("get", |_, this, ()| Ok(this.clone()));
})?;
lua.globals()
.set("s", AnyUserData::wrap("hello".to_string()))?;
lua.load(
r#"
assert(s:get() == "hello")
"#,
)
.exec()
.unwrap();
Ok(())
}
#[test]
fn test_userdata_ext() -> Result<()> {
let lua = Lua::new();
+8 -3
View File
@@ -101,11 +101,16 @@ fn test_value_to_string() -> Result<()> {
);
assert_eq!(Value::Integer(1).to_string()?, "1");
assert_eq!(Value::Number(34.59).to_string()?, "34.59");
#[cfg(feature = "luau")]
#[cfg(all(feature = "luau", not(feature = "luau-vector4")))]
assert_eq!(
Value::Vector(10.0, 11.1, 12.2).to_string()?,
Value::Vector(mlua::Vector::new(10.0, 11.1, 12.2)).to_string()?,
"vector(10, 11.1, 12.2)"
);
#[cfg(feature = "luau-vector4")]
assert_eq!(
Value::Vector(mlua::Vector::new(10.0, 11.1, 12.2, 13.3)).to_string()?,
"vector(10, 11.1, 12.2, 13.3)"
);
assert_eq!(
Value::String(lua.create_string("hello")?).to_string()?,
"hello"
@@ -135,7 +140,7 @@ fn test_value_to_string() -> Result<()> {
let ud: Value = Value::UserData(lua.create_userdata(MyUserData)?);
assert!(ud.to_string()?.starts_with("MyUserData:"));
let err = Value::Error(Error::RuntimeError("test error".to_string()));
let err = Value::Error(Error::runtime("test error"));
assert_eq!(err.to_string()?, "runtime error: test error");
Ok(())