From cfc3fb9af24cba33d31be71b40fe9aa171de1dbc Mon Sep 17 00:00:00 2001 From: bjcscat Date: Fri, 6 Dec 2024 17:27:11 +0000 Subject: [PATCH] add vector support --- build.rs | 4 +++ src/ffi/luau.rs | 2 ++ src/lib.rs | 95 +++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 90 insertions(+), 11 deletions(-) diff --git a/build.rs b/build.rs index 34b7e3a..fdc57e4 100644 --- a/build.rs +++ b/build.rs @@ -53,7 +53,11 @@ fn do_cfg(config: &mut Config) { // maximum number of captures supported by pattern matching define_lua_cfg!(config, "LUA_MAXCAPTURES", "32"); + #[cfg(not(feature="luau_vector4"))] define_lua_cfg!(config, "LUA_VECTOR_SIZE", "3"); + + #[cfg(feature="luau_vector4")] + define_lua_cfg!(config, "LUA_VECTOR_SIZE", "4"); } fn main() { diff --git a/src/ffi/luau.rs b/src/ffi/luau.rs index 89904f0..e4e168e 100644 --- a/src/ffi/luau.rs +++ b/src/ffi/luau.rs @@ -23,6 +23,7 @@ pub const fn lua_ispseudo(i: c_int) -> bool { // thread status; 0 is OK #[repr(C)] +#[derive(Debug)] #[allow(non_camel_case_types)] pub enum LuauStatus { /// OK status @@ -1050,6 +1051,7 @@ pub unsafe fn lua_tostring(state: *mut _LuaState, i: c_int) -> *const c_char { lua_tolstring(state, i, null_mut()) } +#[macro_export] macro_rules! lua_pushformat { ($state:expr, $fmt:expr, $($args:tt)*) => { let string = std::fmt::format(format_args!($fmt, $($args)*)); diff --git a/src/lib.rs b/src/lib.rs index 13c654b..efe7206 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,7 +22,7 @@ use ffi::{ prelude::*, }; use memory::{luau_alloc_cb, DefaultLuauAllocator}; -use threads::{LuauThread, MainStateDeadError}; +use threads::LuauThread; use userdata::{ drop_userdata, dtor_rs_luau_userdata_callback, Userdata, UserdataBorrowError, UserdataRef, UserdataRefMut, UD_TAG, @@ -176,7 +176,7 @@ impl Luau { } /// Yields the luau state with the number of results - /// + /// /// Should be used as the end expression or a return from a function as this returns `-1` pub fn yield_luau(&self, nresults: c_int) -> c_int { assert!( @@ -184,18 +184,14 @@ impl Luau { "The number of yield returns must not exceed the stack size" ); - unsafe { - lua_yield(self.state, nresults) - } + unsafe { lua_yield(self.state, nresults) } } /// Breaks the luau state for the purposes of a debug interrupt - /// + /// /// Should be used as the end expression or a return from a function as this returns `-1` pub fn break_luau(&self) -> c_int { - unsafe { - lua_break(self.state) - } + unsafe { lua_break(self.state) } } /// Returns the type of a luau value at `idx` @@ -277,6 +273,26 @@ impl Luau { } } + pub fn check_args(&self, count: c_int, extra_message: Option<&str>) { + if self.top() >= count { + return; + } + + unsafe { + luaL_argerrorL( + self.state, + count - self.top(), + extra_message + .map(|v| { + let cstr = + CString::new(v).expect("extra_message should not contain a null byte"); + cstr.as_ptr() + }) + .unwrap_or(null()), + ); + } + } + /// Returns true if the value at `idx` is nil pub fn is_nil(&self, idx: c_int) -> bool { self.type_of(idx) == LuauType::LUA_TNIL @@ -747,10 +763,42 @@ impl Luau { } } + /// Returns true if the value at idx is a vector, false otherwise pub fn is_vector(&self, idx: c_int) -> bool { self.type_of(idx) == LuauType::LUA_TVECTOR } + /// Pushes a vector to the Luau stack + pub fn push_vector(&self, x: f32, y: f32, z: f32, #[cfg(feature = "luau_vector4")] w: f32) { + luau_stack_precondition!(self.check_stack(1)); + + // SAFETY: stack size is validated by precondition + unsafe { + #[cfg(not(feature = "luau_vector4"))] + lua_pushvector(self.state, x, y, z); + #[cfg(feature = "luau_vector4")] + lua_pushvector(self.state, x, y, z, w); + } + } + + #[cfg(not(feature = "luau_vector4"))] + /// Returns the value of a vector if the value at idx is a vector or will return None + pub fn to_vector(&self, idx: c_int) -> Option<(f32, f32, f32)> { + luau_stack_precondition!(self.check_index(idx)); + unsafe { + Option::from(lua_tovector(self.state, idx)).map(|ptr| (*ptr, *ptr.add(1), *ptr.add(2))) + } + } + + #[cfg(feature = "luau_vector4")] + /// Returns the value of a vector if the value at idx is a vector or will return None + pub fn to_vector(&self, idx: c_int) -> Option<(f32, f32, f32, f32)> { + luau_stack_precondition!(self.check_index(idx)); + unsafe { + Option::from(lua_tovector(self.state, idx)).map(|ptr| (*ptr, *ptr.add(1), *ptr.add(2), *ptr.add(3))) + } + } + /// Returns true if the value at `idx` is a thread, false otherwise pub fn is_thread(&self, idx: c_int) -> bool { self.type_of(idx) == LuauType::LUA_TTHREAD @@ -781,7 +829,9 @@ impl Luau { /// Resumes the given Luau thread with the number of arguments. /// /// Will resume the function on the top of the given Luau thread's execution stack - pub fn resume(&self, luau_thread: LuauThread, nargs: c_int) -> LuauStatus { + pub fn resume(&self, luau_thread: &LuauThread, nargs: c_int) -> LuauStatus { + assert!(luau_thread.get_state().is_function(-1)); + unsafe { lua_resume(luau_thread.get_state().state, self.state, nargs) } } @@ -1051,6 +1101,7 @@ mod tests { use crate::{ Luau, LuauAllocator, _LuaState, lua_error, lua_tonumber, lua_upvalueindex, userdata::{UserdataBorrowError, UserdataRef}, + LuauStatus, }; #[test] @@ -1206,7 +1257,7 @@ mod tests { 0, ); - luau.resume(thread, 0); + luau.resume(&thread, 0); assert!(was_called, "Expected thread function to be called"); } @@ -1309,6 +1360,28 @@ mod tests { // todo!(); // } + #[test] + fn function_check() { + let luau = Luau::default(); + + luau.push_function( + |l| { + l.check_args(1, None); + + 0 + }, + None, + 0, + ); + + let status = luau.call(0, 0); + + assert!( + matches!(status, LuauStatus::LUA_ERRRUN), + "Expected there to be a runtime error." + ); + } + #[test] fn userdata_borrow() { let luau = Luau::default();