Compare commits

...

10 Commits

Author SHA1 Message Date
Michael Pfaff 4dd12531d9
API changes, inline more often 2023-07-05 16:34:17 -04:00
Alex Orlenko 399e469328
Update "async userdata method" benchmark 2023-06-21 22:23:43 +01:00
Alex Orlenko 1367a033d7
Don't clone function when doing call_async() 2023-06-21 13:14:17 +01:00
Alex Orlenko c1168d3ec1
Refactor `call_async()` functions to use static dispatch outside of traits 2023-06-21 12:44:24 +01:00
Alex Orlenko b05698d55b
impl UserData for Rc<T> and Arc<T> where T: UserData 2023-06-21 01:30:09 +01:00
Alex Orlenko aeacf6cacc
Remove `allow(dead_code)` from mlua-sys build scripts 2023-06-20 23:49:48 +01:00
Alex Orlenko 1f0e81c9a1
Add a dedicated type for Luau vector.
Refactor existing implementation and add 4-dimensional vectors support.
2023-06-20 13:30:42 +01:00
Alex Orlenko c2bfc9ec52
Implement `PartialEq<[T]>` for tables 2023-06-19 23:28:08 +01:00
Alex Orlenko 9fdba541e9
Update `UserDataMethods::add_async_method()` functions to take `&T` as second argument instead of cloning `T`.
New functions: `UserDataMethods::add_async_method_mut()`, `UserDataMethods::add_async_meta_method_mut()`.
2023-06-15 00:34:41 +01:00
Alex Orlenko cf0524aa23
Use lua_iscfunction instead of lua_getinfo in `Function::environment()` 2023-06-08 09:33:48 +01:00
45 changed files with 1405 additions and 679 deletions

View File

@ -9,7 +9,7 @@ jobs:
matrix: matrix:
os: [ubuntu-22.04, macos-latest, windows-latest] os: [ubuntu-22.04, macos-latest, windows-latest]
rust: [stable] rust: [stable]
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit] lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
include: include:
- os: ubuntu-22.04 - os: ubuntu-22.04
target: x86_64-unknown-linux-gnu target: x86_64-unknown-linux-gnu
@ -104,7 +104,7 @@ jobs:
matrix: matrix:
os: [ubuntu-22.04, macos-latest, windows-latest] os: [ubuntu-22.04, macos-latest, windows-latest]
rust: [stable, nightly] rust: [stable, nightly]
lua: [lua54, lua53, lua52, lua51, luajit, luajit52, luau, luau-jit] lua: [lua54, lua53, lua52, lua51, luajit, luajit52, luau, luau-jit, luau-vector4]
include: include:
- os: ubuntu-22.04 - os: ubuntu-22.04
target: x86_64-unknown-linux-gnu target: x86_64-unknown-linux-gnu
@ -140,7 +140,7 @@ jobs:
matrix: matrix:
os: [ubuntu-22.04] os: [ubuntu-22.04]
rust: [nightly] rust: [nightly]
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit] lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
include: include:
- os: ubuntu-22.04 - os: ubuntu-22.04
target: x86_64-unknown-linux-gnu target: x86_64-unknown-linux-gnu
@ -222,7 +222,7 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
strategy: strategy:
matrix: matrix:
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit] lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable

View File

@ -33,6 +33,7 @@ luajit = ["ffi/luajit"]
luajit52 = ["luajit", "ffi/luajit52"] luajit52 = ["luajit", "ffi/luajit52"]
luau = ["ffi/luau"] luau = ["ffi/luau"]
luau-jit = ["luau", "ffi/luau-codegen"] luau-jit = ["luau", "ffi/luau-codegen"]
luau-vector4 = ["luau", "ffi/luau-vector4"]
vendored = ["ffi/vendored"] vendored = ["ffi/vendored"]
module = ["mlua_derive", "ffi/module"] module = ["mlua_derive", "ffi/module"]
async = ["futures-util"] async = ["futures-util"]

View File

@ -46,6 +46,7 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
* `luajit52`: activate [LuaJIT] support with partial compatibility with Lua 5.2 * `luajit52`: activate [LuaJIT] support with partial compatibility with Lua 5.2
* `luau`: activate [Luau] support (auto vendored mode) * `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 experimental jit backend. This is unstable feature and not recommended to use.
* `luau-vector4`: activate [Luau] support with 4-dimensional vector.
* `vendored`: build static Lua(JIT) library from sources during `mlua` compilation using [lua-src] or [luajit-src] crates * `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) * `module`: enable module mode (building loadable `cdylib` library for Lua)
* `async`: enable async/await support (any executor can be used, eg. [tokio] or [async-std]) * `async`: enable async/await support (any executor can be used, eg. [tokio] or [async-std])

View File

