From 0611906c6ab2631682671af66bec04fa2e4df026 Mon Sep 17 00:00:00 2001 From: Alex Orlenko Date: Wed, 5 Nov 2025 22:07:14 +0000 Subject: [PATCH] Add `Lua::type_metatable` helper to get metatable of a primitive type. The accompany function `Lua::set_type_metatable` already exists. --- src/state.rs | 63 ++++++++++++++++++++---------------------------- src/state/raw.rs | 42 +++++++++++++++++++++++++++++++- src/table.rs | 8 ++---- tests/types.rs | 20 ++++++++++----- 4 files changed, 83 insertions(+), 50 deletions(-) diff --git a/src/state.rs b/src/state.rs index b6e23cb..aa27f2f 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1514,7 +1514,27 @@ impl Lua { unsafe { self.lock().make_userdata(UserDataStorage::new(ud)) } } - /// Sets the metatable for a Lua builtin type. + /// Gets the metatable of a Lua built-in (primitive) type. + /// + /// The metatable is shared by all values of the given type. + /// + /// See [`Lua::set_type_metatable`] for examples. + #[allow(private_bounds)] + pub fn type_metatable(&self) -> Option { + let lua = self.lock(); + let state = lua.state(); + unsafe { + let _sg = StackGuard::new(state); + assert_stack(state, 2); + + if lua.push_primitive_type::() && ffi::lua_getmetatable(state, -1) != 0 { + return Some(Table(lua.pop_ref())); + } + } + None + } + + /// Sets the metatable for a Lua built-in (primitive) type. /// /// The metatable will be shared by all values of the given type. /// @@ -1541,44 +1561,13 @@ impl Lua { let _sg = StackGuard::new(state); assert_stack(state, 2); - match T::TYPE_ID { - ffi::LUA_TBOOLEAN => { - ffi::lua_pushboolean(state, 0); + if lua.push_primitive_type::() { + match metatable { + Some(metatable) => lua.push_ref(&metatable.0), + None => ffi::lua_pushnil(state), } - ffi::LUA_TLIGHTUSERDATA => { - ffi::lua_pushlightuserdata(state, ptr::null_mut()); - } - ffi::LUA_TNUMBER => { - ffi::lua_pushnumber(state, 0.); - } - #[cfg(feature = "luau")] - ffi::LUA_TVECTOR => { - #[cfg(not(feature = "luau-vector4"))] - ffi::lua_pushvector(state, 0., 0., 0.); - #[cfg(feature = "luau-vector4")] - ffi::lua_pushvector(state, 0., 0., 0., 0.); - } - ffi::LUA_TSTRING => { - ffi::lua_pushstring(state, b"\0" as *const u8 as *const _); - } - ffi::LUA_TFUNCTION => match self.load("function() end").eval::() { - Ok(func) => lua.push_ref(&func.0), - Err(_) => return, - }, - ffi::LUA_TTHREAD => { - ffi::lua_pushthread(state); - } - #[cfg(feature = "luau")] - ffi::LUA_TBUFFER => { - ffi::lua_newbuffer(state, 0); - } - _ => return, + ffi::lua_setmetatable(state, -2); } - match metatable { - Some(metatable) => lua.push_ref(&metatable.0), - None => ffi::lua_pushnil(state), - } - ffi::lua_setmetatable(state, -2); } } diff --git a/src/state/raw.rs b/src/state/raw.rs index 09831fc..508ac64 100644 --- a/src/state/raw.rs +++ b/src/state/raw.rs @@ -19,7 +19,7 @@ use crate::thread::Thread; use crate::traits::IntoLua; use crate::types::{ AppDataRef, AppDataRefMut, Callback, CallbackUpvalue, DestructedUserdata, Integer, LightUserData, - MaybeSend, ReentrantMutex, RegistryKey, ValueRef, XRc, + LuaType, MaybeSend, ReentrantMutex, RegistryKey, ValueRef, XRc, }; use crate::userdata::{ init_userdata_metatable, AnyUserData, MetaMethod, RawUserDataRegistry, UserData, UserDataRegistry, @@ -665,6 +665,46 @@ impl RawLua { } } + /// Pushes a primitive type value onto the Lua stack. + pub(crate) unsafe fn push_primitive_type(&self) -> bool { + match T::TYPE_ID { + ffi::LUA_TBOOLEAN => { + ffi::lua_pushboolean(self.state(), 0); + } + ffi::LUA_TLIGHTUSERDATA => { + ffi::lua_pushlightuserdata(self.state(), ptr::null_mut()); + } + ffi::LUA_TNUMBER => { + ffi::lua_pushnumber(self.state(), 0.); + } + #[cfg(feature = "luau")] + ffi::LUA_TVECTOR => { + #[cfg(not(feature = "luau-vector4"))] + ffi::lua_pushvector(self.state(), 0., 0., 0.); + #[cfg(feature = "luau-vector4")] + ffi::lua_pushvector(self.state(), 0., 0., 0., 0.); + } + ffi::LUA_TSTRING => { + ffi::lua_pushstring(self.state(), b"\0" as *const u8 as *const _); + } + ffi::LUA_TFUNCTION => { + unsafe extern "C-unwind" fn func(_state: *mut ffi::lua_State) -> c_int { + 0 + } + ffi::lua_pushcfunction(self.state(), func); + } + ffi::LUA_TTHREAD => { + ffi::lua_pushthread(self.state()); + } + #[cfg(feature = "luau")] + ffi::LUA_TBUFFER => { + ffi::lua_newbuffer(self.state(), 0); + } + _ => return false, + } + true + } + /// Pushes a value that implements `IntoLua` onto the Lua stack. /// /// Uses up to 2 stack spaces to push a single value, does not call `checkstack`. diff --git a/src/table.rs b/src/table.rs index 82e40d5..4b18483 100644 --- a/src/table.rs +++ b/src/table.rs @@ -1,14 +1,14 @@ use std::collections::HashSet; use std::fmt; use std::marker::PhantomData; -use std::os::raw::{c_int, c_void}; +use std::os::raw::c_void; use std::string::String as StdString; use crate::error::{Error, Result}; use crate::function::Function; use crate::state::{LuaGuard, RawLua, WeakLua}; use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike}; -use crate::types::{Integer, LuaType, ValueRef}; +use crate::types::{Integer, ValueRef}; use crate::util::{assert_stack, check_stack, get_metatable_ptr, StackGuard}; use crate::value::{Nil, Value}; @@ -935,10 +935,6 @@ where } } -impl LuaType for Table { - const TYPE_ID: c_int = ffi::LUA_TTABLE; -} - impl ObjectLike for Table { #[inline] fn get(&self, key: impl IntoLua) -> Result { diff --git a/tests/types.rs b/tests/types.rs index 27bbe04..6c7a018 100644 --- a/tests/types.rs +++ b/tests/types.rs @@ -31,7 +31,9 @@ fn test_boolean_type_metatable() -> Result<()> { let mt = lua.create_table()?; mt.set("__add", Function::wrap(|a, b| Ok(a || b)))?; - lua.set_type_metatable::(Some(mt)); + assert_eq!(lua.type_metatable::(), None); + lua.set_type_metatable::(Some(mt.clone())); + assert_eq!(lua.type_metatable::().unwrap(), mt); lua.load(r#"assert(true + true == true)"#).exec().unwrap(); lua.load(r#"assert(true + false == true)"#).exec().unwrap(); @@ -52,7 +54,8 @@ fn test_lightuserdata_type_metatable() -> Result<()> { Ok(LightUserData((a.0 as usize + b.0 as usize) as *mut c_void)) }), )?; - lua.set_type_metatable::(Some(mt)); + lua.set_type_metatable::(Some(mt.clone())); + assert_eq!(lua.type_metatable::().unwrap(), mt); let res = lua .load( @@ -77,7 +80,9 @@ fn test_number_type_metatable() -> Result<()> { let mt = lua.create_table()?; mt.set("__call", Function::wrap(|n1: f64, n2: f64| Ok(n1 * n2)))?; - lua.set_type_metatable::(Some(mt)); + lua.set_type_metatable::(Some(mt.clone())); + assert_eq!(lua.type_metatable::().unwrap(), mt); + lua.load(r#"assert((1.5)(3.0) == 4.5)"#).exec().unwrap(); lua.load(r#"assert((5)(5) == 25)"#).exec().unwrap(); @@ -93,7 +98,8 @@ fn test_string_type_metatable() -> Result<()> { "__add", Function::wrap(|a: String, b: String| Ok(format!("{a}{b}"))), )?; - lua.set_type_metatable::(Some(mt)); + lua.set_type_metatable::(Some(mt.clone())); + assert_eq!(lua.type_metatable::().unwrap(), mt); lua.load(r#"assert(("foo" + "bar") == "foobar")"#).exec().unwrap(); @@ -109,7 +115,8 @@ fn test_function_type_metatable() -> Result<()> { "__index", Function::wrap(|_: Function, key: String| Ok(format!("function.{key}"))), )?; - lua.set_type_metatable::(Some(mt)); + lua.set_type_metatable::(Some(mt.clone())); + assert_eq!(lua.type_metatable::(), Some(mt)); lua.load(r#"assert((function() end).foo == "function.foo")"#) .exec() @@ -127,7 +134,8 @@ fn test_thread_type_metatable() -> Result<()> { "__index", Function::wrap(|_: Thread, key: String| Ok(format!("thread.{key}"))), )?; - lua.set_type_metatable::(Some(mt)); + lua.set_type_metatable::(Some(mt.clone())); + assert_eq!(lua.type_metatable::(), Some(mt)); lua.load(r#"assert((coroutine.create(function() end)).foo == "thread.foo")"#) .exec()