mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 355a0606c3 | |||
| 2277ee4860 | |||
| 8a4977e8e7 | |||
| 128c357e07 | |||
| 9f0fc27c52 | |||
| bfd1c29c0a | |||
| 5fca2db6d3 | |||
| 8ecbf5b76a | |||
| d037c31b4d | |||
| 4fe89c9d45 | |||
| 389526bb80 | |||
| 4adc3116f9 | |||
| 8e0bdc9934 | |||
| 7dc6e4c132 | |||
| 44b8c8b7a6 | |||
| 6b8b79266f | |||
| 08ab685d8d | |||
| 057deb0169 | |||
| 44533d2c9d | |||
| 54c7a2d191 | |||
| c38a1f060b | |||
| 5dca743b0c | |||
| 01c1952c9f | |||
| dbc3dd95d4 | |||
| a9b0cdfc03 | |||
| 1c20494158 | |||
| c9294ad642 | |||
| 3a71bfb8a0 | |||
| 24e14c4874 | |||
| 20826a69ae | |||
| 5127903c38 | |||
| 541139b944 | |||
| 925a2816cc | |||
| b3b8d79446 | |||
| 85f17a269d | |||
| b169031d4e | |||
| 399e469328 | |||
| 1367a033d7 | |||
| c1168d3ec1 | |||
| b05698d55b | |||
| aeacf6cacc | |||
| 1f0e81c9a1 | |||
| c2bfc9ec52 | |||
| 9fdba541e9 | |||
| cf0524aa23 |
@@ -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
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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
@@ -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 }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::env;
|
||||
use std::ops::Bound;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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`
|
||||
|
||||
@@ -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`
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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(())
|
||||
|
||||
Reference in New Issue
Block a user