Skip to content

Commit

Permalink
Support LuaJIT cdata type (produced by ffi module)
Browse files Browse the repository at this point in the history
  • Loading branch information
khvzak committed Nov 16, 2023
1 parent 5043447 commit 34476eb
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 31 deletions.
23 changes: 11 additions & 12 deletions src/lua.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::table::Table;
use crate::thread::Thread;
use crate::types::{
AppData, AppDataRef, AppDataRefMut, Callback, CallbackUpvalue, DestructedUserdata, Integer,
LightUserData, LuaRef, MaybeSend, Number, RegistryKey,
LightUserData, LuaRef, MaybeSend, Number, RegistryKey, SubtypeId,
};
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataCell};
use crate::userdata_impl::{UserDataProxy, UserDataRegistry};
Expand Down Expand Up @@ -2419,7 +2419,7 @@ impl Lua {
ffi::lua_pop(state, 1);
Nil
}
_ => Value::UserData(AnyUserData(self.pop_ref(), 0)),
_ => Value::UserData(AnyUserData(self.pop_ref(), SubtypeId::None)),
}
}

Expand All @@ -2428,14 +2428,13 @@ impl Lua {
#[cfg(feature = "luau")]
ffi::LUA_TBUFFER => {
// Buffer is represented as a userdata type
Value::UserData(AnyUserData(self.pop_ref(), crate::types::BUFFER_SUBTYPE_ID))
Value::UserData(AnyUserData(self.pop_ref(), SubtypeId::Buffer))
}

#[cfg(feature = "luajit")]
ffi::LUA_TCDATA => {
ffi::lua_pop(state, 1);
// TODO: Fix this in a next major release
panic!("cdata objects cannot be handled by mlua yet");
// CDATA is represented as a userdata type
Value::UserData(AnyUserData(self.pop_ref(), SubtypeId::CData))
}

_ => mlua_panic!("LUA_TNONE in pop_value"),
Expand Down Expand Up @@ -2520,7 +2519,7 @@ impl Lua {
}
_ => {
ffi::lua_xpush(state, self.ref_thread(), idx);
Value::UserData(AnyUserData(self.pop_ref_thread(), 0))
Value::UserData(AnyUserData(self.pop_ref_thread(), SubtypeId::None))
}
}
}
Expand All @@ -2534,14 +2533,14 @@ impl Lua {
ffi::LUA_TBUFFER => {
// Buffer is represented as a userdata type
ffi::lua_xpush(state, self.ref_thread(), idx);
let subtype_id = crate::types::BUFFER_SUBTYPE_ID;
Value::UserData(AnyUserData(self.pop_ref_thread(), subtype_id))
Value::UserData(AnyUserData(self.pop_ref_thread(), SubtypeId::Buffer))
}

#[cfg(feature = "luajit")]
ffi::LUA_TCDATA => {
// TODO: Fix this in a next major release
panic!("cdata objects cannot be handled by mlua yet");
// CData is represented as a userdata type
ffi::lua_xpush(state, self.ref_thread(), idx);
Value::UserData(AnyUserData(self.pop_ref_thread(), SubtypeId::CData))
}

_ => mlua_panic!("LUA_TNONE in pop_value"),
Expand Down Expand Up @@ -3126,7 +3125,7 @@ impl Lua {
ffi::lua_setuservalue(state, -2);
}

Ok(AnyUserData(self.pop_ref(), 0))
Ok(AnyUserData(self.pop_ref(), SubtypeId::None))
}

#[cfg(not(feature = "luau"))]
Expand Down
4 changes: 2 additions & 2 deletions src/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use serde::Serialize;
use crate::error::{Error, Result};
use crate::function::Function;
use crate::lua::Lua;
use crate::types::{Callback, CallbackUpvalue, LuaRef, MaybeSend};
use crate::types::{Callback, CallbackUpvalue, LuaRef, MaybeSend, SubtypeId};
use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
};
Expand Down Expand Up @@ -511,7 +511,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
#[cfg(not(feature = "luau"))]
std::ptr::write(ud_ptr as _, UserDataCell::new(data));
ffi::lua_setmetatable(state, -2);
let ud = AnyUserData(lua.pop_ref(), 0);
let ud = AnyUserData(lua.pop_ref(), SubtypeId::None);
lua.register_raw_userdata_metatable(mt_ptr, None);

#[cfg(any(feature = "lua51", feature = "luajit"))]
Expand Down
12 changes: 9 additions & 3 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,15 @@ pub type Integer = ffi::lua_Integer;
/// Type of Lua floating point numbers.
pub type Number = ffi::lua_Number;

// LUA_TBUFFER subtype
#[cfg(feature = "luau")]
pub(crate) const BUFFER_SUBTYPE_ID: u8 = 1;
// Represents different subtypes wrapped to AnyUserData
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub(crate) enum SubtypeId {
None,
#[cfg(feature = "luau")]
Buffer,
#[cfg(feature = "luajit")]
CData,
}

