diff --git a/Cargo.lock b/Cargo.lock index cc02e09..92ceea2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,7 +109,7 @@ dependencies = [ [[package]] name = "gmod" -version = "15.0.2" +version = "16.0.0" dependencies = [ "cfg_table", "cstr", diff --git a/gmod/Cargo.toml b/gmod/Cargo.toml index bf4d350..cb237a4 100644 --- a/gmod/Cargo.toml +++ b/gmod/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gmod" -version = "15.0.2" +version = "16.0.0" authors = ["William Venner "] edition = "2021" license = "MIT" diff --git a/gmod/src/lib.rs b/gmod/src/lib.rs index 69d6f32..b22e225 100644 --- a/gmod/src/lib.rs +++ b/gmod/src/lib.rs @@ -1,5 +1,8 @@ //! [Available Lua Functions](https://docs.rs/gmod/latest/gmod/lua/struct.State.html) +#![allow(clippy::missing_safety_doc)] +#![allow(clippy::result_unit_err)] + #![feature(c_unwind)] #![feature(thread_id_value)] @@ -255,11 +258,11 @@ pub struct OpenGmodLibraryErrs(pub std::collections::HashMap<&'static str, liblo impl std::error::Error for OpenGmodLibraryErrs {} impl std::fmt::Display for OpenGmodLibraryErrs { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(f, "")?; + writeln!(f)?; for (path, err) in &self.0 { writeln!(f, "{} = {}", path, err)?; } - writeln!(f, "")?; + writeln!(f)?; Ok(()) } } diff --git a/gmod/src/lua/import.rs b/gmod/src/lua/import.rs index cdaf543..8c2c1ee 100644 --- a/gmod/src/lua/import.rs +++ b/gmod/src/lua/import.rs @@ -114,6 +114,7 @@ impl std::ops::DerefMut for LuaSharedInterface { pub static mut LUA_SHARED: LuaSharedInterface = LuaSharedInterface(UnsafeCell::new(std::ptr::null_mut()), #[cfg(debug_assertions)] AtomicI64::new(-1)); pub struct LuaShared { + pub(crate) library: &'static libloading::Library, pub lual_newstate: Symbol<'static, unsafe extern "C-unwind" fn() -> LuaState>, pub lual_openlibs: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState)>, pub lual_loadfile: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, path: LuaString) -> i32>, @@ -180,7 +181,7 @@ impl LuaShared { fn import() -> Self { unsafe { let (library, path) = Self::find_lua_shared(); - let library = Box::leak(Box::new(library)); // Keep this library referenced forever + let library = Box::leak(Box::new(library)); macro_rules! find_symbol { ( $symbol:literal ) => { @@ -249,6 +250,7 @@ impl LuaShared { lua_status: find_symbol!("lua_status"), lua_xmove: find_symbol!("lua_xmove"), lua_equal: find_symbol!("lua_equal"), + library, } } } diff --git a/gmod/src/lua/lua_state.rs b/gmod/src/lua/lua_state.rs index 66b83cf..7ff8257 100644 --- a/gmod/src/lua/lua_state.rs +++ b/gmod/src/lua/lua_state.rs @@ -1,8 +1,5 @@ use std::{mem::MaybeUninit, borrow::Cow, ffi::c_void}; - -use crate::lua::*; - -use crate::userdata::TaggedUserData; +use crate::{userdata::TaggedUserData, lua::*}; unsafe fn handle_pcall_ignore(lua: State) { crate::lua_stack_guard!(lua => { @@ -219,7 +216,7 @@ impl LuaState { handle_pcall_ignore(*self); false } - err @ _ => { + err => { #[cfg(debug_assertions)] eprintln!("[gmod-rs] pcall_ignore unknown error: {}", err); false @@ -316,11 +313,52 @@ impl LuaState { } #[inline(always)] + /// Creates a closure, which can be used as a function with stored data (upvalues) + /// + /// ## Example + /// + /// ```ignore + /// #[lua_function] + /// unsafe fn foo(lua: gmod::lua::State) { + /// lua.get_closure_arg(1); + /// let hello = lua.get_string(-1); + /// println!("{}", hello); + /// } + /// + /// lua.push_string("Hello, world!"); + /// lua.push_closure(foo, 1); + /// ``` pub unsafe fn push_closure(&self, func: LuaFunction, n: i32) { debug_assert!(n <= 255, "Can't push more than 255 arguments into a closure"); (LUA_SHARED.lua_pushcclosure)(*self, func, n) } + #[inline(always)] + /// Pushes the `n`th closure argument onto the stack + /// + /// ## Example + /// + /// ```ignore + /// #[lua_function] + /// unsafe fn foo(lua: gmod::lua::State) { + /// lua.push_closure_arg(1); + /// let hello = lua.get_string(-1); + /// println!("{}", hello); + /// } + /// + /// lua.push_string("Hello, world!"); + /// lua.push_closure(foo, 1); + /// ``` + pub unsafe fn push_closure_arg(&self, n: i32) { + self.push_value(self.upvalue_index(n)); + } + + #[inline(always)] + /// Equivalent to C `lua_upvalueindex` macro + pub const fn upvalue_index(&self, idx: i32) -> i32 { + LUA_GLOBALSINDEX - idx + } + #[inline(always)] pub unsafe fn set_table(&self, index: i32) { (LUA_SHARED.lua_settable)(*self, index) @@ -393,14 +431,12 @@ impl LuaState { } pub unsafe fn test_userdata(&self, index: i32, name: LuaString) -> bool { - if !(LUA_SHARED.lua_touserdata)(*self, index).is_null() { - if self.get_metatable(index) != 0 { - self.get_field(LUA_REGISTRYINDEX, name); - let result = self.raw_equal(-1, -2); - self.pop_n(2); - if result { - return true; - } + if !(LUA_SHARED.lua_touserdata)(*self, index).is_null() && self.get_metatable(index) != 0 { + self.get_field(LUA_REGISTRYINDEX, name); + let result = self.raw_equal(-1, -2); + self.pop_n(2); + if result { + return true; } } false @@ -463,6 +499,7 @@ impl LuaState { } #[inline(always)] + #[allow(clippy::len_without_is_empty)] pub unsafe fn len(&self, index: i32) -> i32 { (LUA_SHARED.lua_objlen)(*self, index) } @@ -527,7 +564,7 @@ impl LuaState { pub unsafe fn coroutine_resume_call(&self, narg: i32) { match (LUA_SHARED.lua_resume)(*self, narg) { LUA_OK => {}, - LUA_ERRRUN => self.error(self.get_string(-2).unwrap_or_else(|| Cow::Borrowed("Unknown error")).as_ref()), + LUA_ERRRUN => self.error(self.get_string(-2).unwrap_or(Cow::Borrowed("Unknown error")).as_ref()), LUA_ERRMEM => self.error("Out of memory"), _ => self.error("Unknown internal Lua error") } @@ -542,7 +579,7 @@ impl LuaState { handle_pcall_ignore(*self); Err(()) }, - err @ _ => { + err => { #[cfg(debug_assertions)] eprintln!("[gmod-rs] coroutine_resume_pcall_ignore unknown error: {}", err); Err(()) @@ -631,10 +668,8 @@ impl LuaState { pub unsafe fn debug_getinfo_at(&self, level: i32, what: LuaString) -> Option { let mut ar = MaybeUninit::uninit(); - if (LUA_SHARED.lua_getstack)(*self, level, ar.as_mut_ptr()) != 0 { - if (LUA_SHARED.lua_getinfo)(*self, what, ar.as_mut_ptr()) != 0 { - return Some(ar.assume_init()); - } + if (LUA_SHARED.lua_getstack)(*self, level, ar.as_mut_ptr()) != 0 && (LUA_SHARED.lua_getinfo)(*self, what, ar.as_mut_ptr()) != 0 { + return Some(ar.assume_init()); } None } diff --git a/gmod/src/lua/mod.rs b/gmod/src/lua/mod.rs index 11afaa3..7ebaab4 100644 --- a/gmod/src/lua/mod.rs +++ b/gmod/src/lua/mod.rs @@ -14,6 +14,8 @@ pub use push::*; mod returns; pub use returns::ValuesReturned; +mod raw_bind; + #[derive(Debug, Clone)] pub enum LuaError { /// Out of memory @@ -63,13 +65,13 @@ macro_rules! lua_string { /// /// ```rust,norun /// lua_stack_guard!(lua => { -/// lua.get_global(lua_string!("hook")); -/// lua.get_field(-1, lua_string!("Add")); -/// lua.push_string("PlayerInitialSpawn"); -/// lua.push_string("RustHook"); -/// lua.push_function(player_initial_spawn); -/// lua.call(3, 0); -/// // lua.pop(); +/// lua.get_global(lua_string!("hook")); +/// lua.get_field(-1, lua_string!("Add")); +/// lua.push_string("PlayerInitialSpawn"); +/// lua.push_string("RustHook"); +/// lua.push_function(player_initial_spawn); +/// lua.call(3, 0); +/// // lua.pop(); /// }); /// // PANIC: stack is dirty! We forgot to pop the hook library off the stack. /// ``` @@ -78,7 +80,7 @@ macro_rules! lua_stack_guard { ( $lua:ident => $code:block ) => {{ #[cfg(debug_assertions)] { let top = $lua.get_top(); - let ret = (|| $code)(); + let ret = $code; if top != $lua.get_top() { $lua.dump_stack(); panic!("Stack is dirty! Expected the stack to have {} elements, but it has {}!", top, $lua.get_top()); diff --git a/gmod/src/lua/push.rs b/gmod/src/lua/push.rs index d0cd9e4..e20100e 100644 --- a/gmod/src/lua/push.rs +++ b/gmod/src/lua/push.rs @@ -98,7 +98,7 @@ impl PushToLua for Vec { impl PushToLua for &[u8] { #[inline] unsafe fn push_to_lua(self, lua: crate::lua::State) { - lua.push_binary_string(&self); + lua.push_binary_string(self); } } impl PushToLua for Duration { diff --git a/gmod/src/lua/raw_bind.rs b/gmod/src/lua/raw_bind.rs new file mode 100644 index 0000000..db7ad12 --- /dev/null +++ b/gmod/src/lua/raw_bind.rs @@ -0,0 +1,45 @@ +use crate::lua::*; + +pub trait CLuaFunction: Copy {} + +macro_rules! impl_c_lua_function { + ($($($arg:ident) *;)*) => { + $( + impl<$($arg, )* R> CLuaFunction for extern "C-unwind" fn($($arg),*) -> R {} + impl<$($arg, )* R> CLuaFunction for unsafe extern "C-unwind" fn($($arg),*) -> R {} + impl<$($arg, )* R> CLuaFunction for extern "C" fn($($arg),*) -> R {} + impl<$($arg, )* R> CLuaFunction for unsafe extern "C" fn($($arg),*) -> R {} + )* + }; +} +impl_c_lua_function!( + ; + T1; + T1 T2; + T1 T2 T3; + T1 T2 T3 T4; + T1 T2 T3 T4 T5; + T1 T2 T3 T4 T5 T6; + T1 T2 T3 T4 T5 T6 T7; + T1 T2 T3 T4 T5 T6 T7 T8; + T1 T2 T3 T4 T5 T6 T7 T8 T9; + T1 T2 T3 T4 T5 T6 T7 T8 T9 T10; + T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11; + T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12; + T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13; + T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14; + T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15; + T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16; +); + +impl State { + #[inline(always)] + /// Binds to a raw Lua C function. + /// + /// If anything is missing from this library, you can use this function to bind it yourself. + /// + /// Note, this may be a somewhat expensive operation, so storing its result in some way is recommended. + pub unsafe fn raw_bind(&self, symbol: &[u8]) -> Result { + LUA_SHARED.library.get::(symbol).map(|f| *f) + } +} \ No newline at end of file diff --git a/gmod/src/lua/returns.rs b/gmod/src/lua/returns.rs index f211052..d17f1e2 100644 --- a/gmod/src/lua/returns.rs +++ b/gmod/src/lua/returns.rs @@ -3,10 +3,10 @@ use std::{num::NonZeroI32, borrow::Cow}; #[repr(transparent)] pub struct ValuesReturned(pub i32); -impl Into for ValuesReturned { +impl From for i32 { #[inline(always)] - fn into(self) -> i32 { - self.0 + fn from(v: ValuesReturned) -> Self { + v.0 } } @@ -45,11 +45,11 @@ impl From> for ValuesReturned { } pub trait DisplayLuaError { - fn display_lua_error<'a>(&'a self) -> Cow<'a, str>; + fn display_lua_error(&self) -> Cow<'_, str>; } impl DisplayLuaError for E { #[inline(always)] - fn display_lua_error<'a>(&'a self) -> Cow<'a, str> { + fn display_lua_error(&self) -> Cow<'_, str> { Cow::Owned(format!("{:?}", self)) } } diff --git a/gmod/src/userdata.rs b/gmod/src/userdata.rs index 43e0198..b036e0b 100644 --- a/gmod/src/userdata.rs +++ b/gmod/src/userdata.rs @@ -100,7 +100,7 @@ macro_rules! userdata { /// This will NOT perform a type check to ensure that the tagged userdata matches the user data you are coercing to. /// /// Coercing to the wrong type is undefined behaviour and is likely to crash your program. - pub unsafe fn coerce_unchecked(&self) -> &mut T { + pub unsafe fn coerce_unchecked<'a, 'b, T: CoercibleUserData>(&'a self) -> &'b mut T { &mut *(self.data as *mut T) } }