@ -264,17 +264,19 @@ fn call_userdata_method(c: &mut Criterion) {
} }
fn call_async_userdata_method(c: &mut Criterion) { fn call_async_userdata_method(c: &mut Criterion) {
#[derive(Clone, Copy)] struct UserData(String);
struct UserData(i64);
impl LuaUserData for UserData { impl LuaUserData for UserData {
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("method", |_, this, ()| async move { Ok(this.0) }); methods.add_async_method("method", |_, this, ()| async move { Ok(this.0.clone()) });
} }
} }
let options = LuaOptions::new().thread_pool_size(1024); let options = LuaOptions::new().thread_pool_size(1024);
let lua = Lua::new_with(LuaStdLib::ALL_SAFE, options).unwrap(); let lua = Lua::new_with(LuaStdLib::ALL_SAFE, options).unwrap();
lua.globals().set("userdata", UserData(10)).unwrap(); lua.globals()
.set("userdata", UserData("hello".to_string()))
.unwrap();
c.bench_function("call async [userdata method] 10", |b| { c.bench_function("call async [userdata method] 10", |b| {
let rt = Runtime::new().unwrap(); let rt = Runtime::new().unwrap();

View File

@ -3,14 +3,13 @@ use std::collections::HashMap;
use hyper::body::{Body as HyperBody, HttpBody as _}; use hyper::body::{Body as HyperBody, HttpBody as _};
use hyper::Client as HyperClient; use hyper::Client as HyperClient;
use mlua::{chunk, AnyUserData, ExternalResult, Lua, Result, UserData, UserDataMethods}; use mlua::{chunk, ExternalResult, Lua, Result, UserData, UserDataMethods};
struct BodyReader(HyperBody); struct BodyReader(HyperBody);
impl UserData for BodyReader { impl UserData for BodyReader {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_function("read", |lua, reader: AnyUserData| async move { methods.add_async_method_mut("read", |lua, reader, ()| async move {
let mut reader = reader.borrow_mut::<Self>()?;
if let Some(bytes) = reader.0.data().await { if let Some(bytes) = reader.0.data().await {
let bytes = bytes.into_lua_err()?; let bytes = bytes.into_lua_err()?;
return Some(lua.create_string(&bytes)).transpose(); return Some(lua.create_string(&bytes)).transpose();

View File

@ -6,9 +6,7 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener, TcpStream}; use tokio::net::{TcpListener, TcpStream};
use tokio::task; use tokio::task;
use mlua::{ use mlua::{chunk, Function, Lua, RegistryKey, String as LuaString, UserData, UserDataMethods};
chunk, AnyUserData, Function, Lua, RegistryKey, String as LuaString, UserData, UserDataMethods,
};
struct LuaTcpStream(TcpStream); struct LuaTcpStream(TcpStream);
@ -18,28 +16,19 @@ impl UserData for LuaTcpStream {
Ok(this.0.peer_addr()?.to_string()) Ok(this.0.peer_addr()?.to_string())
}); });
methods.add_async_function( methods.add_async_method_mut("read", |lua, this, size| async move {
"read", let mut buf = vec![0; size];
|lua, (this, size): (AnyUserData, usize)| async move { let n = this.0.read(&mut buf).await?;
let mut this = this.borrow_mut::<Self>()?; buf.truncate(n);
let mut buf = vec![0; size]; lua.create_string(&buf)
let n = this.0.read(&mut buf).await?; });
buf.truncate(n);
lua.create_string(&buf)
},
);
methods.add_async_function( methods.add_async_method_mut("write", |_, this, data: LuaString| async move {
"write", let n = this.0.write(&data.as_bytes()).await?;
|_, (this, data): (AnyUserData, LuaString)| async move { Ok(n)
let mut this = this.borrow_mut::<Self>()?; });
let n = this.0.write(&data.as_bytes()).await?;
Ok(n)
},
);
methods.add_async_function("close", |_, this: AnyUserData| async move { methods.add_async_method_mut("close", |_, this, ()| async move {
let mut this = this.borrow_mut::<Self>()?;
this.0.shutdown().await?; this.0.shutdown().await?;
Ok(()) Ok(())
}); });

View File

@ -27,6 +27,7 @@ luajit = []
luajit52 = ["luajit"] luajit52 = ["luajit"]
luau = ["luau0-src"] luau = ["luau0-src"]
luau-codegen = ["luau"] luau-codegen = ["luau"]
luau-vector4 = ["luau"]
vendored = ["lua-src", "luajit-src"] vendored = ["lua-src", "luajit-src"]
module = [] module = []
@ -38,4 +39,4 @@ cfg-if = "1.0"
pkg-config = "0.3.17" pkg-config = "0.3.17"
lua-src = { version = ">= 546.0.0, < 550.0.0", optional = true } lua-src = { version = ">= 546.0.0, < 550.0.0", optional = true }
luajit-src = { version = ">= 210.4.0, < 220.0.0", optional = true } luajit-src = { version = ">= 210.4.0, < 220.0.0", optional = true }
luau0-src = { version = "0.5.8", optional = true } luau0-src = { version = "0.5.10", optional = true }

View File

@ -1,5 +1,3 @@
#![allow(dead_code)]
use std::env; use std::env;
use std::ops::Bound; use std::ops::Bound;
use std::path::PathBuf; use std::path::PathBuf;

View File

@ -1,5 +1,3 @@
#![allow(dead_code)]
use std::path::PathBuf; use std::path::PathBuf;
pub fn probe_lua() -> Option<PathBuf> { pub fn probe_lua() -> Option<PathBuf> {
@ -23,6 +21,7 @@ pub fn probe_lua() -> Option<PathBuf> {
#[cfg(feature = "luau")] #[cfg(feature = "luau")]
let artifacts = luau0_src::Build::new() let artifacts = luau0_src::Build::new()
.enable_codegen(cfg!(feature = "luau-codegen")) .enable_codegen(cfg!(feature = "luau-codegen"))
.set_vector_size(if cfg!(feature = "luau-vector4") { 4 } else { 3 })
.build(); .build();
artifacts.print_cargo_metadata(); artifacts.print_cargo_metadata();

View File

@ -397,6 +397,7 @@ pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
res res
} }
// TODO: why not just checkstack and concat all at the end?
pub unsafe fn luaL_traceback( pub unsafe fn luaL_traceback(
L: *mut lua_State, L: *mut lua_State,
L1: *mut lua_State, L1: *mut lua_State,

View File

@ -156,7 +156,10 @@ extern "C" {
pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number); pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number);
pub fn lua_pushinteger(L: *mut lua_State, n: lua_Integer); pub fn lua_pushinteger(L: *mut lua_State, n: lua_Integer);
pub fn lua_pushunsigned(L: *mut lua_State, n: lua_Unsigned); pub fn lua_pushunsigned(L: *mut lua_State, n: lua_Unsigned);
#[cfg(not(feature = "luau-vector4"))]
pub fn lua_pushvector(L: *mut lua_State, x: c_float, y: c_float, z: c_float); pub fn lua_pushvector(L: *mut lua_State, x: c_float, y: c_float, z: c_float);
#[cfg(feature = "luau-vector4")]
pub fn lua_pushvector(L: *mut lua_State, x: c_float, y: c_float, z: c_float, w: c_float);
#[link_name = "lua_pushlstring"] #[link_name = "lua_pushlstring"]
pub fn lua_pushlstring_(L: *mut lua_State, s: *const c_char, l: usize); pub fn lua_pushlstring_(L: *mut lua_State, s: *const c_char, l: usize);
#[link_name = "lua_pushstring"] #[link_name = "lua_pushstring"]

View File

@ -11,9 +11,6 @@ use crate::lua::Lua;
use crate::table::Table; use crate::table::Table;
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti}; use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti};
#[cfg(feature = "async")]
use futures_util::future::{self, LocalBoxFuture};
/// Trait for types [loadable by Lua] and convertible to a [`Chunk`] /// Trait for types [loadable by Lua] and convertible to a [`Chunk`]
/// ///
/// [loadable by Lua]: https://www.lua.org/manual/5.4/manual.html#3.3.2 /// [loadable by Lua]: https://www.lua.org/manual/5.4/manual.html#3.3.2
@ -312,8 +309,8 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
/// [`exec`]: #method.exec /// [`exec`]: #method.exec
#[cfg(feature = "async")] #[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))] #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn exec_async(self) -> LocalBoxFuture<'lua, Result<()>> { pub async fn exec_async(self) -> Result<()> {
self.call_async(()) self.call_async(()).await
} }
/// Evaluate the chunk as either an expression or block. /// Evaluate the chunk as either an expression or block.
@ -344,17 +341,16 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
/// [`eval`]: #method.eval /// [`eval`]: #method.eval
#[cfg(feature = "async")] #[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))] #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn eval_async<'fut, R>(self) -> LocalBoxFuture<'fut, Result<R>> pub async fn eval_async<R>(self) -> Result<R>
where where
'lua: 'fut, R: FromLuaMulti<'lua> + 'lua,
R: FromLuaMulti<'lua> + 'fut,
{ {
if self.detect_mode() == ChunkMode::Binary { if self.detect_mode() == ChunkMode::Binary {
self.call_async(()) self.call_async(()).await
} else if let Ok(function) = self.to_expression() { } else if let Ok(function) = self.to_expression() {
function.call_async(()) function.call_async(()).await
} else { } else {
self.call_async(()) self.call_async(()).await
} }
} }
@ -374,16 +370,12 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
/// [`call`]: #method.call /// [`call`]: #method.call
#[cfg(feature = "async")] #[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))] #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn call_async<'fut, A, R>(self, args: A) -> LocalBoxFuture<'fut, Result<R>> pub async fn call_async<A, R>(self, args: A) -> Result<R>
where where
'lua: 'fut,
A: IntoLuaMulti<'lua>, A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut, R: FromLuaMulti<'lua> + 'lua,
{ {
match self.into_function() { self.into_function()?.call_async(args).await
Ok(func) => func.call_async(args),
Err(e) => Box::pin(future::err(e)),
}
} }
/// Load this chunk into a regular `Function`. /// Load this chunk into a regular `Function`.

View File

@ -38,30 +38,45 @@ impl<'lua> FromLua<'lua> for Value<'lua> {
} }
} }
impl<'lua> From<String<'lua>> for Value<'lua> {
#[inline]
fn from(value: String<'lua>) -> Self {
Value::String(value)
}
}
impl<'lua> IntoLua<'lua> for String<'lua> { impl<'lua> IntoLua<'lua> for String<'lua> {
#[inline] #[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> { fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(self)) Ok(self.into())
} }
} }
impl<'lua> FromLua<'lua> for String<'lua> { impl<'lua> FromLua<'lua> for String<'lua> {
#[inline] #[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<String<'lua>> { fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<String<'lua>> {
let ty = value.type_name(); match value {
lua.coerce_string(value)? Value::String(s) => Ok(s),
.ok_or_else(|| Error::FromLuaConversionError { _ => Err(Error::FromLuaConversionError {
from: ty, from: value.type_name(),
to: "String", to: "string",
message: Some("expected string or number".to_string()), message: None,
}) })
}
}
}
impl<'lua> From<Table<'lua>> for Value<'lua> {
#[inline]
fn from(value: Table<'lua>) -> Self {
Value::Table(value)
} }
} }
impl<'lua> IntoLua<'lua> for Table<'lua> { impl<'lua> IntoLua<'lua> for Table<'lua> {
#[inline] #[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> { fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(self)) Ok(self.into())
} }
} }
@ -97,10 +112,17 @@ impl<'lua> FromLua<'lua> for OwnedTable {
} }
} }
impl<'lua> From<Function<'lua>> for Value<'lua> {
#[inline]
fn from(value: Function<'lua>) -> Self {
Value::Function(value)
}
}
impl<'lua> IntoLua<'lua> for Function<'lua> { impl<'lua> IntoLua<'lua> for Function<'lua> {
#[inline] #[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> { fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Function(self)) Ok(self.into())
} }
} }
@ -151,10 +173,17 @@ impl<'lua> IntoLua<'lua> for WrappedAsyncFunction<'lua> {
} }
} }
impl<'lua> From<Thread<'lua>> for Value<'lua> {
#[inline]
fn from(value: Thread<'lua>) -> Self {
Value::Thread(value)
}
}
impl<'lua> IntoLua<'lua> for Thread<'lua> { impl<'lua> IntoLua<'lua> for Thread<'lua> {
#[inline] #[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> { fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Thread(self)) Ok(self.into())
} }
} }
@ -172,10 +201,17 @@ impl<'lua> FromLua<'lua> for Thread<'lua> {
} }
} }
impl<'lua> From<AnyUserData<'lua>> for Value<'lua> {
#[inline]
fn from(value: AnyUserData<'lua>) -> Self {
Value::UserData(value)
}
}
impl<'lua> IntoLua<'lua> for AnyUserData<'lua> { impl<'lua> IntoLua<'lua> for AnyUserData<'lua> {
#[inline] #[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> { fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::UserData(self)) Ok(self.into())
} }
} }
@ -232,49 +268,67 @@ impl<'lua, T: 'static> FromLua<'lua> for UserDataRefMut<'lua, T> {
} }
} }
impl<'lua> From<Error> for Value<'lua> {
#[inline]
fn from(value: Error) -> Self {
Value::Error(value)
}
}
impl<'lua> IntoLua<'lua> for Error { impl<'lua> IntoLua<'lua> for Error {
#[inline] #[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> { fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Error(self)) Ok(self.into())
} }
} }
impl<'lua> FromLua<'lua> for Error { impl<'lua> FromLua<'lua> for Error {
#[inline] #[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Error> { fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Error> {
match value { match value {
Value::Error(err) => Ok(err), Value::Error(err) => Ok(err),
val => Ok(Error::RuntimeError( _ => Err(Error::FromLuaConversionError { from: value.type_name(), to: "error", message: None }),
lua.coerce_string(val)?
.and_then(|s| Some(s.to_str().ok()?.to_owned()))
.unwrap_or_else(|| "<unprintable error>".to_owned()),
)),
} }
} }
} }
impl<'lua> From<bool> for Value<'lua> {
#[inline]
fn from(value: bool) -> Self {
Value::Boolean(value)
}
}
impl<'lua> IntoLua<'lua> for bool { impl<'lua> IntoLua<'lua> for bool {
#[inline] #[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> { fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Boolean(self)) Ok(self.into())
} }
} }
impl<'lua> FromLua<'lua> for bool { impl<'lua> FromLua<'lua> for bool {
#[inline] #[inline]
fn from_lua(v: Value<'lua>, _: &'lua Lua) -> Result<Self> { fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match v { match value {
Value::Nil => Ok(false), Value::Nil => Ok(false),
Value::Boolean(b) => Ok(b), Value::Boolean(b) => Ok(b),
_ => Ok(true), _ => Err(Error::FromLuaConversionError { from: value.type_name(), to: "boolean", message: None }),
} }
} }
} }
impl<'lua> From<LightUserData> for Value<'lua> {
#[inline]
fn from(value: LightUserData) -> Self {
Value::LightUserData(value)
}
}
impl<'lua> IntoLua<'lua> for LightUserData { impl<'lua> IntoLua<'lua> for LightUserData {
#[inline] #[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> { fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::LightUserData(self)) Ok(self.into())
} }
} }
@ -292,6 +346,38 @@ impl<'lua> FromLua<'lua> for LightUserData {
} }
} }
#[cfg(feature = "luau")]
impl<'lua> From<crate::types::Vector> for Value<'lua> {
#[inline]
fn from(value: crate::types::Vector) -> Self {
Value::Vector(value)
}
}
#[cfg(feature = "luau")]
impl<'lua> IntoLua<'lua> for crate::types::Vector {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Vector(self))
}
}
#[cfg(feature = "luau")]
impl<'lua> FromLua<'lua> for crate::types::Vector {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match value {
Value::Vector(v) => Ok(v),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "vector",
message: None,
}),
}
}
}
impl<'lua> IntoLua<'lua> for StdString { impl<'lua> IntoLua<'lua> for StdString {
#[inline] #[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> { fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
@ -302,16 +388,8 @@ impl<'lua> IntoLua<'lua> for StdString {
impl<'lua> FromLua<'lua> for StdString { impl<'lua> FromLua<'lua> for StdString {
#[inline] #[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> { fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
let ty = value.type_name(); let s = String::from_lua(value, lua)?;
Ok(lua Ok(s.to_str()?.to_owned())
.coerce_string(value)?
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: "String",
message: Some("expected string or number".to_string()),
})?
.to_str()?
.to_owned())
} }
} }
@ -339,17 +417,7 @@ impl<'lua> IntoLua<'lua> for Box<str> {
impl<'lua> FromLua<'lua> for Box<str> { impl<'lua> FromLua<'lua> for Box<str> {
#[inline] #[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> { fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
let ty = value.type_name(); StdString::from_lua(value, lua).map(|s| s.into_boxed_str())
Ok(lua
.coerce_string(value)?
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: "Box<str>",
message: Some("expected string or number".to_string()),
})?
.to_str()?
.to_owned()
.into_boxed_str())
} }
} }
@ -363,23 +431,15 @@ impl<'lua> IntoLua<'lua> for CString {
impl<'lua> FromLua<'lua> for CString { impl<'lua> FromLua<'lua> for CString {
#[inline] #[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> { fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
let ty = value.type_name(); let s = String::from_lua(value, lua)?;
let string = lua match CStr::from_bytes_with_nul(s.as_bytes_with_nul()) {
.coerce_string(value)? Ok(s) => Ok(s.to_owned()),
.ok_or_else(|| Error::FromLuaConversionError { Err(e) => Err(Error::FromLuaConversionError {
from: ty, from: "string",
to: "CString", to: "CString",
message: Some("expected string or number".to_string()), message: Some(e.to_string()),
})?;
match CStr::from_bytes_with_nul(string.as_bytes_with_nul()) {
Ok(s) => Ok(s.into()),
Err(_) => Err(Error::FromLuaConversionError {
from: ty,
to: "CString",
message: Some("invalid C-style string".to_string()),
}), }),
} }
} }
} }
@ -407,17 +467,8 @@ impl<'lua> IntoLua<'lua> for BString {
impl<'lua> FromLua<'lua> for BString { impl<'lua> FromLua<'lua> for BString {
#[inline] #[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> { fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
let ty = value.type_name(); let s = String::from_lua(value, lua)?;
Ok(BString::from( Ok(BString::from(s.as_bytes().to_vec()))
lua.coerce_string(value)?
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: "String",
message: Some("expected string or number".to_string()),
})?
.as_bytes()
.to_vec(),
))
} }
} }
@ -428,9 +479,20 @@ impl<'lua> IntoLua<'lua> for &BStr {
} }
} }
macro_rules! lua_convert_int_infallible {
($($x:ty),*) => {
$(impl<'lua> From<$x> for Value<'lua> {
#[inline]
fn from(value: $x) -> Self {
Self::Integer(value.into())
}
})*
}
}
macro_rules! lua_convert_int { macro_rules! lua_convert_int {
($x:ty) => { ($($x:ty),*) => {
impl<'lua> IntoLua<'lua> for $x { $(impl<'lua> IntoLua<'lua> for $x {
#[inline] #[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> { fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
cast(self) cast(self)
@ -447,53 +509,32 @@ macro_rules! lua_convert_int {
impl<'lua> FromLua<'lua> for $x { impl<'lua> FromLua<'lua> for $x {
#[inline] #[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> { fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
let ty = value.type_name();
(match value { (match value {
Value::Integer(i) => cast(i), Value::Integer(i) => cast(i),
Value::Number(n) => cast(n), Value::Number(n) => cast(n),
_ => { _ => return Err(Error::FromLuaConversionError {
if let Some(i) = lua.coerce_integer(value.clone())? { from: value.type_name(),
cast(i) to: stringify!($x),
} else { message: None,
cast(lua.coerce_number(value)?.ok_or_else(|| { }),
Error::FromLuaConversionError {
from: ty,
to: stringify!($x),
message: Some(
"expected number or string coercible to number".to_string(),
),
}
})?)
}
}
}) })
.ok_or_else(|| Error::FromLuaConversionError { .ok_or_else(|| Error::FromLuaConversionError {
from: ty, from: value.type_name(),
to: stringify!($x), to: stringify!($x),
message: Some("out of range".to_owned()), message: Some("out of range".to_owned()),
}) })
} }
} })*
}; };
} }
lua_convert_int!(i8); lua_convert_int!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize);
lua_convert_int!(u8); lua_convert_int_infallible!(i8, u8, i16, u16, i32);
lua_convert_int!(i16);
lua_convert_int!(u16);
lua_convert_int!(i32);
lua_convert_int!(u32);
lua_convert_int!(i64);
lua_convert_int!(u64);
lua_convert_int!(i128);
lua_convert_int!(u128);
lua_convert_int!(isize);
lua_convert_int!(usize);
macro_rules! lua_convert_float { macro_rules! lua_convert_float {
($x:ty) => { ($($x:ty),*) => {
impl<'lua> IntoLua<'lua> for $x { $(impl<'lua> IntoLua<'lua> for $x {
#[inline] #[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> { fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
cast(self) cast(self)
@ -508,28 +549,27 @@ macro_rules! lua_convert_float {
impl<'lua> FromLua<'lua> for $x { impl<'lua> FromLua<'lua> for $x {
#[inline] #[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> { fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
let ty = value.type_name(); (match value {
lua.coerce_number(value)? Value::Integer(i) => cast(i),
.ok_or_else(|| Error::FromLuaConversionError { Value::Number(n) => cast(n),
from: ty, _ => return Err(Error::FromLuaConversionError {
from: value.type_name(),
to: stringify!($x), to: stringify!($x),
message: Some("expected number or string coercible to number".to_string()), message: None,
}) }),
.and_then(|n| { })
cast(n).ok_or_else(|| Error::FromLuaConversionError { .ok_or_else(|| Error::FromLuaConversionError {
from: ty, from: value.type_name(),
to: stringify!($x), to: stringify!($x),
message: Some("number out of range".to_string()), message: Some("out of range".to_owned()),
}) })
})
} }
} })*
}; };
} }
lua_convert_float!(f32); lua_convert_float!(f32, f64);
lua_convert_float!(f64);
impl<'lua, T> IntoLua<'lua> for &[T] impl<'lua, T> IntoLua<'lua> for &[T]
where where
@ -561,16 +601,17 @@ where
fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self> { fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self> {
match value { match value {
#[cfg(feature = "luau")] #[cfg(feature = "luau")]
Value::Vector(x, y, z) if N == 3 => Ok(mlua_expect!( #[rustfmt::skip]
vec![ Value::Vector(v) if N == crate::types::Vector::SIZE => unsafe {
T::from_lua(Value::Number(x as _), _lua)?, use std::{mem, ptr};
T::from_lua(Value::Number(y as _), _lua)?, let mut arr: [mem::MaybeUninit<T>; N] = mem::MaybeUninit::uninit().assume_init();
T::from_lua(Value::Number(z as _), _lua)?, ptr::write(arr[0].as_mut_ptr() , T::from_lua(Value::Number(v.x() as _), _lua)?);
] ptr::write(arr[1].as_mut_ptr(), T::from_lua(Value::Number(v.y() as _), _lua)?);
.try_into() ptr::write(arr[2].as_mut_ptr(), T::from_lua(Value::Number(v.z() as _), _lua)?);
.map_err(|_| ()), #[cfg(feature = "luau-vector4")]
"cannot convert vector to array" ptr::write(arr[3].as_mut_ptr(), T::from_lua(Value::Number(v.w() as _), _lua)?);
)), Ok(mem::transmute_copy(&arr))
},
Value::Table(table) => { Value::Table(table) => {
let vec = table.sequence_values().collect::<Result<Vec<_>>>()?; let vec = table.sequence_values().collect::<Result<Vec<_>>>()?;
vec.try_into() vec.try_into()
@ -614,12 +655,6 @@ impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Vec<T> {
#[inline] #[inline]
fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self> { fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self> {
match value { match value {
#[cfg(feature = "luau")]
Value::Vector(x, y, z) => Ok(vec![
T::from_lua(Value::Number(x as _), _lua)?,
T::from_lua(Value::Number(y as _), _lua)?,
T::from_lua(Value::Number(z as _), _lua)?,
]),
Value::Table(table) => table.sequence_values().collect(), Value::Table(table) => table.sequence_values().collect(),
_ => Err(Error::FromLuaConversionError { _ => Err(Error::FromLuaConversionError {
from: value.type_name(), from: value.type_name(),

View File

@ -130,7 +130,7 @@ pub enum Error {
/// ///
/// [`AnyUserData`]: crate::AnyUserData /// [`AnyUserData`]: crate::AnyUserData
UserDataDestructed, UserDataDestructed,
/// An [`AnyUserData`] immutable borrow failed because it is already borrowed mutably. /// An [`AnyUserData`] immutable borrow failed.
/// ///
/// This error can occur when a method on a [`UserData`] type calls back into Lua, which then /// This error can occur when a method on a [`UserData`] type calls back into Lua, which then
/// tries to call a method on the same [`UserData`] type. Consider restructuring your API to /// tries to call a method on the same [`UserData`] type. Consider restructuring your API to
@ -139,7 +139,7 @@ pub enum Error {
/// [`AnyUserData`]: crate::AnyUserData /// [`AnyUserData`]: crate::AnyUserData
/// [`UserData`]: crate::UserData /// [`UserData`]: crate::UserData
UserDataBorrowError, UserDataBorrowError,
/// An [`AnyUserData`] mutable borrow failed because it is already borrowed. /// An [`AnyUserData`] mutable borrow failed.
/// ///
/// This error can occur when a method on a [`UserData`] type calls back into Lua, which then /// This error can occur when a method on a [`UserData`] type calls back into Lua, which then
/// tries to call a method on the same [`UserData`] type. Consider restructuring your API to /// tries to call a method on the same [`UserData`] type. Consider restructuring your API to
@ -270,8 +270,8 @@ impl fmt::Display for Error {
Error::CoroutineInactive => write!(fmt, "cannot resume inactive coroutine"), Error::CoroutineInactive => write!(fmt, "cannot resume inactive coroutine"),
Error::UserDataTypeMismatch => write!(fmt, "userdata is not expected type"), Error::UserDataTypeMismatch => write!(fmt, "userdata is not expected type"),
Error::UserDataDestructed => write!(fmt, "userdata has been destructed"), Error::UserDataDestructed => write!(fmt, "userdata has been destructed"),
Error::UserDataBorrowError => write!(fmt, "userdata already mutably borrowed"), Error::UserDataBorrowError => write!(fmt, "error borrowing userdata"),
Error::UserDataBorrowMutError => write!(fmt, "userdata already borrowed"), Error::UserDataBorrowMutError => write!(fmt, "error mutably borrowing userdata"),
Error::MetaMethodRestricted(ref method) => write!(fmt, "metamethod {method} is restricted"), Error::MetaMethodRestricted(ref method) => write!(fmt, "metamethod {method} is restricted"),
Error::MetaMethodTypeError { ref method, type_name, ref message } => { Error::MetaMethodTypeError { ref method, type_name, ref message } => {
write!(fmt, "metamethod {method} has unsupported type {type_name}")?; write!(fmt, "metamethod {method} has unsupported type {type_name}")?;

8
src/ffi.rs Normal file
View File

@ -0,0 +1,8 @@
pub use ::ffi::*;
use crate::util::{push_gc_userdata, WrappedFailure};
#[inline]
pub unsafe fn mlua_pusherror(state: *mut lua_State, err: crate::Error) -> crate::Result<()> {
push_gc_userdata(state, WrappedFailure::Error(err), false)
}

View File

@ -17,7 +17,7 @@ use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti};
#[cfg(feature = "async")] #[cfg(feature = "async")]
use { use {
crate::types::AsyncCallback, crate::types::AsyncCallback,
futures_util::future::{self, Future, LocalBoxFuture, TryFutureExt}, futures_util::future::{self, Future},
}; };
/// Handle to an internal Lua function. /// Handle to an internal Lua function.
@ -77,13 +77,17 @@ pub struct FunctionInfo {
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))] #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct CoverageInfo { pub struct CoverageInfo {
pub function: Option<std::string::String>, pub function: Option<String>,
pub line_defined: i32, pub line_defined: i32,
pub depth: i32, pub depth: i32,
pub hits: Vec<i32>, pub hits: Vec<i32>,
} }
impl<'lua> Function<'lua> { impl<'lua> Function<'lua> {
pub fn as_raw_ref(&self) -> &LuaRef<'lua> {
&self.0
}
/// Calls the function, passing `args` as function arguments. /// Calls the function, passing `args` as function arguments.
/// ///
/// The function's return values are converted to the generic type `R`. /// The function's return values are converted to the generic type `R`.
@ -188,21 +192,18 @@ impl<'lua> Function<'lua> {
/// [`AsyncThread`]: crate::AsyncThread /// [`AsyncThread`]: crate::AsyncThread
#[cfg(feature = "async")] #[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))] #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>> pub fn call_async<A, R>(&self, args: A) -> impl Future<Output = Result<R>> + 'lua
where where
'lua: 'fut,
A: IntoLuaMulti<'lua>, A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut, R: FromLuaMulti<'lua> + 'lua,
{ {
let lua = self.0.lua; let lua = self.0.lua;
match lua.create_recycled_thread(self) { let thread_res = lua.create_recycled_thread(self).map(|th| {
Ok(t) => { let mut th = th.into_async(args);
let mut t = t.into_async(args); th.set_recyclable(true);
t.set_recyclable(true); th
Box::pin(t) });
} async move { thread_res?.await }
Err(e) => Box::pin(future::err(e)),
}
} }
/// Returns a function that, when called, calls `self`, passing `args` as the first set of /// Returns a function that, when called, calls `self`, passing `args` as the first set of
@ -303,17 +304,7 @@ impl<'lua> Function<'lua> {
assert_stack(state, 1); assert_stack(state, 1);
lua.push_ref(&self.0); lua.push_ref(&self.0);
if ffi::lua_iscfunction(state, -1) != 0 {
let mut ar: ffi::lua_Debug = mem::zeroed();
#[cfg(not(feature = "luau"))]
{
ffi::lua_pushvalue(state, -1);
ffi::lua_getinfo(state, cstr!(">S"), &mut ar);
}
#[cfg(feature = "luau")]
ffi::lua_getinfo(state, -1, cstr!("s"), &mut ar);
if ptr_to_cstr_bytes(ar.what) == Some(b"C") {
return None; return None;
} }
@ -350,17 +341,7 @@ impl<'lua> Function<'lua> {
check_stack(state, 2)?; check_stack(state, 2)?;
lua.push_ref(&self.0); lua.push_ref(&self.0);
if ffi::lua_iscfunction(state, -1) != 0 {
let mut ar: ffi::lua_Debug = mem::zeroed();
#[cfg(not(feature = "luau"))]
{
ffi::lua_pushvalue(state, -1);
ffi::lua_getinfo(state, cstr!(">S"), &mut ar);
}
#[cfg(feature = "luau")]
ffi::lua_getinfo(state, -1, cstr!("s"), &mut ar);
if ptr_to_cstr_bytes(ar.what) == Some(b"C") {
return Ok(false); return Ok(false);
} }
@ -564,12 +545,12 @@ impl OwnedFunction {
#[cfg(feature = "async")] #[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))] #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
#[inline] #[inline]
pub fn call_async<'lua, A, R>(&'lua self, args: A) -> LocalBoxFuture<'lua, Result<R>> pub async fn call_async<'lua, A, R>(&'lua self, args: A) -> Result<R>
where where
A: IntoLuaMulti<'lua>, A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua, R: FromLuaMulti<'lua> + 'lua,
{ {
self.to_ref().call_async(args) self.to_ref().call_async(args).await
} }
} }
@ -624,7 +605,8 @@ impl<'lua> Function<'lua> {
Ok(args) => args, Ok(args) => args,
Err(e) => return Box::pin(future::err(e)), Err(e) => return Box::pin(future::err(e)),
}; };
Box::pin(func(lua, args).and_then(move |ret| future::ready(ret.into_lua_multi(lua)))) let fut = func(lua, args);
Box::pin(async move { fut.await?.into_lua_multi(lua) })
})) }))
} }
} }

View File

@ -82,6 +82,7 @@ mod macros;
mod chunk; mod chunk;
mod conversion; mod conversion;
mod error; mod error;
pub mod ffi;
mod function; mod function;
mod hook; mod hook;
mod lua; mod lua;
@ -103,7 +104,7 @@ mod value;
pub mod prelude; pub mod prelude;
pub use ffi::{lua_CFunction, lua_State}; pub use self::ffi::{lua_CFunction, lua_State};
pub use crate::chunk::{AsChunk, Chunk, ChunkMode}; pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
pub use crate::error::{Error, ErrorContext, ExternalError, ExternalResult, Result}; pub use crate::error::{Error, ErrorContext, ExternalError, ExternalResult, Result};
@ -130,7 +131,11 @@ pub use crate::hook::HookTriggers;
#[cfg(any(feature = "luau", doc))] #[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))] #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub use crate::{chunk::Compiler, function::CoverageInfo, types::VmState}; pub use crate::{
chunk::Compiler,
function::CoverageInfo,
types::{Vector, VmState},
};
#[cfg(feature = "async")] #[cfg(feature = "async")]
pub use crate::thread::AsyncThread; pub use crate::thread::AsyncThread;

View File

@ -50,12 +50,15 @@ use crate::{hook::HookTriggers, types::HookCallback};
#[cfg(feature = "luau")] #[cfg(feature = "luau")]
use crate::types::InterruptCallback; use crate::types::InterruptCallback;
#[cfg(any(feature = "luau", doc))] #[cfg(any(feature = "luau", doc))]
use crate::{chunk::Compiler, types::VmState}; use crate::{
chunk::Compiler,
types::{Vector, VmState},
};
#[cfg(feature = "async")] #[cfg(feature = "async")]
use { use {
crate::types::{AsyncCallback, AsyncCallbackUpvalue, AsyncPollUpvalue}, crate::types::{AsyncCallback, AsyncCallbackUpvalue, AsyncPollUpvalue},
futures_util::future::{self, Future, TryFutureExt}, futures_util::future::{self, Future},
futures_util::task::{noop_waker_ref, Context, Poll, Waker}, futures_util::task::{noop_waker_ref, Context, Poll, Waker},
}; };
@ -1555,6 +1558,7 @@ impl Lua {
/// ///
/// # Safety /// # Safety
/// This function is unsafe because provides a way to execute unsafe C function. /// This function is unsafe because provides a way to execute unsafe C function.
#[inline]
pub unsafe fn create_c_function(&self, func: ffi::lua_CFunction) -> Result<Function> { pub unsafe fn create_c_function(&self, func: ffi::lua_CFunction) -> Result<Function> {
let state = self.state(); let state = self.state();
check_stack(state, 1)?; check_stack(state, 1)?;
@ -1616,7 +1620,8 @@ impl Lua {
Ok(args) => args, Ok(args) => args,
Err(e) => return Box::pin(future::err(e)), Err(e) => return Box::pin(future::err(e)),
}; };
Box::pin(func(lua, args).and_then(move |ret| future::ready(ret.into_lua_multi(lua)))) let fut = func(lua, args);
Box::pin(async move { fut.await?.into_lua_multi(lua) })
})) }))
} }
@ -1624,6 +1629,13 @@ impl Lua {
/// ///
/// Equivalent to `coroutine.create`. /// Equivalent to `coroutine.create`.
pub fn create_thread<'lua>(&'lua self, func: Function) -> Result<Thread<'lua>> { pub fn create_thread<'lua>(&'lua self, func: Function) -> Result<Thread<'lua>> {
self.create_thread_inner(&func)
}
/// Wraps a Lua function into a new thread (or coroutine).
///
/// Takes function by reference.
fn create_thread_inner<'lua>(&'lua self, func: &Function) -> Result<Thread<'lua>> {
let state = self.state(); let state = self.state();
unsafe { unsafe {
let _sg = StackGuard::new(state); let _sg = StackGuard::new(state);
@ -1672,7 +1684,7 @@ impl Lua {
return Ok(Thread(LuaRef::new(self, index))); return Ok(Thread(LuaRef::new(self, index)));
} }
}; };
self.create_thread(func.clone()) self.create_thread_inner(func)
} }
/// Resets thread (coroutine) and returns to the pool for later use. /// Resets thread (coroutine) and returns to the pool for later use.
@ -2270,7 +2282,7 @@ impl Lua {
extra.app_data.remove() extra.app_data.remove()
} }
// Uses 2 stack spaces, does not call checkstack /// Uses 2 stack spaces, does not call checkstack
pub(crate) unsafe fn push_value(&self, value: Value) -> Result<()> { pub(crate) unsafe fn push_value(&self, value: Value) -> Result<()> {
let state = self.state(); let state = self.state();
match value { match value {
@ -2295,8 +2307,11 @@ impl Lua {
} }
#[cfg(feature = "luau")] #[cfg(feature = "luau")]
Value::Vector(x, y, z) => { Value::Vector(v) => {
ffi::lua_pushvector(state, x, y, z); #[cfg(not(feature = "luau-vector4"))]
ffi::lua_pushvector(state, v.x(), v.y(), v.z());
#[cfg(feature = "luau-vector4")]
ffi::lua_pushvector(state, v.x(), v.y(), v.z(), v.w());
} }
Value::String(s) => { Value::String(s) => {
@ -2328,7 +2343,7 @@ impl Lua {
Ok(()) Ok(())
} }
// Uses 2 stack spaces, does not call checkstack /// Uses 2 stack spaces, does not call checkstack
pub(crate) unsafe fn pop_value(&self) -> Value { pub(crate) unsafe fn pop_value(&self) -> Value {
let state = self.state(); let state = self.state();
match ffi::lua_type(state, -1) { match ffi::lua_type(state, -1) {
@ -2379,7 +2394,10 @@ impl Lua {
ffi::LUA_TVECTOR => { ffi::LUA_TVECTOR => {
let v = ffi::lua_tovector(state, -1); let v = ffi::lua_tovector(state, -1);
mlua_debug_assert!(!v.is_null(), "vector is null"); mlua_debug_assert!(!v.is_null(), "vector is null");
let vec = Value::Vector(*v, *v.add(1), *v.add(2)); #[cfg(not(feature = "luau-vector4"))]
let vec = Value::Vector(Vector([*v, *v.add(1), *v.add(2)]));
#[cfg(feature = "luau-vector4")]
let vec = Value::Vector(Vector([*v, *v.add(1), *v.add(2), *v.add(3)]));
ffi::lua_pop(state, 1); ffi::lua_pop(state, 1);
vec vec
} }
@ -2658,18 +2676,17 @@ impl Lua {
} }
} }
// Pushes a LuaRef value onto the stack, checking that it's a registered // Returns `TypeId` for the LuaRef, checking that it's a registered
// and not destructed UserData. // and not destructed UserData.
// Uses 2 stack spaces, does not call checkstack. //
pub(crate) unsafe fn push_userdata_ref(&self, lref: &LuaRef) -> Result<Option<TypeId>> { // Returns `None` if the userdata is registered but non-static.
let state = self.state(); pub(crate) unsafe fn get_userdata_type_id(&self, lref: &LuaRef) -> Result<Option<TypeId>> {
self.push_ref(lref); let ref_thread = self.ref_thread();
if ffi::lua_getmetatable(state, -1) == 0 { if ffi::lua_getmetatable(ref_thread, lref.index) == 0 {
ffi::lua_pop(state, 1);
return Err(Error::UserDataTypeMismatch); return Err(Error::UserDataTypeMismatch);
} }
let mt_ptr = ffi::lua_topointer(state, -1); let mt_ptr = ffi::lua_topointer(ref_thread, -1);
ffi::lua_pop(state, 1); ffi::lua_pop(ref_thread, 1);
// Fast path to skip looking up the metatable in the map // Fast path to skip looking up the metatable in the map
let (last_mt, last_type_id) = (*self.extra.get()).last_checked_userdata_mt; let (last_mt, last_type_id) = (*self.extra.get()).last_checked_userdata_mt;
@ -2689,6 +2706,14 @@ impl Lua {
} }
} }
// Pushes a LuaRef (userdata) value onto the stack, returning their `TypeId`.
// Uses 1 stack space, does not call checkstack.
pub(crate) unsafe fn push_userdata_ref(&self, lref: &LuaRef) -> Result<Option<TypeId>> {
let type_id = self.get_userdata_type_id(lref)?;
self.push_ref(lref);
Ok(type_id)
}
// Creates a Function out of a Callback containing a 'static Fn. This is safe ONLY because the // Creates a Function out of a Callback containing a 'static Fn. This is safe ONLY because the
// Fn is 'static, otherwise it could capture 'lua arguments improperly. Without ATCs, we // Fn is 'static, otherwise it could capture 'lua arguments improperly. Without ATCs, we
// cannot easily deal with the "correct" callback type of: // cannot easily deal with the "correct" callback type of:
@ -2774,8 +2799,7 @@ impl Lua {
))] ))]
unsafe { unsafe {
if !(*self.extra.get()).libs.contains(StdLib::COROUTINE) { if !(*self.extra.get()).libs.contains(StdLib::COROUTINE) {
load_from_std_lib(self.main_state, StdLib::COROUTINE)?; return Err(Error::RuntimeError("StdLib::COROUTINE is not loaded".to_owned()));
(*self.extra.get()).libs |= StdLib::COROUTINE;
} }
} }

