Add Lua::type_metatable helper to get metatable of a primitive type.

The accompany function `Lua::set_type_metatable` already exists.
This commit is contained in:
Alex Orlenko
2025-11-05 22:07:14 +00:00
parent 72ac247dca
commit 0611906c6a
4 changed files with 83 additions and 50 deletions
+26 -37
View File
@@ -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<T: LuaType>(&self) -> Option<Table> {
let lua = self.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 2);
if lua.push_primitive_type::<T>() && 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::<T>() {
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::<Function>() {
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);
}
}
+41 -1
View File
@@ -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<T: LuaType>(&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`.
+2 -6
View File
@@ -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<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
+14 -6
View File
@@ -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::<bool>(Some(mt));
assert_eq!(lua.type_metatable::<bool>(), None);
lua.set_type_metatable::<bool>(Some(mt.clone()));
assert_eq!(lua.type_metatable::<bool>().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::<LightUserData>(Some(mt));
lua.set_type_metatable::<LightUserData>(Some(mt.clone()));
assert_eq!(lua.type_metatable::<LightUserData>().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::<Number>(Some(mt));
lua.set_type_metatable::<Number>(Some(mt.clone()));
assert_eq!(lua.type_metatable::<Number>().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::<LuaString>(Some(mt));
lua.set_type_metatable::<LuaString>(Some(mt.clone()));
assert_eq!(lua.type_metatable::<LuaString>().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::<Function>(Some(mt));
lua.set_type_metatable::<Function>(Some(mt.clone()));
assert_eq!(lua.type_metatable::<Function>(), 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::<Thread>(Some(mt));
lua.set_type_metatable::<Thread>(Some(mt.clone()));
assert_eq!(lua.type_metatable::<Thread>(), Some(mt));
lua.load(r#"assert((coroutine.create(function() end)).foo == "thread.foo")"#)
.exec()