Skip to main content

luau_vm/function/
upvalue.rs

1use core::mem::{ManuallyDrop, size_of};
2use core::ptr::{self, NonNull};
3
4use crate::handle::RawHandle;
5use crate::state::GlobalState;
6use crate::value::{RawTValue, TValue, TValueCursor};
7
8#[repr(C)]
9pub struct RawUpVal {
10    pub tt: u8,
11    pub marked: u8,
12    pub memcat: u8,
13    pub marked_open: u8,
14    pub value: *mut RawTValue,
15    pub data: RawUpValData,
16}
17
18#[repr(C)]
19pub struct RawUpValOpen {
20    pub prev: *mut RawUpVal,
21    pub next: *mut RawUpVal,
22    pub thread_next: *mut RawUpVal,
23}
24
25#[repr(C)]
26pub union RawUpValData {
27    pub value: ManuallyDrop<RawTValue>,
28    pub open: ManuallyDrop<RawUpValOpen>,
29}
30
31#[derive(Clone, Copy, PartialEq, Eq)]
32#[repr(transparent)]
33/// Non-owning identity of a VM upvalue record.
34///
35/// # Safety model for unsafe methods
36///
37/// The upvalue, its value slot, and any stack used for rebasing must remain
38/// live in the same VM. Callers must preserve open-list ordering and closed
39/// storage state while moving or closing it.
40pub struct UpVal {
41    raw: NonNull<RawUpVal>,
42}
43
44#[derive(Clone, Copy, PartialEq, Eq)]
45#[repr(transparent)]
46/// Non-owning view of an open-upvalue list record.
47///
48/// Unsafe operations require a live open upvalue in the owning VM and valid
49/// neighboring list links; mutation must preserve both open-upvalue lists.
50pub struct UpValOpen {
51    raw: NonNull<RawUpValOpen>,
52}
53
54#[allow(
55    clippy::missing_safety_doc,
56    reason = "UpVal's shared raw-handle contract is documented on UpVal"
57)]
58impl UpVal {
59    pub const fn allocation_size() -> usize {
60        size_of::<RawUpVal>()
61    }
62
63    pub const unsafe fn from_raw(raw: NonNull<RawUpVal>) -> Self {
64        Self { raw }
65    }
66
67    pub unsafe fn from_ref(raw: &RawUpVal) -> Self {
68        Self {
69            raw: NonNull::from(raw),
70        }
71    }
72
73    pub unsafe fn open_data(&self) -> UpValOpen {
74        unsafe {
75            UpValOpen::from_raw(NonNull::new_unchecked(
76                (&raw mut (*self.as_ptr()).data.open).cast::<RawUpValOpen>(),
77            ))
78        }
79    }
80
81    pub unsafe fn value_ptr(&self) -> *mut RawTValue {
82        unsafe { self.as_ptr().as_ref().unwrap_unchecked().value }
83    }
84
85    pub unsafe fn value(&self) -> TValue {
86        unsafe { TValue::from_raw(NonNull::new_unchecked(self.value_ptr())) }
87    }
88
89    pub unsafe fn closed_value(&self) -> TValue {
90        unsafe {
91            TValue::from_raw(NonNull::new_unchecked(
92                (&raw mut (*self.as_ptr()).data.value).cast::<RawTValue>(),
93            ))
94        }
95    }
96
97    pub unsafe fn set_value(&self, value: TValue) {
98        unsafe {
99            (*self.as_ptr()).value = value.as_ptr();
100        }
101    }
102
103    pub unsafe fn rebase_value(&self, old_stack: TValueCursor, new_stack: TValueCursor) {
104        debug_assert!(unsafe { self.is_open() });
105
106        unsafe {
107            let value = TValueCursor::from_ptr(self.value_ptr());
108            let value_offset = value.addr_offset_from(old_stack) as usize;
109            (*self.as_ptr()).value = new_stack.add(value_offset).as_ptr();
110        }
111    }
112
113    pub unsafe fn close(&self) {
114        unsafe {
115            let closed_value = self.closed_value();
116            closed_value.set_obj(self.value());
117            self.set_value(closed_value);
118        }
119    }
120
121    pub unsafe fn is_open(&self) -> bool {
122        let closed_value = unsafe { (&raw const (*self.as_ptr()).data.value).cast::<RawTValue>() };
123        !core::ptr::eq(unsafe { self.value_ptr() }.cast_const(), closed_value)
124    }
125}
126
127#[allow(
128    clippy::missing_safety_doc,
129    reason = "UpValOpen's shared raw-view contract is documented on UpValOpen"
130)]
131impl UpValOpen {
132    pub const unsafe fn from_raw(raw: NonNull<RawUpValOpen>) -> Self {
133        Self { raw }
134    }
135
136    pub unsafe fn prev(&self) -> UpVal {
137        unsafe {
138            UpVal::from_raw(NonNull::new_unchecked(
139                self.as_ptr().as_ref().unwrap_unchecked().prev,
140            ))
141        }
142    }
143
144    pub unsafe fn set_prev(&self, prev: UpVal) {
145        unsafe {
146            self.as_ptr().as_mut().unwrap_unchecked().prev = prev.as_ptr();
147        }
148    }
149
150    pub unsafe fn next(&self) -> UpVal {
151        unsafe {
152            UpVal::from_raw(NonNull::new_unchecked(
153                self.as_ptr().as_ref().unwrap_unchecked().next,
154            ))
155        }
156    }
157
158    pub unsafe fn set_next(&self, next: UpVal) {
159        unsafe {
160            self.as_ptr().as_mut().unwrap_unchecked().next = next.as_ptr();
161        }
162    }
163
164    pub unsafe fn thread_next(&self) -> Option<UpVal> {
165        unsafe {
166            NonNull::new(self.as_ptr().as_ref().unwrap_unchecked().thread_next)
167                .map(|upvalue| UpVal::from_raw(upvalue))
168        }
169    }
170
171    pub unsafe fn set_thread_next(&self, thread_next: Option<UpVal>) {
172        unsafe {
173            self.as_ptr().as_mut().unwrap_unchecked().thread_next =
174                thread_next.map_or(ptr::null_mut(), |upvalue| upvalue.as_ptr());
175        }
176    }
177}
178
179#[allow(
180    clippy::missing_safety_doc,
181    reason = "GlobalState's shared raw-handle contract is documented on GlobalState"
182)]
183impl GlobalState {
184    pub unsafe fn uv_head(&self) -> UpVal {
185        unsafe { UpVal::from_raw(NonNull::new_unchecked(&raw mut (*self.as_ptr()).uv_head)) }
186    }
187}
188impl crate::handle::sealed::Sealed for UpVal {}
189impl crate::handle::sealed::Sealed for UpValOpen {}
190impl RawHandle for UpVal {
191    type Raw = RawUpVal;
192
193    fn as_ptr(&self) -> *mut Self::Raw {
194        self.raw.as_ptr()
195    }
196}
197
198impl AsRef<UpVal> for UpVal {
199    fn as_ref(&self) -> &UpVal {
200        self
201    }
202}
203
204impl RawHandle for UpValOpen {
205    type Raw = RawUpValOpen;
206
207    fn as_ptr(&self) -> *mut Self::Raw {
208        self.raw.as_ptr()
209    }
210}
211
212impl AsRef<UpValOpen> for UpValOpen {
213    fn as_ref(&self) -> &UpValOpen {
214        self
215    }
216}