View File

@ -126,6 +126,12 @@ unsafe extern "C" fn lua_vector(state: *mut ffi::lua_State) -> c_int {
let x = ffi::luaL_checknumber(state, 1) as c_float; let x = ffi::luaL_checknumber(state, 1) as c_float;
let y = ffi::luaL_checknumber(state, 2) as c_float; let y = ffi::luaL_checknumber(state, 2) as c_float;
let z = ffi::luaL_checknumber(state, 3) as c_float; let z = ffi::luaL_checknumber(state, 3) as c_float;
#[cfg(feature = "luau-vector4")]
let w = ffi::luaL_checknumber(state, 4) as c_float;
#[cfg(not(feature = "luau-vector4"))]
ffi::lua_pushvector(state, x, y, z); ffi::lua_pushvector(state, x, y, z);
#[cfg(feature = "luau-vector4")]
ffi::lua_pushvector(state, x, y, z, w);
1 1
} }

View File

@ -69,14 +69,23 @@ macro_rules! mlua_debug_assert {
}; };
} }
#[cfg(debug_assertions)]
macro_rules! mlua_expect { macro_rules! mlua_expect {
($res:expr, $msg:expr) => { ($res:expr, $msg:expr $(,)?) => {
$res.expect(bug_msg!($msg)) $res.expect(bug_msg!($msg))
}; };
}
($res:expr, $msg:expr,) => { #[cfg(not(debug_assertions))]
mlua_expect!($res, $msg) macro_rules! mlua_expect {
}; ($res:expr, $msg:expr $(,)?) => {{
let x;
#[allow(unused_unsafe)]
{
x = unsafe { $res.into_iter().next().unwrap_unchecked() };
}
x
}};
} }
#[cfg(feature = "module")] #[cfg(feature = "module")]

View File

@ -23,7 +23,7 @@ pub use crate::HookTriggers as LuaHookTriggers;
#[cfg(feature = "luau")] #[cfg(feature = "luau")]
#[doc(no_inline)] #[doc(no_inline)]
pub use crate::{CoverageInfo as LuaCoverageInfo, VmState as LuaVmState}; pub use crate::{CoverageInfo as LuaCoverageInfo, Vector as LuaVector, VmState as LuaVmState};
#[cfg(feature = "async")] #[cfg(feature = "async")]
#[doc(no_inline)] #[doc(no_inline)]

View File

