pub use self::class::*;
use self::property::PropertyKey;
use crate::gc::Object;
use crate::value::UnsafeValue;
use crate::{Lua, Nil, Table, Value, luaH_getid};
use core::alloc::Layout;
use core::any::{Any, TypeId};
use core::cell::Cell;
use core::marker::{PhantomData, PhantomPinned};
use core::mem::transmute;
use core::ptr::{addr_of_mut, null};
mod class;
mod property;
#[repr(C)]
pub struct UserData<A, T: ?Sized> {
pub(crate) hdr: Object<A>,
pub(crate) props: Cell<*const Table<A>>,
pub(crate) mt: *const Table<A>,
pub(crate) uv: UnsafeValue<A>,
pub(crate) ptr: *const dyn Any,
phantom: PhantomData<T>,
pin: PhantomPinned,
}
impl<A, T: Any> UserData<A, T> {
#[inline(never)]
pub(crate) unsafe fn new(g: *const Lua<A>, value: T) -> *const UserData<A, ()> {
let layout = Layout::new::<T>();
let (layout, offset) = Layout::new::<UserData<A, ()>>().extend(layout).unwrap();
let layout = layout.pad_to_align();
let id = TypeId::of::<T>();
let mt = unsafe { luaH_getid((*g).metatables(), &id) };
let mt = match unsafe { (*mt).tt_ & 0xf } {
5 => unsafe { (*mt).value_.gc.cast::<Table<A>>() },
_ => null(),
};
let o = unsafe { (*g).gc.alloc(7 | 0 << 4, layout).cast::<UserData<A, ()>>() };
unsafe { addr_of_mut!((*o).props).write(Cell::new(null())) };
unsafe { addr_of_mut!((*o).mt).write(mt) };
unsafe { addr_of_mut!((*o).uv).write(Nil.into()) };
let v = unsafe { o.byte_add(offset).cast::<T>() };
unsafe { v.write(value) };
let v: &dyn Any = unsafe { &*v };
unsafe { addr_of_mut!((*o).ptr).write(v) };
o
}
#[inline(always)]
pub fn value(&self) -> &T {
unsafe { &*self.ptr.cast() }
}
}
impl<A, T: ?Sized> UserData<A, T> {
#[inline(always)]
pub fn get<K: PropertyKey>(&self, name: K) -> Value<'_, A> {
let props = self.props.get();
let value = match props.is_null() {
true => return Value::Nil,
false => unsafe { name.get(props) },
};
unsafe { Value::from_unsafe(value) }
}
#[inline(never)]
pub fn set<K: PropertyKey>(&self, name: K, value: impl Into<UnsafeValue<A>>) {
let value = value.into();
if unsafe { (value.tt_ & 1 << 6 != 0) && (*value.value_.gc).global != self.hdr.global } {
panic!("attempt to set a property with value from a different Lua");
}
let mut props = self.props.get();
if props.is_null() {
let g = self.hdr.global();
props = unsafe { Table::new(g) };
if self.hdr.marked.get() & 1 << 5 != 0 {
unsafe { g.gc.barrier(&self.hdr, props.cast()) };
}
self.props.set(props);
}
unsafe { name.set(props, value) };
}
}
impl<A> UserData<A, dyn Any> {
#[inline(always)]
pub fn is<T: Any>(&self) -> bool {
unsafe { (*self.ptr).is::<T>() }
}
#[inline(always)]
pub fn downcast<T: Any>(&self) -> Option<&UserData<A, T>> {
match self.is::<T>() {
true => Some(unsafe { transmute(self) }),
false => None,
}
}
}