use core::{fmt, marker::PhantomData};
use luau_vm::Thread as VmThread;
use luau_vm::VmErrorResult;
use luau_vm::thread::StackGuard;
use crate::error::Error;
use crate::lua::Lua;
use crate::lua::runtime::RuntimeData;
use crate::thread::Thread;
pub(crate) struct ValueRef<'lua> {
thread: &'lua VmThread,
runtime: &'lua RuntimeData,
reference: i32,
pointer: *const (),
_marker: PhantomData<&'lua Lua>,
}
impl<'lua> ValueRef<'lua> {
pub(crate) const fn new(
thread: &'lua VmThread,
runtime: &'lua RuntimeData,
reference: i32,
pointer: *const (),
) -> Self {
Self {
thread,
runtime,
reference,
pointer,
_marker: PhantomData,
}
}
pub(crate) fn from_stack(thread: &Thread<'lua>, index: i32) -> VmErrorResult<Self> {
let pointer = unsafe { thread.as_vm().to_pointer(index) };
let reference = unsafe { thread.as_vm().ref_value(index)? };
Ok(Self::new(
thread.reference_thread(),
thread.runtime(),
reference,
pointer,
))
}
pub(crate) const fn runtime(&self) -> &'lua RuntimeData {
self.runtime
}
pub(crate) const fn reference_thread(&self) -> &'lua VmThread {
self.thread
}
pub(crate) fn thread(&self) -> Thread<'lua> {
Thread::new(self.thread, self.runtime)
}
pub(crate) const fn pointer(&self) -> *const () {
self.pointer
}
pub(crate) fn same_value(&self, other: &Self) -> bool {
(unsafe { self.thread.same_vm(other.thread) }) && self.pointer() == other.pointer()
}
pub(crate) fn try_clone(&self) -> Result<Self, Error> {
unsafe {
let thread = self.thread();
let vm_thread = thread.as_vm();
let _stack = StackGuard::new(vm_thread);
self.push()
.map_err(|exit| Error::from_thread_exit(vm_thread, exit))?;
let reference = vm_thread
.ref_value(-1)
.map_err(|exit| Error::from_thread_exit(vm_thread, exit))?;
Ok(Self::new(
self.thread,
self.runtime,
reference,
self.pointer,
))
}
}
pub(crate) fn push_to(&self, target: impl AsRef<VmThread>) -> Result<(), Error> {
unsafe {
let vm_thread = self.thread;
let target = target.as_ref();
if !target.same_vm(vm_thread) {
return Err(Error::foreign_lua_handle());
}
if *vm_thread == *target {
self.push()
.map_err(|exit| Error::from_thread_exit(vm_thread, exit))?;
} else {
let _stack = StackGuard::new(vm_thread);
self.push()
.map_err(|exit| Error::from_thread_exit(vm_thread, exit))?;
vm_thread
.x_move(target, 1)
.map_err(|exit| Error::from_thread_exit(vm_thread, exit))?;
}
}
Ok(())
}
pub(crate) fn push(&self) -> VmErrorResult {
unsafe { self.thread.get_ref(self.reference).map(drop) }
}
}
impl Drop for ValueRef<'_> {
fn drop(&mut self) {
let thread = self.thread();
unsafe {
thread.as_vm().unref_value(self.reference);
}
}
}
impl PartialEq for ValueRef<'_> {
fn eq(&self, other: &Self) -> bool {
self.same_value(other)
}
}
impl Eq for ValueRef<'_> {}
impl fmt::Debug for ValueRef<'_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "Ref({:p})", self.pointer())
}
}