mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Set __type metatable field for Luau instead of __name.
Other Lua versions continue to use `__name` field. Also make `MetaMethod` enum non-exhaustive (and add `Type` variant) Closes #295
This commit is contained in:
@@ -323,7 +323,7 @@ pub unsafe fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char)
|
||||
pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_int {
|
||||
if luaL_newmetatable_(L, tname) != 0 {
|
||||
lua_pushstring(L, tname);
|
||||
lua_setfield(L, -2, cstr!("__name"));
|
||||
lua_setfield(L, -2, cstr!("__type"));
|
||||
1
|
||||
} else {
|
||||
0
|
||||
@@ -454,7 +454,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize)
|
||||
}
|
||||
}
|
||||
t => {
|
||||
let tt = luaL_getmetafield(L, idx, cstr!("__name"));
|
||||
let tt = luaL_getmetafield(L, idx, cstr!("__type"));
|
||||
let name = if tt == LUA_TSTRING {
|
||||
lua_tostring(L, -1)
|
||||
} else {
|
||||
@@ -462,7 +462,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize)
|
||||
};
|
||||
lua_pushfstring(L, cstr!("%s: %p"), name, lua_topointer(L, idx));
|
||||
if tt != LUA_TNIL {
|
||||
lua_replace(L, -2); // remove '__name'
|
||||
lua_replace(L, -2); // remove '__type'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
+3
-3
@@ -2531,15 +2531,15 @@ impl Lua {
|
||||
}
|
||||
let mut has_name = false;
|
||||
for (k, f) in registry.meta_fields {
|
||||
has_name = has_name || k == "__name";
|
||||
has_name = has_name || k == MetaMethod::Type;
|
||||
self.push_value(f(self, MultiValue::new())?.pop_front().unwrap())?;
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
// Set `__name` if not provided
|
||||
// Set `__name/__type` if not provided
|
||||
if !has_name {
|
||||
let type_name = short_type_name::<T>();
|
||||
push_string(state, type_name.as_bytes(), !self.unlikely_memory_error())?;
|
||||
rawset_field(state, -2, "__name")?;
|
||||
rawset_field(state, -2, MetaMethod::Type.name())?;
|
||||
}
|
||||
let metatable_index = ffi::lua_absindex(state, -1);
|
||||
|
||||
|
||||
+23
-3
@@ -1,5 +1,6 @@
|
||||
use std::any::{type_name, TypeId};
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::ffi::CStr;
|
||||
use std::fmt;
|
||||
use std::hash::Hash;
|
||||
use std::mem;
|
||||
@@ -36,6 +37,7 @@ pub(crate) const USER_VALUE_MAXSLOT: usize = 8;
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub enum MetaMethod {
|
||||
/// The `+` operator.
|
||||
Add,
|
||||
@@ -141,6 +143,11 @@ pub enum MetaMethod {
|
||||
#[cfg(feature = "lua54")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
||||
Close,
|
||||
/// The `__name`/`__type` metafield.
|
||||
///
|
||||
/// This is not a function, but it's value can be used by `tostring` and `typeof` built-in functions.
|
||||
#[doc(hidden)]
|
||||
Type,
|
||||
}
|
||||
|
||||
impl PartialEq<MetaMethod> for &str {
|
||||
@@ -212,6 +219,19 @@ impl MetaMethod {
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
MetaMethod::Close => "__close",
|
||||
|
||||
#[rustfmt::skip]
|
||||
MetaMethod::Type => if cfg!(feature = "luau") { "__type" } else { "__name" },
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) const fn as_cstr(self) -> &'static CStr {
|
||||
match self {
|
||||
#[rustfmt::skip]
|
||||
MetaMethod::Type => unsafe {
|
||||
CStr::from_bytes_with_nul_unchecked(if cfg!(feature = "luau") { b"__type\0" } else { b"__name\0" })
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1089,7 +1109,7 @@ impl<'lua> AnyUserData<'lua> {
|
||||
unsafe { self.0.lua.get_userdata_type_id(&self.0) }
|
||||
}
|
||||
|
||||
/// Returns a type name of this `UserData` (from `__name` metatable field).
|
||||
/// Returns a type name of this `UserData` (from a metatable field).
|
||||
pub(crate) fn type_name(&self) -> Result<Option<StdString>> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
@@ -1101,10 +1121,10 @@ impl<'lua> AnyUserData<'lua> {
|
||||
let protect = !lua.unlikely_memory_error();
|
||||
let name_type = if protect {
|
||||
protect_lua!(state, 1, 1, |state| {
|
||||
ffi::luaL_getmetafield(state, -1, cstr!("__name"))
|
||||
ffi::luaL_getmetafield(state, -1, MetaMethod::Type.as_cstr().as_ptr())
|
||||
})?
|
||||
} else {
|
||||
ffi::luaL_getmetafield(state, -1, cstr!("__name"))
|
||||
ffi::luaL_getmetafield(state, -1, MetaMethod::Type.as_cstr().as_ptr())
|
||||
};
|
||||
match name_type {
|
||||
ffi::LUA_TSTRING => Ok(Some(String(lua.pop_ref()).to_str()?.to_owned())),
|
||||
|
||||
+1
-1
@@ -223,7 +223,7 @@ impl<'lua> Value<'lua> {
|
||||
f @ Value::Function(_) => write!(fmt, "function: {:?}", f.to_pointer()),
|
||||
t @ Value::Thread(_) => write!(fmt, "thread: {:?}", t.to_pointer()),
|
||||
u @ Value::UserData(ud) => {
|
||||
// Try `__name` first then `__tostring`
|
||||
// Try `__name/__type` first then `__tostring`
|
||||
let name = ud.type_name().ok().flatten();
|
||||
let s = name
|
||||
.map(|name| format!("{name}: {:?}", u.to_pointer()))
|
||||
|
||||
+14
-6
@@ -543,7 +543,7 @@ fn test_metatable() -> Result<()> {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_function("my_type_name", |_, data: AnyUserData| {
|
||||
let metatable = data.get_metatable()?;
|
||||
metatable.get::<String>("__name")
|
||||
metatable.get::<String>(MetaMethod::Type)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -551,8 +551,13 @@ fn test_metatable() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("ud", MyUserData)?;
|
||||
lua.load(r#"assert(ud:my_type_name() == "MyUserData")"#)
|
||||
.exec()?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(ud:my_type_name() == "MyUserData")
|
||||
assert(tostring(ud):sub(1, 14) == "MyUserData: 0x")
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
let ud: AnyUserData = globals.get("ud")?;
|
||||
let metatable = ud.get_metatable()?;
|
||||
@@ -574,7 +579,7 @@ fn test_metatable() -> Result<()> {
|
||||
.map(|kv: Result<(_, Value)>| Ok(kv?.0))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
methods.sort();
|
||||
assert_eq!(methods, vec!["__index", "__name"]);
|
||||
assert_eq!(methods, vec!["__index", MetaMethod::Type.name()]);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct MyUserData2;
|
||||
@@ -596,13 +601,16 @@ fn test_metatable() -> Result<()> {
|
||||
|
||||
impl UserData for MyUserData3 {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_meta_field_with("__name", |_| Ok("CustomName"));
|
||||
fields.add_meta_field_with(MetaMethod::Type, |_| Ok("CustomName"));
|
||||
}
|
||||
}
|
||||
|
||||
let ud = lua.create_userdata(MyUserData3)?;
|
||||
let metatable = ud.get_metatable()?;
|
||||
assert_eq!(metatable.get::<String>("__name")?.to_str()?, "CustomName");
|
||||
assert_eq!(
|
||||
metatable.get::<String>(MetaMethod::Type)?.to_str()?,
|
||||
"CustomName"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user