@ -611,12 +611,28 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
} }
#[cfg(feature = "async")] #[cfg(feature = "async")]
fn add_async_method<M, A, MR, R>(&mut self, _name: impl AsRef<str>, _method: M) fn add_async_method<'s, M, A, MR, R>(&mut self, _name: impl AsRef<str>, _method: M)
where where
T: Clone, 'lua: 's,
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static, T: 'static,
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 'lua, MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
{
// The panic should never happen as async non-static code wouldn't compile
// Non-static lifetime must be bounded to 'lua lifetime
panic!("asynchronous methods are not supported for non-static userdata")
}
#[cfg(feature = "async")]
fn add_async_method_mut<'s, M, A, MR, R>(&mut self, _name: impl AsRef<str>, _method: M)
where
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>, R: IntoLuaMulti<'lua>,
{ {
// The panic should never happen as async non-static code wouldn't compile // The panic should never happen as async non-static code wouldn't compile
@ -686,12 +702,28 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
} }
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))] #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_method<M, A, MR, R>(&mut self, _name: impl AsRef<str>, _method: M) fn add_async_meta_method<'s, M, A, MR, R>(&mut self, _name: impl AsRef<str>, _method: M)
where where
T: Clone, 'lua: 's,
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static, T: 'static,
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 'lua, MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
{
// The panic should never happen as async non-static code wouldn't compile
// Non-static lifetime must be bounded to 'lua lifetime
panic!("asynchronous meta methods are not supported for non-static userdata")
}
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_method_mut<'s, M, A, MR, R>(&mut self, _name: impl AsRef<str>, _method: M)
where
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>, R: IntoLuaMulti<'lua>,
{ {
// The panic should never happen as async non-static code wouldn't compile // The panic should never happen as async non-static code wouldn't compile

View File

@ -124,7 +124,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
#[allow(clippy::useless_conversion)] #[allow(clippy::useless_conversion)]
Value::Number(n) => visitor.visit_f64(n.into()), Value::Number(n) => visitor.visit_f64(n.into()),
#[cfg(feature = "luau")] #[cfg(feature = "luau")]
Value::Vector(_, _, _) => self.deserialize_seq(visitor), Value::Vector(_) => self.deserialize_seq(visitor),
Value::String(s) => match s.to_str() { Value::String(s) => match s.to_str() {
Ok(s) => visitor.visit_str(s), Ok(s) => visitor.visit_str(s),
Err(_) => visitor.visit_bytes(s.as_bytes()), Err(_) => visitor.visit_bytes(s.as_bytes()),
@ -223,9 +223,9 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
{ {
match self.value { match self.value {
#[cfg(feature = "luau")] #[cfg(feature = "luau")]
Value::Vector(x, y, z) => { Value::Vector(vec) => {
let mut deserializer = VecDeserializer { let mut deserializer = VecDeserializer {
vec: [x, y, z], vec,
next: 0, next: 0,
options: self.options, options: self.options,
visited: self.visited, visited: self.visited,
@ -412,7 +412,7 @@ impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
#[cfg(feature = "luau")] #[cfg(feature = "luau")]
struct VecDeserializer { struct VecDeserializer {
vec: [f32; 3], vec: crate::types::Vector,
next: usize, next: usize,
options: Options, options: Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>, visited: Rc<RefCell<FxHashSet<*const c_void>>>,
@ -426,7 +426,7 @@ impl<'de> de::SeqAccess<'de> for VecDeserializer {
where where
T: de::DeserializeSeed<'de>, T: de::DeserializeSeed<'de>,
{ {
match self.vec.get(self.next) { match self.vec.0.get(self.next) {
Some(&n) => { Some(&n) => {
self.next += 1; self.next += 1;
let visited = Rc::clone(&self.visited); let visited = Rc::clone(&self.visited);
@ -439,7 +439,7 @@ impl<'de> de::SeqAccess<'de> for VecDeserializer {
} }
fn size_hint(&self) -> Option<usize> { fn size_hint(&self) -> Option<usize> {
Some(3) Some(crate::types::Vector::SIZE)
} }
} }

View File

@ -7,8 +7,6 @@ use crate::error::{Error, Result};
use crate::lua::Lua; use crate::lua::Lua;
use crate::string::String; use crate::string::String;
use crate::table::Table; use crate::table::Table;
use crate::types::Integer;
use crate::util::{check_stack, StackGuard};
use crate::value::{IntoLua, Value}; use crate::value::{IntoLua, Value};
/// A struct for serializing Rust values into Lua values. /// A struct for serializing Rust values into Lua values.
@ -120,9 +118,9 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
// Associated types for keeping track of additional state while serializing // Associated types for keeping track of additional state while serializing
// compound data structures like sequences and maps. // compound data structures like sequences and maps.
type SerializeSeq = SerializeVec<'lua>; type SerializeSeq = SerializeSeq<'lua>;
type SerializeTuple = SerializeVec<'lua>; type SerializeTuple = SerializeSeq<'lua>;
type SerializeTupleStruct = SerializeVec<'lua>; type SerializeTupleStruct = SerializeSeq<'lua>;
type SerializeTupleVariant = SerializeTupleVariant<'lua>; type SerializeTupleVariant = SerializeTupleVariant<'lua>;
type SerializeMap = SerializeMap<'lua>; type SerializeMap = SerializeMap<'lua>;
type SerializeStruct = SerializeMap<'lua>; type SerializeStruct = SerializeMap<'lua>;
@ -240,8 +238,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
if self.options.set_array_metatable { if self.options.set_array_metatable {
table.set_metatable(Some(self.lua.array_metatable())); table.set_metatable(Some(self.lua.array_metatable()));
} }
let options = self.options; Ok(SerializeSeq::new(table, self.options))
Ok(SerializeVec { table, options })
} }
#[inline] #[inline]
@ -252,9 +249,14 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
#[inline] #[inline]
fn serialize_tuple_struct( fn serialize_tuple_struct(
self, self,
_name: &'static str, name: &'static str,
len: usize, len: usize,
) -> Result<Self::SerializeTupleStruct> { ) -> Result<Self::SerializeTupleStruct> {
#[cfg(feature = "luau")]
if name == "Vector" && len == crate::types::Vector::SIZE {
return Ok(SerializeSeq::new_vector(self.lua, self.options));
}
_ = name;
self.serialize_seq(Some(len)) self.serialize_seq(Some(len))
} }
@ -305,12 +307,40 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
} }
#[doc(hidden)] #[doc(hidden)]
pub struct SerializeVec<'lua> { pub struct SerializeSeq<'lua> {
table: Table<'lua>, lua: &'lua Lua,
#[cfg(feature = "luau")]
vector: Option<crate::types::Vector>,
table: Option<Table<'lua>>,
next: usize,
options: Options, options: Options,
} }
impl<'lua> ser::SerializeSeq for SerializeVec<'lua> { impl<'lua> SerializeSeq<'lua> {
const fn new(table: Table<'lua>, options: Options) -> Self {
Self {
lua: table.0.lua,
#[cfg(feature = "luau")]
vector: None,
table: Some(table),
next: 0,
options,
}
}
#[cfg(feature = "luau")]
const fn new_vector(lua: &'lua Lua, options: Options) -> Self {
Self {
lua,
vector: Some(crate::types::Vector::zero()),
table: None,
next: 0,
options,
}
}
}
impl<'lua> ser::SerializeSeq for SerializeSeq<'lua> {
type Ok = Value<'lua>; type Ok = Value<'lua>;
type Error = Error; type Error = Error;
@ -318,35 +348,19 @@ impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
where where
T: Serialize + ?Sized, T: Serialize + ?Sized,
{ {
let lua = self.table.0.lua; let value = self.lua.to_value_with(value, self.options)?;
let state = lua.state(); let table = self.table.as_ref().unwrap();
let value = lua.to_value_with(value, self.options)?; table.raw_seti(self.next + 1, value)?;
unsafe { self.next += 1;
let _sg = StackGuard::new(state); Ok(())
check_stack(state, 4)?;
lua.push_ref(&self.table.0);
lua.push_value(value)?;
if lua.unlikely_memory_error() {
let len = ffi::lua_rawlen(state, -2) as Integer;
ffi::lua_rawseti(state, -2, len + 1);
ffi::lua_pop(state, 1);
Ok(())
} else {
protect_lua!(state, 2, 0, fn(state) {
let len = ffi::lua_rawlen(state, -2) as Integer;
ffi::lua_rawseti(state, -2, len + 1);
})
}
}
} }
fn end(self) -> Result<Value<'lua>> { fn end(self) -> Result<Value<'lua>> {
Ok(Value::Table(self.table)) Ok(Value::Table(self.table.unwrap()))
} }
} }
impl<'lua> ser::SerializeTuple for SerializeVec<'lua> { impl<'lua> ser::SerializeTuple for SerializeSeq<'lua> {
type Ok = Value<'lua>; type Ok = Value<'lua>;
type Error = Error; type Error = Error;
@ -362,7 +376,7 @@ impl<'lua> ser::SerializeTuple for SerializeVec<'lua> {
} }
} }
impl<'lua> ser::SerializeTupleStruct for SerializeVec<'lua> { impl<'lua> ser::SerializeTupleStruct for SerializeSeq<'lua> {
type Ok = Value<'lua>; type Ok = Value<'lua>;
type Error = Error; type Error = Error;
@ -370,10 +384,22 @@ impl<'lua> ser::SerializeTupleStruct for SerializeVec<'lua> {
where where
T: Serialize + ?Sized, T: Serialize + ?Sized,
{ {
#[cfg(feature = "luau")]
if let Some(vector) = self.vector.as_mut() {
let value = self.lua.to_value_with(value, self.options)?;
let value = self.lua.unpack(value)?;
vector.0[self.next] = value;
self.next += 1;
return Ok(());
}
ser::SerializeSeq::serialize_element(self, value) ser::SerializeSeq::serialize_element(self, value)
} }
fn end(self) -> Result<Value<'lua>> { fn end(self) -> Result<Value<'lua>> {
#[cfg(feature = "luau")]
if let Some(vector) = self.vector {
return Ok(Value::Vector(vector));
}
ser::SerializeSeq::end(self) ser::SerializeSeq::end(self)
} }
} }
@ -394,9 +420,7 @@ impl<'lua> ser::SerializeTupleVariant for SerializeTupleVariant<'lua> {
T: Serialize + ?Sized, T: Serialize + ?Sized,
{ {
let lua = self.table.0.lua; let lua = self.table.0.lua;
let idx = self.table.raw_len() + 1; self.table.raw_push(lua.to_value_with(value, self.options)?)
self.table
.raw_insert(idx, lua.to_value_with(value, self.options)?)
} }
fn end(self) -> Result<Value<'lua>> { fn end(self) -> Result<Value<'lua>> {

View File

@ -41,6 +41,10 @@ impl OwnedString {
} }
impl<'lua> String<'lua> { impl<'lua> String<'lua> {
pub fn as_raw_ref(&self) -> &LuaRef<'lua> {
&self.0
}
/// Get a `&str` slice if the Lua string is valid UTF-8. /// Get a `&str` slice if the Lua string is valid UTF-8.
/// ///
/// # Examples /// # Examples
@ -139,8 +143,7 @@ impl<'lua> String<'lua> {
/// Typically this function is used only for hashing and debug information. /// Typically this function is used only for hashing and debug information.
#[inline] #[inline]
pub fn to_pointer(&self) -> *const c_void { pub fn to_pointer(&self) -> *const c_void {
let ref_thread = self.0.lua.ref_thread(); self.0.to_pointer()
unsafe { ffi::lua_topointer(ref_thread, self.0.index) }
} }
/// Convert this handle to owned version. /// Convert this handle to owned version.

View File

@ -47,6 +47,10 @@ impl OwnedTable {
#[allow(clippy::len_without_is_empty)] #[allow(clippy::len_without_is_empty)]
impl<'lua> Table<'lua> { impl<'lua> Table<'lua> {
pub fn as_raw_ref(&self) -> &LuaRef<'lua> {
&self.0
}
/// Sets a key-value pair in the table. /// Sets a key-value pair in the table.
/// ///
/// If the value is `nil`, this will effectively remove the pair. /// If the value is `nil`, this will effectively remove the pair.
@ -557,6 +561,7 @@ impl<'lua> Table<'lua> {
/// Requires `feature = "luau"` /// Requires `feature = "luau"`
#[cfg(any(feature = "luau", doc))] #[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))] #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[inline]
pub fn set_readonly(&self, enabled: bool) { pub fn set_readonly(&self, enabled: bool) {
let ref_thread = self.0.lua.ref_thread(); let ref_thread = self.0.lua.ref_thread();
unsafe { unsafe {
@ -568,11 +573,22 @@ impl<'lua> Table<'lua> {
} }
} }
#[inline]
pub fn set_safe(&self, enabled: bool) {
let ref_thread = self.0.lua.ref_thread();
let enabled = enabled as _;
unsafe {
ffi::lua_setreadonly(ref_thread, self.0.index, enabled);
ffi::lua_setsafeenv(ref_thread, self.0.index, enabled);
}
}
/// Returns `readonly` attribute of the table. /// Returns `readonly` attribute of the table.
/// ///
/// Requires `feature = "luau"` /// Requires `feature = "luau"`
#[cfg(any(feature = "luau", doc))] #[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))] #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[inline]
pub fn is_readonly(&self) -> bool { pub fn is_readonly(&self) -> bool {
let ref_thread = self.0.lua.ref_thread(); let ref_thread = self.0.lua.ref_thread();
unsafe { ffi::lua_getreadonly(ref_thread, self.0.index) != 0 } unsafe { ffi::lua_getreadonly(ref_thread, self.0.index) != 0 }
@ -586,8 +602,7 @@ impl<'lua> Table<'lua> {
/// Typically this function is used only for hashing and debug information. /// Typically this function is used only for hashing and debug information.
#[inline] #[inline]
pub fn to_pointer(&self) -> *const c_void { pub fn to_pointer(&self) -> *const c_void {
let ref_thread = self.0.lua.ref_thread(); self.0.to_pointer()
unsafe { ffi::lua_topointer(ref_thread, self.0.index) }
} }
/// Convert this handle to owned version. /// Convert this handle to owned version.
@ -720,6 +735,32 @@ impl<'lua> Table<'lua> {
} }
} }
/// Sets element value at position `idx` without invoking metamethods.
#[allow(dead_code)]
pub(crate) fn raw_seti<V: IntoLua<'lua>>(&self, idx: usize, value: V) -> Result<()> {
#[cfg(feature = "luau")]
self.check_readonly_write()?;
let lua = self.0.lua;
let state = lua.state();
let value = value.into_lua(lua)?;
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 5)?;
lua.push_ref(&self.0);
lua.push_value(value)?;
if lua.unlikely_memory_error() {
ffi::lua_rawseti(state, -2, idx as _);
} else {
protect_lua!(state, 2, 0, |state| ffi::lua_rawseti(state, -2, idx as _))?;
}
Ok(())
}
}
#[cfg(feature = "serialize")] #[cfg(feature = "serialize")]
pub(crate) fn is_array(&self) -> bool { pub(crate) fn is_array(&self) -> bool {
let lua = self.0.lua; let lua = self.0.lua;
@ -797,6 +838,56 @@ impl<'lua> AsRef<Table<'lua>> for Table<'lua> {
} }
} }
impl<'lua, T> PartialEq<[T]> for Table<'lua>
where
T: IntoLua<'lua> + Clone,
{
fn eq(&self, other: &[T]) -> bool {
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 4);
lua.push_ref(&self.0);
let len = ffi::lua_rawlen(state, -1);
for i in 0..len {
ffi::lua_rawgeti(state, -1, (i + 1) as _);
let val = lua.pop_value();
if val == Nil {
return i == other.len();
}
match other.get(i).map(|v| v.clone().into_lua(lua)) {
Some(Ok(other_val)) if val == other_val => continue,
_ => return false,
}
}
}
true
}
}
impl<'lua, T> PartialEq<&[T]> for Table<'lua>
where
T: IntoLua<'lua> + Clone,
{
#[inline]
fn eq(&self, other: &&[T]) -> bool {
self == *other
}
}
impl<'lua, T, const N: usize> PartialEq<[T; N]> for Table<'lua>
where
T: IntoLua<'lua> + Clone,
{
#[inline]
fn eq(&self, other: &[T; N]) -> bool {
self == &other[..]
}
}
/// An extension trait for `Table`s that provides a variety of convenient functionality. /// An extension trait for `Table`s that provides a variety of convenient functionality.
pub trait TableExt<'lua>: Sealed { pub trait TableExt<'lua>: Sealed {
/// Calls the table as function assuming it has `__call` metamethod. /// Calls the table as function assuming it has `__call` metamethod.
@ -812,11 +903,10 @@ pub trait TableExt<'lua>: Sealed {
/// The metamethod is called with the table as its first argument, followed by the passed arguments. /// The metamethod is called with the table as its first argument, followed by the passed arguments.
#[cfg(feature = "async")] #[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))] #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>> fn call_async<A, R>(&self, args: A) -> LocalBoxFuture<'lua, Result<R>>
where where
'lua: 'fut,
A: IntoLuaMulti<'lua>, A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut; R: FromLuaMulti<'lua> + 'lua;
/// Gets the function associated to `key` from the table and executes it, /// Gets the function associated to `key` from the table and executes it,
/// passing the table itself along with `args` as function arguments. /// passing the table itself along with `args` as function arguments.
@ -852,12 +942,11 @@ pub trait TableExt<'lua>: Sealed {
/// This might invoke the `__index` metamethod. /// This might invoke the `__index` metamethod.
#[cfg(feature = "async")] #[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))] #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async_method<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>> fn call_async_method<K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'lua, Result<R>>
where where
'lua: 'fut,
K: IntoLua<'lua>, K: IntoLua<'lua>,
A: IntoLuaMulti<'lua>, A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut; R: FromLuaMulti<'lua> + 'lua;
/// Gets the function associated to `key` from the table and asynchronously executes it, /// Gets the function associated to `key` from the table and asynchronously executes it,
/// passing `args` as function arguments and returning Future. /// passing `args` as function arguments and returning Future.
@ -867,16 +956,11 @@ pub trait TableExt<'lua>: Sealed {
/// This might invoke the `__index` metamethod. /// This might invoke the `__index` metamethod.
#[cfg(feature = "async")] #[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))] #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async_function<'fut, K, A, R>( fn call_async_function<K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'lua, Result<R>>
&self,
key: K,
args: A,
) -> LocalBoxFuture<'fut, Result<R>>
where where
'lua: 'fut,
K: IntoLua<'lua>, K: IntoLua<'lua>,
A: IntoLuaMulti<'lua>, A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut; R: FromLuaMulti<'lua> + 'lua;
} }
impl<'lua> TableExt<'lua> for Table<'lua> { impl<'lua> TableExt<'lua> for Table<'lua> {
@ -890,13 +974,17 @@ impl<'lua> TableExt<'lua> for Table<'lua> {
} }
#[cfg(feature = "async")] #[cfg(feature = "async")]
fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>> fn call_async<A, R>(&self, args: A) -> LocalBoxFuture<'lua, Result<R>>
where where
'lua: 'fut,
A: IntoLuaMulti<'lua>, A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut, R: FromLuaMulti<'lua> + 'lua,
{ {
Function(self.0.clone()).call_async(args) let args = match args.into_lua_multi(self.0.lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
let func = Function(self.0.clone());
Box::pin(async move { func.call_async(args).await })
} }
fn call_method<K, A, R>(&self, key: K, args: A) -> Result<R> fn call_method<K, A, R>(&self, key: K, args: A) -> Result<R>
@ -921,12 +1009,11 @@ impl<'lua> TableExt<'lua> for Table<'lua> {
} }
#[cfg(feature = "async")] #[cfg(feature = "async")]
fn call_async_method<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>> fn call_async_method<K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'lua, Result<R>>
where where
'lua: 'fut,
K: IntoLua<'lua>, K: IntoLua<'lua>,
A: IntoLuaMulti<'lua>, A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut, R: FromLuaMulti<'lua> + 'lua,
{ {
let lua = self.0.lua; let lua = self.0.lua;
let mut args = match args.into_lua_multi(lua) { let mut args = match args.into_lua_multi(lua) {
@ -938,15 +1025,19 @@ impl<'lua> TableExt<'lua> for Table<'lua> {
} }
#[cfg(feature = "async")] #[cfg(feature = "async")]
fn call_async_function<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>> fn call_async_function<K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'lua, Result<R>>
where where
'lua: 'fut,
K: IntoLua<'lua>, K: IntoLua<'lua>,
A: IntoLuaMulti<'lua>, A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut, R: FromLuaMulti<'lua> + 'lua,
{ {
let lua = self.0.lua;
let args = match args.into_lua_multi(lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
match self.get::<_, Function>(key) { match self.get::<_, Function>(key) {
Ok(func) => func.call_async(args), Ok(func) => Box::pin(async move { func.call_async(args).await }),
Err(e) => Box::pin(future::err(e)), Err(e) => Box::pin(future::err(e)),
} }
} }

View File

@ -73,6 +73,10 @@ pub struct AsyncThread<'lua, R> {
} }
impl<'lua> Thread<'lua> { impl<'lua> Thread<'lua> {
pub fn as_raw_ref(&self) -> &LuaRef<'lua> {
&self.0
}
/// Resumes execution of this thread. /// Resumes execution of this thread.
/// ///
/// Equivalent to `coroutine.resume`. /// Equivalent to `coroutine.resume`.
@ -88,6 +92,8 @@ impl<'lua> Thread<'lua> {
/// If the thread calls `coroutine.yield`, returns the values passed to `yield`. If the thread /// If the thread calls `coroutine.yield`, returns the values passed to `yield`. If the thread
/// `return`s values from its main function, returns those. /// `return`s values from its main function, returns those.
/// ///
/// Also returns `true` if the coroutine is yielded.
///
/// # Examples /// # Examples
/// ///
/// ``` /// ```
@ -114,7 +120,7 @@ impl<'lua> Thread<'lua> {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
pub fn resume<A, R>(&self, args: A) -> Result<R> pub fn resume<A, R>(&self, args: A) -> Result<(bool, R)>
where where
A: IntoLuaMulti<'lua>, A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>, R: FromLuaMulti<'lua>,
@ -124,7 +130,7 @@ impl<'lua> Thread<'lua> {
let mut args = args.into_lua_multi(lua)?; let mut args = args.into_lua_multi(lua)?;
let nargs = args.len() as c_int; let nargs = args.len() as c_int;
let results = unsafe { let (yielded, results) = unsafe {
let _sg = StackGuard::new(state); let _sg = StackGuard::new(state);
check_stack(state, cmp::max(nargs + 1, 3))?; check_stack(state, cmp::max(nargs + 1, 3))?;
@ -164,9 +170,9 @@ impl<'lua> Thread<'lua> {
for _ in 0..nresults { for _ in 0..nresults {
results.push_front(lua.pop_value()); results.push_front(lua.pop_value());
} }
results (ret == ffi::LUA_YIELD, results)
}; };
R::from_lua_multi(results, lua) R::from_lua_multi(results, lua).map(|result| (yielded, result))
} }
/// Gets the status of the thread. /// Gets the status of the thread.
@ -428,9 +434,9 @@ where
// This is safe as we are not moving the whole struct // This is safe as we are not moving the whole struct
let this = unsafe { self.get_unchecked_mut() }; let this = unsafe { self.get_unchecked_mut() };
let ret: MultiValue = if let Some(args) = this.args0.take() { let ret: MultiValue = if let Some(args) = this.args0.take() {
this.thread.resume(args?)? this.thread.resume(args?)?.1
} else { } else {
this.thread.resume(())? this.thread.resume(())?.1
}; };
if is_poll_pending(&ret) { if is_poll_pending(&ret) {
@ -462,9 +468,9 @@ where
// This is safe as we are not moving the whole struct // This is safe as we are not moving the whole struct
let this = unsafe { self.get_unchecked_mut() }; let this = unsafe { self.get_unchecked_mut() };
let ret: MultiValue = if let Some(args) = this.args0.take() { let ret: MultiValue = if let Some(args) = this.args0.take() {
this.thread.resume(args?)? this.thread.resume(args?)?.1
} else { } else {
this.thread.resume(())? this.thread.resume(())?.1
}; };
if is_poll_pending(&ret) { if is_poll_pending(&ret) {

View File

@ -26,6 +26,9 @@ use crate::value::MultiValue;
#[cfg(feature = "unstable")] #[cfg(feature = "unstable")]
use {crate::lua::LuaInner, std::marker::PhantomData}; use {crate::lua::LuaInner, std::marker::PhantomData};
#[cfg(all(feature = "luau", feature = "serialize"))]
use serde::ser::{Serialize, SerializeTupleStruct, Serializer};
/// Type of Lua integer numbers. /// Type of Lua integer numbers.
pub type Integer = ffi::lua_Integer; pub type Integer = ffi::lua_Integer;
/// Type of Lua floating point numbers. /// Type of Lua floating point numbers.
@ -91,6 +94,92 @@ pub trait MaybeSend {}
#[cfg(not(feature = "send"))] #[cfg(not(feature = "send"))]
impl<T> MaybeSend for T {} impl<T> MaybeSend for T {}
/// A Luau vector type.
///
/// By default vectors are 3-dimensional, but can be 4-dimensional
/// if the `luau-vector4` feature is enabled.
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[derive(Debug, Default, Clone, Copy, PartialEq)]
pub struct Vector(pub(crate) [f32; Self::SIZE]);
#[cfg(feature = "luau")]
impl fmt::Display for Vector {
#[rustfmt::skip]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(not(feature = "luau-vector4"))]
return write!(f, "vector({}, {}, {})", self.x(), self.y(), self.z());
#[cfg(feature = "luau-vector4")]
return write!(f, "vector({}, {}, {}, {})", self.x(), self.y(), self.z(), self.w());
}
}
#[cfg(feature = "luau")]
impl Vector {
pub(crate) const SIZE: usize = if cfg!(feature = "luau-vector4") { 4 } else { 3 };
/// Creates a new vector.
#[cfg(not(feature = "luau-vector4"))]
pub const fn new(x: f32, y: f32, z: f32) -> Self {
Self([x, y, z])
}
/// Creates a new vector.
#[cfg(feature = "luau-vector4")]
pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
Self([x, y, z, w])
}
/// Creates a new vector with all components set to `0.0`.
#[doc(hidden)]
pub const fn zero() -> Self {
Self([0.0; Self::SIZE])
}
/// Returns 1st component of the vector.
pub const fn x(&self) -> f32 {
self.0[0]
}
/// Returns 2nd component of the vector.
pub const fn y(&self) -> f32 {
self.0[1]
}
/// Returns 3rd component of the vector.
pub const fn z(&self) -> f32 {
self.0[2]
}
/// Returns 4th component of the vector.
#[cfg(any(feature = "luau-vector4", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau-vector4")))]
pub const fn w(&self) -> f32 {
self.0[3]
}
}
#[cfg(all(feature = "luau", feature = "serialize"))]
impl Serialize for Vector {
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
let mut ts = serializer.serialize_tuple_struct("Vector", Self::SIZE)?;
ts.serialize_field(&self.x())?;
ts.serialize_field(&self.y())?;
ts.serialize_field(&self.z())?;
#[cfg(feature = "luau-vector4")]
ts.serialize_field(&self.w())?;
ts.end()
}
}
#[cfg(feature = "luau")]
impl PartialEq<[f32; Self::SIZE]> for Vector {
#[inline]
fn eq(&self, other: &[f32; Self::SIZE]) -> bool {
self.0 == *other
}
}
pub(crate) struct DestructedUserdata; pub(crate) struct DestructedUserdata;
/// An auto generated key into the Lua registry. /// An auto generated key into the Lua registry.
@ -184,7 +273,7 @@ impl RegistryKey {
} }
} }
pub(crate) struct LuaRef<'lua> { pub struct LuaRef<'lua> {
pub(crate) lua: &'lua Lua, pub(crate) lua: &'lua Lua,
pub(crate) index: c_int, pub(crate) index: c_int,
pub(crate) drop: bool, pub(crate) drop: bool,
@ -207,6 +296,12 @@ impl<'lua> LuaRef<'lua> {
mem::forget(self); mem::forget(self);
owned_ref owned_ref
} }
#[inline]
pub fn to_pointer(&self) -> *const std::os::raw::c_void {
let ref_thread = self.lua.ref_thread();
unsafe { ffi::lua_topointer(ref_thread, self.index) }
}
} }
impl<'lua> fmt::Debug for LuaRef<'lua> { impl<'lua> fmt::Debug for LuaRef<'lua> {

View File

@ -259,8 +259,7 @@ pub trait UserDataMethods<'lua, T> {
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>; R: IntoLuaMulti<'lua>;
/// Add an async method which accepts a `T` as the first parameter and returns Future. /// Add an async method which accepts a `&T` as the first parameter and returns Future.
/// The passed `T` is cloned from the original value.
/// ///
/// Refer to [`add_method`] for more information about the implementation. /// Refer to [`add_method`] for more information about the implementation.
/// ///
@ -269,12 +268,31 @@ pub trait UserDataMethods<'lua, T> {
/// [`add_method`]: #method.add_method /// [`add_method`]: #method.add_method
#[cfg(feature = "async")] #[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))] #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn add_async_method<M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M) fn add_async_method<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where where
T: Clone, 'lua: 's,
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static, T: 'static,
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 'lua, MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>;
/// Add an async method which accepts a `&mut T` as the first parameter and returns Future.
///
/// Refer to [`add_method`] for more information about the implementation.
///
/// Requires `feature = "async"`
///
/// [`add_method`]: #method.add_method
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn add_async_method_mut<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>; R: IntoLuaMulti<'lua>;
/// Add a regular method as a function which accepts generic arguments, the first argument will /// Add a regular method as a function which accepts generic arguments, the first argument will
@ -349,8 +367,7 @@ pub trait UserDataMethods<'lua, T> {
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>; R: IntoLuaMulti<'lua>;
/// Add an async metamethod which accepts a `T` as the first parameter and returns Future. /// Add an async metamethod which accepts a `&T` as the first parameter and returns Future.
/// The passed `T` is cloned from the original value.
/// ///
/// This is an async version of [`add_meta_method`]. /// This is an async version of [`add_meta_method`].
/// ///
@ -359,12 +376,31 @@ pub trait UserDataMethods<'lua, T> {
/// [`add_meta_method`]: #method.add_meta_method /// [`add_meta_method`]: #method.add_meta_method
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))] #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))] #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M) fn add_async_meta_method<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where where
T: Clone, 'lua: 's,
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static, T: 'static,
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 'lua, MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>;
/// Add an async metamethod which accepts a `&mut T` as the first parameter and returns Future.
///
/// This is an async version of [`add_meta_method_mut`].
///
/// Requires `feature = "async"`
///
/// [`add_meta_method_mut`]: #method.add_meta_method_mut
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn add_async_meta_method_mut<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>; R: IntoLuaMulti<'lua>;
/// Add a metamethod which accepts generic arguments. /// Add a metamethod which accepts generic arguments.
@ -770,6 +806,10 @@ impl OwnedAnyUserData {
} }
impl<'lua> AnyUserData<'lua> { impl<'lua> AnyUserData<'lua> {
pub fn as_raw_ref(&self) -> &LuaRef<'lua> {
&self.0
}
/// Checks whether the type of this userdata is `T`. /// Checks whether the type of this userdata is `T`.
pub fn is<T: 'static>(&self) -> bool { pub fn is<T: 'static>(&self) -> bool {
match self.inspect(|_: &UserDataCell<T>| Ok(())) { match self.inspect(|_: &UserDataCell<T>| Ok(())) {
@ -1055,6 +1095,11 @@ impl<'lua> AnyUserData<'lua> {
OwnedAnyUserData(self.0.into_owned()) OwnedAnyUserData(self.0.into_owned())
} }
#[inline(always)]
pub(crate) fn type_id(&self) -> Result<Option<TypeId>> {
unsafe { self.0.lua.get_userdata_type_id(&self.0) }
}
/// Returns a type name of this `UserData` (from `__name` metatable field). /// Returns a type name of this `UserData` (from `__name` metatable field).
pub(crate) fn type_name(&self) -> Result<Option<StdString>> { pub(crate) fn type_name(&self) -> Result<Option<StdString>> {
let lua = self.0.lua; let lua = self.0.lua;

View File

@ -27,11 +27,10 @@ pub trait AnyUserDataExt<'lua>: Sealed {
/// The metamethod is called with the userdata as its first argument, followed by the passed arguments. /// The metamethod is called with the userdata as its first argument, followed by the passed arguments.
#[cfg(feature = "async")] #[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))] #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>> fn call_async<A, R>(&self, args: A) -> LocalBoxFuture<'lua, Result<R>>
where where
'lua: 'fut,
A: IntoLuaMulti<'lua>, A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut; R: FromLuaMulti<'lua> + 'lua;
/// Calls the userdata method, assuming it has `__index` metamethod /// Calls the userdata method, assuming it has `__index` metamethod
/// and a function associated to `name`. /// and a function associated to `name`.
@ -48,15 +47,14 @@ pub trait AnyUserDataExt<'lua>: Sealed {
/// This might invoke the `__index` metamethod. /// This might invoke the `__index` metamethod.
#[cfg(feature = "async")] #[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))] #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async_method<'fut, A, R>( fn call_async_method<A, R>(
&self, &self,
name: impl AsRef<str>, name: impl AsRef<str>,
args: A, args: A,
) -> LocalBoxFuture<'fut, Result<R>> ) -> LocalBoxFuture<'lua, Result<R>>
where where
'lua: 'fut,
A: IntoLuaMulti<'lua>, A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut; R: FromLuaMulti<'lua> + 'lua;
/// Gets the function associated to `key` from the table and executes it, /// Gets the function associated to `key` from the table and executes it,
/// passing `args` as function arguments. /// passing `args` as function arguments.
@ -78,15 +76,14 @@ pub trait AnyUserDataExt<'lua>: Sealed {
/// This might invoke the `__index` metamethod. /// This might invoke the `__index` metamethod.
#[cfg(feature = "async")] #[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))] #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async_function<'fut, A, R>( fn call_async_function<A, R>(
&self, &self,
name: impl AsRef<str>, name: impl AsRef<str>,
args: A, args: A,
) -> LocalBoxFuture<'fut, Result<R>> ) -> LocalBoxFuture<'lua, Result<R>>
where where
'lua: 'fut,
A: IntoLuaMulti<'lua>, A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut; R: FromLuaMulti<'lua> + 'lua;
} }
impl<'lua> AnyUserDataExt<'lua> for AnyUserData<'lua> { impl<'lua> AnyUserDataExt<'lua> for AnyUserData<'lua> {
@ -127,18 +124,24 @@ impl<'lua> AnyUserDataExt<'lua> for AnyUserData<'lua> {
} }
#[cfg(feature = "async")] #[cfg(feature = "async")]
fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>> fn call_async<A, R>(&self, args: A) -> LocalBoxFuture<'lua, Result<R>>
where where
'lua: 'fut,
A: IntoLuaMulti<'lua>, A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut, R: FromLuaMulti<'lua> + 'lua,
{ {
let metatable = match self.get_metatable() { let metatable = match self.get_metatable() {
Ok(metatable) => metatable, Ok(metatable) => metatable,
Err(err) => return Box::pin(future::err(err)), Err(err) => return Box::pin(future::err(err)),
}; };
match metatable.get::<Value>(MetaMethod::Call) { match metatable.get::<Value>(MetaMethod::Call) {
Ok(Value::Function(func)) => func.call_async((self.clone(), args)), Ok(Value::Function(func)) => {
let mut args = match args.into_lua_multi(self.0.lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
args.push_front(Value::UserData(self.clone()));
Box::pin(async move { func.call_async(args).await })
}
Ok(_) => Box::pin(future::err(Error::RuntimeError( Ok(_) => Box::pin(future::err(Error::RuntimeError(
"attempt to call a userdata value".to_string(), "attempt to call a userdata value".to_string(),
))), ))),
@ -155,15 +158,14 @@ impl<'lua> AnyUserDataExt<'lua> for AnyUserData<'lua> {
} }
#[cfg(feature = "async")] #[cfg(feature = "async")]
fn call_async_method<'fut, A, R>( fn call_async_method<A, R>(
&self, &self,
name: impl AsRef<str>, name: impl AsRef<str>,
args: A, args: A,
) -> LocalBoxFuture<'fut, Result<R>> ) -> LocalBoxFuture<'lua, Result<R>>
where where
'lua: 'fut,
A: IntoLuaMulti<'lua>, A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut, R: FromLuaMulti<'lua> + 'lua,
{ {
self.call_async_function(name, (self.clone(), args)) self.call_async_function(name, (self.clone(), args))
} }
@ -183,22 +185,28 @@ impl<'lua> AnyUserDataExt<'lua> for AnyUserData<'lua> {
} }
#[cfg(feature = "async")] #[cfg(feature = "async")]
fn call_async_function<'fut, A, R>( fn call_async_function<A, R>(
&self, &self,
name: impl AsRef<str>, name: impl AsRef<str>,
args: A, args: A,
) -> LocalBoxFuture<'fut, Result<R>> ) -> LocalBoxFuture<'lua, Result<R>>
where where
'lua: 'fut,
A: IntoLuaMulti<'lua>, A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut, R: FromLuaMulti<'lua> + 'lua,
{ {
match self.get(name.as_ref()) { match self.get(name.as_ref()) {
Ok(Value::Function(func)) => func.call_async(args), Ok(Value::Function(func)) => {
Ok(val) => Box::pin(future::err(Error::RuntimeError(format!( let args = match args.into_lua_multi(self.0.lua) {
"attempt to call a {} value", Ok(args) => args,
val.type_name() Err(e) => return Box::pin(future::err(e)),
)))), };
Box::pin(async move { func.call_async(args).await })
}
Ok(val) => {
let type_name = val.type_name();
let msg = format!("attempt to call a {type_name} value");
Box::pin(future::err(Error::RuntimeError(msg)))
}
Err(err) => Box::pin(future::err(err)), Err(err) => Box::pin(future::err(err)),
} }
} }

View File

@ -1,6 +1,9 @@
#![allow(clippy::await_holding_refcell_ref, clippy::await_holding_lock)]
use std::any::TypeId; use std::any::TypeId;
use std::cell::{Ref, RefCell, RefMut}; use std::cell::{Ref, RefCell, RefMut};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::os::raw::c_int;
use std::string::String as StdString; use std::string::String as StdString;
use std::sync::{Arc, Mutex, RwLock}; use std::sync::{Arc, Mutex, RwLock};
@ -10,7 +13,7 @@ use crate::types::{Callback, MaybeSend};
use crate::userdata::{ use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods, AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
}; };
use crate::util::{check_stack, get_userdata, short_type_name, StackGuard}; use crate::util::{get_userdata, short_type_name};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Value}; use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Value};
#[cfg(not(feature = "send"))] #[cfg(not(feature = "send"))]
@ -75,62 +78,63 @@ impl<'lua, T: 'static> UserDataRegistrar<'lua, T> {
} }
Box::new(move |lua, mut args| { Box::new(move |lua, mut args| {
let front = args.pop_front(); let front = args
.pop_front()
.ok_or_else(|| Error::from_lua_conversion("missing argument", "userdata", None));
let front = try_self_arg!(front);
let call = |ud| { let call = |ud| {
// Self was at index 1, so we pass 2 here // Self was at index 1, so we pass 2 here
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?; let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args)?.into_lua_multi(lua) method(lua, ud, args)?.into_lua_multi(lua)
}; };
if let Some(front) = front { let userdata = try_self_arg!(AnyUserData::from_lua(front, lua));
let state = lua.state(); let (ref_thread, index) = (lua.ref_thread(), userdata.0.index);
let userdata = try_self_arg!(AnyUserData::from_lua(front, lua)); match try_self_arg!(userdata.type_id()) {
unsafe { Some(id) if id == TypeId::of::<T>() => unsafe {
let _sg = StackGuard::new(state); let ud = try_self_arg!(get_userdata_ref::<T>(ref_thread, index));
check_stack(state, 2)?; call(&ud)
},
let type_id = try_self_arg!(lua.push_userdata_ref(&userdata.0)); #[cfg(not(feature = "send"))]
match type_id { Some(id) if id == TypeId::of::<Rc<T>>() => unsafe {
Some(id) if id == TypeId::of::<T>() => { let ud = try_self_arg!(get_userdata_ref::<Rc<T>>(ref_thread, index));
let ud = try_self_arg!(get_userdata_ref::<T>(state)); call(&ud)
call(&ud) },
} #[cfg(not(feature = "send"))]
#[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!(get_userdata_ref::<Rc<RefCell<T>>>(state)); let ud = try_self_arg!(ud.try_borrow(), Error::UserDataBorrowError);
let ud = try_self_arg!(ud.try_borrow(), Error::UserDataBorrowError); call(&ud)
call(&ud) },
} Some(id) if id == TypeId::of::<Arc<T>>() => unsafe {
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => { let ud = try_self_arg!(get_userdata_ref::<Arc<T>>(ref_thread, index));
let ud = try_self_arg!(get_userdata_ref::<Arc<Mutex<T>>>(state)); call(&ud)
let ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowError); },
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));
#[cfg(feature = "parking_lot")] let ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowError);
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => { call(&ud)
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(state); },
let ud = try_self_arg!(ud); #[cfg(feature = "parking_lot")]
let ud = try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowError)); Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => unsafe {
call(&ud) let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(ref_thread, index);
} let ud = try_self_arg!(ud);
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => { let ud = try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowError));
let ud = try_self_arg!(get_userdata_ref::<Arc<RwLock<T>>>(state)); call(&ud)
let ud = try_self_arg!(ud.try_read(), 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));
#[cfg(feature = "parking_lot")] let ud = try_self_arg!(ud.try_read(), Error::UserDataBorrowError);
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => { call(&ud)
let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(state); },
let ud = try_self_arg!(ud); #[cfg(feature = "parking_lot")]
let ud = try_self_arg!(ud.try_read().ok_or(Error::UserDataBorrowError)); Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => unsafe {
call(&ud) let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(ref_thread, index);
} let ud = try_self_arg!(ud);
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)), let ud = try_self_arg!(ud.try_read().ok_or(Error::UserDataBorrowError));
} call(&ud)
} },
} else { _ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
let err = Error::from_lua_conversion("missing argument", "userdata", None);
Err(Error::bad_self_argument(&name, err))
} }
}) })
} }
@ -156,158 +160,258 @@ impl<'lua, T: 'static> UserDataRegistrar<'lua, T> {
let mut method = method let mut method = method
.try_borrow_mut() .try_borrow_mut()
.map_err(|_| Error::RecursiveMutCallback)?; .map_err(|_| Error::RecursiveMutCallback)?;
let front = args.pop_front(); let front = args
.pop_front()
.ok_or_else(|| Error::from_lua_conversion("missing argument", "userdata", None));
let front = try_self_arg!(front);
let call = |ud| { let call = |ud| {
// Self was at index 1, so we pass 2 here // Self was at index 1, so we pass 2 here
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?; let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args)?.into_lua_multi(lua) method(lua, ud, args)?.into_lua_multi(lua)
}; };
if let Some(front) = front { let userdata = try_self_arg!(AnyUserData::from_lua(front, lua));
let state = lua.state(); let (ref_thread, index) = (lua.ref_thread(), userdata.0.index);
let userdata = try_self_arg!(AnyUserData::from_lua(front, lua)); match try_self_arg!(userdata.type_id()) {
unsafe { Some(id) if id == TypeId::of::<T>() => unsafe {
let _sg = StackGuard::new(state); let mut ud = try_self_arg!(get_userdata_mut::<T>(ref_thread, index));
check_stack(state, 2)?; call(&mut ud)
},
let type_id = try_self_arg!(lua.push_userdata_ref(&userdata.0)); #[cfg(not(feature = "send"))]
match type_id { Some(id) if id == TypeId::of::<Rc<T>>() => Err(Error::UserDataBorrowMutError),
Some(id) if id == TypeId::of::<T>() => { #[cfg(not(feature = "send"))]
let mut ud = try_self_arg!(get_userdata_mut::<T>(state)); Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => unsafe {
call(&mut ud) 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);
#[cfg(not(feature = "send"))] call(&mut ud)
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => { },
let ud = try_self_arg!(get_userdata_mut::<Rc<RefCell<T>>>(state)); Some(id) if id == TypeId::of::<Arc<T>>() => Err(Error::UserDataBorrowMutError),
let mut ud = Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => unsafe {
try_self_arg!(ud.try_borrow_mut(), Error::UserDataBorrowMutError); let ud = try_self_arg!(get_userdata_mut::<Arc<Mutex<T>>>(ref_thread, index));
call(&mut ud) let mut ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowMutError);
} call(&mut ud)
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => { },
let ud = try_self_arg!(get_userdata_mut::<Arc<Mutex<T>>>(state)); #[cfg(feature = "parking_lot")]
let mut ud = Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => unsafe {
try_self_arg!(ud.try_lock(), Error::UserDataBorrowMutError); let ud = get_userdata_mut::<Arc<parking_lot::Mutex<T>>>(ref_thread, index);
call(&mut ud) let ud = try_self_arg!(ud);
} let mut ud = try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowMutError));
#[cfg(feature = "parking_lot")] call(&mut ud)
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => { },
let ud = get_userdata_mut::<Arc<parking_lot::Mutex<T>>>(state); Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => unsafe {
let ud = try_self_arg!(ud); let ud = try_self_arg!(get_userdata_mut::<Arc<RwLock<T>>>(ref_thread, index));
let mut ud = let mut ud = try_self_arg!(ud.try_write(), Error::UserDataBorrowMutError);
try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowMutError)); call(&mut ud)
call(&mut ud) },
} #[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => { Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => unsafe {
let ud = try_self_arg!(get_userdata_mut::<Arc<RwLock<T>>>(state)); let ud = get_userdata_mut::<Arc<parking_lot::RwLock<T>>>(ref_thread, index);
let mut ud = let ud = try_self_arg!(ud);
try_self_arg!(ud.try_write(), Error::UserDataBorrowMutError); let mut ud = try_self_arg!(ud.try_write().ok_or(Error::UserDataBorrowMutError));
call(&mut ud) call(&mut ud)
} },
#[cfg(feature = "parking_lot")] _ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
let ud = get_userdata_mut::<Arc<parking_lot::RwLock<T>>>(state);
let ud = try_self_arg!(ud);
let mut ud =
try_self_arg!(ud.try_write().ok_or(Error::UserDataBorrowMutError));
call(&mut ud)
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
}
} else {
let err = Error::from_lua_conversion("missing argument", "userdata", None);
Err(Error::bad_self_argument(&name, err))
} }
}) })
} }
#[cfg(feature = "async")] #[cfg(feature = "async")]
fn box_async_method<M, A, MR, R>(name: &str, method: M) -> AsyncCallback<'lua, 'static> fn box_async_method<'s, M, A, MR, R>(name: &str, method: M) -> AsyncCallback<'lua, 'static>
where where
T: Clone, 'lua: 's,
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static, T: 'static,
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 'lua, MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>, R: IntoLuaMulti<'lua>,
{ {
let name = get_function_name::<T>(name); let name = get_function_name::<T>(name);
macro_rules! try_self_arg { let method = Arc::new(method);
($res:expr) => {
$res.map_err(|err| Error::bad_self_argument(&name, err))?
};
($res:expr, $err:expr) => {
$res.map_err(|_| Error::bad_self_argument(&name, $err))?
};
}
Box::new(move |lua, mut args| { Box::new(move |lua, mut args| {
let front = args.pop_front(); let name = name.clone();
let call = |ud| { let method = method.clone();
// Self was at index 1, so we pass 2 here macro_rules! try_self_arg {
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?; ($res:expr) => {
Ok(method(lua, ud, args)) $res.map_err(|err| Error::bad_self_argument(&name, err))?
}; };
($res:expr, $err:expr) => {
let fut_res = || { $res.map_err(|_| Error::bad_self_argument(&name, $err))?
if let Some(front) = front { };
let state = lua.state();
let userdata = AnyUserData::from_lua(front, lua)?;
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 2)?;
let type_id = try_self_arg!(lua.push_userdata_ref(&userdata.0));
match type_id {
Some(id) if id == TypeId::of::<T>() => {
let ud = get_userdata_ref::<T>(state)?;
call(ud.clone())
}
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
let ud = try_self_arg!(get_userdata_ref::<Rc<RefCell<T>>>(state));
let ud = try_self_arg!(ud.try_borrow(), Error::UserDataBorrowError);
call(ud.clone())
}
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
let ud = try_self_arg!(get_userdata_ref::<Arc<Mutex<T>>>(state));
let ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowError);
call(ud.clone())
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(state);
let ud = try_self_arg!(ud);
let ud =
try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowError));
call(ud.clone())
}
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
let ud = try_self_arg!(get_userdata_ref::<Arc<RwLock<T>>>(state));
let ud = try_self_arg!(ud.try_read(), Error::UserDataBorrowError);
call(ud.clone())
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(state);
let ud = try_self_arg!(ud);
let ud =
try_self_arg!(ud.try_read().ok_or(Error::UserDataBorrowError));
call(ud.clone())
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
}
} else {
let err = Error::from_lua_conversion("missing argument", "userdata", None);
Err(Error::bad_self_argument(&name, err))
}
};
match fut_res() {
Ok(fut) => {
Box::pin(fut.and_then(move |ret| future::ready(ret.into_lua_multi(lua))))
}
Err(e) => Box::pin(future::err(e)),
} }
Box::pin(async move {
let front = args.pop_front().ok_or_else(|| {
Error::from_lua_conversion("missing argument", "userdata", None)
});
let front = try_self_arg!(front);
let userdata: AnyUserData = try_self_arg!(AnyUserData::from_lua(front, lua));
let (ref_thread, index) = (lua.ref_thread(), userdata.0.index);
match try_self_arg!(userdata.type_id()) {
Some(id) if id == TypeId::of::<T>() => unsafe {
let ud = try_self_arg!(get_userdata_ref::<T>(ref_thread, index));
let ud = std::mem::transmute::<&T, &T>(&ud);
// Self was at index 1, so we pass 2 here
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<T>>() => unsafe {
let ud = try_self_arg!(get_userdata_ref::<Rc<T>>(ref_thread, index));
let ud = std::mem::transmute::<&T, &T>(&ud);
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => unsafe {
let ud =
try_self_arg!(get_userdata_ref::<Rc<RefCell<T>>>(ref_thread, index));
let ud = try_self_arg!(ud.try_borrow(), Error::UserDataBorrowError);
let ud = std::mem::transmute::<&T, &T>(&ud);
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
Some(id) if id == TypeId::of::<Arc<T>>() => unsafe {
let ud = try_self_arg!(get_userdata_ref::<Arc<T>>(ref_thread, index));
let ud = std::mem::transmute::<&T, &T>(&ud);
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => unsafe {
let ud =
try_self_arg!(get_userdata_ref::<Arc<Mutex<T>>>(ref_thread, index));
let ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowError);
let ud = std::mem::transmute::<&T, &T>(&ud);
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => unsafe {
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(ref_thread, index);
let ud = try_self_arg!(ud);
let ud = try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowError));
let ud = std::mem::transmute::<&T, &T>(&ud);
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => unsafe {
let ud =
try_self_arg!(get_userdata_ref::<Arc<RwLock<T>>>(ref_thread, index));
let ud = try_self_arg!(ud.try_read(), Error::UserDataBorrowError);
let ud = std::mem::transmute::<&T, &T>(&ud);
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => unsafe {
let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(ref_thread, index);
let ud = try_self_arg!(ud);
let ud = try_self_arg!(ud.try_read().ok_or(Error::UserDataBorrowError));
let ud = std::mem::transmute::<&T, &T>(&ud);
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
})
})
}
#[cfg(feature = "async")]
fn box_async_method_mut<'s, M, A, MR, R>(name: &str, method: M) -> AsyncCallback<'lua, 'static>
where
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
{
let name = get_function_name::<T>(name);
let method = Arc::new(method);
Box::new(move |lua, mut args| {
let name = name.clone();
let method = method.clone();
macro_rules! try_self_arg {
($res:expr) => {
$res.map_err(|err| Error::bad_self_argument(&name, err))?
};
($res:expr, $err:expr) => {
$res.map_err(|_| Error::bad_self_argument(&name, $err))?
};
}
Box::pin(async move {
let front = args.pop_front().ok_or_else(|| {
Error::from_lua_conversion("missing argument", "userdata", None)
});
let front = try_self_arg!(front);
let userdata: AnyUserData = try_self_arg!(AnyUserData::from_lua(front, lua));
let (ref_thread, index) = (lua.ref_thread(), userdata.0.index);
match try_self_arg!(userdata.type_id()) {
Some(id) if id == TypeId::of::<T>() => unsafe {
let mut ud = try_self_arg!(get_userdata_mut::<T>(ref_thread, index));
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
// Self was at index 1, so we pass 2 here
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
Err(Error::UserDataBorrowMutError)
}
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => unsafe {
let ud =
try_self_arg!(get_userdata_mut::<Rc<RefCell<T>>>(ref_thread, index));
let mut ud =
try_self_arg!(ud.try_borrow_mut(), Error::UserDataBorrowMutError);
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Arc<T>>() => Err(Error::UserDataBorrowMutError),
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => unsafe {
let ud =
try_self_arg!(get_userdata_mut::<Arc<Mutex<T>>>(ref_thread, index));
let mut ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowMutError);
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => unsafe {
let ud = get_userdata_mut::<Arc<parking_lot::Mutex<T>>>(ref_thread, index);
let ud = try_self_arg!(ud);
let mut ud =
try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowMutError));
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => unsafe {
let ud =
try_self_arg!(get_userdata_mut::<Arc<RwLock<T>>>(ref_thread, index));
let mut ud = try_self_arg!(ud.try_write(), Error::UserDataBorrowMutError);
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => unsafe {
let ud = get_userdata_mut::<Arc<parking_lot::RwLock<T>>>(ref_thread, index);
let ud = try_self_arg!(ud);
let mut ud =
try_self_arg!(ud.try_write().ok_or(Error::UserDataBorrowMutError));
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args).await?.into_lua_multi(lua)
},
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
})
}) })
} }
@ -500,12 +604,13 @@ impl<'lua, T: 'static> UserDataMethods<'lua, T> for UserDataRegistrar<'lua, T> {
} }
#[cfg(feature = "async")] #[cfg(feature = "async")]
fn add_async_method<M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M) fn add_async_method<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where where
T: Clone, 'lua: 's,
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static, T: 'static,
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 'lua, MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>, R: IntoLuaMulti<'lua>,
{ {
let name = name.as_ref(); let name = name.as_ref();
@ -513,6 +618,21 @@ impl<'lua, T: 'static> UserDataMethods<'lua, T> for UserDataRegistrar<'lua, T> {
.push((name.into(), Self::box_async_method(name, method))); .push((name.into(), Self::box_async_method(name, method)));
} }
#[cfg(feature = "async")]
fn add_async_method_mut<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
{
let name = name.as_ref();
self.async_methods
.push((name.into(), Self::box_async_method_mut(name, method)));
}
fn add_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F) fn add_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
where where
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static, F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
@ -571,12 +691,13 @@ impl<'lua, T: 'static> UserDataMethods<'lua, T> for UserDataRegistrar<'lua, T> {
} }
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))] #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M) fn add_async_meta_method<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where where
T: Clone, 'lua: 's,
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static, T: 'static,
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 'lua, MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>, R: IntoLuaMulti<'lua>,
{ {
let name = name.as_ref(); let name = name.as_ref();
@ -584,6 +705,21 @@ impl<'lua, T: 'static> UserDataMethods<'lua, T> for UserDataRegistrar<'lua, T> {
.push((name.into(), Self::box_async_method(name, method))); .push((name.into(), Self::box_async_method(name, method)));
} }
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_method_mut<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
{
let name = name.as_ref();
self.async_meta_methods
.push((name.into(), Self::box_async_method_mut(name, method)));
}
fn add_meta_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F) fn add_meta_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
where where
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static, F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
@ -632,13 +768,16 @@ impl<'lua, T: 'static> UserDataMethods<'lua, T> for UserDataRegistrar<'lua, T> {
} }
#[inline] #[inline]
unsafe fn get_userdata_ref<'a, T>(state: *mut ffi::lua_State) -> Result<Ref<'a, T>> { unsafe fn get_userdata_ref<'a, T>(state: *mut ffi::lua_State, index: c_int) -> Result<Ref<'a, T>> {
(*get_userdata::<UserDataCell<T>>(state, -1)).try_borrow() (*get_userdata::<UserDataCell<T>>(state, index)).try_borrow()
} }
#[inline] #[inline]
unsafe fn get_userdata_mut<'a, T>(state: *mut ffi::lua_State) -> Result<RefMut<'a, T>> { unsafe fn get_userdata_mut<'a, T>(
(*get_userdata::<UserDataCell<T>>(state, -1)).try_borrow_mut() state: *mut ffi::lua_State,
index: c_int,
) -> Result<RefMut<'a, T>> {
(*get_userdata::<UserDataCell<T>>(state, index)).try_borrow_mut()
} }
macro_rules! lua_userdata_impl { macro_rules! lua_userdata_impl {
@ -659,8 +798,12 @@ macro_rules! lua_userdata_impl {
}; };
} }
#[cfg(not(feature = "send"))]
lua_userdata_impl!(Rc<T>);
#[cfg(not(feature = "send"))] #[cfg(not(feature = "send"))]
lua_userdata_impl!(Rc<RefCell<T>>); lua_userdata_impl!(Rc<RefCell<T>>);
lua_userdata_impl!(Arc<T>);
lua_userdata_impl!(Arc<Mutex<T>>); lua_userdata_impl!(Arc<Mutex<T>>);
lua_userdata_impl!(Arc<RwLock<T>>); lua_userdata_impl!(Arc<RwLock<T>>);
#[cfg(feature = "parking_lot")] #[cfg(feature = "parking_lot")]

