mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
36 Commits
v0.9.0-rc.2
...
v0.9.1
| Author | SHA1 | Date | |
|---|---|---|---|
| fc159e0c46 | |||
| a802276c56 | |||
| 65b816f2f0 | |||
| e2b3464ec9 | |||
| 89cf5bf362 | |||
| bb0a6070c4 | |||
| 60f1c16ddb | |||
| 17809a390c | |||
| 7662a7e4ff | |||
| d769a95fc5 | |||
| 0e4476c2e3 | |||
| d48a2b3f6c | |||
| b3592bc23e | |||
| 052310e93d | |||
| 09eb7f251b | |||
| c137da7618 | |||
| c0c6a33f94 | |||
| 0cb0a345dd | |||
| 3e479be4e5 | |||
| 170aa53e29 | |||
| 021ee946fc | |||
| 0b928fdfee | |||
| e858384cd4 | |||
| c062cddd87 | |||
| 94a79656ad | |||
| cd0c8a4584 | |||
| 4fff14a144 | |||
| 196c09a0d6 | |||
| 114f072269 | |||
| b3211f13ee | |||
| 1f1463c482 | |||
| bec40ee5ea | |||
| dc94d51d97 | |||
| 3a096ae64a | |||
| a85e757d4d | |||
| 312886846c |
@@ -1,3 +1,27 @@
|
||||
## v0.9.1
|
||||
|
||||
- impl Default for Lua
|
||||
- impl IntoLuaMulti for `std::result::Result<(), E>`
|
||||
- Fix using wrong userdata index after processing Variadic args (#311)
|
||||
|
||||
## v0.9.0
|
||||
|
||||
Changes since v0.9.0-rc.3
|
||||
|
||||
- Improved non-static (scoped) userdata support
|
||||
- Added `Scope::create_any_userdata()` method
|
||||
- Added `Lua::set_vector_metatable()` method (`unstable` feature flag)
|
||||
- Added `OwnedThread` type (`unstable` feature flag)
|
||||
- Minimal Luau updated to 0.590
|
||||
- Added new option `sort_keys` to `DeserializeOptions` (`Lua::from_value()` method)
|
||||
- Changed `Table::raw_len()` output type to `usize`
|
||||
- Helper functions for `Value` (eg: `Value::as_number()`/`Value::as_string`/etc)
|
||||
- Performance improvements
|
||||
|
||||
## v0.9.0-rc.3
|
||||
|
||||
- Minimal Luau updated to 0.588
|
||||
|
||||
## v0.9.0-rc.2
|
||||
|
||||
- Added `#[derive(FromLua)]` macro to opt-in into `FromLua<T> where T: 'static + Clone` (userdata type).
|
||||
@@ -81,6 +105,18 @@ Other:
|
||||
- Support setting module name in `#[lua_module(name = "...")]` macro
|
||||
- Minor fixes and improvements
|
||||
|
||||
## v0.8.10
|
||||
|
||||
- Update to Luau 0.590 (luau0-src to 0.7.x)
|
||||
- Fix loading luau code starting with \t
|
||||
- Pin lua-src and luajit-src versions
|
||||
|
||||
## v0.8.9
|
||||
|
||||
- Update minimal (vendored) Lua 5.4 to 5.4.6
|
||||
- Use `lua_closethread` instead of `lua_resetthread` in vendored mode (Lua 5.4.6)
|
||||
- Allow deserializing Lua null into unit (`()`) or unit struct.
|
||||
|
||||
## v0.8.8
|
||||
|
||||
- Fix potential deadlock when trying to reuse dropped registry keys.
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.9.0-rc.2" # remember to update mlua_derive
|
||||
version = "0.9.1" # remember to update mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
|
||||
rust-version = "1.71"
|
||||
edition = "2021"
|
||||
@@ -44,7 +44,7 @@ macros = ["mlua_derive/macros"]
|
||||
unstable = []
|
||||
|
||||
[dependencies]
|
||||
mlua_derive = { version = "=0.9.0-rc.2", optional = true, path = "mlua_derive" }
|
||||
mlua_derive = { version = "=0.9.0", 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" }
|
||||
@@ -55,7 +55,7 @@ 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.3.0", path = "mlua-sys" }
|
||||
ffi = { package = "mlua-sys", version = "0.3.2", path = "mlua-sys" }
|
||||
|
||||
[dev-dependencies]
|
||||
rustyline = "12.0"
|
||||
|
||||
@@ -19,9 +19,7 @@
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> Please see the [v0.8](https://github.com/khvzak/mlua/tree/v0.8) branch for the stable versions of `mlua` released to crates.io.
|
||||
>
|
||||
> v0.9 release notes can be found [here](docs/release_notes/v0.9.md).
|
||||
> See v0.9 [release notes](https://github.com/khvzak/mlua/blob/master/docs/release_notes/v0.9.md).
|
||||
|
||||
`mlua` is bindings to [Lua](https://www.lua.org) programming language for Rust with a goal to provide
|
||||
_safe_ (as far as it's possible), high level, easy to use, practical and flexible API.
|
||||
@@ -47,7 +45,7 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
|
||||
* `luajit`: activate [LuaJIT] support
|
||||
* `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-jit`: activate [Luau] support with JIT backend.
|
||||
* `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)
|
||||
@@ -119,7 +117,7 @@ Add to `Cargo.toml` :
|
||||
|
||||
``` toml
|
||||
[dependencies]
|
||||
mlua = { version = "0.9.0-rc.2", features = ["lua54", "vendored"] }
|
||||
mlua = { version = "0.9.1", features = ["lua54", "vendored"] }
|
||||
```
|
||||
|
||||
`main.rs`
|
||||
@@ -154,7 +152,7 @@ Add to `Cargo.toml` :
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { version = "0.9.0-rc.2", features = ["lua54", "vendored", "module"] }
|
||||
mlua = { version = "0.9.1", features = ["lua54", "module"] }
|
||||
```
|
||||
|
||||
`lib.rs` :
|
||||
|
||||
@@ -59,6 +59,31 @@ fn create_string_table(c: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
fn table_get_set(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
let table = lua.create_table().unwrap();
|
||||
|
||||
c.bench_function("table raw_get and raw_set [10]", |b| {
|
||||
b.iter_batched(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
table.clear().unwrap();
|
||||
},
|
||||
|_| {
|
||||
for (i, &s) in ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
table.raw_set(s, i).unwrap();
|
||||
assert_eq!(table.raw_get::<_, usize>(s).unwrap(), i);
|
||||
}
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn create_function(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
@@ -305,6 +330,7 @@ criterion_group! {
|
||||
create_table,
|
||||
create_array,
|
||||
create_string_table,
|
||||
table_get_set,
|
||||
create_function,
|
||||
call_lua_function,
|
||||
call_sum_callback,
|
||||
|
||||
@@ -83,7 +83,7 @@ println!("{s}!");
|
||||
|
||||
One of the common questions was how to embed a Lua type into Rust struct to use it later. It was non-trivial to do because of the `'lua` lifetime attached to every Lua value.
|
||||
|
||||
In v0.9 mlua introduces "owned" types `OwnedTable`/`OwnedFunction`/`OwnedString`/`OwnedAnyUserData` that are `'static` (no lifetime attached).
|
||||
In v0.9 mlua introduces "owned" types `OwnedTable`/`OwnedFunction`/`OwnedString`/`OwnedAnyUserData`/ `OwnedThread`that are `'static` (no lifetime attached).
|
||||
|
||||
```rust
|
||||
let lua = Lua::new();
|
||||
@@ -141,11 +141,10 @@ unsafe {
|
||||
|
||||
#### Luau JIT support
|
||||
|
||||
mlua brings support for the new experimental [Luau] JIT backend under the `luau-jit` feature flag. This backend is still under development and not yet ready for production use.
|
||||
mlua brings support for the new [Luau] JIT backend under the `luau-jit` feature flag.
|
||||
|
||||
To enable it, just call `lua.enable_jit(true)` before loading Lua code. mlua will automatically trigger JIT compilation for new Lua chunks.
|
||||
|
||||
When calling this function with `false` argument, mlua will disable JIT compilation but any previously compiled chunks will remain JIT-compiled.
|
||||
It will automatically trigger JIT compilation for new Lua chunks. To disable it, just call `lua.enable_jit(false)` before loading Lua code
|
||||
(but any previously compiled chunks will remain JIT-compiled).
|
||||
|
||||
[Luau]: https://luau-lang.org
|
||||
|
||||
@@ -197,16 +196,14 @@ let read = lua.create_function(|lua, path: String| {
|
||||
|
||||
lua.load(chunk! {
|
||||
local ok, err = pcall($read, "/nonexistent")
|
||||
/// Prints:
|
||||
/// Failed to open /nonexistent
|
||||
/// No such file or directory (os error 2)
|
||||
/// stack traceback:
|
||||
/// ...
|
||||
print(err)
|
||||
})
|
||||
.exec()?;
|
||||
|
||||
Prints:
|
||||
```text
|
||||
Failed to open /nonexistent
|
||||
No such file or directory (os error 2)
|
||||
stack traceback:
|
||||
...
|
||||
```
|
||||
|
||||
[`anyhow`]: https://crates.io/crates/anyhow
|
||||
|
||||
+4
-3
@@ -1,7 +1,8 @@
|
||||
[package]
|
||||
name = "mlua-sys"
|
||||
version = "0.3.0"
|
||||
version = "0.3.2"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
rust-version = "1.71"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
documentation = "https://docs.rs/mlua-sys"
|
||||
@@ -38,5 +39,5 @@ cc = "1.0"
|
||||
cfg-if = "1.0"
|
||||
pkg-config = "0.3.17"
|
||||
lua-src = { version = ">= 546.0.0, < 546.1.0", optional = true }
|
||||
luajit-src = { version = ">= 210.4.6, < 210.6.0", optional = true }
|
||||
luau0-src = { version = "0.5.11", optional = true }
|
||||
luajit-src = { version = ">= 210.4.6, < 210.5.0", optional = true }
|
||||
luau0-src = { version = "0.7.0", optional = true }
|
||||
|
||||
@@ -10,7 +10,8 @@ pub struct lua_CompileOptions {
|
||||
pub coverageLevel: c_int,
|
||||
pub vectorLib: *const c_char,
|
||||
pub vectorCtor: *const c_char,
|
||||
pub mutableGlobals: *mut *const c_char,
|
||||
pub vectorType: *const c_char,
|
||||
pub mutableGlobals: *const *const c_char,
|
||||
}
|
||||
|
||||
extern "C-unwind" {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua_derive"
|
||||
version = "0.9.0-rc.2"
|
||||
version = "0.9.0"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2021"
|
||||
description = "Procedural macros for the mlua crate."
|
||||
|
||||
@@ -61,12 +61,8 @@ pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
#[no_mangle]
|
||||
unsafe extern "C-unwind" fn #ext_entrypoint_name(state: *mut ::mlua::lua_State) -> ::std::os::raw::c_int {
|
||||
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")
|
||||
lua.skip_memory_check(#skip_memory_check);
|
||||
lua.entrypoint1(#func_name)
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+19
-6
@@ -125,6 +125,7 @@ pub struct Compiler {
|
||||
coverage_level: u8,
|
||||
vector_lib: Option<String>,
|
||||
vector_ctor: Option<String>,
|
||||
vector_type: Option<String>,
|
||||
mutable_globals: Vec<String>,
|
||||
}
|
||||
|
||||
@@ -146,6 +147,7 @@ impl Compiler {
|
||||
coverage_level: 0,
|
||||
vector_lib: None,
|
||||
vector_ctor: None,
|
||||
vector_type: None,
|
||||
mutable_globals: Vec::new(),
|
||||
}
|
||||
}
|
||||
@@ -188,15 +190,22 @@ impl Compiler {
|
||||
|
||||
#[doc(hidden)]
|
||||
#[must_use]
|
||||
pub fn set_vector_lib(mut self, lib: Option<String>) -> Self {
|
||||
self.vector_lib = lib;
|
||||
pub fn set_vector_lib(mut self, lib: impl Into<String>) -> Self {
|
||||
self.vector_lib = Some(lib.into());
|
||||
self
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[must_use]
|
||||
pub fn set_vector_ctor(mut self, ctor: Option<String>) -> Self {
|
||||
self.vector_ctor = ctor;
|
||||
pub fn set_vector_ctor(mut self, ctor: impl Into<String>) -> Self {
|
||||
self.vector_ctor = Some(ctor.into());
|
||||
self
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[must_use]
|
||||
pub fn set_vector_type(mut self, r#type: impl Into<String>) -> Self {
|
||||
self.vector_type = Some(r#type.into());
|
||||
self
|
||||
}
|
||||
|
||||
@@ -220,6 +229,9 @@ impl Compiler {
|
||||
let vector_ctor = self.vector_ctor.clone();
|
||||
let vector_ctor = vector_ctor.and_then(|ctor| CString::new(ctor).ok());
|
||||
let vector_ctor = vector_ctor.as_ref();
|
||||
let vector_type = self.vector_type.clone();
|
||||
let vector_type = vector_type.and_then(|t| CString::new(t).ok());
|
||||
let vector_type = vector_type.as_ref();
|
||||
|
||||
let mutable_globals = self
|
||||
.mutable_globals
|
||||
@@ -231,10 +243,10 @@ impl Compiler {
|
||||
.iter()
|
||||
.map(|s| s.as_ptr())
|
||||
.collect::<Vec<_>>();
|
||||
let mut mutable_globals_ptr = ptr::null_mut();
|
||||
let mut mutable_globals_ptr = ptr::null();
|
||||
if !mutable_globals.is_empty() {
|
||||
mutable_globals.push(ptr::null());
|
||||
mutable_globals_ptr = mutable_globals.as_mut_ptr();
|
||||
mutable_globals_ptr = mutable_globals.as_ptr();
|
||||
}
|
||||
|
||||
unsafe {
|
||||
@@ -244,6 +256,7 @@ impl Compiler {
|
||||
coverageLevel: self.coverage_level as c_int,
|
||||
vectorLib: vector_lib.map_or(ptr::null(), |s| s.as_ptr()),
|
||||
vectorCtor: vector_ctor.map_or(ptr::null(), |s| s.as_ptr()),
|
||||
vectorType: vector_type.map_or(ptr::null(), |s| s.as_ptr()),
|
||||
mutableGlobals: mutable_globals_ptr,
|
||||
};
|
||||
ffi::luau_compile(source.as_ref(), options)
|
||||
|
||||
@@ -3,7 +3,9 @@ use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
use std::convert::TryInto;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::hash::{BuildHasher, Hash};
|
||||
use std::os::raw::c_int;
|
||||
use std::string::String as StdString;
|
||||
use std::{slice, str};
|
||||
|
||||
use bstr::{BStr, BString};
|
||||
use num_traits::cast;
|
||||
@@ -302,6 +304,11 @@ impl<'lua> IntoLua<'lua> for StdString {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(&self)?))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
push_bytes_into_stack(self, lua)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for StdString {
|
||||
@@ -318,6 +325,25 @@ impl<'lua> FromLua<'lua> for StdString {
|
||||
.to_str()?
|
||||
.to_owned())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack(idx: c_int, lua: &'lua Lua) -> Result<Self> {
|
||||
let state = lua.state();
|
||||
if ffi::lua_type(state, idx) == ffi::LUA_TSTRING {
|
||||
let mut size = 0;
|
||||
let data = ffi::lua_tolstring(state, idx, &mut size);
|
||||
let bytes = slice::from_raw_parts(data as *const u8, size);
|
||||
return str::from_utf8(bytes).map(|s| s.to_owned()).map_err(|e| {
|
||||
Error::FromLuaConversionError {
|
||||
from: "string",
|
||||
to: "String",
|
||||
message: Some(e.to_string()),
|
||||
}
|
||||
});
|
||||
}
|
||||
// Fallback to default
|
||||
Self::from_lua(lua.stack_value(idx), lua)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for &str {
|
||||
@@ -325,6 +351,11 @@ impl<'lua> IntoLua<'lua> for &str {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(self)?))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
push_bytes_into_stack(self, lua)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for Cow<'_, str> {
|
||||
@@ -433,6 +464,21 @@ impl<'lua> IntoLua<'lua> for &BStr {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_bytes_into_stack<'lua, T>(this: T, lua: &'lua Lua) -> Result<()>
|
||||
where
|
||||
T: IntoLua<'lua> + AsRef<[u8]>,
|
||||
{
|
||||
let bytes = this.as_ref();
|
||||
if lua.unlikely_memory_error() && bytes.len() < (1 << 30) {
|
||||
// Fast path: push directly into the Lua stack.
|
||||
ffi::lua_pushlstring(lua.state(), bytes.as_ptr() as *const _, bytes.len());
|
||||
return Ok(());
|
||||
}
|
||||
// Fallback to default
|
||||
lua.push_value(T::into_lua(this, lua)?)
|
||||
}
|
||||
|
||||
macro_rules! lua_convert_int {
|
||||
($x:ty) => {
|
||||
impl<'lua> IntoLua<'lua> for $x {
|
||||
|
||||
+18
-25
@@ -126,34 +126,25 @@ impl<'lua> Function<'lua> {
|
||||
pub fn call<A: IntoLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> Result<R> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
|
||||
let mut args = args.into_lua_multi(lua)?;
|
||||
let nargs = args.len() as c_int;
|
||||
|
||||
let results = unsafe {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, nargs + 3)?;
|
||||
check_stack(state, 2)?;
|
||||
|
||||
// Push error handler
|
||||
MemoryState::relax_limit_with(state, || ffi::lua_pushcfunction(state, error_traceback));
|
||||
let stack_start = ffi::lua_gettop(state);
|
||||
// Push function and the arguments
|
||||
lua.push_ref(&self.0);
|
||||
for arg in args.drain_all() {
|
||||
lua.push_value(arg)?;
|
||||
}
|
||||
let nargs = args.push_into_stack_multi(lua)?;
|
||||
// Call the function
|
||||
let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start);
|
||||
if ret != ffi::LUA_OK {
|
||||
return Err(pop_error(state, ret));
|
||||
}
|
||||
// Get the results
|
||||
let nresults = ffi::lua_gettop(state) - stack_start;
|
||||
let mut results = args; // Reuse MultiValue container
|
||||
assert_stack(state, 2);
|
||||
for _ in 0..nresults {
|
||||
results.push_front(lua.pop_value());
|
||||
}
|
||||
ffi::lua_pop(state, 1);
|
||||
results
|
||||
};
|
||||
R::from_lua_multi(results, lua)
|
||||
R::from_stack_multi(nresults, lua)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a future that, when polled, calls `self`, passing `args` as function arguments,
|
||||
@@ -564,8 +555,9 @@ impl<'lua> Function<'lua> {
|
||||
R: IntoLuaMulti<'lua>,
|
||||
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
{
|
||||
WrappedFunction(Box::new(move |lua, args| {
|
||||
func(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
WrappedFunction(Box::new(move |lua, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 1, None, lua)?;
|
||||
func(lua, args)?.push_into_stack_multi(lua)
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -578,11 +570,12 @@ impl<'lua> Function<'lua> {
|
||||
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
{
|
||||
let func = RefCell::new(func);
|
||||
WrappedFunction(Box::new(move |lua, args| {
|
||||
WrappedFunction(Box::new(move |lua, nargs| unsafe {
|
||||
let mut func = func
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
func(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
let args = A::from_stack_args(nargs, 1, None, lua)?;
|
||||
func(lua, args)?.push_into_stack_multi(lua)
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -596,13 +589,13 @@ impl<'lua> Function<'lua> {
|
||||
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
|
||||
FR: Future<Output = Result<R>> + 'lua,
|
||||
{
|
||||
WrappedAsyncFunction(Box::new(move |lua, args| {
|
||||
let args = match A::from_lua_multi(args, lua) {
|
||||
WrappedAsyncFunction(Box::new(move |lua, args| unsafe {
|
||||
let args = match A::from_lua_args(args, 1, None, lua) {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::err(e)),
|
||||
};
|
||||
let fut = func(lua, args);
|
||||
Box::pin(async move { fut.await?.into_lua_multi(lua) })
|
||||
Box::pin(async move { fut.await?.push_into_stack_multi(lua) })
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -157,7 +157,8 @@ extern crate mlua_derive;
|
||||
// Unstable features
|
||||
#[cfg(feature = "unstable")]
|
||||
pub use crate::{
|
||||
function::OwnedFunction, string::OwnedString, table::OwnedTable, userdata::OwnedAnyUserData,
|
||||
function::OwnedFunction, string::OwnedString, table::OwnedTable, thread::OwnedThread,
|
||||
userdata::OwnedAnyUserData,
|
||||
};
|
||||
|
||||
/// Create a type that implements [`AsChunk`] and can capture Rust variables.
|
||||
|
||||
+185
-90
@@ -107,7 +107,7 @@ pub(crate) struct ExtraData {
|
||||
// Pool of `WrappedFailure` enums in the ref thread (as userdata)
|
||||
wrapped_failure_pool: Vec<c_int>,
|
||||
// Pool of `MultiValue` containers
|
||||
multivalue_pool: Vec<MultiValue<'static>>,
|
||||
multivalue_pool: Vec<Vec<Value<'static>>>,
|
||||
// Pool of `Thread`s (coroutines) for async execution
|
||||
#[cfg(feature = "async")]
|
||||
thread_pool: Vec<c_int>,
|
||||
@@ -282,6 +282,13 @@ impl Deref for Lua {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Lua {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Lua::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Lua {
|
||||
/// Creates a new Lua state and loads the **safe** subset of the standard libraries.
|
||||
///
|
||||
@@ -292,7 +299,6 @@ impl Lua {
|
||||
/// See [`StdLib`] documentation for a list of unsafe modules that cannot be loaded.
|
||||
///
|
||||
/// [`StdLib`]: crate::StdLib
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new() -> Lua {
|
||||
mlua_expect!(
|
||||
Self::new_with(StdLib::ALL_SAFE, LuaOptions::default()),
|
||||
@@ -718,50 +724,28 @@ impl Lua {
|
||||
// The returned value then pushed onto the stack.
|
||||
#[doc(hidden)]
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
pub unsafe fn entrypoint<'lua, A, R, F>(self, func: F) -> Result<c_int>
|
||||
pub unsafe fn entrypoint<'lua, A, R, F>(self, func: F) -> c_int
|
||||
where
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLua<'lua>,
|
||||
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
{
|
||||
let entrypoint_inner = |lua: &'lua Lua, func: F| {
|
||||
let state = lua.state();
|
||||
let nargs = ffi::lua_gettop(state);
|
||||
check_stack(state, 3)?;
|
||||
let (state, extra) = (self.state(), self.extra.get());
|
||||
// It must be safe to drop `self` as in the module mode we keep strong reference to `Lua` in the registry
|
||||
drop(self);
|
||||
|
||||
let mut args = MultiValue::new();
|
||||
args.reserve(nargs as usize);
|
||||
for _ in 0..nargs {
|
||||
args.push_front(lua.pop_value());
|
||||
}
|
||||
|
||||
// We create callback rather than call `func` directly to catch errors
|
||||
// with attached stacktrace.
|
||||
let callback = lua.create_callback(Box::new(move |lua, args| {
|
||||
func(lua, A::from_lua_multi_args(args, 1, None, lua)?)?.into_lua_multi(lua)
|
||||
}))?;
|
||||
callback.call(args)
|
||||
};
|
||||
|
||||
match entrypoint_inner(mem::transmute(&self), func) {
|
||||
Ok(res) => {
|
||||
self.push_value(res)?;
|
||||
Ok(1)
|
||||
}
|
||||
Err(err) => {
|
||||
self.push_value(Value::Error(err))?;
|
||||
let state = self.state();
|
||||
// Lua (self) must be dropped before triggering longjmp
|
||||
drop(self);
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
}
|
||||
callback_error_ext(state, extra, move |nargs| {
|
||||
let lua: &Lua = mem::transmute((*extra).inner.assume_init_ref());
|
||||
let args = A::from_stack_args(nargs, 1, None, lua)?;
|
||||
func(lua, args)?.push_into_stack(lua)?;
|
||||
Ok(1)
|
||||
})
|
||||
}
|
||||
|
||||
// A simple module entrypoint without arguments
|
||||
#[doc(hidden)]
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
pub unsafe fn entrypoint1<'lua, R, F>(self, func: F) -> Result<c_int>
|
||||
pub unsafe fn entrypoint1<'lua, R, F>(self, func: F) -> c_int
|
||||
where
|
||||
R: IntoLua<'lua>,
|
||||
F: Fn(&'lua Lua) -> Result<R> + MaybeSend + 'static,
|
||||
@@ -1537,8 +1521,9 @@ impl Lua {
|
||||
R: IntoLuaMulti<'lua>,
|
||||
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)
|
||||
self.create_callback(Box::new(move |lua, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 1, None, lua)?;
|
||||
func(lua, args)?.push_into_stack_multi(lua)
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1621,13 +1606,13 @@ impl Lua {
|
||||
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) {
|
||||
self.create_async_callback(Box::new(move |lua, args| unsafe {
|
||||
let args = match A::from_lua_args(args, 1, None, lua) {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::err(e)),
|
||||
};
|
||||
let fut = func(lua, args);
|
||||
Box::pin(async move { fut.await?.into_lua_multi(lua) })
|
||||
Box::pin(async move { fut.await?.push_into_stack_multi(lua) })
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1655,7 +1640,7 @@ impl Lua {
|
||||
self.push_ref(&func.0);
|
||||
ffi::lua_xmove(state, thread_state, 1);
|
||||
|
||||
Ok(Thread(self.pop_ref()))
|
||||
Ok(Thread::new(self.pop_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1683,7 +1668,7 @@ impl Lua {
|
||||
ffi::lua_replace(thread_state, ffi::LUA_GLOBALSINDEX);
|
||||
}
|
||||
|
||||
return Ok(Thread(LuaRef::new(self, index)));
|
||||
return Ok(Thread::new(LuaRef::new(self, index)));
|
||||
}
|
||||
};
|
||||
self.create_thread_inner(func)
|
||||
@@ -1816,6 +1801,27 @@ impl Lua {
|
||||
unsafe { self.make_userdata(UserDataCell::new(UserDataProxy::<T>(PhantomData))) }
|
||||
}
|
||||
|
||||
/// Sets the metatable for a Luau builtin vector type.
|
||||
#[cfg(any(all(feature = "luau", feature = "unstable"), doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "luau", feature = "unstable"))))]
|
||||
pub fn set_vector_metatable(&self, metatable: Option<Table>) {
|
||||
unsafe {
|
||||
let state = self.state();
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
ffi::lua_pushvector(state, 0., 0., 0.);
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
ffi::lua_pushvector(state, 0., 0., 0., 0.);
|
||||
match metatable {
|
||||
Some(metatable) => self.push_ref(&metatable.0),
|
||||
None => ffi::lua_pushnil(state),
|
||||
};
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a handle to the global environment.
|
||||
pub fn globals(&self) -> Table {
|
||||
let state = self.state();
|
||||
@@ -1838,7 +1844,7 @@ impl Lua {
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 1);
|
||||
ffi::lua_pushthread(state);
|
||||
Thread(self.pop_ref())
|
||||
Thread::new(self.pop_ref())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2433,7 +2439,7 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
ffi::LUA_TTHREAD => Value::Thread(Thread(self.pop_ref())),
|
||||
ffi::LUA_TTHREAD => Value::Thread(Thread::new(self.pop_ref())),
|
||||
|
||||
#[cfg(feature = "luajit")]
|
||||
ffi::LUA_TCDATA => {
|
||||
@@ -2446,6 +2452,104 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns value at given stack index without popping it.
|
||||
///
|
||||
/// Uses 2 stack spaces, does not call checkstack.
|
||||
pub(crate) unsafe fn stack_value(&self, idx: c_int) -> Value {
|
||||
let state = self.state();
|
||||
match ffi::lua_type(state, idx) {
|
||||
ffi::LUA_TNIL => Nil,
|
||||
|
||||
ffi::LUA_TBOOLEAN => Value::Boolean(ffi::lua_toboolean(state, idx) != 0),
|
||||
|
||||
ffi::LUA_TLIGHTUSERDATA => {
|
||||
Value::LightUserData(LightUserData(ffi::lua_touserdata(state, idx)))
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
ffi::LUA_TNUMBER => {
|
||||
if ffi::lua_isinteger(state, idx) != 0 {
|
||||
Value::Integer(ffi::lua_tointeger(state, idx))
|
||||
} else {
|
||||
Value::Number(ffi::lua_tonumber(state, idx))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
feature = "lua52",
|
||||
feature = "lua51",
|
||||
feature = "luajit",
|
||||
feature = "luau"
|
||||
))]
|
||||
ffi::LUA_TNUMBER => {
|
||||
let n = ffi::lua_tonumber(state, idx);
|
||||
match num_traits::cast(n) {
|
||||
Some(i) if (n - (i as Number)).abs() < Number::EPSILON => Value::Integer(i),
|
||||
_ => Value::Number(n),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::LUA_TVECTOR => {
|
||||
let v = ffi::lua_tovector(state, idx);
|
||||
mlua_debug_assert!(!v.is_null(), "vector is null");
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
return Value::Vector(Vector([*v, *v.add(1), *v.add(2)]));
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
return Value::Vector(Vector([*v, *v.add(1), *v.add(2), *v.add(3)]));
|
||||
}
|
||||
|
||||
ffi::LUA_TSTRING => {
|
||||
ffi::lua_xpush(state, self.ref_thread(), idx);
|
||||
Value::String(String(self.pop_ref_thread()))
|
||||
}
|
||||
|
||||
ffi::LUA_TTABLE => {
|
||||
ffi::lua_xpush(state, self.ref_thread(), idx);
|
||||
Value::Table(Table(self.pop_ref_thread()))
|
||||
}
|
||||
|
||||
ffi::LUA_TFUNCTION => {
|
||||
ffi::lua_xpush(state, self.ref_thread(), idx);
|
||||
Value::Function(Function(self.pop_ref_thread()))
|
||||
}
|
||||
|
||||
ffi::LUA_TUSERDATA => {
|
||||
let wrapped_failure_mt_ptr = (*self.extra.get()).wrapped_failure_mt_ptr;
|
||||
// We must prevent interaction with userdata types other than UserData OR a WrappedError.
|
||||
// WrappedPanics are automatically resumed.
|
||||
match get_gc_userdata::<WrappedFailure>(state, idx, wrapped_failure_mt_ptr).as_mut()
|
||||
{
|
||||
Some(WrappedFailure::Error(err)) => Value::Error(err.clone()),
|
||||
Some(WrappedFailure::Panic(panic)) => {
|
||||
if let Some(panic) = panic.take() {
|
||||
resume_unwind(panic);
|
||||
}
|
||||
// Previously resumed panic?
|
||||
Value::Nil
|
||||
}
|
||||
_ => {
|
||||
ffi::lua_xpush(state, self.ref_thread(), idx);
|
||||
Value::UserData(AnyUserData(self.pop_ref_thread()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ffi::LUA_TTHREAD => {
|
||||
ffi::lua_xpush(state, self.ref_thread(), idx);
|
||||
Value::Thread(Thread::new(self.pop_ref_thread()))
|
||||
}
|
||||
|
||||
#[cfg(feature = "luajit")]
|
||||
ffi::LUA_TCDATA => {
|
||||
// TODO: Fix this in a next major release
|
||||
panic!("cdata objects cannot be handled by mlua yet");
|
||||
}
|
||||
|
||||
_ => mlua_panic!("LUA_TNONE in pop_value"),
|
||||
}
|
||||
}
|
||||
|
||||
// Pushes a LuaRef value onto the stack, uses 1 stack space, does not call checkstack
|
||||
pub(crate) unsafe fn push_ref(&self, lref: &LuaRef) {
|
||||
assert!(
|
||||
@@ -2532,7 +2636,7 @@ impl Lua {
|
||||
let mut has_name = false;
|
||||
for (k, f) in registry.meta_fields {
|
||||
has_name = has_name || k == MetaMethod::Type;
|
||||
self.push_value(f(self, MultiValue::new())?.pop_front().unwrap())?;
|
||||
mlua_assert!(f(self, 0)? == 1, "field function must return one value");
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
// Set `__name/__type` if not provided
|
||||
@@ -2557,7 +2661,7 @@ impl Lua {
|
||||
push_table(state, 0, fields_nrec, true)?;
|
||||
}
|
||||
for (k, f) in registry.fields {
|
||||
self.push_value(f(self, MultiValue::new())?.pop_front().unwrap())?;
|
||||
mlua_assert!(f(self, 0)? == 1, "field function must return one value");
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
rawset_field(state, metatable_index, "__index")?;
|
||||
@@ -2686,17 +2790,28 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns `TypeId` for the LuaRef, checking that it's a registered
|
||||
// and not destructed UserData.
|
||||
// Returns `TypeId` for the `lref` userdata, checking that it's registered and not destructed.
|
||||
//
|
||||
// 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 {
|
||||
pub(crate) unsafe fn get_userdata_ref_type_id(&self, lref: &LuaRef) -> Result<Option<TypeId>> {
|
||||
self.get_userdata_type_id_inner(self.ref_thread(), lref.index)
|
||||
}
|
||||
|
||||
// Same as `get_userdata_ref_type_id` but assumes the userdata is already on the stack.
|
||||
pub(crate) unsafe fn get_userdata_type_id(&self, idx: c_int) -> Result<Option<TypeId>> {
|
||||
self.get_userdata_type_id_inner(self.state(), idx)
|
||||
}
|
||||
|
||||
unsafe fn get_userdata_type_id_inner(
|
||||
&self,
|
||||
state: *mut ffi::lua_State,
|
||||
idx: c_int,
|
||||
) -> Result<Option<TypeId>> {
|
||||
if ffi::lua_getmetatable(state, idx) == 0 {
|
||||
return Err(Error::UserDataTypeMismatch);
|
||||
}
|
||||
let mt_ptr = ffi::lua_topointer(ref_thread, -1);
|
||||
ffi::lua_pop(ref_thread, 1);
|
||||
let mt_ptr = ffi::lua_topointer(state, -1);
|
||||
ffi::lua_pop(state, 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;
|
||||
@@ -2719,7 +2834,7 @@ 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)?;
|
||||
let type_id = self.get_userdata_type_id_inner(self.ref_thread(), lref.index)?;
|
||||
self.push_ref(lref);
|
||||
Ok(type_id)
|
||||
}
|
||||
@@ -2754,24 +2869,9 @@ impl Lua {
|
||||
|
||||
let lua: &Lua = mem::transmute((*extra).inner.assume_init_ref());
|
||||
let _guard = StateGuard::new(&lua.0, state);
|
||||
|
||||
let mut args = MultiValue::new_or_pooled(lua);
|
||||
args.reserve(nargs as usize);
|
||||
for _ in 0..nargs {
|
||||
args.push_front(lua.pop_value());
|
||||
}
|
||||
|
||||
let func = &*(*upvalue).data;
|
||||
let mut results = func(lua, args)?;
|
||||
let nresults = results.len() as c_int;
|
||||
|
||||
check_stack(state, nresults)?;
|
||||
for r in results.drain_all() {
|
||||
lua.push_value(r)?;
|
||||
}
|
||||
MultiValue::return_to_pool(results, lua);
|
||||
|
||||
Ok(nresults)
|
||||
func(lua, nargs)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2824,12 +2924,7 @@ impl Lua {
|
||||
let lua: &Lua = mem::transmute((*extra).inner.assume_init_ref());
|
||||
let _guard = StateGuard::new(&lua.0, state);
|
||||
|
||||
let mut args = MultiValue::new_or_pooled(lua);
|
||||
args.reserve(nargs as usize);
|
||||
for _ in 0..nargs {
|
||||
args.push_front(lua.pop_value());
|
||||
}
|
||||
|
||||
let args = MultiValue::from_stack_multi(nargs, lua)?;
|
||||
let func = &*(*upvalue).data;
|
||||
let fut = func(lua, args);
|
||||
let extra = Arc::clone(&(*upvalue).extra);
|
||||
@@ -2851,6 +2946,7 @@ impl Lua {
|
||||
let upvalue = get_userdata::<AsyncPollUpvalue>(state, ffi::lua_upvalueindex(1));
|
||||
let extra = (*upvalue).extra.get();
|
||||
callback_error_ext(state, extra, |_| {
|
||||
// Lua ensures that `LUA_MINSTACK` stack spaces are available (after pushing arguments)
|
||||
let lua: &Lua = mem::transmute((*extra).inner.assume_init_ref());
|
||||
let _guard = StateGuard::new(&lua.0, state);
|
||||
|
||||
@@ -2858,21 +2954,20 @@ impl Lua {
|
||||
let mut ctx = Context::from_waker(lua.waker());
|
||||
match fut.as_mut().poll(&mut ctx) {
|
||||
Poll::Pending => Ok(0),
|
||||
Poll::Ready(results) => {
|
||||
let mut results = results?;
|
||||
let nresults = results.len();
|
||||
lua.push_value(Value::Integer(nresults as _))?;
|
||||
Poll::Ready(nresults) => {
|
||||
let nresults = nresults?;
|
||||
match nresults {
|
||||
0 => Ok(1),
|
||||
1 | 2 => {
|
||||
// Fast path for 1 or 2 results without creating a table
|
||||
for r in results.drain_all() {
|
||||
lua.push_value(r)?;
|
||||
0..=2 => {
|
||||
// Fast path for up to 2 results without creating a table
|
||||
ffi::lua_pushinteger(state, nresults as _);
|
||||
if nresults > 0 {
|
||||
ffi::lua_insert(state, -nresults - 1);
|
||||
}
|
||||
MultiValue::return_to_pool(results, lua);
|
||||
Ok(nresults as c_int + 1)
|
||||
Ok(nresults + 1)
|
||||
}
|
||||
_ => {
|
||||
let results = MultiValue::from_stack_multi(nresults, lua)?;
|
||||
ffi::lua_pushinteger(state, nresults as _);
|
||||
lua.push_value(Value::Table(lua.create_sequence_from(results)?))?;
|
||||
Ok(2)
|
||||
}
|
||||
@@ -3110,13 +3205,13 @@ impl LuaInner {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn new_multivalue_from_pool(&self) -> MultiValue {
|
||||
pub(crate) fn pop_multivalue_from_pool(&self) -> Option<Vec<Value>> {
|
||||
let extra = unsafe { &mut *self.extra.get() };
|
||||
extra.multivalue_pool.pop().unwrap_or_default()
|
||||
extra.multivalue_pool.pop()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn return_multivalue_to_pool(&self, mut multivalue: MultiValue) {
|
||||
pub(crate) fn push_multivalue_to_pool(&self, mut multivalue: Vec<Value>) {
|
||||
let extra = unsafe { &mut *self.extra.get() };
|
||||
if extra.multivalue_pool.len() < MULTIVALUE_POOL_SIZE {
|
||||
multivalue.clear();
|
||||
|
||||
+122
-27
@@ -1,9 +1,11 @@
|
||||
use std::iter::FromIterator;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::c_int;
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::lua::Lua;
|
||||
use crate::util::check_stack;
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil};
|
||||
|
||||
/// Result is convertible to `MultiValue` following the common Lua idiom of returning the result
|
||||
@@ -11,7 +13,7 @@ use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil
|
||||
impl<'lua, T: IntoLua<'lua>, E: IntoLua<'lua>> IntoLuaMulti<'lua> for StdResult<T, E> {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut result = MultiValue::new_or_pooled(lua);
|
||||
let mut result = MultiValue::with_lua_and_capacity(lua, 2);
|
||||
match self {
|
||||
Ok(v) => result.push_front(v.into_lua(lua)?),
|
||||
Err(e) => {
|
||||
@@ -23,33 +25,71 @@ impl<'lua, T: IntoLua<'lua>, E: IntoLua<'lua>> IntoLuaMulti<'lua> for StdResult<
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, E: IntoLua<'lua>> IntoLuaMulti<'lua> for StdResult<(), E> {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
match self {
|
||||
Ok(_) => return Ok(MultiValue::new()),
|
||||
Err(e) => {
|
||||
let mut result = MultiValue::with_lua_and_capacity(lua, 2);
|
||||
result.push_front(e.into_lua(lua)?);
|
||||
result.push_front(Nil);
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: IntoLua<'lua>> IntoLuaMulti<'lua> for T {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut v = MultiValue::new_or_pooled(lua);
|
||||
let mut v = MultiValue::with_lua_and_capacity(lua, 1);
|
||||
v.push_front(self.into_lua(lua)?);
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack_multi(self, lua: &'lua Lua) -> Result<c_int> {
|
||||
self.push_into_stack(lua)?;
|
||||
Ok(1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for T {
|
||||
#[inline]
|
||||
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let res = T::from_lua(values.pop_front().unwrap_or(Nil), lua);
|
||||
MultiValue::return_to_pool(values, lua);
|
||||
res
|
||||
T::from_lua(values.pop_front().unwrap_or(Nil), lua)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_lua_multi_args(
|
||||
mut values: MultiValue<'lua>,
|
||||
fn from_lua_args(
|
||||
mut args: MultiValue<'lua>,
|
||||
i: usize,
|
||||
to: Option<&str>,
|
||||
lua: &'lua Lua,
|
||||
) -> Result<Self> {
|
||||
let res = T::from_lua_arg(values.pop_front().unwrap_or(Nil), i, to, lua);
|
||||
MultiValue::return_to_pool(values, lua);
|
||||
res
|
||||
T::from_lua_arg(args.pop_front().unwrap_or(Nil), i, to, lua)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack_multi(nvals: c_int, lua: &'lua Lua) -> Result<Self> {
|
||||
if nvals == 0 {
|
||||
return T::from_lua(Nil, lua);
|
||||
}
|
||||
T::from_stack(-nvals, lua)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack_args(
|
||||
nargs: c_int,
|
||||
i: usize,
|
||||
to: Option<&str>,
|
||||
lua: &'lua Lua,
|
||||
) -> Result<Self> {
|
||||
if nargs == 0 {
|
||||
return T::from_lua_arg(Nil, i, to, lua);
|
||||
}
|
||||
T::from_stack_arg(-nargs, i, to, lua)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +181,7 @@ impl<T> DerefMut for Variadic<T> {
|
||||
impl<'lua, T: IntoLua<'lua>> IntoLuaMulti<'lua> for Variadic<T> {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut values = MultiValue::new_or_pooled(lua);
|
||||
let mut values = MultiValue::with_lua_and_capacity(lua, self.0.len());
|
||||
values.refill(self.0.into_iter().map(|e| e.into_lua(lua)))?;
|
||||
Ok(values)
|
||||
}
|
||||
@@ -150,13 +190,11 @@ impl<'lua, T: IntoLua<'lua>> IntoLuaMulti<'lua> for Variadic<T> {
|
||||
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for Variadic<T> {
|
||||
#[inline]
|
||||
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let res = values
|
||||
values
|
||||
.drain_all()
|
||||
.map(|e| T::from_lua(e, lua))
|
||||
.collect::<Result<Vec<T>>>()
|
||||
.map(Variadic);
|
||||
MultiValue::return_to_pool(values, lua);
|
||||
res
|
||||
.map(Variadic)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,14 +203,26 @@ macro_rules! impl_tuple {
|
||||
impl<'lua> IntoLuaMulti<'lua> for () {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
Ok(MultiValue::new_or_pooled(lua))
|
||||
Ok(MultiValue::with_lua_and_capacity(lua, 0))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack_multi(self, _lua: &'lua Lua) -> Result<c_int> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLuaMulti<'lua> for () {
|
||||
#[inline]
|
||||
fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
MultiValue::return_to_pool(values, lua);
|
||||
fn from_lua_multi(_values: MultiValue<'lua>, _lua: &'lua Lua) -> Result<Self> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack_multi(nvals: c_int, lua: &'lua Lua) -> Result<Self> {
|
||||
if nvals > 0 {
|
||||
ffi::lua_pop(lua.state(), nvals);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -183,8 +233,7 @@ macro_rules! impl_tuple {
|
||||
where $($name: IntoLua<'lua>,)*
|
||||
$last: IntoLuaMulti<'lua>
|
||||
{
|
||||
#[allow(unused_mut)]
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(unused_mut, non_snake_case)]
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let ($($name,)* $last,) = self;
|
||||
@@ -193,14 +242,30 @@ macro_rules! impl_tuple {
|
||||
push_reverse!(results, $($name.into_lua(lua)?,)*);
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[inline]
|
||||
unsafe fn push_into_stack_multi(self, lua: &'lua Lua) -> Result<c_int> {
|
||||
let ($($name,)* $last,) = self;
|
||||
let mut nresults = 0;
|
||||
$(
|
||||
_ = $name;
|
||||
nresults += 1;
|
||||
)*
|
||||
check_stack(lua.state(), nresults + 1)?;
|
||||
$(
|
||||
$name.push_into_stack(lua)?;
|
||||
)*
|
||||
nresults += $last.push_into_stack_multi(lua)?;
|
||||
Ok(nresults)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, $($name,)* $last> FromLuaMulti<'lua> for ($($name,)* $last,)
|
||||
where $($name: FromLua<'lua>,)*
|
||||
$last: FromLuaMulti<'lua>
|
||||
{
|
||||
#[allow(unused_mut)]
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(unused_mut, non_snake_case)]
|
||||
#[inline]
|
||||
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
$(let $name = FromLua::from_lua(values.pop_front().unwrap_or(Nil), lua)?;)*
|
||||
@@ -208,15 +273,45 @@ macro_rules! impl_tuple {
|
||||
Ok(($($name,)* $last,))
|
||||
}
|
||||
|
||||
#[allow(unused_mut)]
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(unused_mut, non_snake_case)]
|
||||
#[inline]
|
||||
fn from_lua_multi_args(mut values: MultiValue<'lua>, mut i: usize, to: Option<&str>, lua: &'lua Lua) -> Result<Self> {
|
||||
fn from_lua_args(mut args: MultiValue<'lua>, mut i: usize, to: Option<&str>, lua: &'lua Lua) -> Result<Self> {
|
||||
$(
|
||||
let $name = FromLua::from_lua_arg(values.pop_front().unwrap_or(Nil), i, to, lua)?;
|
||||
let $name = FromLua::from_lua_arg(args.pop_front().unwrap_or(Nil), i, to, lua)?;
|
||||
i += 1;
|
||||
)*
|
||||
let $last = FromLuaMulti::from_lua_multi_args(values, i, to, lua)?;
|
||||
let $last = FromLuaMulti::from_lua_args(args, i, to, lua)?;
|
||||
Ok(($($name,)* $last,))
|
||||
}
|
||||
|
||||
#[allow(unused_mut, non_snake_case)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_multi(mut nvals: c_int, lua: &'lua Lua) -> Result<Self> {
|
||||
$(
|
||||
let $name = if nvals > 0 {
|
||||
nvals -= 1;
|
||||
FromLua::from_stack(-(nvals + 1), lua)
|
||||
} else {
|
||||
FromLua::from_lua(Nil, lua)
|
||||
}?;
|
||||
)*
|
||||
let $last = FromLuaMulti::from_stack_multi(nvals, lua)?;
|
||||
Ok(($($name,)* $last,))
|
||||
}
|
||||
|
||||
#[allow(unused_mut, non_snake_case)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_args(mut nargs: c_int, mut i: usize, to: Option<&str>, lua: &'lua Lua) -> Result<Self> {
|
||||
$(
|
||||
let $name = if nargs > 0 {
|
||||
nargs -= 1;
|
||||
FromLua::from_stack_arg(-(nargs + 1), i, to, lua)
|
||||
} else {
|
||||
FromLua::from_lua_arg(Nil, i, to, lua)
|
||||
}?;
|
||||
i += 1;
|
||||
)*
|
||||
let $last = FromLuaMulti::from_stack_args(nargs, i, to, lua)?;
|
||||
Ok(($($name,)* $last,))
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -40,5 +40,5 @@ pub use crate::{
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{
|
||||
OwnedAnyUserData as LuaOwnedAnyUserData, OwnedFunction as LuaOwnedFunction,
|
||||
OwnedString as LuaOwnedString, OwnedTable as LuaOwnedTable,
|
||||
OwnedString as LuaOwnedString, OwnedTable as LuaOwnedTable, OwnedThread as LuaOwnedThread,
|
||||
};
|
||||
|
||||
+250
-181
@@ -2,6 +2,7 @@ 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;
|
||||
@@ -15,10 +16,10 @@ use crate::userdata::{
|
||||
};
|
||||
use crate::userdata_impl::UserDataRegistry;
|
||||
use crate::util::{
|
||||
self, assert_stack, check_stack, get_userdata, init_userdata_metatable, push_table,
|
||||
rawset_field, take_userdata, StackGuard,
|
||||
self, assert_stack, check_stack, init_userdata_metatable, push_string, push_table,
|
||||
rawset_field, short_type_name, take_userdata, StackGuard,
|
||||
};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Value};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
use crate::userdata::USER_VALUE_MAXSLOT;
|
||||
@@ -75,8 +76,9 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
// to make sure callbacks can't capture handles with lifetime outside the scope, inside the
|
||||
// scope, and owned inside the callback itself.
|
||||
unsafe {
|
||||
self.create_callback(Box::new(move |lua, args| {
|
||||
func(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
self.create_callback(Box::new(move |lua, nargs| {
|
||||
let args = A::from_stack_args(nargs, 1, None, lua)?;
|
||||
func(lua, args)?.push_into_stack_multi(lua)
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -186,6 +188,23 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a Lua userdata object from a custom Rust type.
|
||||
///
|
||||
/// This is a version of [`Lua::create_any_userdata`] that creates a userdata which expires on
|
||||
/// scope drop and does not require that the userdata type be Send (but still requires that the
|
||||
/// UserData be 'static). See [`Lua::scope`] for more details.
|
||||
#[inline]
|
||||
pub fn create_any_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
unsafe {
|
||||
let ud = self.lua.make_any_userdata(UserDataCell::new(data))?;
|
||||
self.seal_userdata::<T>(&ud)?;
|
||||
Ok(ud)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a Lua userdata object from a reference to custom Rust type.
|
||||
///
|
||||
/// This is a version of [`Lua::create_any_userdata`] that creates a userdata which expires on
|
||||
@@ -293,9 +312,10 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
// 'lua. This is safe though, because `UserData::add_methods` does not get to pick the 'lua
|
||||
// lifetime, so none of the static methods UserData types can add can possibly capture
|
||||
// parameters.
|
||||
fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
|
||||
unsafe fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
|
||||
scope: &Scope<'lua, 'scope>,
|
||||
ud_ptr: *const UserDataCell<T>,
|
||||
name: &str,
|
||||
method: NonStaticMethod<'callback, T>,
|
||||
) -> Result<Function<'lua>> {
|
||||
// On methods that actually receive the userdata, we fake a type check on the passed in
|
||||
@@ -305,61 +325,56 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
// with a type mismatch, but here without this check would proceed as though you had
|
||||
// called the method on the original value (since we otherwise completely ignore the
|
||||
// first argument).
|
||||
let check_ud_type = move |lua: &Lua, value| -> Result<&UserDataCell<T>> {
|
||||
if let Some(Value::UserData(ud)) = value {
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 2)?;
|
||||
lua.push_userdata_ref(&ud.0)?;
|
||||
if get_userdata(state, -1) as *const _ == ud_ptr {
|
||||
return Ok(&*ud_ptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
let func_name = format!("{}.{name}", short_type_name::<T>());
|
||||
let check_self_type = move |lua: &Lua, nargs: c_int| -> Result<&UserDataCell<T>> {
|
||||
let state = lua.state();
|
||||
if nargs > 0 && ffi::lua_touserdata(state, -nargs) as *const _ == ud_ptr {
|
||||
return Ok(&*ud_ptr);
|
||||
}
|
||||
Err(Error::bad_self_argument(
|
||||
&func_name,
|
||||
Error::UserDataTypeMismatch,
|
||||
))
|
||||
};
|
||||
|
||||
match method {
|
||||
NonStaticMethod::Method(method) => {
|
||||
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
|
||||
let data = check_ud_type(lua, args.pop_front())?;
|
||||
let f = Box::new(move |lua, nargs| {
|
||||
let data = check_self_type(lua, nargs)?;
|
||||
let data = data.try_borrow()?;
|
||||
method(lua, &*data, args)
|
||||
method(lua, &*data, nargs - 1)
|
||||
});
|
||||
unsafe { scope.create_callback(f) }
|
||||
scope.create_callback(f)
|
||||
}
|
||||
NonStaticMethod::MethodMut(method) => {
|
||||
let method = RefCell::new(method);
|
||||
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
|
||||
let data = check_ud_type(lua, args.pop_front())?;
|
||||
let f = Box::new(move |lua, nargs| {
|
||||
let data = check_self_type(lua, nargs)?;
|
||||
let mut method = method
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
let mut data = data.try_borrow_mut()?;
|
||||
(*method)(lua, &mut *data, args)
|
||||
(*method)(lua, &mut *data, nargs - 1)
|
||||
});
|
||||
unsafe { scope.create_callback(f) }
|
||||
scope.create_callback(f)
|
||||
}
|
||||
NonStaticMethod::Function(function) => unsafe { scope.create_callback(function) },
|
||||
NonStaticMethod::Function(function) => scope.create_callback(function),
|
||||
NonStaticMethod::FunctionMut(function) => {
|
||||
let function = RefCell::new(function);
|
||||
let f = Box::new(move |lua, args| {
|
||||
(*function
|
||||
let f = Box::new(move |lua, nargs| {
|
||||
let mut func = function
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?)(
|
||||
lua, args
|
||||
)
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
func(lua, nargs)
|
||||
});
|
||||
unsafe { scope.create_callback(f) }
|
||||
scope.create_callback(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut ud_fields = NonStaticUserDataFields::default();
|
||||
let mut ud_methods = NonStaticUserDataMethods::default();
|
||||
T::add_fields(&mut ud_fields);
|
||||
T::add_methods(&mut ud_methods);
|
||||
let mut registry = NonStaticUserDataRegistry::new();
|
||||
T::add_fields(&mut registry);
|
||||
T::add_methods(&mut registry);
|
||||
|
||||
let lua = self.lua;
|
||||
let state = lua.state();
|
||||
@@ -387,48 +402,82 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
};
|
||||
|
||||
// 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;
|
||||
let meta_methods_nrec = registry.meta_methods.len() + registry.meta_fields.len() + 1;
|
||||
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)?))?;
|
||||
for (k, m) in registry.meta_methods {
|
||||
lua.push_value(Value::Function(wrap_method(self, ud_ptr, &k, m)?))?;
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
for (k, f) in ud_fields.meta_fields {
|
||||
lua.push_value(f(lua, MultiValue::new())?.pop_front().unwrap())?;
|
||||
let mut has_name = false;
|
||||
for (k, f) in registry.meta_fields {
|
||||
has_name = has_name || k == MetaMethod::Type;
|
||||
mlua_assert!(f(lua, 0)? == 1, "field function must return one value");
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
// Set `__name/__type` if not provided
|
||||
if !has_name {
|
||||
let type_name = short_type_name::<T>();
|
||||
push_string(state, type_name.as_bytes(), !lua.unlikely_memory_error())?;
|
||||
rawset_field(state, -2, MetaMethod::Type.name())?;
|
||||
}
|
||||
let metatable_index = ffi::lua_absindex(state, -1);
|
||||
|
||||
let fields_nrec = registry.fields.len();
|
||||
if fields_nrec > 0 {
|
||||
// If __index is a table then update it inplace
|
||||
let index_type = ffi::lua_getfield(state, metatable_index, cstr!("__index"));
|
||||
match index_type {
|
||||
ffi::LUA_TNIL | ffi::LUA_TTABLE => {
|
||||
if index_type == ffi::LUA_TNIL {
|
||||
// Create a new table
|
||||
ffi::lua_pop(state, 1);
|
||||
push_table(state, 0, fields_nrec, true)?;
|
||||
}
|
||||
for (k, f) in registry.fields {
|
||||
let NonStaticMethod::Function(f) = f else { unreachable!() };
|
||||
mlua_assert!(f(lua, 0)? == 1, "field function must return one value");
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
rawset_field(state, metatable_index, "__index")?;
|
||||
}
|
||||
_ => {
|
||||
// Propagate fields to the field getters
|
||||
for (k, f) in registry.fields {
|
||||
registry.field_getters.push((k, f))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut field_getters_index = None;
|
||||
let field_getters_nrec = ud_fields.field_getters.len();
|
||||
let field_getters_nrec = registry.field_getters.len();
|
||||
if field_getters_nrec > 0 {
|
||||
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)?))?;
|
||||
for (k, m) in registry.field_getters {
|
||||
lua.push_value(Value::Function(wrap_method(self, ud_ptr, &k, m)?))?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
field_getters_index = Some(ffi::lua_absindex(state, -1));
|
||||
}
|
||||
|
||||
let mut field_setters_index = None;
|
||||
let field_setters_nrec = ud_fields.field_setters.len();
|
||||
let field_setters_nrec = registry.field_setters.len();
|
||||
if field_setters_nrec > 0 {
|
||||
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)?))?;
|
||||
for (k, m) in registry.field_setters {
|
||||
lua.push_value(Value::Function(wrap_method(self, ud_ptr, &k, m)?))?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
field_setters_index = Some(ffi::lua_absindex(state, -1));
|
||||
}
|
||||
|
||||
let mut methods_index = None;
|
||||
let methods_nrec = ud_methods.methods.len();
|
||||
let methods_nrec = registry.methods.len();
|
||||
if methods_nrec > 0 {
|
||||
// Create table used for methods lookup
|
||||
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)?))?;
|
||||
for (k, m) in registry.methods {
|
||||
lua.push_value(Value::Function(wrap_method(self, ud_ptr, &k, m)?))?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
methods_index = Some(ffi::lua_absindex(state, -1));
|
||||
@@ -572,35 +621,144 @@ impl<'lua, 'scope> Drop for Scope<'lua, 'scope> {
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
enum NonStaticMethod<'lua, T> {
|
||||
Method(Box<dyn Fn(&'lua Lua, &T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
|
||||
MethodMut(Box<dyn FnMut(&'lua Lua, &mut T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
|
||||
Function(Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
|
||||
FunctionMut(Box<dyn FnMut(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
|
||||
Method(Box<dyn Fn(&'lua Lua, &T, c_int) -> Result<c_int>>),
|
||||
MethodMut(Box<dyn FnMut(&'lua Lua, &mut T, c_int) -> Result<c_int>>),
|
||||
Function(Box<dyn Fn(&'lua Lua, c_int) -> Result<c_int>>),
|
||||
FunctionMut(Box<dyn FnMut(&'lua Lua, c_int) -> Result<c_int>>),
|
||||
}
|
||||
|
||||
struct NonStaticUserDataMethods<'lua, T: UserData> {
|
||||
struct NonStaticUserDataRegistry<'lua, T> {
|
||||
// Fields
|
||||
fields: Vec<(String, NonStaticMethod<'lua, T>)>,
|
||||
field_getters: Vec<(String, NonStaticMethod<'lua, T>)>,
|
||||
field_setters: Vec<(String, NonStaticMethod<'lua, T>)>,
|
||||
meta_fields: Vec<(String, Callback<'lua, 'static>)>,
|
||||
|
||||
// Methods
|
||||
methods: Vec<(String, NonStaticMethod<'lua, T>)>,
|
||||
meta_methods: Vec<(String, NonStaticMethod<'lua, T>)>,
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData> Default for NonStaticUserDataMethods<'lua, T> {
|
||||
fn default() -> NonStaticUserDataMethods<'lua, T> {
|
||||
NonStaticUserDataMethods {
|
||||
impl<'lua, T> NonStaticUserDataRegistry<'lua, T> {
|
||||
const fn new() -> NonStaticUserDataRegistry<'lua, T> {
|
||||
NonStaticUserDataRegistry {
|
||||
fields: Vec::new(),
|
||||
field_getters: Vec::new(),
|
||||
field_setters: Vec::new(),
|
||||
meta_fields: Vec::new(),
|
||||
methods: Vec::new(),
|
||||
meta_methods: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'lua, T> {
|
||||
impl<'lua, T> UserDataFields<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
|
||||
fn add_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
self.fields.push((
|
||||
name,
|
||||
NonStaticMethod::Function(Box::new(move |lua, _| unsafe {
|
||||
value.clone().push_into_stack_multi(lua)
|
||||
})),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: Fn(&'lua Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let method = NonStaticMethod::Method(Box::new(move |lua, ud, _| unsafe {
|
||||
method(lua, ud)?.push_into_stack_multi(lua)
|
||||
}));
|
||||
self.field_getters.push((name.as_ref().into(), method));
|
||||
}
|
||||
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl AsRef<str>, mut method: M)
|
||||
where
|
||||
M: FnMut(&'lua Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua<'lua>,
|
||||
{
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, nargs| unsafe {
|
||||
let val = A::from_stack_args(nargs, 2, Some(&func_name), lua)?;
|
||||
method(lua, ud, val)?.push_into_stack_multi(lua)
|
||||
}));
|
||||
self.field_setters.push((name.as_ref().into(), method));
|
||||
}
|
||||
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let func = NonStaticMethod::Function(Box::new(move |lua, nargs| unsafe {
|
||||
let ud = AnyUserData::from_stack_args(nargs, 1, Some(&func_name), lua)?;
|
||||
function(lua, ud)?.push_into_stack_multi(lua)
|
||||
}));
|
||||
self.field_getters.push((name.as_ref().into(), func));
|
||||
}
|
||||
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl AsRef<str>, mut function: F)
|
||||
where
|
||||
F: FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua<'lua>,
|
||||
{
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let func = NonStaticMethod::FunctionMut(Box::new(move |lua, nargs| unsafe {
|
||||
let (ud, val) = <_>::from_stack_args(nargs, 1, Some(&func_name), lua)?;
|
||||
function(lua, ud, val)?.push_into_stack_multi(lua)
|
||||
}));
|
||||
self.field_setters.push((name.as_ref().into(), func));
|
||||
}
|
||||
|
||||
fn add_meta_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| unsafe {
|
||||
UserDataRegistry::<()>::check_meta_field(lua, &name2, value.clone())?
|
||||
.push_into_stack_multi(lua)
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl AsRef<str>, f: F)
|
||||
where
|
||||
F: Fn(&'lua Lua) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| unsafe {
|
||||
UserDataRegistry::<()>::check_meta_field(lua, &name2, f(lua)?)?
|
||||
.push_into_stack_multi(lua)
|
||||
}),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'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,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let method = NonStaticMethod::Method(Box::new(move |lua, ud, args| {
|
||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let method = NonStaticMethod::Method(Box::new(move |lua, ud, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 2, Some(&func_name), lua)?;
|
||||
method(lua, ud, args)?.push_into_stack_multi(lua)
|
||||
}));
|
||||
self.methods.push((name.as_ref().into(), method));
|
||||
}
|
||||
@@ -611,8 +769,10 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
|
||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 2, Some(&func_name), lua)?;
|
||||
method(lua, ud, args)?.push_into_stack_multi(lua)
|
||||
}));
|
||||
self.methods.push((name.as_ref().into(), method));
|
||||
}
|
||||
@@ -653,8 +813,10 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let func = NonStaticMethod::Function(Box::new(move |lua, args| {
|
||||
function(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let func = NonStaticMethod::Function(Box::new(move |lua, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 1, Some(&func_name), lua)?;
|
||||
function(lua, args)?.push_into_stack_multi(lua)
|
||||
}));
|
||||
self.methods.push((name.as_ref().into(), func));
|
||||
}
|
||||
@@ -665,8 +827,10 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let func = NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
|
||||
function(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let func = NonStaticMethod::FunctionMut(Box::new(move |lua, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 1, Some(&func_name), lua)?;
|
||||
function(lua, args)?.push_into_stack_multi(lua)
|
||||
}));
|
||||
self.methods.push((name.as_ref().into(), func));
|
||||
}
|
||||
@@ -690,8 +854,10 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let method = NonStaticMethod::Method(Box::new(move |lua, ud, args| {
|
||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let method = NonStaticMethod::Method(Box::new(move |lua, ud, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 2, Some(&func_name), lua)?;
|
||||
method(lua, ud, args)?.push_into_stack_multi(lua)
|
||||
}));
|
||||
self.meta_methods.push((name.as_ref().into(), method));
|
||||
}
|
||||
@@ -702,8 +868,10 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
|
||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 2, Some(&func_name), lua)?;
|
||||
method(lua, ud, args)?.push_into_stack_multi(lua)
|
||||
}));
|
||||
self.meta_methods.push((name.as_ref().into(), method));
|
||||
}
|
||||
@@ -744,8 +912,10 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let func = NonStaticMethod::Function(Box::new(move |lua, args| {
|
||||
function(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let func = NonStaticMethod::Function(Box::new(move |lua, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 1, Some(&func_name), lua)?;
|
||||
function(lua, args)?.push_into_stack_multi(lua)
|
||||
}));
|
||||
self.meta_methods.push((name.as_ref().into(), func));
|
||||
}
|
||||
@@ -756,8 +926,10 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let func = NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
|
||||
function(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let func = NonStaticMethod::FunctionMut(Box::new(move |lua, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 1, Some(&func_name), lua)?;
|
||||
function(lua, args)?.push_into_stack_multi(lua)
|
||||
}));
|
||||
self.meta_methods.push((name.as_ref().into(), func));
|
||||
}
|
||||
@@ -775,106 +947,3 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||
panic!("asynchronous meta functions are not supported for non-static userdata")
|
||||
}
|
||||
}
|
||||
|
||||
struct NonStaticUserDataFields<'lua, T: UserData> {
|
||||
fields: Vec<(String, Callback<'lua, 'static>)>,
|
||||
field_getters: Vec<(String, NonStaticMethod<'lua, T>)>,
|
||||
field_setters: Vec<(String, NonStaticMethod<'lua, T>)>,
|
||||
meta_fields: Vec<(String, Callback<'lua, 'static>)>,
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData> Default for NonStaticUserDataFields<'lua, T> {
|
||||
fn default() -> NonStaticUserDataFields<'lua, T> {
|
||||
NonStaticUserDataFields {
|
||||
fields: Vec::new(),
|
||||
field_getters: Vec::new(),
|
||||
field_setters: Vec::new(),
|
||||
meta_fields: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData> UserDataFields<'lua, T> for NonStaticUserDataFields<'lua, T> {
|
||||
fn add_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
self.fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| value.clone().into_lua_multi(lua)),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: Fn(&'lua Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let method = NonStaticMethod::Method(Box::new(move |lua, ud, _| {
|
||||
method(lua, ud)?.into_lua_multi(lua)
|
||||
}));
|
||||
self.field_getters.push((name.as_ref().into(), method));
|
||||
}
|
||||
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl AsRef<str>, mut method: M)
|
||||
where
|
||||
M: FnMut(&'lua Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua<'lua>,
|
||||
{
|
||||
let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
|
||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
}));
|
||||
self.field_setters.push((name.as_ref().into(), method));
|
||||
}
|
||||
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let func = NonStaticMethod::Function(Box::new(move |lua, args| {
|
||||
function(lua, AnyUserData::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
}));
|
||||
self.field_getters.push((name.as_ref().into(), func));
|
||||
}
|
||||
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl AsRef<str>, mut function: F)
|
||||
where
|
||||
F: FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua<'lua>,
|
||||
{
|
||||
let func = NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
|
||||
let (ud, val) = <_>::from_lua_multi(args, lua)?;
|
||||
function(lua, ud, val)?.into_lua_multi(lua)
|
||||
}));
|
||||
self.field_setters.push((name.as_ref().into(), func));
|
||||
}
|
||||
|
||||
fn add_meta_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| {
|
||||
UserDataRegistry::<()>::check_meta_field(lua, &name2, value.clone())
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl AsRef<str>, f: F)
|
||||
where
|
||||
F: Fn(&'lua Lua) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| UserDataRegistry::<()>::check_meta_field(lua, &name2, f(lua)?)),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
+79
-17
@@ -2,6 +2,7 @@ use std::cell::RefCell;
|
||||
use std::convert::TryInto;
|
||||
use std::os::raw::c_void;
|
||||
use std::rc::Rc;
|
||||
use std::result::Result as StdResult;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use rustc_hash::FxHashSet;
|
||||
@@ -24,14 +25,14 @@ pub struct Deserializer<'lua> {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[non_exhaustive]
|
||||
pub struct Options {
|
||||
/// If true, an attempt to serialize types such as [`Thread`], [`UserData`], [`LightUserData`]
|
||||
/// If true, an attempt to serialize types such as [`Function`], [`Thread`], [`LightUserData`]
|
||||
/// and [`Error`] will cause an error.
|
||||
/// Otherwise these types skipped when iterating or serialized as unit type.
|
||||
///
|
||||
/// Default: **true**
|
||||
///
|
||||
/// [`Function`]: crate::Function
|
||||
/// [`Thread`]: crate::Thread
|
||||
/// [`UserData`]: crate::UserData
|
||||
/// [`LightUserData`]: crate::LightUserData
|
||||
/// [`Error`]: crate::Error
|
||||
pub deny_unsupported_types: bool,
|
||||
@@ -42,6 +43,11 @@ pub struct Options {
|
||||
///
|
||||
/// Default: **true**
|
||||
pub deny_recursive_tables: bool,
|
||||
|
||||
/// If true, keys in tables will be iterated in sorted order.
|
||||
///
|
||||
/// Default: **false**
|
||||
pub sort_keys: bool,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
@@ -56,6 +62,7 @@ impl Options {
|
||||
Options {
|
||||
deny_unsupported_types: true,
|
||||
deny_recursive_tables: true,
|
||||
sort_keys: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +83,15 @@ impl Options {
|
||||
self.deny_recursive_tables = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets [`sort_keys`] option.
|
||||
///
|
||||
/// [`sort_keys`]: #structfield.sort_keys
|
||||
#[must_use]
|
||||
pub const fn sort_keys(mut self, enabled: bool) -> Self {
|
||||
self.sort_keys = enabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Deserializer<'lua> {
|
||||
@@ -141,10 +157,8 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
| Value::LightUserData(_)
|
||||
| Value::Error(_) => {
|
||||
if self.options.deny_unsupported_types {
|
||||
Err(de::Error::custom(format!(
|
||||
"unsupported value type `{}`",
|
||||
self.value.type_name()
|
||||
)))
|
||||
let msg = format!("unsupported value type `{}`", self.value.type_name());
|
||||
Err(de::Error::custom(msg))
|
||||
} else {
|
||||
visitor.visit_unit()
|
||||
}
|
||||
@@ -195,7 +209,9 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
&"map with a single key",
|
||||
));
|
||||
}
|
||||
if check_value_if_skip(&value, self.options, &self.visited)? {
|
||||
let skip = check_value_for_skip(&value, self.options, &self.visited)
|
||||
.map_err(|err| Error::DeserializeError(err.to_string()))?;
|
||||
if skip {
|
||||
return Err(de::Error::custom("bad enum value"));
|
||||
}
|
||||
|
||||
@@ -235,7 +251,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
Value::Table(t) => {
|
||||
let _guard = RecursionGuard::new(&t, &self.visited);
|
||||
|
||||
let len = t.raw_len() as usize;
|
||||
let len = t.raw_len();
|
||||
let mut deserializer = SeqDeserializer {
|
||||
seq: t.sequence_values(),
|
||||
options: self.options,
|
||||
@@ -292,7 +308,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
let _guard = RecursionGuard::new(&t, &self.visited);
|
||||
|
||||
let mut deserializer = MapDeserializer {
|
||||
pairs: t.pairs(),
|
||||
pairs: MapPairs::new(t, self.options.sort_keys)?,
|
||||
value: None,
|
||||
options: self.options,
|
||||
visited: self.visited,
|
||||
@@ -390,7 +406,9 @@ impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
|
||||
match self.seq.next() {
|
||||
Some(value) => {
|
||||
let value = value?;
|
||||
if check_value_if_skip(&value, self.options, &self.visited)? {
|
||||
let skip = check_value_for_skip(&value, self.options, &self.visited)
|
||||
.map_err(|err| Error::DeserializeError(err.to_string()))?;
|
||||
if skip {
|
||||
continue;
|
||||
}
|
||||
let visited = Rc::clone(&self.visited);
|
||||
@@ -443,8 +461,50 @@ impl<'de> de::SeqAccess<'de> for VecDeserializer {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum MapPairs<'lua> {
|
||||
Iter(TablePairs<'lua, Value<'lua>, Value<'lua>>),
|
||||
Vec(Vec<(Value<'lua>, Value<'lua>)>),
|
||||
}
|
||||
|
||||
impl<'lua> MapPairs<'lua> {
|
||||
pub(crate) fn new(t: Table<'lua>, sort_keys: bool) -> Result<Self> {
|
||||
if sort_keys {
|
||||
let mut pairs = t.pairs::<Value, Value>().collect::<Result<Vec<_>>>()?;
|
||||
pairs.sort_by(|(a, _), (b, _)| b.cmp(a)); // reverse order as we pop values from the end
|
||||
Ok(MapPairs::Vec(pairs))
|
||||
} else {
|
||||
Ok(MapPairs::Iter(t.pairs::<Value, Value>()))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn count(self) -> usize {
|
||||
match self {
|
||||
MapPairs::Iter(iter) => iter.count(),
|
||||
MapPairs::Vec(vec) => vec.len(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
match self {
|
||||
MapPairs::Iter(iter) => iter.size_hint(),
|
||||
MapPairs::Vec(vec) => (vec.len(), Some(vec.len())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Iterator for MapPairs<'lua> {
|
||||
type Item = Result<(Value<'lua>, Value<'lua>)>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self {
|
||||
MapPairs::Iter(iter) => iter.next(),
|
||||
MapPairs::Vec(vec) => vec.pop().map(Ok),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MapDeserializer<'lua> {
|
||||
pairs: TablePairs<'lua, Value<'lua>, Value<'lua>>,
|
||||
pairs: MapPairs<'lua>,
|
||||
value: Option<Value<'lua>>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
@@ -462,9 +522,11 @@ impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
|
||||
match self.pairs.next() {
|
||||
Some(item) => {
|
||||
let (key, value) = item?;
|
||||
if check_value_if_skip(&key, self.options, &self.visited)?
|
||||
|| check_value_if_skip(&value, self.options, &self.visited)?
|
||||
{
|
||||
let skip_key = check_value_for_skip(&key, self.options, &self.visited)
|
||||
.map_err(|err| Error::DeserializeError(err.to_string()))?;
|
||||
let skip_value = check_value_for_skip(&value, self.options, &self.visited)
|
||||
.map_err(|err| Error::DeserializeError(err.to_string()))?;
|
||||
if skip_key || skip_value {
|
||||
continue;
|
||||
}
|
||||
self.processed += 1;
|
||||
@@ -615,17 +677,17 @@ impl Drop for RecursionGuard {
|
||||
}
|
||||
|
||||
// Checks `options` and decides should we emit an error or skip next element
|
||||
fn check_value_if_skip(
|
||||
pub(crate) fn check_value_for_skip(
|
||||
value: &Value,
|
||||
options: Options,
|
||||
visited: &RefCell<FxHashSet<*const c_void>>,
|
||||
) -> Result<bool> {
|
||||
) -> StdResult<bool, &'static str> {
|
||||
match value {
|
||||
Value::Table(table) => {
|
||||
let ptr = table.to_pointer();
|
||||
if visited.borrow().contains(&ptr) {
|
||||
if options.deny_recursive_tables {
|
||||
return Err(de::Error::custom("recursive table detected"));
|
||||
return Err("recursive table detected");
|
||||
}
|
||||
return Ok(true); // skip
|
||||
}
|
||||
|
||||
+106
-87
@@ -6,8 +6,8 @@ use std::os::raw::c_void;
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
rustc_hash::FxHashSet,
|
||||
serde::ser::{self, Serialize, SerializeMap, SerializeSeq, Serializer},
|
||||
std::{cell::RefCell, result::Result as StdResult},
|
||||
serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer},
|
||||
std::{cell::RefCell, rc::Rc, result::Result as StdResult},
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
@@ -86,17 +86,14 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let key = key.into_lua(lua)?;
|
||||
let value = value.into_lua(lua)?;
|
||||
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
lua.push_value(value)?;
|
||||
key.push_into_stack(lua)?;
|
||||
value.push_into_stack(lua)?;
|
||||
protect_lua!(state, 3, 0, fn(state) ffi::lua_settable(state, -3))
|
||||
}
|
||||
}
|
||||
@@ -133,19 +130,16 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let key = key.into_lua(lua)?;
|
||||
|
||||
let value = unsafe {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
key.push_into_stack(lua)?;
|
||||
protect_lua!(state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
|
||||
|
||||
lua.pop_value()
|
||||
};
|
||||
V::from_lua(value, lua)
|
||||
V::from_stack(-1, lua)
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether the table contains a non-nil value for `key`.
|
||||
@@ -166,13 +160,12 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let value = value.into_lua(lua)?;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(value)?;
|
||||
value.push_into_stack(lua)?;
|
||||
protect_lua!(state, 2, 0, fn(state) {
|
||||
let len = ffi::luaL_len(state, -2) as Integer;
|
||||
ffi::lua_seti(state, -2, len + 1);
|
||||
@@ -192,7 +185,7 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let value = unsafe {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
@@ -203,9 +196,8 @@ impl<'lua> Table<'lua> {
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_seti(state, -3, len);
|
||||
})?;
|
||||
lua.pop_value()
|
||||
};
|
||||
V::from_lua(value, lua)
|
||||
V::from_stack(-1, lua)
|
||||
}
|
||||
}
|
||||
|
||||
/// Compares two tables for equality.
|
||||
@@ -271,16 +263,13 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let key = key.into_lua(lua)?;
|
||||
let value = value.into_lua(lua)?;
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
lua.push_value(value)?;
|
||||
key.push_into_stack(lua)?;
|
||||
value.push_into_stack(lua)?;
|
||||
|
||||
if lua.unlikely_memory_error() {
|
||||
ffi::lua_rawset(state, -3);
|
||||
@@ -296,39 +285,34 @@ impl<'lua> Table<'lua> {
|
||||
pub fn raw_get<K: IntoLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let key = key.into_lua(lua)?;
|
||||
|
||||
let value = unsafe {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
key.push_into_stack(lua)?;
|
||||
ffi::lua_rawget(state, -2);
|
||||
|
||||
lua.pop_value()
|
||||
};
|
||||
V::from_lua(value, lua)
|
||||
V::from_stack(-1, lua)
|
||||
}
|
||||
}
|
||||
|
||||
/// Inserts element value at position `idx` to the table, shifting up the elements from `table[idx]`.
|
||||
/// The worst case complexity is O(n), where n is the table length.
|
||||
pub fn raw_insert<V: IntoLua<'lua>>(&self, idx: Integer, value: V) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
|
||||
let size = self.raw_len();
|
||||
let size = self.raw_len() as Integer;
|
||||
if idx < 1 || idx > size + 1 {
|
||||
return Err(Error::runtime("index out of bounds"));
|
||||
}
|
||||
|
||||
let value = value.into_lua(lua)?;
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(value)?;
|
||||
value.push_into_stack(lua)?;
|
||||
protect_lua!(state, 2, 0, |state| {
|
||||
for i in (idx..=size).rev() {
|
||||
// table[i+1] = table[i]
|
||||
@@ -347,14 +331,12 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let value = value.into_lua(lua)?;
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(value)?;
|
||||
value.push_into_stack(lua)?;
|
||||
|
||||
unsafe fn callback(state: *mut ffi::lua_State) {
|
||||
let len = ffi::lua_rawlen(state, -2) as Integer;
|
||||
@@ -377,7 +359,7 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let value = unsafe {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
@@ -387,9 +369,9 @@ impl<'lua> Table<'lua> {
|
||||
// Set slot to nil (it must be safe to do)
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_rawseti(state, -3, len);
|
||||
lua.pop_value()
|
||||
};
|
||||
V::from_lua(value, lua)
|
||||
|
||||
V::from_stack(-1, lua)
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes a key from the table.
|
||||
@@ -405,7 +387,7 @@ impl<'lua> Table<'lua> {
|
||||
let key = key.into_lua(lua)?;
|
||||
match key {
|
||||
Value::Integer(idx) => {
|
||||
let size = self.raw_len();
|
||||
let size = self.raw_len() as Integer;
|
||||
if idx < 1 || idx > size {
|
||||
return Err(Error::runtime("index out of bounds"));
|
||||
}
|
||||
@@ -477,7 +459,7 @@ impl<'lua> Table<'lua> {
|
||||
pub fn len(&self) -> Result<Integer> {
|
||||
// Fast track
|
||||
if !self.has_metatable() {
|
||||
return Ok(self.raw_len());
|
||||
return Ok(self.raw_len() as Integer);
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
@@ -492,9 +474,9 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
|
||||
/// Returns the result of the Lua `#` operator, without invoking the `__len` metamethod.
|
||||
pub fn raw_len(&self) -> Integer {
|
||||
pub fn raw_len(&self) -> usize {
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe { ffi::lua_rawlen(ref_thread, self.0.index) as Integer }
|
||||
unsafe { ffi::lua_rawlen(ref_thread, self.0.index) }
|
||||
}
|
||||
|
||||
/// Returns `true` if the table is empty, without invoking metamethods.
|
||||
@@ -725,9 +707,9 @@ impl<'lua> Table<'lua> {
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) fn sequence_values_by_len<V: FromLua<'lua>>(
|
||||
self,
|
||||
len: Option<Integer>,
|
||||
len: Option<usize>,
|
||||
) -> TableSequence<'lua, V> {
|
||||
let len = len.unwrap_or_else(|| self.raw_len());
|
||||
let len = len.unwrap_or_else(|| self.raw_len()) as Integer;
|
||||
TableSequence {
|
||||
table: self.0,
|
||||
index: Some(1),
|
||||
@@ -744,14 +726,12 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
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)?;
|
||||
value.push_into_stack(lua)?;
|
||||
|
||||
let idx = idx.try_into().unwrap();
|
||||
if lua.unlikely_memory_error() {
|
||||
@@ -759,8 +739,8 @@ impl<'lua> Table<'lua> {
|
||||
} else {
|
||||
protect_lua!(state, 2, 0, |state| ffi::lua_rawseti(state, -2, idx))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
@@ -1036,47 +1016,86 @@ impl<'lua> TableExt<'lua> for Table<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapped [`Table`] with customized serialization behavior.
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) struct SerializableTable<'a, 'lua> {
|
||||
table: &'a Table<'lua>,
|
||||
options: crate::serde::de::Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'lua> Serialize for Table<'lua> {
|
||||
#[inline]
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
|
||||
SerializableTable::new(self, Default::default(), Default::default()).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'a, 'lua> SerializableTable<'a, 'lua> {
|
||||
#[inline]
|
||||
pub(crate) fn new(
|
||||
table: &'a Table<'lua>,
|
||||
options: crate::serde::de::Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
table,
|
||||
options,
|
||||
visited,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'a, 'lua> Serialize for SerializableTable<'a, 'lua> {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
thread_local! {
|
||||
static VISITED: RefCell<FxHashSet<*const c_void>> = RefCell::new(FxHashSet::default());
|
||||
use crate::serde::de::{check_value_for_skip, MapPairs};
|
||||
use crate::value::SerializableValue;
|
||||
|
||||
let options = self.options;
|
||||
let visited = &self.visited;
|
||||
visited.borrow_mut().insert(self.table.to_pointer());
|
||||
|
||||
// Array
|
||||
let len = self.table.raw_len();
|
||||
if len > 0 || self.table.is_array() {
|
||||
let mut seq = serializer.serialize_seq(Some(len))?;
|
||||
for value in self.table.clone().sequence_values_by_len::<Value>(None) {
|
||||
let value = &value.map_err(serde::ser::Error::custom)?;
|
||||
let skip = check_value_for_skip(value, self.options, &self.visited)
|
||||
.map_err(serde::ser::Error::custom)?;
|
||||
if skip {
|
||||
continue;
|
||||
}
|
||||
seq.serialize_element(&SerializableValue::new(value, options, Some(visited)))?;
|
||||
}
|
||||
return seq.end();
|
||||
}
|
||||
|
||||
let ptr = self.to_pointer();
|
||||
let res = VISITED.with(|visited| {
|
||||
{
|
||||
let mut visited = visited.borrow_mut();
|
||||
if visited.contains(&ptr) {
|
||||
return Err(ser::Error::custom("recursive table detected"));
|
||||
}
|
||||
visited.insert(ptr);
|
||||
// HashMap
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
let pairs = MapPairs::new(self.table.clone(), self.options.sort_keys)
|
||||
.map_err(serde::ser::Error::custom)?;
|
||||
for kv in pairs {
|
||||
let (key, value) = kv.map_err(serde::ser::Error::custom)?;
|
||||
let skip_key = check_value_for_skip(&key, self.options, &self.visited)
|
||||
.map_err(serde::ser::Error::custom)?;
|
||||
let skip_value = check_value_for_skip(&value, self.options, &self.visited)
|
||||
.map_err(serde::ser::Error::custom)?;
|
||||
if skip_key || skip_value {
|
||||
continue;
|
||||
}
|
||||
|
||||
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().sequence_values_by_len::<Value>(None) {
|
||||
let v = v.map_err(serde::ser::Error::custom)?;
|
||||
seq.serialize_element(&v)?;
|
||||
}
|
||||
return seq.end();
|
||||
}
|
||||
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
for kv in self.clone().pairs::<Value, Value>() {
|
||||
let (k, v) = kv.map_err(serde::ser::Error::custom)?;
|
||||
map.serialize_entry(&k, &v)?;
|
||||
}
|
||||
map.end()
|
||||
});
|
||||
VISITED.with(|visited| {
|
||||
visited.borrow_mut().remove(&ptr);
|
||||
});
|
||||
res
|
||||
map.serialize_entry(
|
||||
&SerializableValue::new(&key, options, Some(visited)),
|
||||
&SerializableValue::new(&value, options, Some(visited)),
|
||||
)?;
|
||||
}
|
||||
map.end()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+189
-130
@@ -1,4 +1,3 @@
|
||||
use std::cmp;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
@@ -16,10 +15,7 @@ use crate::{
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::{
|
||||
lua::ASYNC_POLL_PENDING,
|
||||
value::{MultiValue, Value},
|
||||
},
|
||||
crate::{lua::ASYNC_POLL_PENDING, value::MultiValue},
|
||||
futures_util::stream::Stream,
|
||||
std::{
|
||||
future::Future,
|
||||
@@ -30,7 +26,7 @@ use {
|
||||
},
|
||||
};
|
||||
|
||||
/// Status of a Lua thread (or coroutine).
|
||||
/// Status of a Lua thread (coroutine).
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum ThreadStatus {
|
||||
/// The thread was just created, or is suspended because it has called `coroutine.yield`.
|
||||
@@ -45,9 +41,33 @@ pub enum ThreadStatus {
|
||||
Error,
|
||||
}
|
||||
|
||||
/// Handle to an internal Lua thread (or coroutine).
|
||||
/// Handle to an internal Lua thread (coroutine).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Thread<'lua>(pub(crate) LuaRef<'lua>);
|
||||
pub struct Thread<'lua>(pub(crate) LuaRef<'lua>, pub(crate) *mut ffi::lua_State);
|
||||
|
||||
/// Owned handle to an internal Lua thread (coroutine).
|
||||
///
|
||||
/// The owned handle holds a *strong* reference to the current Lua instance.
|
||||
/// Be warned, if you place it into a Lua type (eg. [`UserData`] or a Rust callback), it is *very easy*
|
||||
/// to accidentally cause reference cycles that would prevent destroying Lua instance.
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
#[cfg(feature = "unstable")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OwnedThread(
|
||||
pub(crate) crate::types::LuaOwnedRef,
|
||||
pub(crate) *mut ffi::lua_State,
|
||||
);
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl OwnedThread {
|
||||
/// Get borrowed handle to the underlying Lua table.
|
||||
#[cfg_attr(feature = "send", allow(unused))]
|
||||
pub const fn to_ref(&self) -> Thread {
|
||||
Thread(self.0.to_ref(), self.1)
|
||||
}
|
||||
}
|
||||
|
||||
/// Thread (coroutine) representation as an async [`Future`] or [`Stream`].
|
||||
///
|
||||
@@ -66,6 +86,16 @@ pub struct AsyncThread<'lua, R> {
|
||||
}
|
||||
|
||||
impl<'lua> Thread<'lua> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new(r#ref: LuaRef<'lua>) -> Self {
|
||||
let state = unsafe { ffi::lua_tothread(r#ref.lua.ref_thread(), r#ref.index) };
|
||||
Thread(r#ref, state)
|
||||
}
|
||||
|
||||
const fn state(&self) -> *mut ffi::lua_State {
|
||||
self.1
|
||||
}
|
||||
|
||||
/// Resumes execution of this thread.
|
||||
///
|
||||
/// Equivalent to `coroutine.resume`.
|
||||
@@ -114,60 +144,59 @@ impl<'lua> Thread<'lua> {
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
|
||||
let mut args = args.into_lua_multi(lua)?;
|
||||
let nargs = args.len() as c_int;
|
||||
let results = unsafe {
|
||||
let thread_state = self.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, cmp::max(nargs + 1, 3))?;
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
|
||||
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.0.index);
|
||||
|
||||
let status = ffi::lua_status(thread_state);
|
||||
if status != ffi::LUA_YIELD && ffi::lua_gettop(thread_state) == 0 {
|
||||
return Err(Error::CoroutineInactive);
|
||||
}
|
||||
|
||||
check_stack(thread_state, nargs)?;
|
||||
for arg in args.drain_all() {
|
||||
lua.push_value(arg)?;
|
||||
}
|
||||
ffi::lua_xmove(state, thread_state, nargs);
|
||||
|
||||
let mut nresults = 0;
|
||||
|
||||
let ret = ffi::lua_resume(thread_state, state, nargs, &mut nresults as *mut c_int);
|
||||
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
|
||||
if ret == ffi::LUA_ERRMEM {
|
||||
// Don't call error handler for memory errors
|
||||
return Err(pop_error(thread_state, ret));
|
||||
}
|
||||
check_stack(state, 3)?;
|
||||
protect_lua!(state, 0, 1, |state| error_traceback_thread(
|
||||
state,
|
||||
thread_state
|
||||
))?;
|
||||
return Err(pop_error(state, ret));
|
||||
}
|
||||
|
||||
let mut results = args; // Reuse MultiValue container
|
||||
check_stack(state, nresults + 2)?; // 2 is extra for `lua.pop_value()` below
|
||||
let nresults = self.resume_inner(args)?;
|
||||
check_stack(state, nresults + 1)?;
|
||||
ffi::lua_xmove(thread_state, state, nresults);
|
||||
|
||||
for _ in 0..nresults {
|
||||
results.push_front(lua.pop_value());
|
||||
R::from_stack_multi(nresults, lua)
|
||||
}
|
||||
}
|
||||
|
||||
/// Resumes execution of this thread.
|
||||
///
|
||||
/// It's similar to `resume()` but leaves `nresults` values on the thread stack.
|
||||
unsafe fn resume_inner<A: IntoLuaMulti<'lua>>(&self, args: A) -> Result<c_int> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let thread_state = self.state();
|
||||
|
||||
if self.status() != ThreadStatus::Resumable {
|
||||
return Err(Error::CoroutineInactive);
|
||||
}
|
||||
|
||||
let nargs = args.push_into_stack_multi(lua)?;
|
||||
if nargs > 0 {
|
||||
check_stack(thread_state, nargs)?;
|
||||
ffi::lua_xmove(state, thread_state, nargs);
|
||||
}
|
||||
|
||||
let mut nresults = 0;
|
||||
let ret = ffi::lua_resume(thread_state, state, nargs, &mut nresults as *mut c_int);
|
||||
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
|
||||
if ret == ffi::LUA_ERRMEM {
|
||||
// Don't call error handler for memory errors
|
||||
return Err(pop_error(thread_state, ret));
|
||||
}
|
||||
results
|
||||
};
|
||||
R::from_lua_multi(results, lua)
|
||||
check_stack(state, 3)?;
|
||||
protect_lua!(state, 0, 1, |state| error_traceback_thread(
|
||||
state,
|
||||
thread_state
|
||||
))?;
|
||||
return Err(pop_error(state, ret));
|
||||
}
|
||||
|
||||
Ok(nresults)
|
||||
}
|
||||
|
||||
/// Gets the status of the thread.
|
||||
pub fn status(&self) -> ThreadStatus {
|
||||
let lua = self.0.lua;
|
||||
let thread_state = self.state();
|
||||
unsafe {
|
||||
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.0.index);
|
||||
|
||||
let status = ffi::lua_status(thread_state);
|
||||
if status != ffi::LUA_OK && status != ffi::LUA_YIELD {
|
||||
ThreadStatus::Error
|
||||
@@ -191,8 +220,7 @@ impl<'lua> Thread<'lua> {
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.0.index);
|
||||
lua.set_thread_hook(thread_state, triggers, callback);
|
||||
lua.set_thread_hook(self.state(), triggers, callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,18 +242,12 @@ impl<'lua> Thread<'lua> {
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn reset(&self, func: crate::function::Function<'lua>) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let thread_state = self.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 2)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let thread_state = ffi::lua_tothread(state, -1);
|
||||
|
||||
#[cfg(all(feature = "lua54", not(feature = "vendored")))]
|
||||
let status = ffi::lua_resetthread(thread_state);
|
||||
#[cfg(all(feature = "lua54", feature = "vendored"))]
|
||||
let status = ffi::lua_closethread(thread_state, state);
|
||||
let status = ffi::lua_closethread(thread_state, lua.state());
|
||||
#[cfg(feature = "lua54")]
|
||||
if status != ffi::LUA_OK {
|
||||
return Err(pop_error(thread_state, status));
|
||||
@@ -233,8 +255,8 @@ impl<'lua> Thread<'lua> {
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_resetthread(thread_state);
|
||||
|
||||
lua.push_ref(&func.0);
|
||||
ffi::lua_xmove(state, thread_state, 1);
|
||||
// Push function to the top of the thread stack
|
||||
ffi::lua_xpush(lua.ref_thread(), thread_state, func.0.index);
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
@@ -345,13 +367,21 @@ impl<'lua> Thread<'lua> {
|
||||
pub fn sandbox(&self) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let thread_state = self.state();
|
||||
unsafe {
|
||||
let thread = ffi::lua_tothread(lua.ref_thread(), self.0.index);
|
||||
check_stack(thread, 3)?;
|
||||
check_stack(thread_state, 3)?;
|
||||
check_stack(state, 3)?;
|
||||
protect_lua!(state, 0, 0, |_| ffi::luaL_sandboxthread(thread))
|
||||
protect_lua!(state, 0, 0, |_| ffi::luaL_sandboxthread(thread_state))
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert this handle to owned version.
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
#[inline]
|
||||
pub fn into_owned(self) -> OwnedThread {
|
||||
OwnedThread(self.0.into_owned(), self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> PartialEq for Thread<'lua> {
|
||||
@@ -360,6 +390,26 @@ impl<'lua> PartialEq for Thread<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
// Additional shortcuts
|
||||
#[cfg(feature = "unstable")]
|
||||
impl OwnedThread {
|
||||
/// Resumes execution of this thread.
|
||||
///
|
||||
/// See [`Thread::resume()`] for more details.
|
||||
pub fn resume<'lua, A, R>(&'lua self, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
self.to_ref().resume(args)
|
||||
}
|
||||
|
||||
/// Gets the status of the thread.
|
||||
pub fn status(&self) -> ThreadStatus {
|
||||
self.to_ref().status()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<'lua, R> AsyncThread<'lua, R> {
|
||||
#[inline]
|
||||
@@ -379,11 +429,10 @@ impl<'lua, R> Drop for AsyncThread<'lua, R> {
|
||||
if !lua.recycle_thread(&mut self.thread) {
|
||||
#[cfg(feature = "lua54")]
|
||||
if self.thread.status() == ThreadStatus::Error {
|
||||
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.thread.0.index);
|
||||
#[cfg(not(feature = "vendored"))]
|
||||
ffi::lua_resetthread(thread_state);
|
||||
ffi::lua_resetthread(self.thread.state());
|
||||
#[cfg(feature = "vendored")]
|
||||
ffi::lua_closethread(thread_state, lua.state());
|
||||
ffi::lua_closethread(self.thread.state(), lua.state());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -399,29 +448,36 @@ where
|
||||
type Item = Result<R>;
|
||||
|
||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
let lua = self.thread.0.lua;
|
||||
|
||||
match self.thread.status() {
|
||||
ThreadStatus::Resumable => {}
|
||||
_ => return Poll::Ready(None),
|
||||
};
|
||||
|
||||
let _wg = WakerGuard::new(lua, cx.waker());
|
||||
|
||||
// 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.init_args.take() {
|
||||
this.thread.resume(args?)?
|
||||
} else {
|
||||
this.thread.resume(())?
|
||||
};
|
||||
|
||||
if is_poll_pending(&ret) {
|
||||
return Poll::Pending;
|
||||
if self.thread.status() != ThreadStatus::Resumable {
|
||||
return Poll::Ready(None);
|
||||
}
|
||||
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::Ready(Some(R::from_lua_multi(ret, lua)))
|
||||
let lua = self.thread.0.lua;
|
||||
let state = lua.state();
|
||||
let thread_state = self.thread.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
let _wg = WakerGuard::new(lua, cx.waker());
|
||||
|
||||
// This is safe as we are not moving the whole struct
|
||||
let this = self.get_unchecked_mut();
|
||||
let nresults = if let Some(args) = this.init_args.take() {
|
||||
this.thread.resume_inner(args?)?
|
||||
} else {
|
||||
this.thread.resume_inner(())?
|
||||
};
|
||||
|
||||
if nresults == 1 && is_poll_pending(thread_state) {
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
check_stack(state, nresults + 1)?;
|
||||
ffi::lua_xmove(thread_state, state, nresults);
|
||||
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::Ready(Some(R::from_stack_multi(nresults, lua)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,46 +489,53 @@ where
|
||||
type Output = Result<R>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
if self.thread.status() != ThreadStatus::Resumable {
|
||||
return Poll::Ready(Err(Error::CoroutineInactive));
|
||||
}
|
||||
|
||||
let lua = self.thread.0.lua;
|
||||
let state = lua.state();
|
||||
let thread_state = self.thread.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
let _wg = WakerGuard::new(lua, cx.waker());
|
||||
|
||||
match self.thread.status() {
|
||||
ThreadStatus::Resumable => {}
|
||||
_ => return Poll::Ready(Err(Error::CoroutineInactive)),
|
||||
};
|
||||
// This is safe as we are not moving the whole struct
|
||||
let this = self.get_unchecked_mut();
|
||||
let nresults = if let Some(args) = this.init_args.take() {
|
||||
this.thread.resume_inner(args?)?
|
||||
} else {
|
||||
this.thread.resume_inner(())?
|
||||
};
|
||||
|
||||
let _wg = WakerGuard::new(lua, cx.waker());
|
||||
if nresults == 1 && is_poll_pending(thread_state) {
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
// 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.init_args.take() {
|
||||
this.thread.resume(args?)?
|
||||
} else {
|
||||
this.thread.resume(())?
|
||||
};
|
||||
if ffi::lua_status(thread_state) == ffi::LUA_YIELD {
|
||||
// Ignore value returned via yield()
|
||||
cx.waker().wake_by_ref();
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
if is_poll_pending(&ret) {
|
||||
return Poll::Pending;
|
||||
check_stack(state, nresults + 1)?;
|
||||
ffi::lua_xmove(thread_state, state, nresults);
|
||||
|
||||
Poll::Ready(R::from_stack_multi(nresults, lua))
|
||||
}
|
||||
|
||||
if let ThreadStatus::Resumable = this.thread.status() {
|
||||
// Ignore value returned via yield()
|
||||
cx.waker().wake_by_ref();
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
Poll::Ready(R::from_lua_multi(ret, lua))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[inline(always)]
|
||||
fn is_poll_pending(val: &MultiValue) -> bool {
|
||||
match val.iter().enumerate().last() {
|
||||
Some((0, Value::LightUserData(ud))) => {
|
||||
std::ptr::eq(ud.0 as *const u8, &ASYNC_POLL_PENDING as *const u8)
|
||||
}
|
||||
_ => false,
|
||||
unsafe fn is_poll_pending(state: *mut ffi::lua_State) -> bool {
|
||||
if ffi::lua_islightuserdata(state, -1) != 0 {
|
||||
let stack_ptr = ffi::lua_touserdata(state, -1) as *const u8;
|
||||
let pending_ptr = &ASYNC_POLL_PENDING as *const u8;
|
||||
return std::ptr::eq(stack_ptr, pending_ptr);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
@@ -486,23 +549,19 @@ struct WakerGuard<'lua, 'a> {
|
||||
impl<'lua, 'a> WakerGuard<'lua, 'a> {
|
||||
#[inline]
|
||||
pub fn new(lua: &'lua Lua, waker: &'a Waker) -> Result<WakerGuard<'lua, 'a>> {
|
||||
unsafe {
|
||||
let prev = lua.set_waker(NonNull::from(waker));
|
||||
Ok(WakerGuard {
|
||||
lua,
|
||||
prev,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
let prev = unsafe { lua.set_waker(NonNull::from(waker)) };
|
||||
Ok(WakerGuard {
|
||||
lua,
|
||||
prev,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<'lua, 'a> Drop for WakerGuard<'lua, 'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.lua.set_waker(self.prev);
|
||||
}
|
||||
unsafe { self.lua.set_waker(self.prev) };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-8
@@ -10,14 +10,13 @@ use std::{fmt, mem, ptr};
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use futures_util::future::LocalBoxFuture;
|
||||
|
||||
use crate::error::Result;
|
||||
#[cfg(not(feature = "luau"))]
|
||||
use crate::hook::Debug;
|
||||
use crate::lua::{ExtraData, Lua};
|
||||
use crate::value::MultiValue;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {crate::value::MultiValue, futures_util::future::LocalBoxFuture};
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
use {crate::lua::LuaInner, std::marker::PhantomData};
|
||||
@@ -34,8 +33,7 @@ pub type Number = ffi::lua_Number;
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct LightUserData(pub *mut c_void);
|
||||
|
||||
pub(crate) type Callback<'lua, 'a> =
|
||||
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>> + 'a>;
|
||||
pub(crate) type Callback<'lua, 'a> = Box<dyn Fn(&'lua Lua, c_int) -> Result<c_int> + 'a>;
|
||||
|
||||
pub(crate) struct Upvalue<T> {
|
||||
pub(crate) data: T,
|
||||
@@ -46,13 +44,13 @@ pub(crate) type CallbackUpvalue = Upvalue<Callback<'static, 'static>>;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) type AsyncCallback<'lua, 'a> =
|
||||
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> LocalBoxFuture<'lua, Result<MultiValue<'lua>>> + 'a>;
|
||||
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> LocalBoxFuture<'lua, Result<c_int>> + 'a>;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) type AsyncCallbackUpvalue = Upvalue<AsyncCallback<'static, 'static>>;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) type AsyncPollUpvalue = Upvalue<LocalBoxFuture<'static, Result<MultiValue<'static>>>>;
|
||||
pub(crate) type AsyncPollUpvalue = Upvalue<LocalBoxFuture<'static, Result<c_int>>>;
|
||||
|
||||
/// Type to set next Luau VM action after executing interrupt function.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
|
||||
+7
-6
@@ -1104,9 +1104,10 @@ impl<'lua> AnyUserData<'lua> {
|
||||
OwnedAnyUserData(self.0.into_owned())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(feature = "async")]
|
||||
#[inline]
|
||||
pub(crate) fn type_id(&self) -> Result<Option<TypeId>> {
|
||||
unsafe { self.0.lua.get_userdata_type_id(&self.0) }
|
||||
unsafe { self.0.lua.get_userdata_ref_type_id(&self.0) }
|
||||
}
|
||||
|
||||
/// Returns a type name of this `UserData` (from a metatable field).
|
||||
@@ -1154,13 +1155,13 @@ impl<'lua> AnyUserData<'lua> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// Returns true if this `AnyUserData` is serializable (eg. was created using `create_ser_userdata`).
|
||||
/// Returns `true` if this `AnyUserData` is serializable (eg. was created using `create_ser_userdata`).
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) fn is_serializable(&self) -> bool {
|
||||
let lua = self.0.lua;
|
||||
let is_serializable = || unsafe {
|
||||
// Userdata can be unregistered or destructed
|
||||
let _ = lua.get_userdata_type_id(&self.0)?;
|
||||
let _ = lua.get_userdata_ref_type_id(&self.0)?;
|
||||
|
||||
let ud = &*get_userdata::<UserDataCell<()>>(lua.ref_thread(), self.0.index);
|
||||
match &*ud.0.try_borrow().map_err(|_| Error::UserDataBorrowError)? {
|
||||
@@ -1178,7 +1179,7 @@ impl<'lua> AnyUserData<'lua> {
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let type_id = lua.get_userdata_type_id(&self.0)?;
|
||||
let type_id = lua.get_userdata_ref_type_id(&self.0)?;
|
||||
match type_id {
|
||||
Some(type_id) if type_id == TypeId::of::<T>() => {
|
||||
let ref_thread = lua.ref_thread();
|
||||
@@ -1327,7 +1328,7 @@ impl<'lua> Serialize for AnyUserData<'lua> {
|
||||
let lua = self.0.lua;
|
||||
let data = unsafe {
|
||||
let _ = lua
|
||||
.get_userdata_type_id(&self.0)
|
||||
.get_userdata_ref_type_id(&self.0)
|
||||
.map_err(ser::Error::custom)?;
|
||||
let ud = &*get_userdata::<UserDataCell<()>>(lua.ref_thread(), self.0.index);
|
||||
ud.0.try_borrow()
|
||||
|
||||
+155
-176
@@ -14,17 +14,13 @@ use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
|
||||
};
|
||||
use crate::util::{get_userdata, short_type_name};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Value};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
use std::rc::Rc;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::types::AsyncCallback,
|
||||
futures_util::future::{self, TryFutureExt},
|
||||
std::future::Future,
|
||||
};
|
||||
use {crate::types::AsyncCallback, futures_util::future, std::future::Future};
|
||||
|
||||
/// Handle to registry for userdata methods and metamethods.
|
||||
pub struct UserDataRegistry<'lua, T: 'static> {
|
||||
@@ -78,63 +74,61 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
|
||||
};
|
||||
}
|
||||
|
||||
Box::new(move |lua, mut args| {
|
||||
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)
|
||||
};
|
||||
Box::new(move |lua, nargs| unsafe {
|
||||
if nargs == 0 {
|
||||
let err = Error::from_lua_conversion("missing argument", "userdata", None);
|
||||
try_self_arg!(Err(err));
|
||||
}
|
||||
let state = lua.state();
|
||||
// Find absolute "self" index before processing args
|
||||
let index = ffi::lua_absindex(state, -nargs);
|
||||
// Self was at position 1, so we pass 2 here
|
||||
let args = A::from_stack_args(nargs - 1, 2, Some(&name), lua);
|
||||
|
||||
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)
|
||||
},
|
||||
match try_self_arg!(lua.get_userdata_type_id(index)) {
|
||||
Some(id) if id == TypeId::of::<T>() => {
|
||||
let ud = try_self_arg!(get_userdata_ref::<T>(state, index));
|
||||
method(lua, &ud, args?)?.push_into_stack_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));
|
||||
call(&ud)
|
||||
},
|
||||
Some(id) if id == TypeId::of::<Rc<T>>() => {
|
||||
let ud = try_self_arg!(get_userdata_ref::<Rc<T>>(state, index));
|
||||
method(lua, &ud, args?)?.push_into_stack_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));
|
||||
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
let ud = try_self_arg!(get_userdata_ref::<Rc<RefCell<T>>>(state, 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));
|
||||
method(lua, &ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<T>>() => {
|
||||
let ud = try_self_arg!(get_userdata_ref::<Arc<T>>(state, index));
|
||||
method(lua, &ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
|
||||
let ud = try_self_arg!(get_userdata_ref::<Arc<Mutex<T>>>(state, index));
|
||||
let ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowError);
|
||||
call(&ud)
|
||||
},
|
||||
method(lua, &ud, args?)?.push_into_stack_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);
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
|
||||
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(state, 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));
|
||||
method(lua, &ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
|
||||
let ud = try_self_arg!(get_userdata_ref::<Arc<RwLock<T>>>(state, index));
|
||||
let ud = try_self_arg!(ud.try_read(), Error::UserDataBorrowError);
|
||||
call(&ud)
|
||||
},
|
||||
method(lua, &ud, args?)?.push_into_stack_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);
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
|
||||
let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(state, index);
|
||||
let ud = try_self_arg!(ud);
|
||||
let ud = try_self_arg!(ud.try_read().ok_or(Error::UserDataBorrowError));
|
||||
call(&ud)
|
||||
},
|
||||
method(lua, &ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
})
|
||||
@@ -157,60 +151,58 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
|
||||
}
|
||||
|
||||
let method = RefCell::new(method);
|
||||
Box::new(move |lua, mut args| {
|
||||
Box::new(move |lua, nargs| unsafe {
|
||||
let mut method = method
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
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 nargs == 0 {
|
||||
let err = Error::from_lua_conversion("missing argument", "userdata", None);
|
||||
try_self_arg!(Err(err));
|
||||
}
|
||||
let state = lua.state();
|
||||
// Find absolute "self" index before processing args
|
||||
let index = ffi::lua_absindex(state, -nargs);
|
||||
// Self was at position 1, so we pass 2 here
|
||||
let args = A::from_stack_args(nargs - 1, 2, Some(&name), lua);
|
||||
|
||||
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)
|
||||
},
|
||||
match try_self_arg!(lua.get_userdata_type_id(index)) {
|
||||
Some(id) if id == TypeId::of::<T>() => {
|
||||
let mut ud = try_self_arg!(get_userdata_mut::<T>(state, index));
|
||||
method(lua, &mut ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[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));
|
||||
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
let ud = try_self_arg!(get_userdata_mut::<Rc<RefCell<T>>>(state, index));
|
||||
let mut ud = try_self_arg!(ud.try_borrow_mut(), Error::UserDataBorrowMutError);
|
||||
call(&mut ud)
|
||||
},
|
||||
method(lua, &mut ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
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));
|
||||
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
|
||||
let ud = try_self_arg!(get_userdata_mut::<Arc<Mutex<T>>>(state, index));
|
||||
let mut ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowMutError);
|
||||
call(&mut ud)
|
||||
},
|
||||
method(lua, &mut ud, args?)?.push_into_stack_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);
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
|
||||
let ud = get_userdata_mut::<Arc<parking_lot::Mutex<T>>>(state, 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));
|
||||
method(lua, &mut ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
|
||||
let ud = try_self_arg!(get_userdata_mut::<Arc<RwLock<T>>>(state, index));
|
||||
let mut ud = try_self_arg!(ud.try_write(), Error::UserDataBorrowMutError);
|
||||
call(&mut ud)
|
||||
},
|
||||
method(lua, &mut ud, args?)?.push_into_stack_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);
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
|
||||
let ud = get_userdata_mut::<Arc<parking_lot::RwLock<T>>>(state, index);
|
||||
let ud = try_self_arg!(ud);
|
||||
let mut ud = try_self_arg!(ud.try_write().ok_or(Error::UserDataBorrowMutError));
|
||||
call(&mut ud)
|
||||
},
|
||||
method(lua, &mut ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
})
|
||||
@@ -229,7 +221,7 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
|
||||
let name = get_function_name::<T>(name);
|
||||
let method = Arc::new(method);
|
||||
|
||||
Box::new(move |lua, mut args| {
|
||||
Box::new(move |lua, mut args| unsafe {
|
||||
let name = name.clone();
|
||||
let method = method.clone();
|
||||
macro_rules! try_self_arg {
|
||||
@@ -242,76 +234,68 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
|
||||
}
|
||||
|
||||
Box::pin(async move {
|
||||
let front = args.pop_front().ok_or_else(|| {
|
||||
let this = 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 this = try_self_arg!(AnyUserData::from_lua(try_self_arg!(this), lua));
|
||||
let args = A::from_lua_args(args, 2, Some(&name), lua);
|
||||
|
||||
let (ref_thread, index) = (lua.ref_thread(), this.0.index);
|
||||
match try_self_arg!(this.type_id()) {
|
||||
Some(id) if id == TypeId::of::<T>() => {
|
||||
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)
|
||||
},
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
Some(id) if id == TypeId::of::<Rc<T>>() => unsafe {
|
||||
Some(id) if id == TypeId::of::<Rc<T>>() => {
|
||||
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)
|
||||
},
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => unsafe {
|
||||
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
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 {
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<T>>() => {
|
||||
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 {
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
|
||||
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)
|
||||
},
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => unsafe {
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
|
||||
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 {
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
|
||||
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)
|
||||
},
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => unsafe {
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
|
||||
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)
|
||||
},
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
})
|
||||
@@ -331,7 +315,7 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
|
||||
let name = get_function_name::<T>(name);
|
||||
let method = Arc::new(method);
|
||||
|
||||
Box::new(move |lua, mut args| {
|
||||
Box::new(move |lua, mut args| unsafe {
|
||||
let name = name.clone();
|
||||
let method = method.clone();
|
||||
macro_rules! try_self_arg {
|
||||
@@ -344,72 +328,66 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
|
||||
}
|
||||
|
||||
Box::pin(async move {
|
||||
let front = args.pop_front().ok_or_else(|| {
|
||||
let this = 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 this = try_self_arg!(AnyUserData::from_lua(try_self_arg!(this), lua));
|
||||
let args = A::from_lua_args(args, 2, Some(&name), lua);
|
||||
|
||||
let (ref_thread, index) = (lua.ref_thread(), this.0.index);
|
||||
match try_self_arg!(this.type_id()) {
|
||||
Some(id) if id == TypeId::of::<T>() => {
|
||||
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)
|
||||
},
|
||||
method(lua, ud, args?).await?.push_into_stack_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 {
|
||||
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
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)
|
||||
},
|
||||
method(lua, ud, args?).await?.push_into_stack_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 {
|
||||
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
|
||||
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)
|
||||
},
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => unsafe {
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
|
||||
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 {
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
|
||||
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)
|
||||
},
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => unsafe {
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
|
||||
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)
|
||||
},
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
})
|
||||
@@ -423,8 +401,9 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
Box::new(move |lua, args| {
|
||||
function(lua, A::from_lua_multi_args(args, 1, Some(&name), lua)?)?.into_lua_multi(lua)
|
||||
Box::new(move |lua, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 1, Some(&name), lua)?;
|
||||
function(lua, args)?.push_into_stack_multi(lua)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -436,11 +415,12 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
let function = RefCell::new(function);
|
||||
Box::new(move |lua, args| {
|
||||
Box::new(move |lua, nargs| unsafe {
|
||||
let function = &mut *function
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
function(lua, A::from_lua_multi_args(args, 1, Some(&name), lua)?)?.into_lua_multi(lua)
|
||||
let args = A::from_stack_args(nargs, 1, Some(&name), lua)?;
|
||||
function(lua, args)?.push_into_stack_multi(lua)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -453,22 +433,17 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
Box::new(move |lua, args| {
|
||||
let args = match A::from_lua_multi_args(args, 1, Some(&name), lua) {
|
||||
Box::new(move |lua, args| unsafe {
|
||||
let args = match A::from_lua_args(args, 1, Some(&name), lua) {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::err(e)),
|
||||
};
|
||||
Box::pin(
|
||||
function(lua, args).and_then(move |ret| future::ready(ret.into_lua_multi(lua))),
|
||||
)
|
||||
let fut = function(lua, args);
|
||||
Box::pin(async move { fut.await?.push_into_stack_multi(lua) })
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn check_meta_field<V>(
|
||||
lua: &'lua Lua,
|
||||
name: &str,
|
||||
value: V,
|
||||
) -> Result<MultiValue<'lua>>
|
||||
pub(crate) fn check_meta_field<V>(lua: &'lua Lua, name: &str, value: V) -> Result<Value<'lua>>
|
||||
where
|
||||
V: IntoLua<'lua>,
|
||||
{
|
||||
@@ -485,7 +460,7 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
value.into_lua_multi(lua)
|
||||
value.into_lua(lua)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -502,7 +477,7 @@ impl<'lua, T: 'static> UserDataFields<'lua, T> for UserDataRegistry<'lua, T> {
|
||||
let name = name.as_ref().to_string();
|
||||
self.fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| value.clone().into_lua_multi(lua)),
|
||||
Box::new(move |lua, _| unsafe { value.clone().push_into_stack_multi(lua) }),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -554,7 +529,9 @@ impl<'lua, T: 'static> UserDataFields<'lua, T> for UserDataRegistry<'lua, T> {
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| Self::check_meta_field(lua, &name2, value.clone())),
|
||||
Box::new(move |lua, _| unsafe {
|
||||
Self::check_meta_field(lua, &name2, value.clone())?.push_into_stack_multi(lua)
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -567,7 +544,9 @@ impl<'lua, T: 'static> UserDataFields<'lua, T> for UserDataRegistry<'lua, T> {
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| Self::check_meta_field(lua, &name2, f(lua)?)),
|
||||
Box::new(move |lua, _| unsafe {
|
||||
Self::check_meta_field(lua, &name2, f(lua)?)?.push_into_stack_multi(lua)
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,11 @@ impl StackGuard {
|
||||
top: ffi::lua_gettop(state),
|
||||
}
|
||||
}
|
||||
|
||||
// Same as `new()`, but allows specifying the expected stack size at the end of the scope.
|
||||
pub const fn with_top(state: *mut ffi::lua_State, top: c_int) -> StackGuard {
|
||||
StackGuard { state, top }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for StackGuard {
|
||||
|
||||
+496
-57
@@ -1,17 +1,21 @@
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashSet;
|
||||
use std::iter::{self, FromIterator};
|
||||
use std::ops::Index;
|
||||
use std::os::raw::c_void;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, ptr, slice, str, vec};
|
||||
use std::{fmt, mem, ptr, slice, str, vec};
|
||||
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
crate::table::SerializableTable,
|
||||
rustc_hash::FxHashSet,
|
||||
serde::ser::{self, Serialize, Serializer},
|
||||
std::convert::TryInto,
|
||||
std::result::Result as StdResult,
|
||||
std::{cell::RefCell, convert::TryInto, rc::Rc, result::Result as StdResult},
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
@@ -121,7 +125,7 @@ impl<'lua> Value<'lua> {
|
||||
Value::String(String(r))
|
||||
| Value::Table(Table(r))
|
||||
| Value::Function(Function(r))
|
||||
| Value::Thread(Thread(r))
|
||||
| Value::Thread(Thread(r, ..))
|
||||
| Value::UserData(AnyUserData(r)) => r.to_pointer(),
|
||||
_ => ptr::null(),
|
||||
}
|
||||
@@ -143,7 +147,7 @@ impl<'lua> Value<'lua> {
|
||||
Value::String(s) => Ok(s.to_str()?.to_string()),
|
||||
Value::Table(Table(r))
|
||||
| Value::Function(Function(r))
|
||||
| Value::Thread(Thread(r))
|
||||
| Value::Thread(Thread(r, ..))
|
||||
| Value::UserData(AnyUserData(r)) => unsafe {
|
||||
let state = r.lua.state();
|
||||
let _guard = StackGuard::new(state);
|
||||
@@ -159,6 +163,263 @@ impl<'lua> Value<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a [`Nil`].
|
||||
#[inline]
|
||||
pub fn is_nil(&self) -> bool {
|
||||
self == &Nil
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a [`NULL`].
|
||||
#[inline]
|
||||
pub fn is_null(&self) -> bool {
|
||||
self == &Self::NULL
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a boolean.
|
||||
#[inline]
|
||||
pub fn is_boolean(&self) -> bool {
|
||||
self.as_boolean().is_some()
|
||||
}
|
||||
|
||||
/// Cast the value to boolean.
|
||||
///
|
||||
/// If the value is a Boolean, returns it or `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_boolean(&self) -> Option<bool> {
|
||||
match *self {
|
||||
Value::Boolean(b) => Some(b),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a [`LightUserData`].
|
||||
#[inline]
|
||||
pub fn is_light_userdata(&self) -> bool {
|
||||
self.as_light_userdata().is_some()
|
||||
}
|
||||
|
||||
/// Cast the value to [`LightUserData`].
|
||||
///
|
||||
/// If the value is a [`LightUserData`], returns it or `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_light_userdata(&self) -> Option<LightUserData> {
|
||||
match *self {
|
||||
Value::LightUserData(l) => Some(l),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is an [`Integer`].
|
||||
#[inline]
|
||||
pub fn is_integer(&self) -> bool {
|
||||
self.as_integer().is_some()
|
||||
}
|
||||
|
||||
/// Cast the value to [`Integer`].
|
||||
///
|
||||
/// If the value is a Lua [`Integer`], returns it or `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_integer(&self) -> Option<Integer> {
|
||||
match *self {
|
||||
Value::Integer(i) => Some(i),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Cast the value to `i32`.
|
||||
///
|
||||
/// If the value is a Lua [`Integer`], try to convert it to `i32` or return `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_i32(&self) -> Option<i32> {
|
||||
#[allow(clippy::useless_conversion)]
|
||||
self.as_integer().and_then(|i| i32::try_from(i).ok())
|
||||
}
|
||||
|
||||
/// Cast the value to `u32`.
|
||||
///
|
||||
/// If the value is a Lua [`Integer`], try to convert it to `u32` or return `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_u32(&self) -> Option<u32> {
|
||||
self.as_integer().and_then(|i| u32::try_from(i).ok())
|
||||
}
|
||||
|
||||
/// Cast the value to `i64`.
|
||||
///
|
||||
/// If the value is a Lua [`Integer`], try to convert it to `i64` or return `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_i64(&self) -> Option<i64> {
|
||||
#[allow(clippy::useless_conversion)]
|
||||
self.as_integer().and_then(|i| i64::try_from(i).ok())
|
||||
}
|
||||
|
||||
/// Cast the value to `u64`.
|
||||
///
|
||||
/// If the value is a Lua [`Integer`], try to convert it to `u64` or return `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_u64(&self) -> Option<u64> {
|
||||
self.as_integer().and_then(|i| u64::try_from(i).ok())
|
||||
}
|
||||
|
||||
/// Cast the value to `isize`.
|
||||
///
|
||||
/// If the value is a Lua [`Integer`], try to convert it to `isize` or return `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_isize(&self) -> Option<isize> {
|
||||
self.as_integer().and_then(|i| isize::try_from(i).ok())
|
||||
}
|
||||
|
||||
/// Cast the value to `usize`.
|
||||
///
|
||||
/// If the value is a Lua [`Integer`], try to convert it to `usize` or return `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_usize(&self) -> Option<usize> {
|
||||
self.as_integer().and_then(|i| usize::try_from(i).ok())
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a Lua [`Number`].
|
||||
#[inline]
|
||||
pub fn is_number(&self) -> bool {
|
||||
self.as_number().is_some()
|
||||
}
|
||||
|
||||
/// Cast the value to [`Number`].
|
||||
///
|
||||
/// If the value is a Lua [`Number`], returns it or `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_number(&self) -> Option<Number> {
|
||||
match *self {
|
||||
Value::Number(n) => Some(n),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Cast the value to `f32`.
|
||||
///
|
||||
/// If the value is a Lua [`Number`], try to convert it to `f32` or return `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_f32(&self) -> Option<f32> {
|
||||
self.as_number().and_then(f32::from_f64)
|
||||
}
|
||||
|
||||
/// Cast the value to `f64`.
|
||||
///
|
||||
/// If the value is a Lua [`Number`], try to convert it to `f64` or return `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_f64(&self) -> Option<f64> {
|
||||
self.as_number()
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a Lua [`String`].
|
||||
#[inline]
|
||||
pub fn is_string(&self) -> bool {
|
||||
self.as_string().is_some()
|
||||
}
|
||||
|
||||
/// Cast the value to Lua [`String`].
|
||||
///
|
||||
/// If the value is a Lua [`String`], returns it or `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_string(&self) -> Option<&String> {
|
||||
match self {
|
||||
Value::String(s) => Some(s),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Cast the value to [`str`].
|
||||
///
|
||||
/// If the value is a Lua [`String`], try to convert it to [`str`] or return `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
self.as_string().and_then(|s| s.to_str().ok())
|
||||
}
|
||||
|
||||
/// Cast the value to [`Cow<str>`].
|
||||
///
|
||||
/// If the value is a Lua [`String`], converts it to [`Cow<str>`] or returns `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_string_lossy(&self) -> Option<Cow<str>> {
|
||||
self.as_string().map(|s| s.to_string_lossy())
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a Lua [`Table`].
|
||||
#[inline]
|
||||
pub fn is_table(&self) -> bool {
|
||||
self.as_table().is_some()
|
||||
}
|
||||
|
||||
/// Cast the value to [`Table`].
|
||||
///
|
||||
/// If the value is a Lua [`Table`], returns it or `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_table(&self) -> Option<&Table> {
|
||||
match self {
|
||||
Value::Table(t) => Some(t),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a Lua [`Thread`].
|
||||
#[inline]
|
||||
pub fn is_thread(&self) -> bool {
|
||||
self.as_thread().is_some()
|
||||
}
|
||||
|
||||
/// Cast the value to [`Thread`].
|
||||
///
|
||||
/// If the value is a Lua [`Thread`], returns it or `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_thread(&self) -> Option<&Thread> {
|
||||
match self {
|
||||
Value::Thread(t) => Some(t),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a Lua [`Function`].
|
||||
#[inline]
|
||||
pub fn is_function(&self) -> bool {
|
||||
self.as_function().is_some()
|
||||
}
|
||||
|
||||
/// Cast the value to [`Function`].
|
||||
///
|
||||
/// If the value is a Lua [`Function`], returns it or `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_function(&self) -> Option<&Function> {
|
||||
match self {
|
||||
Value::Function(f) => Some(f),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is an [`AnyUserData`].
|
||||
#[inline]
|
||||
pub fn is_userdata(&self) -> bool {
|
||||
self.as_userdata().is_some()
|
||||
}
|
||||
|
||||
/// Cast the value to [`AnyUserData`].
|
||||
///
|
||||
/// If the value is an [`AnyUserData`], returns it or `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_userdata(&self) -> Option<&AnyUserData> {
|
||||
match self {
|
||||
Value::UserData(ud) => Some(ud),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap reference to this Value into [`SerializableValue`].
|
||||
///
|
||||
/// This allows customizing serialization behavior using serde.
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
#[doc(hidden)]
|
||||
pub fn to_serializable(&self) -> SerializableValue<'_, 'lua> {
|
||||
SerializableValue::new(self, Default::default(), None)
|
||||
}
|
||||
|
||||
// Compares two values.
|
||||
// Used to sort values for Debug printing.
|
||||
pub(crate) fn cmp(&self, other: &Self) -> Ordering {
|
||||
@@ -289,13 +550,86 @@ impl<'lua> AsRef<Value<'lua>> for Value<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapped [`Value`] with customized serialization behavior.
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
pub struct SerializableValue<'a, 'lua> {
|
||||
value: &'a Value<'lua>,
|
||||
options: crate::serde::de::Options,
|
||||
// In many cases we don't need `visited` map, so don't allocate memory by default
|
||||
visited: Option<Rc<RefCell<FxHashSet<*const c_void>>>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'lua> Serialize for Value<'lua> {
|
||||
#[inline]
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
|
||||
SerializableValue::new(self, Default::default(), None).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'a, 'lua> SerializableValue<'a, 'lua> {
|
||||
#[inline]
|
||||
pub(crate) fn new(
|
||||
value: &'a Value<'lua>,
|
||||
options: crate::serde::de::Options,
|
||||
visited: Option<&Rc<RefCell<FxHashSet<*const c_void>>>>,
|
||||
) -> Self {
|
||||
if let Value::Table(_) = value {
|
||||
return Self {
|
||||
value,
|
||||
options,
|
||||
// We need to always initialize the `visited` map for Tables
|
||||
visited: visited.cloned().or_else(|| Some(Default::default())),
|
||||
};
|
||||
}
|
||||
Self {
|
||||
value,
|
||||
options,
|
||||
visited: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// If true, an attempt to serialize types such as [`Function`], [`Thread`], [`LightUserData`]
|
||||
/// and [`Error`] will cause an error.
|
||||
/// Otherwise these types skipped when iterating or serialized as unit type.
|
||||
///
|
||||
/// Default: **true**
|
||||
#[must_use]
|
||||
pub const fn deny_unsupported_types(mut self, enabled: bool) -> Self {
|
||||
self.options.deny_unsupported_types = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// If true, an attempt to serialize a recursive table (table that refers to itself)
|
||||
/// will cause an error.
|
||||
/// Otherwise subsequent attempts to serialize the same table will be ignored.
|
||||
///
|
||||
/// Default: **true**
|
||||
#[must_use]
|
||||
pub const fn deny_recursive_tables(mut self, enabled: bool) -> Self {
|
||||
self.options.deny_recursive_tables = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// If true, keys in tables will be iterated (and serialized) in sorted order.
|
||||
///
|
||||
/// Default: **false**
|
||||
#[must_use]
|
||||
pub const fn sort_keys(mut self, enabled: bool) -> Self {
|
||||
self.options.sort_keys = enabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'a, 'lua> Serialize for SerializableValue<'a, 'lua> {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match self {
|
||||
match self.value {
|
||||
Value::Nil => serializer.serialize_unit(),
|
||||
Value::Boolean(b) => serializer.serialize_bool(*b),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
@@ -305,21 +639,44 @@ impl<'lua> Serialize for Value<'lua> {
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(v) => v.serialize(serializer),
|
||||
Value::String(s) => s.serialize(serializer),
|
||||
Value::Table(t) => t.serialize(serializer),
|
||||
Value::UserData(ud) => ud.serialize(serializer),
|
||||
Value::Table(t) => {
|
||||
let visited = self.visited.as_ref().unwrap().clone();
|
||||
SerializableTable::new(t, self.options, visited).serialize(serializer)
|
||||
}
|
||||
Value::LightUserData(ud) if ud.0.is_null() => serializer.serialize_none(),
|
||||
Value::Error(_) | Value::LightUserData(_) | Value::Function(_) | Value::Thread(_) => {
|
||||
let msg = format!("cannot serialize <{}>", self.type_name());
|
||||
Err(ser::Error::custom(msg))
|
||||
Value::UserData(ud) if ud.is_serializable() || self.options.deny_unsupported_types => {
|
||||
ud.serialize(serializer)
|
||||
}
|
||||
Value::Function(_)
|
||||
| Value::Thread(_)
|
||||
| Value::UserData(_)
|
||||
| Value::LightUserData(_)
|
||||
| Value::Error(_) => {
|
||||
if self.options.deny_unsupported_types {
|
||||
let msg = format!("cannot serialize <{}>", self.value.type_name());
|
||||
Err(ser::Error::custom(msg))
|
||||
} else {
|
||||
serializer.serialize_unit()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types convertible to `Value`.
|
||||
pub trait IntoLua<'lua> {
|
||||
pub trait IntoLua<'lua>: Sized {
|
||||
/// Performs the conversion.
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>>;
|
||||
|
||||
/// Pushes the value into the Lua stack.
|
||||
///
|
||||
/// # Safety
|
||||
/// This method does not check Lua stack space.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
lua.push_value(self.into_lua(lua)?)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types convertible from `Value`.
|
||||
@@ -332,13 +689,33 @@ pub trait FromLua<'lua>: Sized {
|
||||
/// `i` is the argument index (position),
|
||||
/// `to` is a function name that received the argument.
|
||||
#[doc(hidden)]
|
||||
fn from_lua_arg(
|
||||
value: Value<'lua>,
|
||||
#[inline]
|
||||
fn from_lua_arg(arg: Value<'lua>, i: usize, to: Option<&str>, lua: &'lua Lua) -> Result<Self> {
|
||||
Self::from_lua(arg, lua).map_err(|err| Error::BadArgument {
|
||||
to: to.map(|s| s.to_string()),
|
||||
pos: i,
|
||||
name: None,
|
||||
cause: Arc::new(err),
|
||||
})
|
||||
}
|
||||
|
||||
/// Performs the conversion for a value in the Lua stack at index `idx`.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack(idx: c_int, lua: &'lua Lua) -> Result<Self> {
|
||||
Self::from_lua(lua.stack_value(idx), lua)
|
||||
}
|
||||
|
||||
/// Same as `from_lua_arg` but for a value in the Lua stack at index `idx`.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_arg(
|
||||
idx: c_int,
|
||||
i: usize,
|
||||
to: Option<&str>,
|
||||
lua: &'lua Lua,
|
||||
) -> Result<Self> {
|
||||
Self::from_lua(value, lua).map_err(|err| Error::BadArgument {
|
||||
Self::from_stack(idx, lua).map_err(|err| Error::BadArgument {
|
||||
to: to.map(|s| s.to_string()),
|
||||
pos: i,
|
||||
name: None,
|
||||
@@ -349,24 +726,43 @@ pub trait FromLua<'lua>: Sized {
|
||||
|
||||
/// Multiple Lua values used for both argument passing and also for multiple return values.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MultiValue<'lua>(Vec<Value<'lua>>);
|
||||
pub struct MultiValue<'lua> {
|
||||
vec: Vec<Value<'lua>>,
|
||||
lua: Option<&'lua Lua>,
|
||||
}
|
||||
|
||||
impl Drop for MultiValue<'_> {
|
||||
fn drop(&mut self) {
|
||||
if let Some(lua) = self.lua {
|
||||
let vec = mem::take(&mut self.vec);
|
||||
lua.push_multivalue_to_pool(vec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> MultiValue<'lua> {
|
||||
/// Creates an empty `MultiValue` containing no values.
|
||||
pub const fn new() -> MultiValue<'lua> {
|
||||
MultiValue(Vec::new())
|
||||
MultiValue {
|
||||
vec: Vec::new(),
|
||||
lua: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Similar to `new` but can return previously used container with allocated capacity.
|
||||
/// Similar to `new` but can reuse previously used container with allocated capacity.
|
||||
#[inline]
|
||||
pub(crate) fn new_or_pooled(lua: &'lua Lua) -> MultiValue<'lua> {
|
||||
lua.new_multivalue_from_pool()
|
||||
}
|
||||
|
||||
/// Clears and returns previously allocated multivalue container to the pool.
|
||||
#[inline]
|
||||
pub(crate) fn return_to_pool(multivalue: Self, lua: &Lua) {
|
||||
lua.return_multivalue_to_pool(multivalue);
|
||||
pub(crate) fn with_lua_and_capacity(lua: &'lua Lua, capacity: usize) -> MultiValue<'lua> {
|
||||
let vec = lua
|
||||
.pop_multivalue_from_pool()
|
||||
.map(|mut vec| {
|
||||
vec.reserve(capacity);
|
||||
vec
|
||||
})
|
||||
.unwrap_or_else(|| Vec::with_capacity(capacity));
|
||||
MultiValue {
|
||||
vec,
|
||||
lua: Some(lua),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,8 +785,10 @@ impl<'lua> IntoIterator for MultiValue<'lua> {
|
||||
type IntoIter = iter::Rev<vec::IntoIter<Value<'lua>>>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter().rev()
|
||||
fn into_iter(mut self) -> Self::IntoIter {
|
||||
let vec = mem::take(&mut self.vec);
|
||||
mem::forget(self);
|
||||
vec.into_iter().rev()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -400,7 +798,7 @@ impl<'a, 'lua> IntoIterator for &'a MultiValue<'lua> {
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.iter().rev()
|
||||
self.vec.iter().rev()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,64 +821,60 @@ impl<'lua> Index<usize> for MultiValue<'lua> {
|
||||
|
||||
impl<'lua> MultiValue<'lua> {
|
||||
#[inline]
|
||||
pub fn from_vec(mut v: Vec<Value<'lua>>) -> MultiValue<'lua> {
|
||||
v.reverse();
|
||||
MultiValue(v)
|
||||
pub fn from_vec(mut vec: Vec<Value<'lua>>) -> MultiValue<'lua> {
|
||||
vec.reverse();
|
||||
MultiValue { vec, lua: None }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_vec(self) -> Vec<Value<'lua>> {
|
||||
let mut v = self.0;
|
||||
v.reverse();
|
||||
v
|
||||
pub fn into_vec(mut self) -> Vec<Value<'lua>> {
|
||||
let mut vec = mem::take(&mut self.vec);
|
||||
mem::forget(self);
|
||||
vec.reverse();
|
||||
vec
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self, index: usize) -> Option<&Value<'lua>> {
|
||||
if index < self.0.len() {
|
||||
return self.0.get(self.0.len() - index - 1);
|
||||
if index < self.vec.len() {
|
||||
return self.vec.get(self.vec.len() - index - 1);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn reserve(&mut self, size: usize) {
|
||||
self.0.reserve(size);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pop_front(&mut self) -> Option<Value<'lua>> {
|
||||
self.0.pop()
|
||||
self.vec.pop()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn push_front(&mut self, value: Value<'lua>) {
|
||||
self.0.push(value);
|
||||
self.vec.push(value);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
self.vec.clear();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
self.vec.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
self.vec.is_empty()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn iter(&self) -> iter::Rev<slice::Iter<Value<'lua>>> {
|
||||
self.0.iter().rev()
|
||||
self.vec.iter().rev()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn drain_all(&mut self) -> iter::Rev<vec::Drain<Value<'lua>>> {
|
||||
self.0.drain(..).rev()
|
||||
self.vec.drain(..).rev()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -488,11 +882,11 @@ impl<'lua> MultiValue<'lua> {
|
||||
&mut self,
|
||||
iter: impl IntoIterator<Item = Result<Value<'lua>>>,
|
||||
) -> Result<()> {
|
||||
self.0.clear();
|
||||
self.vec.clear();
|
||||
for value in iter {
|
||||
self.0.push(value?);
|
||||
self.vec.push(value?);
|
||||
}
|
||||
self.0.reverse();
|
||||
self.vec.reverse();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -501,9 +895,26 @@ impl<'lua> MultiValue<'lua> {
|
||||
///
|
||||
/// This is a generalization of `IntoLua`, allowing any number of resulting Lua values instead of just
|
||||
/// one. Any type that implements `IntoLua` will automatically implement this trait.
|
||||
pub trait IntoLuaMulti<'lua> {
|
||||
pub trait IntoLuaMulti<'lua>: Sized {
|
||||
/// Performs the conversion.
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>>;
|
||||
|
||||
/// Pushes the values into the Lua stack.
|
||||
///
|
||||
/// Returns number of pushed values.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn push_into_stack_multi(self, lua: &'lua Lua) -> Result<c_int> {
|
||||
let mut values = self.into_lua_multi(lua)?;
|
||||
let len: c_int = values.len().try_into().unwrap();
|
||||
unsafe {
|
||||
check_stack(lua.state(), len + 1)?;
|
||||
for v in values.drain_all() {
|
||||
lua.push_value(v)?;
|
||||
}
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types that can be created from an arbitrary number of Lua values.
|
||||
@@ -525,15 +936,43 @@ pub trait FromLuaMulti<'lua>: Sized {
|
||||
/// `to` is a function name that received the arguments.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
fn from_lua_multi_args(
|
||||
values: MultiValue<'lua>,
|
||||
fn from_lua_args(
|
||||
args: MultiValue<'lua>,
|
||||
i: usize,
|
||||
to: Option<&str>,
|
||||
lua: &'lua Lua,
|
||||
) -> Result<Self> {
|
||||
let _ = (i, to);
|
||||
Self::from_lua_multi(args, lua)
|
||||
}
|
||||
|
||||
/// Performs the conversion for a number of values in the Lua stack.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_multi(nvals: c_int, lua: &'lua Lua) -> Result<Self> {
|
||||
let mut values = MultiValue::with_lua_and_capacity(lua, nvals as usize);
|
||||
for idx in 1..=nvals {
|
||||
values.push_front(lua.stack_value(-idx));
|
||||
}
|
||||
if nvals > 0 {
|
||||
// It's safe to clear the stack as all references moved to ref thread
|
||||
ffi::lua_pop(lua.state(), nvals);
|
||||
}
|
||||
Self::from_lua_multi(values, lua)
|
||||
}
|
||||
|
||||
/// Same as `from_lua_args` but for a number of values in the Lua stack.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_args(
|
||||
nargs: c_int,
|
||||
i: usize,
|
||||
to: Option<&str>,
|
||||
lua: &'lua Lua,
|
||||
) -> Result<Self> {
|
||||
let _ = (i, to);
|
||||
Self::from_stack_multi(nargs, lua)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
+21
-2
@@ -6,8 +6,8 @@ use std::time::Duration;
|
||||
use futures_util::stream::TryStreamExt;
|
||||
|
||||
use mlua::{
|
||||
AnyUserDataExt, Error, Function, Lua, LuaOptions, Result, StdLib, Table, TableExt, UserData,
|
||||
UserDataMethods, Value,
|
||||
AnyUserDataExt, Error, Function, Lua, LuaOptions, MultiValue, Result, StdLib, Table, TableExt,
|
||||
UserData, UserDataMethods, Value,
|
||||
};
|
||||
|
||||
async fn sleep_ms(ms: u64) {
|
||||
@@ -82,6 +82,25 @@ async fn test_async_call() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_call_many_returns() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let hello = lua.create_async_function(|_lua, ()| async move {
|
||||
sleep_ms(10).await;
|
||||
Ok(("a", "b", "c", 1))
|
||||
})?;
|
||||
|
||||
let vals = hello.call_async::<_, MultiValue>(()).await?;
|
||||
assert_eq!(vals.len(), 4);
|
||||
assert_eq!(vals[0].to_string()?, "a");
|
||||
assert_eq!(vals[1].to_string()?, "b");
|
||||
assert_eq!(vals[2].to_string()?, "c");
|
||||
assert_eq!(vals[3], Value::Integer(1));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_bind_call() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
use std::fs;
|
||||
use std::io;
|
||||
|
||||
use mlua::{AnyUserData, Function, Lua, Result};
|
||||
use mlua::{Lua, Result};
|
||||
|
||||
#[test]
|
||||
fn test_chunk_path() -> Result<()> {
|
||||
@@ -36,8 +36,8 @@ fn test_chunk_macro() -> Result<()> {
|
||||
let data = lua.create_table()?;
|
||||
data.raw_set("num", 1)?;
|
||||
|
||||
let ud = AnyUserData::wrap("hello");
|
||||
let f = Function::wrap(|_lua, ()| Ok(()));
|
||||
let ud = mlua::AnyUserData::wrap("hello");
|
||||
let f = mlua::Function::wrap(|_lua, ()| Ok(()));
|
||||
|
||||
lua.globals().set("g", 123)?;
|
||||
|
||||
|
||||
+44
-2
@@ -84,7 +84,7 @@ fn test_vectors() -> Result<()> {
|
||||
assert(v.z == 3)
|
||||
"#,
|
||||
)
|
||||
.set_compiler(Compiler::new().set_vector_ctor(Some("vector".to_string())))
|
||||
.set_compiler(Compiler::new().set_vector_ctor("vector"))
|
||||
.exec()?;
|
||||
|
||||
Ok(())
|
||||
@@ -124,7 +124,49 @@ fn test_vectors() -> Result<()> {
|
||||
assert(v.w == 4)
|
||||
"#,
|
||||
)
|
||||
.set_compiler(Compiler::new().set_vector_ctor(Some("vector".to_string())))
|
||||
.set_compiler(Compiler::new().set_vector_ctor("vector"))
|
||||
.exec()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "luau-vector4"), feature = "unstable"))]
|
||||
#[test]
|
||||
fn test_vector_metatable() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let vector_mt = lua
|
||||
.load(
|
||||
r#"
|
||||
{
|
||||
__index = {
|
||||
new = vector,
|
||||
|
||||
product = function(a, b)
|
||||
return vector(a.x * b.x, a.y * b.y, a.z * b.z)
|
||||
end
|
||||
}
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.eval::<Table>()?;
|
||||
vector_mt.set_metatable(Some(vector_mt.clone()));
|
||||
lua.set_vector_metatable(Some(vector_mt.clone()));
|
||||
lua.globals().set("Vector3", vector_mt)?;
|
||||
|
||||
let compiler = Compiler::new()
|
||||
.set_vector_lib("Vector3")
|
||||
.set_vector_ctor("new");
|
||||
|
||||
// Test vector methods (fastcall)
|
||||
lua.load(
|
||||
r#"
|
||||
local v = Vector3.new(1, 2, 3)
|
||||
local v2 = v:product(Vector3.new(2, 3, 4))
|
||||
assert(v2.x == 2 and v2.y == 6 and v2.z == 12)
|
||||
"#,
|
||||
)
|
||||
.set_compiler(compiler)
|
||||
.exec()?;
|
||||
|
||||
Ok(())
|
||||
|
||||
+42
-3
@@ -1,5 +1,6 @@
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
|
||||
use mlua::{
|
||||
@@ -76,6 +77,7 @@ fn test_scope_userdata_fields() -> Result<()> {
|
||||
|
||||
impl<'a> UserData for MyUserData<'a> {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field("field", "hello");
|
||||
fields.add_field_method_get("val", |_, data| Ok(data.0.get()));
|
||||
fields.add_field_method_set("val", |_, data, val| {
|
||||
data.0.set(val);
|
||||
@@ -91,6 +93,7 @@ fn test_scope_userdata_fields() -> Result<()> {
|
||||
.load(
|
||||
r#"
|
||||
function(u)
|
||||
assert(u.field == "hello")
|
||||
assert(u.val == 42)
|
||||
u.val = 44
|
||||
end
|
||||
@@ -227,9 +230,19 @@ fn test_scope_userdata_mismatch() -> Result<()> {
|
||||
let bu = scope.create_nonstatic_userdata(MyUserData(&b))?;
|
||||
assert!(okay.call::<_, ()>((au.clone(), bu.clone())).is_ok());
|
||||
match bad.call::<_, ()>((au, bu)) {
|
||||
Err(Error::CallbackError { ref cause, .. }) => match *cause.as_ref() {
|
||||
Error::UserDataTypeMismatch => {}
|
||||
ref other => panic!("wrong error type {:?}", other),
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
Error::BadArgument {
|
||||
to,
|
||||
pos,
|
||||
name,
|
||||
cause,
|
||||
} => {
|
||||
assert_eq!(to.as_deref(), Some("MyUserData.inc"));
|
||||
assert_eq!(*pos, 1);
|
||||
assert_eq!(name.as_deref(), Some("self"));
|
||||
assert!(matches!(*cause.as_ref(), Error::UserDataTypeMismatch));
|
||||
}
|
||||
other => panic!("wrong error type {:?}", other),
|
||||
},
|
||||
Err(other) => panic!("wrong error type {:?}", other),
|
||||
Ok(_) => panic!("incorrectly returned Ok"),
|
||||
@@ -417,6 +430,32 @@ fn test_scope_userdata_ref_mut() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_any_userdata() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.register_userdata_type::<StdString>(|reg| {
|
||||
reg.add_meta_method("__tostring", |_, data, ()| Ok(data.clone()));
|
||||
})?;
|
||||
|
||||
lua.scope(|scope| {
|
||||
let ud = scope.create_any_userdata(StdString::from("foo"))?;
|
||||
lua.globals().set("ud", ud)?;
|
||||
lua.load("assert(tostring(ud) == 'foo')").exec()
|
||||
})?;
|
||||
|
||||
// Check that userdata is destructed
|
||||
match lua.load("tostring(ud)").exec() {
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
Error::CallbackDestructed => {}
|
||||
err => panic!("expected CallbackDestructed, got {:?}", err),
|
||||
},
|
||||
r => panic!("improper return for destructed userdata: {:?}", r),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_any_userdata_ref() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
+88
-2
@@ -4,8 +4,8 @@ use std::collections::HashMap;
|
||||
use std::error::Error as StdError;
|
||||
|
||||
use mlua::{
|
||||
DeserializeOptions, Error, Lua, LuaSerdeExt, Result as LuaResult, SerializeOptions, UserData,
|
||||
Value,
|
||||
DeserializeOptions, Error, ExternalResult, Lua, LuaSerdeExt, Result as LuaResult,
|
||||
SerializeOptions, UserData, Value,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -191,6 +191,69 @@ fn test_serialize_vector() -> Result<(), Box<dyn StdError>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_sorted() -> LuaResult<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let globals = lua.globals();
|
||||
globals.set("null", lua.null())?;
|
||||
|
||||
let empty_array = lua.create_table()?;
|
||||
empty_array.set_metatable(Some(lua.array_metatable()));
|
||||
globals.set("empty_array", empty_array)?;
|
||||
|
||||
let value = lua
|
||||
.load(
|
||||
r#"
|
||||
{
|
||||
_bool = true,
|
||||
_integer = 123,
|
||||
_number = 321.99,
|
||||
_string = "test string serialization",
|
||||
_table_arr = {nil, "value 1", nil, "value 2", {}},
|
||||
_table_map = {["table"] = "map", ["null"] = null},
|
||||
_bytes = "\240\040\140\040",
|
||||
_null = null,
|
||||
_empty_map = {},
|
||||
_empty_array = empty_array,
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.eval::<Value>()?;
|
||||
|
||||
let json = serde_json::to_string(&value.to_serializable().sort_keys(true)).unwrap();
|
||||
assert_eq!(
|
||||
json,
|
||||
r#"{"_bool":true,"_bytes":[240,40,140,40],"_empty_array":[],"_empty_map":{},"_integer":123,"_null":null,"_number":321.99,"_string":"test string serialization","_table_arr":[null,"value 1",null,"value 2",{}],"_table_map":{"null":null,"table":"map"}}"#
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_globals() -> LuaResult<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let globals = Value::Table(lua.globals());
|
||||
|
||||
// By default it should not work
|
||||
if let Ok(v) = serde_json::to_value(&globals) {
|
||||
panic!("expected serialization error, got {v:?}");
|
||||
}
|
||||
|
||||
// It should work with `deny_recursive_tables` and `deny_unsupported_types` disabled
|
||||
if let Err(err) = serde_json::to_value(
|
||||
globals
|
||||
.to_serializable()
|
||||
.deny_recursive_tables(false)
|
||||
.deny_unsupported_types(false),
|
||||
) {
|
||||
panic!("expected no errors, got {err:?}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_value_struct() -> LuaResult<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -611,3 +674,26 @@ fn test_from_value_userdata() -> Result<(), Box<dyn StdError>> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value_sorted() -> Result<(), Box<dyn StdError>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let to_json = lua.create_function(|lua, value| {
|
||||
let json_value: serde_json::Value =
|
||||
lua.from_value_with(value, DeserializeOptions::new().sort_keys(true))?;
|
||||
serde_json::to_string(&json_value).into_lua_err()
|
||||
})?;
|
||||
lua.globals().set("to_json", to_json)?;
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
local json = to_json({c = 3, b = 2, hello = "world", x = {1}, ["0a"] = {z = "z", d = "d"}})
|
||||
assert(json == '{"0a":{"d":"d","z":"z"},"b":2,"c":3,"hello":"world","x":[1]}', "invalid json")
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+16
-9
@@ -504,25 +504,32 @@ fn test_result_conversions() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
let err = lua.create_function(|_, ()| {
|
||||
Ok(Err::<String, _>(
|
||||
"only through failure can we succeed".into_lua_err(),
|
||||
))
|
||||
})?;
|
||||
let ok = lua.create_function(|_, ()| Ok(Ok::<_, Error>("!".to_owned())))?;
|
||||
let ok = lua.create_function(|_, ()| Ok(Ok::<(), Error>(())))?;
|
||||
let err = lua.create_function(|_, ()| Ok(Err::<(), _>("failure1".into_lua_err())))?;
|
||||
let ok2 = lua.create_function(|_, ()| Ok(Ok::<_, Error>("!".to_owned())))?;
|
||||
let err2 = lua.create_function(|_, ()| Ok(Err::<String, _>("failure2".into_lua_err())))?;
|
||||
|
||||
globals.set("err", err)?;
|
||||
globals.set("ok", ok)?;
|
||||
globals.set("ok2", ok2)?;
|
||||
globals.set("err", err)?;
|
||||
globals.set("err2", err2)?;
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
local r, e = ok()
|
||||
assert(r == nil and e == nil)
|
||||
|
||||
local r, e = err()
|
||||
assert(r == nil)
|
||||
assert(tostring(e):find("only through failure can we succeed") ~= nil)
|
||||
assert(tostring(e):find("failure1") ~= nil)
|
||||
|
||||
local r, e = ok()
|
||||
local r, e = ok2()
|
||||
assert(r == "!")
|
||||
assert(e == nil)
|
||||
|
||||
local r, e = err2()
|
||||
assert(r == nil)
|
||||
assert(tostring(e):find("failure2") ~= nil)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
@@ -190,3 +190,34 @@ fn test_coroutine_panic() {
|
||||
Err(p) => assert!(*p.downcast::<&str>().unwrap() == "test_panic"),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_thread() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let accumulate = lua
|
||||
.create_thread(
|
||||
lua.load(
|
||||
r#"
|
||||
function (sum)
|
||||
while true do
|
||||
sum = sum + coroutine.yield(sum)
|
||||
end
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.eval::<Function>()?,
|
||||
)?
|
||||
.into_owned();
|
||||
|
||||
for i in 0..4 {
|
||||
accumulate.resume::<_, ()>(i)?;
|
||||
}
|
||||
assert_eq!(accumulate.resume::<_, i64>(4)?, 10);
|
||||
assert_eq!(accumulate.status(), ThreadStatus::Resumable);
|
||||
assert!(accumulate.resume::<_, ()>("error").is_err());
|
||||
assert_eq!(accumulate.status(), ThreadStatus::Error);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+25
-1
@@ -15,7 +15,7 @@ use std::sync::atomic::{AtomicI64, Ordering};
|
||||
|
||||
use mlua::{
|
||||
AnyUserData, AnyUserDataExt, Error, ExternalError, Function, Lua, MetaMethod, Nil, Result,
|
||||
String, UserData, UserDataFields, UserDataMethods, UserDataRef, Value,
|
||||
String, UserData, UserDataFields, UserDataMethods, UserDataRef, Value, Variadic,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -92,6 +92,30 @@ fn test_methods() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_method_variadic() -> Result<()> {
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("get", |_, data, ()| Ok(data.0));
|
||||
methods.add_method_mut("add", |_, data, vals: Variadic<i64>| {
|
||||
data.0 += vals.into_iter().sum::<i64>();
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("userdata", MyUserData(0))?;
|
||||
lua.load("userdata:add(1, 5, -10)").exec()?;
|
||||
let ud: UserDataRef<MyUserData> = globals.get("userdata")?;
|
||||
assert_eq!(ud.0, -4);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_metamethods() -> Result<()> {
|
||||
#[derive(Copy, Clone)]
|
||||
|
||||
@@ -158,3 +158,74 @@ fn test_debug_format() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_value_conversions() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
assert!(Value::Nil.is_nil());
|
||||
assert!(!Value::NULL.is_nil());
|
||||
assert!(Value::NULL.is_null());
|
||||
assert!(Value::NULL.is_light_userdata());
|
||||
assert!(Value::NULL.as_light_userdata() == Some(LightUserData(ptr::null_mut())));
|
||||
assert!(Value::Boolean(true).is_boolean());
|
||||
assert_eq!(Value::Boolean(false).as_boolean(), Some(false));
|
||||
assert!(Value::Integer(1).is_integer());
|
||||
assert_eq!(Value::Integer(1).as_integer(), Some(1));
|
||||
assert_eq!(Value::Integer(1).as_i32(), Some(1i32));
|
||||
assert_eq!(Value::Integer(1).as_u32(), Some(1u32));
|
||||
assert_eq!(Value::Integer(1).as_i64(), Some(1i64));
|
||||
assert_eq!(Value::Integer(1).as_u64(), Some(1u64));
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
{
|
||||
assert_eq!(Value::Integer(mlua::Integer::MAX).as_i32(), None);
|
||||
assert_eq!(Value::Integer(mlua::Integer::MAX).as_u32(), None);
|
||||
}
|
||||
assert_eq!(Value::Integer(1).as_isize(), Some(1isize));
|
||||
assert_eq!(Value::Integer(1).as_usize(), Some(1usize));
|
||||
assert!(Value::Number(1.23).is_number());
|
||||
assert_eq!(Value::Number(1.23).as_number(), Some(1.23));
|
||||
assert_eq!(Value::Number(1.23).as_f32(), Some(1.23f32));
|
||||
assert_eq!(Value::Number(1.23).as_f64(), Some(1.23f64));
|
||||
assert!(Value::String(lua.create_string("hello")?).is_string());
|
||||
assert_eq!(
|
||||
Value::String(lua.create_string("hello")?)
|
||||
.as_string()
|
||||
.unwrap(),
|
||||
"hello"
|
||||
);
|
||||
assert_eq!(
|
||||
Value::String(lua.create_string("hello")?).as_str().unwrap(),
|
||||
"hello"
|
||||
);
|
||||
assert_eq!(
|
||||
Value::String(lua.create_string("hello")?)
|
||||
.as_string_lossy()
|
||||
.unwrap(),
|
||||
"hello"
|
||||
);
|
||||
assert!(Value::Table(lua.create_table()?).is_table());
|
||||
assert!(Value::Table(lua.create_table()?).as_table().is_some());
|
||||
assert!(Value::Function(lua.create_function(|_, ()| Ok(())).unwrap()).is_function());
|
||||
assert!(
|
||||
Value::Function(lua.create_function(|_, ()| Ok(())).unwrap())
|
||||
.as_function()
|
||||
.is_some()
|
||||
);
|
||||
assert!(Value::Thread(lua.create_thread(lua.load("function() end").eval()?)?).is_thread());
|
||||
assert!(
|
||||
Value::Thread(lua.create_thread(lua.load("function() end").eval()?)?)
|
||||
.as_thread()
|
||||
.is_some()
|
||||
);
|
||||
assert!(Value::UserData(lua.create_any_userdata("hello")?).is_userdata());
|
||||
assert_eq!(
|
||||
Value::UserData(lua.create_any_userdata("hello")?)
|
||||
.as_userdata()
|
||||
.and_then(|ud| ud.borrow::<&str>().ok())
|
||||
.as_deref(),
|
||||
Some(&"hello")
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user