/// A "light" userdata value. Equivalent to an unmanaged raw pointer.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
Expand Down
15 changes: 9 additions & 6 deletions src/userdata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use crate::function::Function;
use crate::lua::Lua;
use crate::string::String;
use crate::table::{Table, TablePairs};
use crate::types::{LuaRef, MaybeSend};
use crate::types::{LuaRef, MaybeSend, SubtypeId};
use crate::util::{check_stack, get_userdata, take_userdata, StackGuard};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
use crate::UserDataRegistry;
Expand Down Expand Up @@ -791,7 +791,7 @@ impl<T> Deref for UserDataVariant<T> {
/// [`is`]: crate::AnyUserData::is
/// [`borrow`]: crate::AnyUserData::borrow
#[derive(Clone, Debug)]
pub struct AnyUserData<'lua>(pub(crate) LuaRef<'lua>, pub(crate) u8);
pub struct AnyUserData<'lua>(pub(crate) LuaRef<'lua>, pub(crate) SubtypeId);

/// Owned handle to an internal Lua userdata.
///
Expand All @@ -801,7 +801,7 @@ pub struct AnyUserData<'lua>(pub(crate) LuaRef<'lua>, pub(crate) u8);
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
#[derive(Clone, Debug)]
pub struct OwnedAnyUserData(pub(crate) crate::types::LuaOwnedRef, pub(crate) u8);
pub struct OwnedAnyUserData(pub(crate) crate::types::LuaOwnedRef, pub(crate) SubtypeId);

#[cfg(feature = "unstable")]
impl OwnedAnyUserData {
Expand Down Expand Up @@ -1112,9 +1112,12 @@ impl<'lua> AnyUserData<'lua> {

/// Returns a type name of this `UserData` (from a metatable field).
pub(crate) fn type_name(&self) -> Result<Option<StdString>> {
#[cfg(feature = "luau")]
if self.1 == crate::types::BUFFER_SUBTYPE_ID {
return Ok(Some("buffer".to_owned()));
match self.1 {
SubtypeId::None => {}
#[cfg(feature = "luau")]
SubtypeId::Buffer => return Ok(Some("buffer".to_owned())),
#[cfg(feature = "luajit")]
SubtypeId::CData => return Ok(Some("cdata".to_owned())),
}

let lua = self.0.lua;
Expand Down
21 changes: 17 additions & 4 deletions src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::lua::Lua;
use crate::string::String;
use crate::table::Table;
use crate::thread::Thread;
use crate::types::{Integer, LightUserData, Number};
use crate::types::{Integer, LightUserData, Number, SubtypeId};
use crate::userdata::AnyUserData;
use crate::util::{check_stack, StackGuard};

Expand Down Expand Up @@ -88,9 +88,11 @@ impl<'lua> Value<'lua> {
Value::Table(_) => "table",
Value::Function(_) => "function",
Value::Thread(_) => "thread",
Value::UserData(AnyUserData(_, SubtypeId::None)) => "userdata",
#[cfg(feature = "luau")]
Value::UserData(AnyUserData(_, crate::types::BUFFER_SUBTYPE_ID)) => "buffer",
Value::UserData(_) => "userdata",
Value::UserData(AnyUserData(_, SubtypeId::Buffer)) => "buffer",
#[cfg(feature = "luajit")]
Value::UserData(AnyUserData(_, SubtypeId::CData)) => "cdata",
Value::Error(_) => "error",
}
}
Expand Down Expand Up @@ -419,7 +421,18 @@ impl<'lua> Value<'lua> {
#[inline]
pub fn is_buffer(&self) -> bool {
self.as_userdata()
.map(|ud| ud.1 == crate::types::BUFFER_SUBTYPE_ID)
.map(|ud| ud.1 == SubtypeId::Buffer)
.unwrap_or_default()
}

/// Returns `true` if the value is a CData wrapped in [`AnyUserData`].
#[cfg(any(feature = "luajit", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luajit")))]
#[doc(hidden)]
#[inline]
pub fn is_cdata(&self) -> bool {
self.as_userdata()
.map(|ud| ud.1 == SubtypeId::CData)
.unwrap_or_default()
}

Expand Down
13 changes: 9 additions & 4 deletions tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1325,10 +1325,10 @@ fn test_warnings() -> Result<()> {

#[test]
#[cfg(feature = "luajit")]
#[should_panic]
fn test_luajit_cdata() {
fn test_luajit_cdata() -> Result<()> {
let lua = unsafe { Lua::unsafe_new() };
let _v: Result<Value> = lua

let cdata = lua
.load(
r#"
local ffi = require("ffi")
Expand All @@ -1341,7 +1341,12 @@ fn test_luajit_cdata() {
return ptr
"#,
)
.eval();
.eval::<Value>()?;
assert!(cdata.is_userdata() && cdata.is_cdata());
assert_eq!(cdata.type_name(), "cdata");
assert!(cdata.to_string()?.starts_with("cdata<void *>:"));

Ok(())
}

#[test]
Expand Down

0 comments on commit 34476eb

Please sign in to comment.