diff --git a/src/function.rs b/src/function.rs index b4c7965..ce6970b 100644 --- a/src/function.rs +++ b/src/function.rs @@ -598,13 +598,13 @@ impl<'lua> Function<'lua> { F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static, FR: Future> + 'lua, { - WrappedAsyncFunction(Box::new(move |lua, args| { - let args = match A::from_lua_multi(args, lua) { + WrappedAsyncFunction(Box::new(move |lua, args| unsafe { + let args = match A::from_lua_args(args, 1, None, lua) { Ok(args) => args, Err(e) => return Box::pin(future::err(e)), }; let fut = func(lua, args); - Box::pin(async move { fut.await?.into_lua_multi(lua) }) + Box::pin(async move { fut.await?.push_into_stack_multi(lua) }) })) } } diff --git a/src/lua.rs b/src/lua.rs index d3a96bc..f0b1446 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -1600,13 +1600,13 @@ impl Lua { F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static, FR: Future> + 'lua, { - self.create_async_callback(Box::new(move |lua, args| { + self.create_async_callback(Box::new(move |lua, args| unsafe { let args = match A::from_lua_args(args, 1, None, lua) { Ok(args) => args, Err(e) => return Box::pin(future::err(e)), }; let fut = func(lua, args); - Box::pin(async move { fut.await?.into_lua_multi(lua) }) + Box::pin(async move { fut.await?.push_into_stack_multi(lua) }) })) } @@ -1634,7 +1634,7 @@ impl Lua { self.push_ref(&func.0); ffi::lua_xmove(state, thread_state, 1); - Ok(Thread(self.pop_ref())) + Ok(Thread::new(self.pop_ref())) } } @@ -1662,7 +1662,7 @@ impl Lua { ffi::lua_replace(thread_state, ffi::LUA_GLOBALSINDEX); } - return Ok(Thread(LuaRef::new(self, index))); + return Ok(Thread::new(LuaRef::new(self, index))); } }; self.create_thread_inner(func) @@ -1817,7 +1817,7 @@ impl Lua { let _sg = StackGuard::new(state); assert_stack(state, 1); ffi::lua_pushthread(state); - Thread(self.pop_ref()) + Thread::new(self.pop_ref()) } } @@ -2412,7 +2412,7 @@ impl Lua { } } - ffi::LUA_TTHREAD => Value::Thread(Thread(self.pop_ref())), + ffi::LUA_TTHREAD => Value::Thread(Thread::new(self.pop_ref())), #[cfg(feature = "luajit")] ffi::LUA_TCDATA => { @@ -2510,7 +2510,7 @@ impl Lua { ffi::LUA_TTHREAD => { ffi::lua_xpush(state, self.ref_thread(), idx); - Value::Thread(Thread(self.pop_ref_thread())) + Value::Thread(Thread::new(self.pop_ref_thread())) } #[cfg(feature = "luajit")] @@ -2763,16 +2763,15 @@ impl Lua { } } - // Returns `TypeId` for the LuaRef, checking that it's a registered - // and not destructed UserData. + // Returns `TypeId` for the `lref` userdata, checking that it's registered and not destructed. // // Returns `None` if the userdata is registered but non-static. - pub(crate) unsafe fn get_userdata_type_id(&self, lref: &LuaRef) -> Result> { + pub(crate) unsafe fn get_userdata_ref_type_id(&self, lref: &LuaRef) -> Result> { self.get_userdata_type_id_inner(self.ref_thread(), lref.index) } - // Same as `get_userdata_type_id` but assumes the value is already on the current stack - pub(crate) unsafe fn get_userdata_type_id_stack(&self, idx: c_int) -> Result> { + // Same as `get_userdata_ref_type_id` but assumes the userdata is already on the stack. + pub(crate) unsafe fn get_userdata_type_id(&self, idx: c_int) -> Result> { self.get_userdata_type_id_inner(self.state(), idx) } @@ -2808,7 +2807,7 @@ impl Lua { // Pushes a LuaRef (userdata) value onto the stack, returning their `TypeId`. // Uses 1 stack space, does not call checkstack. pub(crate) unsafe fn push_userdata_ref(&self, lref: &LuaRef) -> Result> { - let type_id = self.get_userdata_type_id(lref)?; + let type_id = self.get_userdata_type_id_inner(self.ref_thread(), lref.index)?; self.push_ref(lref); Ok(type_id) } @@ -2898,11 +2897,7 @@ impl Lua { let lua: &Lua = mem::transmute((*extra).inner.assume_init_ref()); let _guard = StateGuard::new(&lua.0, state); - let mut args = MultiValue::with_lua_and_capacity(lua, nargs as usize); - for _ in 0..nargs { - args.push_front(lua.pop_value()); - } - + let args = MultiValue::from_stack_multi(nargs, lua)?; let func = &*(*upvalue).data; let fut = func(lua, args); let extra = Arc::clone(&(*upvalue).extra); @@ -2924,6 +2919,7 @@ impl Lua { let upvalue = get_userdata::(state, ffi::lua_upvalueindex(1)); let extra = (*upvalue).extra.get(); callback_error_ext(state, extra, |_| { + // Lua ensures that `LUA_MINSTACK` stack spaces are available (after pushing arguments) let lua: &Lua = mem::transmute((*extra).inner.assume_init_ref()); let _guard = StateGuard::new(&lua.0, state); @@ -2931,20 +2927,20 @@ impl Lua { let mut ctx = Context::from_waker(lua.waker()); match fut.as_mut().poll(&mut ctx) { Poll::Pending => Ok(0), - Poll::Ready(results) => { - let mut results = results?; - let nresults = results.len(); - lua.push_value(Value::Integer(nresults as _))?; + Poll::Ready(nresults) => { + let nresults = nresults?; match nresults { - 0 => Ok(1), - 1 | 2 => { - // Fast path for 1 or 2 results without creating a table - for r in results.drain_all() { - lua.push_value(r)?; + 0..=2 => { + // Fast path for up to 2 results without creating a table + ffi::lua_pushinteger(state, nresults as _); + if nresults > 0 { + ffi::lua_insert(state, -nresults - 1); } - Ok(nresults as c_int + 1) + Ok(nresults + 1) } _ => { + let results = MultiValue::from_stack_multi(nresults, lua)?; + ffi::lua_pushinteger(state, nresults as _); lua.push_value(Value::Table(lua.create_sequence_from(results)?))?; Ok(2) } diff --git a/src/scope.rs b/src/scope.rs index 2126f43..fa37877 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -338,13 +338,13 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { }); scope.create_callback(f) } - NonStaticMethod::Function(function) => unsafe { + NonStaticMethod::Function(function) => { scope.create_callback(Box::new(move |lua, nargs| { let args = MultiValue::from_stack_args(nargs, 1, None, lua)?; function(lua, args)?.push_into_stack_multi(lua) })) - }, - NonStaticMethod::FunctionMut(function) => unsafe { + } + NonStaticMethod::FunctionMut(function) => { let function = RefCell::new(function); let f = Box::new(move |lua, nargs| { let mut func = function @@ -354,7 +354,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { func(lua, args)?.push_into_stack_multi(lua) }); scope.create_callback(f) - }, + } } } diff --git a/src/thread.rs b/src/thread.rs index f062957..cdce77c 100644 --- a/src/thread.rs +++ b/src/thread.rs @@ -1,4 +1,3 @@ -use std::cmp; use std::os::raw::c_int; use crate::error::{Error, Result}; @@ -16,10 +15,7 @@ use crate::{ #[cfg(feature = "async")] use { - crate::{ - lua::ASYNC_POLL_PENDING, - value::{MultiValue, Value}, - }, + crate::{lua::ASYNC_POLL_PENDING, value::MultiValue}, futures_util::stream::Stream, std::{ future::Future, @@ -47,7 +43,7 @@ pub enum ThreadStatus { /// Handle to an internal Lua thread (or coroutine). #[derive(Clone, Debug)] -pub struct Thread<'lua>(pub(crate) LuaRef<'lua>); +pub struct Thread<'lua>(pub(crate) LuaRef<'lua>, pub(crate) *mut ffi::lua_State); /// Thread (coroutine) representation as an async [`Future`] or [`Stream`]. /// @@ -66,6 +62,16 @@ pub struct AsyncThread<'lua, R> { } impl<'lua> Thread<'lua> { + #[inline(always)] + pub(crate) fn new(r#ref: LuaRef<'lua>) -> Self { + let state = unsafe { ffi::lua_tothread(r#ref.lua.ref_thread(), r#ref.index) }; + Thread(r#ref, state) + } + + const fn state(&self) -> *mut ffi::lua_State { + self.1 + } + /// Resumes execution of this thread. /// /// Equivalent to `coroutine.resume`. @@ -114,60 +120,59 @@ impl<'lua> Thread<'lua> { { let lua = self.0.lua; let state = lua.state(); - - let mut args = args.into_lua_multi(lua)?; - let nargs = args.len() as c_int; - let results = unsafe { + let thread_state = self.state(); + unsafe { let _sg = StackGuard::new(state); - check_stack(state, cmp::max(nargs + 1, 3))?; + let _thread_sg = StackGuard::with_top(thread_state, 0); - let thread_state = ffi::lua_tothread(lua.ref_thread(), self.0.index); - - let status = ffi::lua_status(thread_state); - if status != ffi::LUA_YIELD && ffi::lua_gettop(thread_state) == 0 { - return Err(Error::CoroutineInactive); - } - - check_stack(thread_state, nargs)?; - for arg in args.drain_all() { - lua.push_value(arg)?; - } - ffi::lua_xmove(state, thread_state, nargs); - - let mut nresults = 0; - - let ret = ffi::lua_resume(thread_state, state, nargs, &mut nresults as *mut c_int); - if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD { - if ret == ffi::LUA_ERRMEM { - // Don't call error handler for memory errors - return Err(pop_error(thread_state, ret)); - } - check_stack(state, 3)?; - protect_lua!(state, 0, 1, |state| error_traceback_thread( - state, - thread_state - ))?; - return Err(pop_error(state, ret)); - } - - let mut results = args; // Reuse MultiValue container - check_stack(state, nresults + 2)?; // 2 is extra for `lua.pop_value()` below + let nresults = self.resume_inner(args)?; + check_stack(state, nresults + 1)?; ffi::lua_xmove(thread_state, state, nresults); - for _ in 0..nresults { - results.push_front(lua.pop_value()); + R::from_stack_multi(nresults, lua) + } + } + + /// Resumes execution of this thread. + /// + /// It's similar to `resume()` but leaves `nresults` values on the thread stack. + unsafe fn resume_inner>(&self, args: A) -> Result { + let lua = self.0.lua; + let state = lua.state(); + let thread_state = self.state(); + + if self.status() != ThreadStatus::Resumable { + return Err(Error::CoroutineInactive); + } + + let nargs = args.push_into_stack_multi(lua)?; + if nargs > 0 { + check_stack(thread_state, nargs)?; + ffi::lua_xmove(state, thread_state, nargs); + } + + let mut nresults = 0; + let ret = ffi::lua_resume(thread_state, state, nargs, &mut nresults as *mut c_int); + if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD { + if ret == ffi::LUA_ERRMEM { + // Don't call error handler for memory errors + return Err(pop_error(thread_state, ret)); } - results - }; - R::from_lua_multi(results, lua) + check_stack(state, 3)?; + protect_lua!(state, 0, 1, |state| error_traceback_thread( + state, + thread_state + ))?; + return Err(pop_error(state, ret)); + } + + Ok(nresults) } /// Gets the status of the thread. pub fn status(&self) -> ThreadStatus { - let lua = self.0.lua; + let thread_state = self.state(); unsafe { - let thread_state = ffi::lua_tothread(lua.ref_thread(), self.0.index); - let status = ffi::lua_status(thread_state); if status != ffi::LUA_OK && status != ffi::LUA_YIELD { ThreadStatus::Error @@ -191,8 +196,7 @@ impl<'lua> Thread<'lua> { { let lua = self.0.lua; unsafe { - let thread_state = ffi::lua_tothread(lua.ref_thread(), self.0.index); - lua.set_thread_hook(thread_state, triggers, callback); + lua.set_thread_hook(self.state(), triggers, callback); } } @@ -214,18 +218,12 @@ impl<'lua> Thread<'lua> { #[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))] pub fn reset(&self, func: crate::function::Function<'lua>) -> Result<()> { let lua = self.0.lua; - let state = lua.state(); + let thread_state = self.state(); unsafe { - let _sg = StackGuard::new(state); - check_stack(state, 2)?; - - lua.push_ref(&self.0); - let thread_state = ffi::lua_tothread(state, -1); - #[cfg(all(feature = "lua54", not(feature = "vendored")))] let status = ffi::lua_resetthread(thread_state); #[cfg(all(feature = "lua54", feature = "vendored"))] - let status = ffi::lua_closethread(thread_state, state); + let status = ffi::lua_closethread(thread_state, lua.state()); #[cfg(feature = "lua54")] if status != ffi::LUA_OK { return Err(pop_error(thread_state, status)); @@ -233,8 +231,8 @@ impl<'lua> Thread<'lua> { #[cfg(feature = "luau")] ffi::lua_resetthread(thread_state); - lua.push_ref(&func.0); - ffi::lua_xmove(state, thread_state, 1); + // Push function to the top of the thread stack + ffi::lua_xpush(lua.ref_thread(), thread_state, func.0.index); #[cfg(feature = "luau")] { @@ -345,11 +343,11 @@ impl<'lua> Thread<'lua> { pub fn sandbox(&self) -> Result<()> { let lua = self.0.lua; let state = lua.state(); + let thread_state = self.state(); unsafe { - let thread = ffi::lua_tothread(lua.ref_thread(), self.0.index); - check_stack(thread, 3)?; + check_stack(thread_state, 3)?; check_stack(state, 3)?; - protect_lua!(state, 0, 0, |_| ffi::luaL_sandboxthread(thread)) + protect_lua!(state, 0, 0, |_| ffi::luaL_sandboxthread(thread_state)) } } } @@ -379,11 +377,10 @@ impl<'lua, R> Drop for AsyncThread<'lua, R> { if !lua.recycle_thread(&mut self.thread) { #[cfg(feature = "lua54")] if self.thread.status() == ThreadStatus::Error { - let thread_state = ffi::lua_tothread(lua.ref_thread(), self.thread.0.index); #[cfg(not(feature = "vendored"))] - ffi::lua_resetthread(thread_state); + ffi::lua_resetthread(self.thread.state()); #[cfg(feature = "vendored")] - ffi::lua_closethread(thread_state, lua.state()); + ffi::lua_closethread(self.thread.state(), lua.state()); } } } @@ -399,29 +396,36 @@ where type Item = Result; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let lua = self.thread.0.lua; - - match self.thread.status() { - ThreadStatus::Resumable => {} - _ => return Poll::Ready(None), - }; - - let _wg = WakerGuard::new(lua, cx.waker()); - - // This is safe as we are not moving the whole struct - let this = unsafe { self.get_unchecked_mut() }; - let ret: MultiValue = if let Some(args) = this.init_args.take() { - this.thread.resume(args?)? - } else { - this.thread.resume(())? - }; - - if is_poll_pending(&ret) { - return Poll::Pending; + if self.thread.status() != ThreadStatus::Resumable { + return Poll::Ready(None); } - cx.waker().wake_by_ref(); - Poll::Ready(Some(R::from_lua_multi(ret, lua))) + let lua = self.thread.0.lua; + let state = lua.state(); + let thread_state = self.thread.state(); + unsafe { + let _sg = StackGuard::new(state); + let _thread_sg = StackGuard::with_top(thread_state, 0); + let _wg = WakerGuard::new(lua, cx.waker()); + + // This is safe as we are not moving the whole struct + let this = self.get_unchecked_mut(); + let nresults = if let Some(args) = this.init_args.take() { + this.thread.resume_inner(args?)? + } else { + this.thread.resume_inner(())? + }; + + if nresults == 1 && is_poll_pending(thread_state) { + return Poll::Pending; + } + + check_stack(state, nresults + 1)?; + ffi::lua_xmove(thread_state, state, nresults); + + cx.waker().wake_by_ref(); + Poll::Ready(Some(R::from_stack_multi(nresults, lua))) + } } } @@ -433,46 +437,53 @@ where type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if self.thread.status() != ThreadStatus::Resumable { + return Poll::Ready(Err(Error::CoroutineInactive)); + } + let lua = self.thread.0.lua; + let state = lua.state(); + let thread_state = self.thread.state(); + unsafe { + let _sg = StackGuard::new(state); + let _thread_sg = StackGuard::with_top(thread_state, 0); + let _wg = WakerGuard::new(lua, cx.waker()); - match self.thread.status() { - ThreadStatus::Resumable => {} - _ => return Poll::Ready(Err(Error::CoroutineInactive)), - }; + // This is safe as we are not moving the whole struct + let this = self.get_unchecked_mut(); + let nresults = if let Some(args) = this.init_args.take() { + this.thread.resume_inner(args?)? + } else { + this.thread.resume_inner(())? + }; - let _wg = WakerGuard::new(lua, cx.waker()); + if nresults == 1 && is_poll_pending(thread_state) { + return Poll::Pending; + } - // This is safe as we are not moving the whole struct - let this = unsafe { self.get_unchecked_mut() }; - let ret: MultiValue = if let Some(args) = this.init_args.take() { - this.thread.resume(args?)? - } else { - this.thread.resume(())? - }; + if ffi::lua_status(thread_state) == ffi::LUA_YIELD { + // Ignore value returned via yield() + cx.waker().wake_by_ref(); + return Poll::Pending; + } - if is_poll_pending(&ret) { - return Poll::Pending; + check_stack(state, nresults + 1)?; + ffi::lua_xmove(thread_state, state, nresults); + + Poll::Ready(R::from_stack_multi(nresults, lua)) } - - if let ThreadStatus::Resumable = this.thread.status() { - // Ignore value returned via yield() - cx.waker().wake_by_ref(); - return Poll::Pending; - } - - Poll::Ready(R::from_lua_multi(ret, lua)) } } #[cfg(feature = "async")] #[inline(always)] -fn is_poll_pending(val: &MultiValue) -> bool { - match val.iter().enumerate().last() { - Some((0, Value::LightUserData(ud))) => { - std::ptr::eq(ud.0 as *const u8, &ASYNC_POLL_PENDING as *const u8) - } - _ => false, +unsafe fn is_poll_pending(state: *mut ffi::lua_State) -> bool { + if ffi::lua_islightuserdata(state, -1) != 0 { + let stack_ptr = ffi::lua_touserdata(state, -1) as *const u8; + let pending_ptr = &ASYNC_POLL_PENDING as *const u8; + return std::ptr::eq(stack_ptr, pending_ptr); } + false } #[cfg(feature = "async")] @@ -486,23 +497,19 @@ struct WakerGuard<'lua, 'a> { impl<'lua, 'a> WakerGuard<'lua, 'a> { #[inline] pub fn new(lua: &'lua Lua, waker: &'a Waker) -> Result> { - unsafe { - let prev = lua.set_waker(NonNull::from(waker)); - Ok(WakerGuard { - lua, - prev, - _phantom: PhantomData, - }) - } + let prev = unsafe { lua.set_waker(NonNull::from(waker)) }; + Ok(WakerGuard { + lua, + prev, + _phantom: PhantomData, + }) } } #[cfg(feature = "async")] impl<'lua, 'a> Drop for WakerGuard<'lua, 'a> { fn drop(&mut self) { - unsafe { - self.lua.set_waker(self.prev); - } + unsafe { self.lua.set_waker(self.prev) }; } } diff --git a/src/types.rs b/src/types.rs index baf31dc..a2b38e9 100644 --- a/src/types.rs +++ b/src/types.rs @@ -10,16 +10,13 @@ use std::{fmt, mem, ptr}; use rustc_hash::FxHashMap; -#[cfg(feature = "async")] -use futures_util::future::LocalBoxFuture; - use crate::error::Result; #[cfg(not(feature = "luau"))] use crate::hook::Debug; use crate::lua::{ExtraData, Lua}; #[cfg(feature = "async")] -use crate::value::MultiValue; +use {crate::value::MultiValue, futures_util::future::LocalBoxFuture}; #[cfg(feature = "unstable")] use {crate::lua::LuaInner, std::marker::PhantomData}; @@ -47,13 +44,13 @@ pub(crate) type CallbackUpvalue = Upvalue>; #[cfg(feature = "async")] pub(crate) type AsyncCallback<'lua, 'a> = - Box) -> LocalBoxFuture<'lua, Result>> + 'a>; + Box) -> LocalBoxFuture<'lua, Result> + 'a>; #[cfg(feature = "async")] pub(crate) type AsyncCallbackUpvalue = Upvalue>; #[cfg(feature = "async")] -pub(crate) type AsyncPollUpvalue = Upvalue>>>; +pub(crate) type AsyncPollUpvalue = Upvalue>>; /// Type to set next Luau VM action after executing interrupt function. #[cfg(any(feature = "luau", doc))] diff --git a/src/userdata.rs b/src/userdata.rs index 9d9c318..2a29d7a 100644 --- a/src/userdata.rs +++ b/src/userdata.rs @@ -1105,9 +1105,9 @@ impl<'lua> AnyUserData<'lua> { } #[cfg(feature = "async")] - #[inline(always)] + #[inline] pub(crate) fn type_id(&self) -> Result> { - unsafe { self.0.lua.get_userdata_type_id(&self.0) } + unsafe { self.0.lua.get_userdata_ref_type_id(&self.0) } } /// Returns a type name of this `UserData` (from a metatable field). @@ -1161,7 +1161,7 @@ impl<'lua> AnyUserData<'lua> { let lua = self.0.lua; let is_serializable = || unsafe { // Userdata can be unregistered or destructed - let _ = lua.get_userdata_type_id(&self.0)?; + let _ = lua.get_userdata_ref_type_id(&self.0)?; let ud = &*get_userdata::>(lua.ref_thread(), self.0.index); match &*ud.0.try_borrow().map_err(|_| Error::UserDataBorrowError)? { @@ -1179,7 +1179,7 @@ impl<'lua> AnyUserData<'lua> { { let lua = self.0.lua; unsafe { - let type_id = lua.get_userdata_type_id(&self.0)?; + let type_id = lua.get_userdata_ref_type_id(&self.0)?; match type_id { Some(type_id) if type_id == TypeId::of::() => { let ref_thread = lua.ref_thread(); @@ -1328,7 +1328,7 @@ impl<'lua> Serialize for AnyUserData<'lua> { let lua = self.0.lua; let data = unsafe { let _ = lua - .get_userdata_type_id(&self.0) + .get_userdata_ref_type_id(&self.0) .map_err(ser::Error::custom)?; let ud = &*get_userdata::>(lua.ref_thread(), self.0.index); ud.0.try_borrow() diff --git a/src/userdata_impl.rs b/src/userdata_impl.rs index 565dafe..af1dbaf 100644 --- a/src/userdata_impl.rs +++ b/src/userdata_impl.rs @@ -20,11 +20,7 @@ use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value}; use std::rc::Rc; #[cfg(feature = "async")] -use { - crate::types::AsyncCallback, - futures_util::future::{self, TryFutureExt}, - std::future::Future, -}; +use {crate::types::AsyncCallback, futures_util::future, std::future::Future}; /// Handle to registry for userdata methods and metamethods. pub struct UserDataRegistry<'lua, T: 'static> { @@ -80,62 +76,56 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> { Box::new(move |lua, nargs| unsafe { if nargs == 0 { - try_self_arg!(Err(Error::from_lua_conversion( - "missing argument", - "userdata", - None, - ))); + let err = Error::from_lua_conversion("missing argument", "userdata", None); + try_self_arg!(Err(err)); } - let call = |ud| { - // Self was at index 1, so we pass 2 here - let args = A::from_stack_args(nargs - 1, 2, Some(&name), lua)?; - method(lua, ud, args)?.push_into_stack_multi(lua) - }; + // Self was at index 1, so we pass 2 here + let args = A::from_stack_args(nargs - 1, 2, Some(&name), lua); let (state, index) = (lua.state(), -nargs); - match try_self_arg!(lua.get_userdata_type_id_stack(index)) { + match try_self_arg!(lua.get_userdata_type_id(index)) { Some(id) if id == TypeId::of::() => { let ud = try_self_arg!(get_userdata_ref::(state, index)); - call(&ud) + method(lua, &ud, args?)?.push_into_stack_multi(lua) } #[cfg(not(feature = "send"))] Some(id) if id == TypeId::of::>() => { let ud = try_self_arg!(get_userdata_ref::>(state, index)); - call(&ud) + method(lua, &ud, args?)?.push_into_stack_multi(lua) } #[cfg(not(feature = "send"))] Some(id) if id == TypeId::of::>>() => { let ud = try_self_arg!(get_userdata_ref::>>(state, index)); let ud = try_self_arg!(ud.try_borrow(), Error::UserDataBorrowError); - call(&ud) + method(lua, &ud, args?)?.push_into_stack_multi(lua) } Some(id) if id == TypeId::of::>() => { let ud = try_self_arg!(get_userdata_ref::>(state, index)); - call(&ud) + method(lua, &ud, args?)?.push_into_stack_multi(lua) } Some(id) if id == TypeId::of::>>() => { let ud = try_self_arg!(get_userdata_ref::>>(state, index)); let ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowError); - call(&ud) + method(lua, &ud, args?)?.push_into_stack_multi(lua) } #[cfg(feature = "parking_lot")] Some(id) if id == TypeId::of::>>() => { let ud = get_userdata_ref::>>(state, index); let ud = try_self_arg!(ud); let ud = try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowError)); - call(&ud) + method(lua, &ud, args?)?.push_into_stack_multi(lua) } Some(id) if id == TypeId::of::>>() => { let ud = try_self_arg!(get_userdata_ref::>>(state, index)); let ud = try_self_arg!(ud.try_read(), Error::UserDataBorrowError); - call(&ud) + method(lua, &ud, args?)?.push_into_stack_multi(lua) } #[cfg(feature = "parking_lot")] Some(id) if id == TypeId::of::>>() => { let ud = get_userdata_ref::>>(state, index); let ud = try_self_arg!(ud); let ud = try_self_arg!(ud.try_read().ok_or(Error::UserDataBorrowError)); - call(&ud) + method(lua, &ud, args?)?.push_into_stack_multi(lua) } _ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)), } @@ -164,23 +154,17 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> { .try_borrow_mut() .map_err(|_| Error::RecursiveMutCallback)?; if nargs == 0 { - try_self_arg!(Err(Error::from_lua_conversion( - "missing argument", - "userdata", - None, - ))); + let err = Error::from_lua_conversion("missing argument", "userdata", None); + try_self_arg!(Err(err)); } - let mut call = |ud| { - // Self was at index 1, so we pass 2 here - let args = A::from_stack_args(nargs - 1, 2, Some(&name), lua)?; - method(lua, ud, args)?.push_into_stack_multi(lua) - }; + // Self was at index 1, so we pass 2 here + let args = A::from_stack_args(nargs - 1, 2, Some(&name), lua); let (state, index) = (lua.state(), -nargs); - match try_self_arg!(lua.get_userdata_type_id_stack(index)) { + match try_self_arg!(lua.get_userdata_type_id(index)) { Some(id) if id == TypeId::of::() => { let mut ud = try_self_arg!(get_userdata_mut::(state, index)); - call(&mut ud) + method(lua, &mut ud, args?)?.push_into_stack_multi(lua) } #[cfg(not(feature = "send"))] Some(id) if id == TypeId::of::>() => Err(Error::UserDataBorrowMutError), @@ -188,32 +172,32 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> { Some(id) if id == TypeId::of::>>() => { let ud = try_self_arg!(get_userdata_mut::>>(state, index)); let mut ud = try_self_arg!(ud.try_borrow_mut(), Error::UserDataBorrowMutError); - call(&mut ud) + method(lua, &mut ud, args?)?.push_into_stack_multi(lua) } Some(id) if id == TypeId::of::>() => Err(Error::UserDataBorrowMutError), Some(id) if id == TypeId::of::>>() => { let ud = try_self_arg!(get_userdata_mut::>>(state, index)); let mut ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowMutError); - call(&mut ud) + method(lua, &mut ud, args?)?.push_into_stack_multi(lua) } #[cfg(feature = "parking_lot")] Some(id) if id == TypeId::of::>>() => { let ud = get_userdata_mut::>>(state, index); let ud = try_self_arg!(ud); let mut ud = try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowMutError)); - call(&mut ud) + method(lua, &mut ud, args?)?.push_into_stack_multi(lua) } Some(id) if id == TypeId::of::>>() => { let ud = try_self_arg!(get_userdata_mut::>>(state, index)); let mut ud = try_self_arg!(ud.try_write(), Error::UserDataBorrowMutError); - call(&mut ud) + method(lua, &mut ud, args?)?.push_into_stack_multi(lua) } #[cfg(feature = "parking_lot")] Some(id) if id == TypeId::of::>>() => { let ud = get_userdata_mut::>>(state, index); let ud = try_self_arg!(ud); let mut ud = try_self_arg!(ud.try_write().ok_or(Error::UserDataBorrowMutError)); - call(&mut ud) + method(lua, &mut ud, args?)?.push_into_stack_multi(lua) } _ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)), } @@ -233,7 +217,7 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> { let name = get_function_name::(name); let method = Arc::new(method); - Box::new(move |lua, mut args| { + Box::new(move |lua, mut args| unsafe { let name = name.clone(); let method = method.clone(); macro_rules! try_self_arg { @@ -246,76 +230,68 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> { } Box::pin(async move { - let front = args.pop_front().ok_or_else(|| { + let this = args.pop_front().ok_or_else(|| { Error::from_lua_conversion("missing argument", "userdata", None) }); - let front = try_self_arg!(front); - let userdata: AnyUserData = try_self_arg!(AnyUserData::from_lua(front, lua)); - let (ref_thread, index) = (lua.ref_thread(), userdata.0.index); - match try_self_arg!(userdata.type_id()) { - Some(id) if id == TypeId::of::() => unsafe { + let this = try_self_arg!(AnyUserData::from_lua(try_self_arg!(this), lua)); + let args = A::from_lua_args(args, 2, Some(&name), lua); + + let (ref_thread, index) = (lua.ref_thread(), this.0.index); + match try_self_arg!(this.type_id()) { + Some(id) if id == TypeId::of::() => { let ud = try_self_arg!(get_userdata_ref::(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_args(args, 2, Some(&name), lua)?; - method(lua, ud, args).await?.into_lua_multi(lua) - }, + method(lua, ud, args?).await?.push_into_stack_multi(lua) + } #[cfg(not(feature = "send"))] - Some(id) if id == TypeId::of::>() => unsafe { + Some(id) if id == TypeId::of::>() => { let ud = try_self_arg!(get_userdata_ref::>(ref_thread, index)); let ud = std::mem::transmute::<&T, &T>(&ud); - let args = A::from_lua_args(args, 2, Some(&name), lua)?; - method(lua, ud, args).await?.into_lua_multi(lua) - }, + method(lua, ud, args?).await?.push_into_stack_multi(lua) + } #[cfg(not(feature = "send"))] - Some(id) if id == TypeId::of::>>() => unsafe { + Some(id) if id == TypeId::of::>>() => { let ud = try_self_arg!(get_userdata_ref::>>(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_args(args, 2, Some(&name), lua)?; - method(lua, ud, args).await?.into_lua_multi(lua) - }, - Some(id) if id == TypeId::of::>() => unsafe { + method(lua, ud, args?).await?.push_into_stack_multi(lua) + } + Some(id) if id == TypeId::of::>() => { let ud = try_self_arg!(get_userdata_ref::>(ref_thread, index)); let ud = std::mem::transmute::<&T, &T>(&ud); - let args = A::from_lua_args(args, 2, Some(&name), lua)?; - method(lua, ud, args).await?.into_lua_multi(lua) - }, - Some(id) if id == TypeId::of::>>() => unsafe { + method(lua, ud, args?).await?.push_into_stack_multi(lua) + } + Some(id) if id == TypeId::of::>>() => { let ud = try_self_arg!(get_userdata_ref::>>(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_args(args, 2, Some(&name), lua)?; - method(lua, ud, args).await?.into_lua_multi(lua) - }, + method(lua, ud, args?).await?.push_into_stack_multi(lua) + } #[cfg(feature = "parking_lot")] - Some(id) if id == TypeId::of::>>() => unsafe { + Some(id) if id == TypeId::of::>>() => { let ud = get_userdata_ref::>>(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_args(args, 2, Some(&name), lua)?; - method(lua, ud, args).await?.into_lua_multi(lua) - }, - Some(id) if id == TypeId::of::>>() => unsafe { + method(lua, ud, args?).await?.push_into_stack_multi(lua) + } + Some(id) if id == TypeId::of::>>() => { let ud = try_self_arg!(get_userdata_ref::>>(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_args(args, 2, Some(&name), lua)?; - method(lua, ud, args).await?.into_lua_multi(lua) - }, + method(lua, ud, args?).await?.push_into_stack_multi(lua) + } #[cfg(feature = "parking_lot")] - Some(id) if id == TypeId::of::>>() => unsafe { + Some(id) if id == TypeId::of::>>() => { let ud = get_userdata_ref::>>(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_args(args, 2, Some(&name), lua)?; - method(lua, ud, args).await?.into_lua_multi(lua) - }, + method(lua, ud, args?).await?.push_into_stack_multi(lua) + } _ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)), } }) @@ -335,7 +311,7 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> { let name = get_function_name::(name); let method = Arc::new(method); - Box::new(move |lua, mut args| { + Box::new(move |lua, mut args| unsafe { let name = name.clone(); let method = method.clone(); macro_rules! try_self_arg { @@ -348,72 +324,66 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> { } Box::pin(async move { - let front = args.pop_front().ok_or_else(|| { + let this = args.pop_front().ok_or_else(|| { Error::from_lua_conversion("missing argument", "userdata", None) }); - let front = try_self_arg!(front); - let userdata: AnyUserData = try_self_arg!(AnyUserData::from_lua(front, lua)); - let (ref_thread, index) = (lua.ref_thread(), userdata.0.index); - match try_self_arg!(userdata.type_id()) { - Some(id) if id == TypeId::of::() => unsafe { + let this = try_self_arg!(AnyUserData::from_lua(try_self_arg!(this), lua)); + let args = A::from_lua_args(args, 2, Some(&name), lua); + + let (ref_thread, index) = (lua.ref_thread(), this.0.index); + match try_self_arg!(this.type_id()) { + Some(id) if id == TypeId::of::() => { let mut ud = try_self_arg!(get_userdata_mut::(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_args(args, 2, Some(&name), lua)?; - method(lua, ud, args).await?.into_lua_multi(lua) - }, + method(lua, ud, args?).await?.push_into_stack_multi(lua) + } #[cfg(not(feature = "send"))] Some(id) if id == TypeId::of::>>() => { Err(Error::UserDataBorrowMutError) } #[cfg(not(feature = "send"))] - Some(id) if id == TypeId::of::>>() => unsafe { + Some(id) if id == TypeId::of::>>() => { let ud = try_self_arg!(get_userdata_mut::>>(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_args(args, 2, Some(&name), lua)?; - method(lua, ud, args).await?.into_lua_multi(lua) - }, + method(lua, ud, args?).await?.push_into_stack_multi(lua) + } #[cfg(not(feature = "send"))] Some(id) if id == TypeId::of::>() => Err(Error::UserDataBorrowMutError), - Some(id) if id == TypeId::of::>>() => unsafe { + Some(id) if id == TypeId::of::>>() => { let ud = try_self_arg!(get_userdata_mut::>>(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_args(args, 2, Some(&name), lua)?; - method(lua, ud, args).await?.into_lua_multi(lua) - }, + method(lua, ud, args?).await?.push_into_stack_multi(lua) + } #[cfg(feature = "parking_lot")] - Some(id) if id == TypeId::of::>>() => unsafe { + Some(id) if id == TypeId::of::>>() => { let ud = get_userdata_mut::>>(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_args(args, 2, Some(&name), lua)?; - method(lua, ud, args).await?.into_lua_multi(lua) - }, - Some(id) if id == TypeId::of::>>() => unsafe { + method(lua, ud, args?).await?.push_into_stack_multi(lua) + } + Some(id) if id == TypeId::of::>>() => { let ud = try_self_arg!(get_userdata_mut::>>(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_args(args, 2, Some(&name), lua)?; - method(lua, ud, args).await?.into_lua_multi(lua) - }, + method(lua, ud, args?).await?.push_into_stack_multi(lua) + } #[cfg(feature = "parking_lot")] - Some(id) if id == TypeId::of::>>() => unsafe { + Some(id) if id == TypeId::of::>>() => { let ud = get_userdata_mut::>>(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_args(args, 2, Some(&name), lua)?; - method(lua, ud, args).await?.into_lua_multi(lua) - }, + method(lua, ud, args?).await?.push_into_stack_multi(lua) + } _ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)), } }) @@ -459,14 +429,13 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> { R: IntoLuaMulti<'lua>, { let name = get_function_name::(name); - Box::new(move |lua, args| { + Box::new(move |lua, args| unsafe { let args = match A::from_lua_args(args, 1, Some(&name), lua) { Ok(args) => args, Err(e) => return Box::pin(future::err(e)), }; - Box::pin( - function(lua, args).and_then(move |ret| future::ready(ret.into_lua_multi(lua))), - ) + let fut = function(lua, args); + Box::pin(async move { fut.await?.push_into_stack_multi(lua) }) }) } diff --git a/src/util/mod.rs b/src/util/mod.rs index d52119f..1351fbf 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -63,6 +63,11 @@ impl StackGuard { top: ffi::lua_gettop(state), } } + + // Same as `new()`, but allows specifying the expected stack size at the end of the scope. + pub const fn with_top(state: *mut ffi::lua_State, top: c_int) -> StackGuard { + StackGuard { state, top } + } } impl Drop for StackGuard { diff --git a/src/value.rs b/src/value.rs index 2caa98e..0931351 100644 --- a/src/value.rs +++ b/src/value.rs @@ -121,7 +121,7 @@ impl<'lua> Value<'lua> { Value::String(String(r)) | Value::Table(Table(r)) | Value::Function(Function(r)) - | Value::Thread(Thread(r)) + | Value::Thread(Thread(r, ..)) | Value::UserData(AnyUserData(r)) => r.to_pointer(), _ => ptr::null(), } @@ -143,7 +143,7 @@ impl<'lua> Value<'lua> { Value::String(s) => Ok(s.to_str()?.to_string()), Value::Table(Table(r)) | Value::Function(Function(r)) - | Value::Thread(Thread(r)) + | Value::Thread(Thread(r, ..)) | Value::UserData(AnyUserData(r)) => unsafe { let state = r.lua.state(); let _guard = StackGuard::new(state); @@ -387,7 +387,7 @@ pub struct MultiValue<'lua> { impl Drop for MultiValue<'_> { fn drop(&mut self) { if let Some(lua) = self.lua { - let vec = mem::replace(&mut self.vec, Vec::new()); + let vec = mem::take(&mut self.vec); lua.push_multivalue_to_pool(vec); } } @@ -439,7 +439,7 @@ impl<'lua> IntoIterator for MultiValue<'lua> { #[inline] fn into_iter(mut self) -> Self::IntoIter { - let vec = mem::replace(&mut self.vec, Vec::new()); + let vec = mem::take(&mut self.vec); mem::forget(self); vec.into_iter().rev() } @@ -481,7 +481,7 @@ impl<'lua> MultiValue<'lua> { #[inline] pub fn into_vec(mut self) -> Vec> { - let mut vec = mem::replace(&mut self.vec, Vec::new()); + let mut vec = mem::take(&mut self.vec); mem::forget(self); vec.reverse(); vec diff --git a/tests/async.rs b/tests/async.rs index f3094c5..fd4e929 100644 --- a/tests/async.rs +++ b/tests/async.rs @@ -6,8 +6,8 @@ use std::time::Duration; use futures_util::stream::TryStreamExt; use mlua::{ - AnyUserDataExt, Error, Function, Lua, LuaOptions, Result, StdLib, Table, TableExt, UserData, - UserDataMethods, Value, + AnyUserDataExt, Error, Function, Lua, LuaOptions, MultiValue, Result, StdLib, Table, TableExt, + UserData, UserDataMethods, Value, }; async fn sleep_ms(ms: u64) { @@ -82,6 +82,25 @@ async fn test_async_call() -> Result<()> { Ok(()) } +#[tokio::test] +async fn test_async_call_many_returns() -> Result<()> { + let lua = Lua::new(); + + let hello = lua.create_async_function(|_lua, ()| async move { + sleep_ms(10).await; + Ok(("a", "b", "c", 1)) + })?; + + let vals = hello.call_async::<_, MultiValue>(()).await?; + assert_eq!(vals.len(), 4); + assert_eq!(vals[0].to_string()?, "a"); + assert_eq!(vals[1].to_string()?, "b"); + assert_eq!(vals[2].to_string()?, "c"); + assert_eq!(vals[3], Value::Integer(1)); + + Ok(()) +} + #[tokio::test] async fn test_async_bind_call() -> Result<()> { let lua = Lua::new();