View File

@ -359,6 +359,7 @@ pub unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
// Pushes the userdata and attaches a metatable with __gc method. // Pushes the userdata and attaches a metatable with __gc method.
// Internally uses 3 stack spaces, does not call checkstack. // Internally uses 3 stack spaces, does not call checkstack.
#[inline]
pub unsafe fn push_gc_userdata<T: Any>( pub unsafe fn push_gc_userdata<T: Any>(
state: *mut ffi::lua_State, state: *mut ffi::lua_State,
t: T, t: T,
@ -398,7 +399,7 @@ pub unsafe fn get_gc_userdata<T: Any>(
} }
unsafe extern "C" fn lua_error_impl(state: *mut ffi::lua_State) -> c_int { unsafe extern "C" fn lua_error_impl(state: *mut ffi::lua_State) -> c_int {
ffi::lua_error(state); ffi::lua_error(state)
} }
unsafe extern "C" fn lua_isfunction_impl(state: *mut ffi::lua_State) -> c_int { unsafe extern "C" fn lua_isfunction_impl(state: *mut ffi::lua_State) -> c_int {
@ -628,6 +629,7 @@ pub unsafe extern "C" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c
// This function uses some of the bottom of the stack for error handling, the given callback will be // This function uses some of the bottom of the stack for error handling, the given callback will be
// given the number of arguments available as an argument, and should return the number of returns // given the number of arguments available as an argument, and should return the number of returns
// as normal, but cannot assume that the arguments available start at 0. // as normal, but cannot assume that the arguments available start at 0.
#[inline]
pub unsafe fn callback_error<F, R>(state: *mut ffi::lua_State, f: F) -> R pub unsafe fn callback_error<F, R>(state: *mut ffi::lua_State, f: F) -> R
where where
F: FnOnce(c_int) -> Result<R>, F: FnOnce(c_int) -> Result<R>,
@ -854,6 +856,7 @@ pub unsafe fn init_gc_metatable<T: Any>(
Ok(()) Ok(())
} }
#[inline]
pub unsafe fn get_gc_metatable<T: Any>(state: *mut ffi::lua_State) { pub unsafe fn get_gc_metatable<T: Any>(state: *mut ffi::lua_State) {
let type_id = TypeId::of::<T>(); let type_id = TypeId::of::<T>();
let ref_addr = let ref_addr =

View File

@ -44,7 +44,7 @@ pub enum Value<'lua> {
/// A Luau vector. /// A Luau vector.
#[cfg(any(feature = "luau", doc))] #[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))] #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
Vector(f32, f32, f32), Vector(crate::types::Vector),
/// An interned string, managed by Lua. /// An interned string, managed by Lua.
/// ///
/// Unlike Rust strings, Lua strings may not be valid UTF-8. /// Unlike Rust strings, Lua strings may not be valid UTF-8.
@ -79,7 +79,7 @@ impl<'lua> Value<'lua> {
Value::Integer(_) => "integer", Value::Integer(_) => "integer",
Value::Number(_) => "number", Value::Number(_) => "number",
#[cfg(feature = "luau")] #[cfg(feature = "luau")]
Value::Vector(_, _, _) => "vector", Value::Vector(_) => "vector",
Value::String(_) => "string", Value::String(_) => "string",
Value::Table(_) => "table", Value::Table(_) => "table",
Value::Function(_) => "function", Value::Function(_) => "function",
@ -116,18 +116,14 @@ impl<'lua> Value<'lua> {
/// Typically this function is used only for hashing and debug information. /// Typically this function is used only for hashing and debug information.
#[inline] #[inline]
pub fn to_pointer(&self) -> *const c_void { pub fn to_pointer(&self) -> *const c_void {
unsafe { match self {
match self { Value::LightUserData(ud) => ud.0,
Value::LightUserData(ud) => ud.0, Value::Table(Table(r))
Value::Table(t) => t.to_pointer(), | Value::String(String(r))
Value::String(s) => s.to_pointer(), | Value::Function(Function(r))
Value::Function(Function(r)) | Value::Thread(Thread(r))
| Value::Thread(Thread(r)) | Value::UserData(AnyUserData(r)) => r.to_pointer(),
| Value::UserData(AnyUserData(r)) => { _ => ptr::null(),
ffi::lua_topointer(r.lua.ref_thread(), r.index)
}
_ => ptr::null(),
}
} }
} }
@ -143,7 +139,7 @@ impl<'lua> Value<'lua> {
Value::Integer(i) => Ok(i.to_string()), Value::Integer(i) => Ok(i.to_string()),
Value::Number(n) => Ok(n.to_string()), Value::Number(n) => Ok(n.to_string()),
#[cfg(feature = "luau")] #[cfg(feature = "luau")]
Value::Vector(x, y, z) => Ok(format!("vector({x}, {y}, {z})")), Value::Vector(v) => Ok(v.to_string()),
Value::String(s) => Ok(s.to_str()?.to_string()), Value::String(s) => Ok(s.to_str()?.to_string()),
Value::Table(Table(r)) Value::Table(Table(r))
| Value::Function(Function(r)) | Value::Function(Function(r))
@ -218,7 +214,7 @@ impl<'lua> Value<'lua> {
Value::Integer(i) => write!(fmt, "{i}"), Value::Integer(i) => write!(fmt, "{i}"),
Value::Number(n) => write!(fmt, "{n}"), Value::Number(n) => write!(fmt, "{n}"),
#[cfg(feature = "luau")] #[cfg(feature = "luau")]
Value::Vector(x, y, z) => write!(fmt, "vector({x}, {y}, {z})"), Value::Vector(v) => write!(fmt, "{v}"),
Value::String(s) => write!(fmt, "{s:?}"), Value::String(s) => write!(fmt, "{s:?}"),
Value::Table(t) if recursive && !visited.contains(&t.to_pointer()) => { Value::Table(t) if recursive && !visited.contains(&t.to_pointer()) => {
t.fmt_pretty(fmt, ident, visited) t.fmt_pretty(fmt, ident, visited)
@ -249,7 +245,7 @@ impl fmt::Debug for Value<'_> {
Value::Integer(i) => write!(fmt, "Integer({i})"), Value::Integer(i) => write!(fmt, "Integer({i})"),
Value::Number(n) => write!(fmt, "Number({n})"), Value::Number(n) => write!(fmt, "Number({n})"),
#[cfg(feature = "luau")] #[cfg(feature = "luau")]
Value::Vector(x, y, z) => write!(fmt, "Vector({x}, {y}, {z})"), Value::Vector(v) => write!(fmt, "{v:?}"),
Value::String(s) => write!(fmt, "String({s:?})"), Value::String(s) => write!(fmt, "String({s:?})"),
Value::Table(t) => write!(fmt, "{t:?}"), Value::Table(t) => write!(fmt, "{t:?}"),
Value::Function(f) => write!(fmt, "{f:?}"), Value::Function(f) => write!(fmt, "{f:?}"),
@ -271,7 +267,7 @@ impl<'lua> PartialEq for Value<'lua> {
(Value::Number(a), Value::Integer(b)) => *a == *b as Number, (Value::Number(a), Value::Integer(b)) => *a == *b as Number,
(Value::Number(a), Value::Number(b)) => *a == *b, (Value::Number(a), Value::Number(b)) => *a == *b,
#[cfg(feature = "luau")] #[cfg(feature = "luau")]
(Value::Vector(x1, y1, z1), Value::Vector(x2, y2, z2)) => (x1, y1, z1) == (x2, y2, z2), (Value::Vector(v1), Value::Vector(v2)) => v1 == v2,
(Value::String(a), Value::String(b)) => a == b, (Value::String(a), Value::String(b)) => a == b,
(Value::Table(a), Value::Table(b)) => a == b, (Value::Table(a), Value::Table(b)) => a == b,
(Value::Function(a), Value::Function(b)) => a == b, (Value::Function(a), Value::Function(b)) => a == b,
@ -303,7 +299,7 @@ impl<'lua> Serialize for Value<'lua> {
.serialize_i64((*i).try_into().expect("cannot convert Lua Integer to i64")), .serialize_i64((*i).try_into().expect("cannot convert Lua Integer to i64")),
Value::Number(n) => serializer.serialize_f64(*n), Value::Number(n) => serializer.serialize_f64(*n),
#[cfg(feature = "luau")] #[cfg(feature = "luau")]
Value::Vector(x, y, z) => (x, y, z).serialize(serializer), Value::Vector(v) => v.serialize(serializer),
Value::String(s) => s.serialize(serializer), Value::String(s) => s.serialize(serializer),
Value::Table(t) => t.serialize(serializer), Value::Table(t) => t.serialize(serializer),
Value::UserData(ud) => ud.serialize(serializer), Value::UserData(ud) => ud.serialize(serializer),

View File

@ -1,6 +1,5 @@
#![cfg(feature = "async")] #![cfg(feature = "async")]
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::time::Duration; use std::time::Duration;
@ -361,19 +360,18 @@ async fn test_async_thread_pool() -> Result<()> {
#[tokio::test] #[tokio::test]
async fn test_async_userdata() -> Result<()> { async fn test_async_userdata() -> Result<()> {
#[derive(Clone)] struct MyUserData(u64);
struct MyUserData(Arc<AtomicU64>);
impl UserData for MyUserData { impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("get_value", |_, data, ()| async move { methods.add_async_method("get_value", |_, data, ()| async move {
Delay::new(Duration::from_millis(10)).await; Delay::new(Duration::from_millis(10)).await;
Ok(data.0.load(Ordering::Relaxed)) Ok(data.0)
}); });
methods.add_async_method("set_value", |_, data, n| async move { methods.add_async_method_mut("set_value", |_, data, n| async move {
Delay::new(Duration::from_millis(10)).await; Delay::new(Duration::from_millis(10)).await;
data.0.store(n, Ordering::Relaxed); data.0 = n;
Ok(()) Ok(())
}); });
@ -384,7 +382,7 @@ async fn test_async_userdata() -> Result<()> {
#[cfg(not(any(feature = "lua51", feature = "luau")))] #[cfg(not(any(feature = "lua51", feature = "luau")))]
methods.add_async_meta_method(mlua::MetaMethod::Call, |_, data, ()| async move { methods.add_async_meta_method(mlua::MetaMethod::Call, |_, data, ()| async move {
let n = data.0.load(Ordering::Relaxed); let n = data.0;
Delay::new(Duration::from_millis(n)).await; Delay::new(Duration::from_millis(n)).await;
Ok(format!("elapsed:{}ms", n)) Ok(format!("elapsed:{}ms", n))
}); });
@ -395,23 +393,24 @@ async fn test_async_userdata() -> Result<()> {
|_, data, key: String| async move { |_, data, key: String| async move {
Delay::new(Duration::from_millis(10)).await; Delay::new(Duration::from_millis(10)).await;
match key.as_str() { match key.as_str() {
"ms" => Ok(Some(data.0.load(Ordering::Relaxed) as f64)), "ms" => Ok(Some(data.0 as f64)),
"s" => Ok(Some((data.0.load(Ordering::Relaxed) as f64) / 1000.0)), "s" => Ok(Some((data.0 as f64) / 1000.0)),
_ => Ok(None), _ => Ok(None),
} }
}, },
); );
#[cfg(not(any(feature = "lua51", feature = "luau")))] #[cfg(not(any(feature = "lua51", feature = "luau")))]
methods.add_async_meta_method( methods.add_async_meta_method_mut(
mlua::MetaMethod::NewIndex, mlua::MetaMethod::NewIndex,
|_, data, (key, value): (String, f64)| async move { |_, data, (key, value): (String, f64)| async move {
Delay::new(Duration::from_millis(10)).await; Delay::new(Duration::from_millis(10)).await;
match key.as_str() { match key.as_str() {
"ms" => Ok(data.0.store(value as u64, Ordering::Relaxed)), "ms" => data.0 = value as u64,
"s" => Ok(data.0.store((value * 1000.0) as u64, Ordering::Relaxed)), "s" => data.0 = (value * 1000.0) as u64,
_ => Err(Error::external(format!("key '{}' not found", key))), _ => return Err(Error::external(format!("key '{}' not found", key))),
} }
Ok(())
}, },
); );
} }
@ -420,7 +419,7 @@ async fn test_async_userdata() -> Result<()> {
let lua = Lua::new(); let lua = Lua::new();
let globals = lua.globals(); let globals = lua.globals();
let userdata = lua.create_userdata(MyUserData(Arc::new(AtomicU64::new(11))))?; let userdata = lua.create_userdata(MyUserData(11))?;
globals.set("userdata", userdata.clone())?; globals.set("userdata", userdata.clone())?;
lua.load( lua.load(

View File

@ -15,7 +15,11 @@ fn test_compilation() {
t.compile_fail("tests/compile/static_callback_args.rs"); t.compile_fail("tests/compile/static_callback_args.rs");
#[cfg(feature = "async")] #[cfg(feature = "async")]
t.compile_fail("tests/compile/async_nonstatic_userdata.rs"); {
t.compile_fail("tests/compile/async_any_userdata_method.rs");
t.compile_fail("tests/compile/async_nonstatic_userdata.rs");
t.compile_fail("tests/compile/async_userdata_method.rs");
}
#[cfg(feature = "send")] #[cfg(feature = "send")]
t.compile_fail("tests/compile/non_send.rs"); t.compile_fail("tests/compile/non_send.rs");

View File

@ -0,0 +1,14 @@
use mlua::{UserDataMethods, Lua};
fn main() {
let lua = Lua::new();
lua.register_userdata_type::<String>(|reg| {
let s = String::new();
let mut s = &s;
reg.add_async_method("t", |_, this: &String, ()| async {
s = this;
Ok(())
});
}).unwrap();
}

View File

@ -0,0 +1,81 @@
error: lifetime may not live long enough
--> tests/compile/async_any_userdata_method.rs:9:58
|
9 | reg.add_async_method("t", |_, this: &String, ()| async {
| ___________________________________----------------------_^
| | | |
| | | return type of closure `[async block@$DIR/tests/compile/async_any_userdata_method.rs:9:58: 12:10]` contains a lifetime `'2`
| | lifetime `'1` represents this closure's body
10 | | s = this;
11 | | Ok(())
12 | | });
| |_________^ returning this value requires that `'1` must outlive `'2`
|
= note: closure implements `Fn`, so references to captured variables can't escape the closure
error[E0596]: cannot borrow `s` as mutable, as it is a captured variable in a `Fn` closure
--> tests/compile/async_any_userdata_method.rs:9:58
|
9 | reg.add_async_method("t", |_, this: &String, ()| async {
| __________________________________________________________^
10 | | s = this;
| | - mutable borrow occurs due to use of `s` in closure
11 | | Ok(())
12 | | });
| |_________^ cannot borrow as mutable
error[E0597]: `s` does not live long enough
--> tests/compile/async_any_userdata_method.rs:8:21
|
8 | let mut s = &s;
| ^^ borrowed value does not live long enough
9 | / reg.add_async_method("t", |_, this: &String, ()| async {
10 | | s = this;
11 | | Ok(())
12 | | });
| |__________- argument requires that `s` is borrowed for `'static`
13 | }).unwrap();
| - `s` dropped here while still borrowed
error[E0521]: borrowed data escapes outside of closure
--> tests/compile/async_any_userdata_method.rs:9:9
|
6 | lua.register_userdata_type::<String>(|reg| {
| ---
| |
| `reg` is a reference that is only valid in the closure body
| has type `&mut LuaUserDataRegistrar<'1, std::string::String>`
...
9 | / reg.add_async_method("t", |_, this: &String, ()| async {
10 | | s = this;
11 | | Ok(())
12 | | });
| | ^
| | |
| |__________`reg` escapes the closure body here
| argument requires that `'1` must outlive `'static`
|
= note: requirement occurs because of a mutable reference to `LuaUserDataRegistrar<'_, std::string::String>`
= note: mutable references are invariant over their type parameter
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error[E0373]: closure may outlive the current function, but it borrows `s`, which is owned by the current function
--> tests/compile/async_any_userdata_method.rs:9:35
|
9 | reg.add_async_method("t", |_, this: &String, ()| async {
| ^^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `s`
10 | s = this;
| - `s` is borrowed here
|
note: function requires argument type to outlive `'static`
--> tests/compile/async_any_userdata_method.rs:9:9
|
9 | / reg.add_async_method("t", |_, this: &String, ()| async {
10 | | s = this;
11 | | Ok(())
12 | | });
| |__________^
help: to force the closure to take ownership of `s` (and any other referenced variables), use the `move` keyword
|
9 | reg.add_async_method("t", move |_, this: &String, ()| async {
| ++++

View File

@ -4,11 +4,8 @@ error: lifetime may not live long enough
7 | impl<'a> UserData for MyUserData<'a> { 7 | impl<'a> UserData for MyUserData<'a> {
| -- lifetime `'a` defined here | -- lifetime `'a` defined here
8 | fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { 8 | fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
| ---- lifetime `'lua` defined here
9 | / methods.add_async_method("print", |_, data, ()| async move { 9 | / methods.add_async_method("print", |_, data, ()| async move {
10 | | println!("{}", data.0); 10 | | println!("{}", data.0);
11 | | Ok(()) 11 | | Ok(())
12 | | }); 12 | | });
| |______________^ argument requires that `'a` must outlive `'lua` | |______________^ requires that `'a` must outlive `'static`
|
= help: consider adding the following bound: `'a: 'lua`

View File

@ -0,0 +1,14 @@
use mlua::{UserData, UserDataMethods};
struct MyUserData;
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("method", |_, this: &'static Self, ()| async {
Ok(())
});
// ^ lifetime may not live long enough
}
}
fn main() {}

View File

@ -0,0 +1,17 @@
warning: unused variable: `this`
--> tests/compile/async_userdata_method.rs:7:48
|
7 | methods.add_async_method("method", |_, this: &'static Self, ()| async {
| ^^^^ help: if this is intentional, prefix it with an underscore: `_this`
|
= note: `#[warn(unused_variables)]` on by default
error: lifetime may not live long enough
--> tests/compile/async_userdata_method.rs:7:9
|
6 | fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
| ---- lifetime `'lua` defined here
7 | / methods.add_async_method("method", |_, this: &'static Self, ()| async {
8 | | Ok(())
9 | | });
| |__________^ argument requires that `'lua` must outlive `'static`

View File

@ -7,7 +7,9 @@ use std::panic::{catch_unwind, AssertUnwindSafe};
use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc; use std::sync::Arc;
use mlua::{Compiler, CoverageInfo, Error, Lua, Result, Table, ThreadStatus, Value, VmState}; use mlua::{
Compiler, CoverageInfo, Error, Lua, Result, Table, ThreadStatus, Value, Vector, VmState,
};
#[test] #[test]
fn test_version() -> Result<()> { fn test_version() -> Result<()> {
@ -50,13 +52,18 @@ fn test_require() -> Result<()> {
.exec() .exec()
} }
#[cfg(not(feature = "luau-vector4"))]
#[test] #[test]
fn test_vectors() -> Result<()> { fn test_vectors() -> Result<()> {
let lua = Lua::new(); let lua = Lua::new();
let v: [f32; 3] = lua.load("vector(1, 2, 3) + vector(3, 2, 1)").eval()?; let v: Vector = lua.load("vector(1, 2, 3) + vector(3, 2, 1)").eval()?;
assert_eq!(v, [4.0, 4.0, 4.0]); assert_eq!(v, [4.0, 4.0, 4.0]);
// Test conversion into Rust array
let v: [f64; 3] = lua.load("vector(1, 2, 3)").eval()?;
assert!(v == [1.0, 2.0, 3.0]);
// Test vector methods // Test vector methods
lua.load( lua.load(
r#" r#"
@ -83,6 +90,46 @@ fn test_vectors() -> Result<()> {
Ok(()) Ok(())
} }
#[cfg(feature = "luau-vector4")]
#[test]
fn test_vectors() -> Result<()> {
let lua = Lua::new();
let v: Vector = lua.load("vector(1, 2, 3, 4) + vector(4, 3, 2, 1)").eval()?;
assert_eq!(v, [5.0, 5.0, 5.0, 5.0]);
// Test conversion into Rust array
let v: [f64; 4] = lua.load("vector(1, 2, 3, 4)").eval()?;
assert!(v == [1.0, 2.0, 3.0, 4.0]);
// Test vector methods
lua.load(
r#"
local v = vector(1, 2, 3, 4)
assert(v.x == 1)
assert(v.y == 2)
assert(v.z == 3)
assert(v.w == 4)
"#,
)
.exec()?;
// Test vector methods (fastcall)
lua.load(
r#"
local v = vector(1, 2, 3, 4)
assert(v.x == 1)
assert(v.y == 2)
assert(v.z == 3)
assert(v.w == 4)
"#,
)
.set_compiler(Compiler::new().set_vector_ctor(Some("vector".to_string())))
.exec()?;
Ok(())
}
#[test] #[test]
fn test_readonly_table() -> Result<()> { fn test_readonly_table() -> Result<()> {
let lua = Lua::new(); let lua = Lua::new();
@ -254,8 +301,8 @@ fn test_coverage() -> Result<()> {
let f = lua let f = lua
.load( .load(
r#"local v = vector(1, 2, 3) r#"local s = "abc"
assert(v.x == 1 and v.y == 2 and v.z == 3) assert(#s == 3)
function abc(i) function abc(i)
if i < 5 then if i < 5 then

View File

@ -145,7 +145,7 @@ fn test_serialize_failure() -> Result<(), Box<dyn StdError>> {
Ok(()) Ok(())
} }
#[cfg(feature = "luau")] #[cfg(all(feature = "luau", not(feature = "luau-vector4")))]
#[test] #[test]
fn test_serialize_vector() -> Result<(), Box<dyn StdError>> { fn test_serialize_vector() -> Result<(), Box<dyn StdError>> {
let lua = Lua::new(); let lua = Lua::new();
@ -153,7 +153,7 @@ fn test_serialize_vector() -> Result<(), Box<dyn StdError>> {
let globals = lua.globals(); let globals = lua.globals();
globals.set( globals.set(
"vector", "vector",
lua.create_function(|_, (x, y, z)| Ok(Value::Vector(x, y, z)))?, lua.create_function(|_, (x, y, z)| Ok(mlua::Vector::new(x, y, z)))?,
)?; )?;
let val = lua.load("{_vector = vector(1, 2, 3)}").eval::<Value>()?; let val = lua.load("{_vector = vector(1, 2, 3)}").eval::<Value>()?;
@ -168,6 +168,29 @@ fn test_serialize_vector() -> Result<(), Box<dyn StdError>> {
Ok(()) Ok(())
} }
#[cfg(feature = "luau-vector4")]
#[test]
fn test_serialize_vector() -> Result<(), Box<dyn StdError>> {
let lua = Lua::new();
let globals = lua.globals();
globals.set(
"vector",
lua.create_function(|_, (x, y, z, w)| Ok(mlua::Vector::new(x, y, z, w)))?,
)?;
let val = lua.load("{_vector = vector(1, 2, 3, 4)}").eval::<Value>()?;
let json = serde_json::json!({
"_vector": [1.0, 2.0, 3.0, 4.0],
});
assert_eq!(serde_json::to_value(&val)?, json);
let expected_json = lua.from_value::<serde_json::Value>(val)?;
assert_eq!(expected_json, json);
Ok(())
}
#[test] #[test]
fn test_to_value_struct() -> LuaResult<()> { fn test_to_value_struct() -> LuaResult<()> {
let lua = Lua::new(); let lua = Lua::new();

View File

@ -57,6 +57,7 @@ fn test_table() -> Result<()> {
.collect::<Result<Vec<i64>>>()?, .collect::<Result<Vec<i64>>>()?,
vec![1, 2, 3, 4, 5] vec![1, 2, 3, 4, 5]
); );
assert_eq!(table1, [1, 2, 3, 4, 5]);
assert_eq!(table2.len()?, 0); assert_eq!(table2.len()?, 0);
assert_eq!( assert_eq!(
@ -66,12 +67,10 @@ fn test_table() -> Result<()> {
.collect::<Result<Vec<(i64, i64)>>>()?, .collect::<Result<Vec<(i64, i64)>>>()?,
vec![] vec![]
); );
assert_eq!( assert_eq!(table2, [0; 0]);
table2.sequence_values().collect::<Result<Vec<i64>>>()?,
vec![]
);
// sequence_values should only iterate until the first border // sequence_values should only iterate until the first border
assert_eq!(table3, [1, 2]);
assert_eq!( assert_eq!(
table3.sequence_values().collect::<Result<Vec<i64>>>()?, table3.sequence_values().collect::<Result<Vec<i64>>>()?,
vec![1, 2] vec![1, 2]
@ -116,17 +115,12 @@ fn test_table_push_pop() -> Result<()> {
// Test raw access // Test raw access
let table1 = lua.create_sequence_from(vec![123])?; let table1 = lua.create_sequence_from(vec![123])?;
table1.raw_push(321)?; table1.raw_push(321)?;
assert_eq!( assert_eq!(table1, [123, 321]);
table1
.clone()
.raw_sequence_values::<i64>()
.collect::<Result<Vec<_>>>()?,
vec![123, 321]
);
assert_eq!(table1.raw_pop::<i64>()?, 321); assert_eq!(table1.raw_pop::<i64>()?, 321);
assert_eq!(table1.raw_pop::<i64>()?, 123); assert_eq!(table1.raw_pop::<i64>()?, 123);
assert_eq!(table1.raw_pop::<Value>()?, Value::Nil); // An extra pop should do nothing assert_eq!(table1.raw_pop::<Value>()?, Value::Nil); // An extra pop should do nothing
assert_eq!(table1.raw_len(), 0); assert_eq!(table1.raw_len(), 0);
assert_eq!(table1, [0; 0]);
// Test access through metamethods // Test access through metamethods
let table2 = lua let table2 = lua
@ -144,6 +138,13 @@ fn test_table_push_pop() -> Result<()> {
.eval::<Table>()?; .eval::<Table>()?;
table2.push(345)?; table2.push(345)?;
assert_eq!(table2.len()?, 2); assert_eq!(table2.len()?, 2);
assert_eq!(
table2
.clone()
.raw_sequence_values::<i64>()
.collect::<Result<Vec<_>>>()?,
vec![]
);
assert_eq!(table2.pop::<i64>()?, 345); assert_eq!(table2.pop::<i64>()?, 345);
assert_eq!(table2.pop::<i64>()?, 234); assert_eq!(table2.pop::<i64>()?, 234);
assert_eq!(table2.pop::<Value>()?, Value::Nil); assert_eq!(table2.pop::<Value>()?, Value::Nil);
@ -205,29 +206,9 @@ fn test_table_sequence_from() -> Result<()> {
let get_table = lua.create_function(|_, t: Table| Ok(t))?; let get_table = lua.create_function(|_, t: Table| Ok(t))?;
assert_eq!( assert_eq!(get_table.call::<_, Table>(vec![1, 2, 3])?, [1, 2, 3]);
get_table assert_eq!(get_table.call::<_, Table>([4, 5, 6])?, [4, 5, 6]);
.call::<_, Table>(vec![1, 2, 3])? assert_eq!(get_table.call::<_, Table>([7, 8, 9].as_slice())?, [7, 8, 9]);
.sequence_values()
.collect::<Result<Vec<i64>>>()?,
vec![1, 2, 3]
);
assert_eq!(
get_table
.call::<_, Table>([1, 2, 3].as_ref())?
.sequence_values()
.collect::<Result<Vec<i64>>>()?,
vec![1, 2, 3]
);
assert_eq!(
get_table
.call::<_, Table>([1, 2, 3])?
.sequence_values()
.collect::<Result<Vec<i64>>>()?,
vec![1, 2, 3]
);
Ok(()) Ok(())
} }

View File

@ -625,10 +625,33 @@ fn test_userdata_wrapped() -> Result<()> {
let lua = Lua::new(); let lua = Lua::new();
let globals = lua.globals(); let globals = lua.globals();
// Rc<T>
#[cfg(not(feature = "send"))] #[cfg(not(feature = "send"))]
{ {
let ud1 = Rc::new(RefCell::new(MyUserData(1))); let ud = Rc::new(MyUserData(1));
globals.set("rc_refcell_ud", ud1.clone())?; globals.set("rc_ud", ud.clone())?;
lua.load(
r#"
assert(rc_ud.static == "constant")
local ok, err = pcall(function() rc_ud.data = 2 end)
assert(
tostring(err):sub(1, 32) == "error mutably borrowing userdata",
"expected error mutably borrowing userdata, got " .. tostring(err)
)
assert(rc_ud.data == 1)
"#,
)
.exec()?;
globals.set("rc_ud", Nil)?;
lua.gc_collect()?;
assert_eq!(Rc::strong_count(&ud), 1);
}
// Rc<RefCell<T>>
#[cfg(not(feature = "send"))]
{
let ud = Rc::new(RefCell::new(MyUserData(1)));
globals.set("rc_refcell_ud", ud.clone())?;
lua.load( lua.load(
r#" r#"
assert(rc_refcell_ud.static == "constant") assert(rc_refcell_ud.static == "constant")
@ -637,12 +660,32 @@ fn test_userdata_wrapped() -> Result<()> {
"#, "#,
) )
.exec()?; .exec()?;
assert_eq!(ud1.borrow().0, 2); assert_eq!(ud.borrow().0, 2);
globals.set("rc_refcell_ud", Nil)?; globals.set("rc_refcell_ud", Nil)?;
lua.gc_collect()?; lua.gc_collect()?;
assert_eq!(Rc::strong_count(&ud1), 1); assert_eq!(Rc::strong_count(&ud), 1);
} }
// Arc<T>
let ud1 = Arc::new(MyUserData(2));
globals.set("arc_ud", ud1.clone())?;
lua.load(
r#"
assert(arc_ud.static == "constant")
local ok, err = pcall(function() arc_ud.data = 3 end)
assert(
tostring(err):sub(1, 32) == "error mutably borrowing userdata",
"expected error mutably borrowing userdata, got " .. tostring(err)
)
assert(arc_ud.data == 2)
"#,
)
.exec()?;
globals.set("arc_ud", Nil)?;
lua.gc_collect()?;
assert_eq!(Arc::strong_count(&ud1), 1);
// Arc<Mutex<T>>
let ud2 = Arc::new(Mutex::new(MyUserData(2))); let ud2 = Arc::new(Mutex::new(MyUserData(2)));
globals.set("arc_mutex_ud", ud2.clone())?; globals.set("arc_mutex_ud", ud2.clone())?;
lua.load( lua.load(
@ -657,7 +700,11 @@ fn test_userdata_wrapped() -> Result<()> {
assert_eq!(ud2.lock().unwrap().0, 3); assert_eq!(ud2.lock().unwrap().0, 3);
#[cfg(feature = "parking_lot")] #[cfg(feature = "parking_lot")]
assert_eq!(ud2.lock().0, 3); assert_eq!(ud2.lock().0, 3);
globals.set("arc_mutex_ud", Nil)?;
lua.gc_collect()?;
assert_eq!(Arc::strong_count(&ud2), 1);
// Arc<RwLock<T>>
let ud3 = Arc::new(RwLock::new(MyUserData(3))); let ud3 = Arc::new(RwLock::new(MyUserData(3)));
globals.set("arc_rwlock_ud", ud3.clone())?; globals.set("arc_rwlock_ud", ud3.clone())?;
lua.load( lua.load(
@ -672,12 +719,8 @@ fn test_userdata_wrapped() -> Result<()> {
assert_eq!(ud3.read().unwrap().0, 4); assert_eq!(ud3.read().unwrap().0, 4);
#[cfg(feature = "parking_lot")] #[cfg(feature = "parking_lot")]
assert_eq!(ud3.read().0, 4); assert_eq!(ud3.read().0, 4);
// Test drop
globals.set("arc_mutex_ud", Nil)?;
globals.set("arc_rwlock_ud", Nil)?; globals.set("arc_rwlock_ud", Nil)?;
lua.gc_collect()?; lua.gc_collect()?;
assert_eq!(Arc::strong_count(&ud2), 1);
assert_eq!(Arc::strong_count(&ud3), 1); assert_eq!(Arc::strong_count(&ud3), 1);
Ok(()) Ok(())

View File

@ -101,11 +101,16 @@ fn test_value_to_string() -> Result<()> {
); );
assert_eq!(Value::Integer(1).to_string()?, "1"); assert_eq!(Value::Integer(1).to_string()?, "1");
assert_eq!(Value::Number(34.59).to_string()?, "34.59"); assert_eq!(Value::Number(34.59).to_string()?, "34.59");
#[cfg(feature = "luau")] #[cfg(all(feature = "luau", not(feature = "luau-vector4")))]
assert_eq!( assert_eq!(
Value::Vector(10.0, 11.1, 12.2).to_string()?, Value::Vector(mlua::Vector::new(10.0, 11.1, 12.2)).to_string()?,
"vector(10, 11.1, 12.2)" "vector(10, 11.1, 12.2)"
); );
#[cfg(feature = "luau-vector4")]
assert_eq!(
Value::Vector(mlua::Vector::new(10.0, 11.1, 12.2, 13.3)).to_string()?,
"vector(10, 11.1, 12.2, 13.3)"
);
assert_eq!( assert_eq!(
Value::String(lua.create_string("hello")?).to_string()?, Value::String(lua.create_string("hello")?).to_string()?,
"hello" "hello"