Skip to main content

luau_vm/gc/
object.rs

1use core::ptr::NonNull;
2
3use crate::Table;
4use crate::buffer::Buffer;
5use crate::function::{Closure, Proto, UpVal};
6use crate::handle::RawHandle;
7use crate::state::ThreadState;
8use crate::string::TString;
9use crate::thread::Thread;
10use crate::types::{
11    LUA_TBUFFER, LUA_TCLASS, LUA_TFUNCTION, LUA_TOBJECT, LUA_TPROTO, LUA_TSTRING, LUA_TTABLE,
12    LUA_TTHREAD, LUA_TUPVALUE, LUA_TUSERDATA,
13};
14use crate::userdata::Userdata;
15use crate::{Class, Object};
16
17use crate::table::LuaNode;
18use crate::value::TValue;
19
20#[repr(C)]
21pub struct RawGcObject {
22    pub tt: u8,
23    pub marked: u8,
24    pub memcat: u8,
25}
26
27#[derive(Clone, Copy, PartialEq, Eq)]
28#[repr(transparent)]
29/// Non-owning identity of a collectable VM record.
30///
31/// # Safety model for unsafe methods
32///
33/// The allocation must be live and correctly tagged for the requested
34/// conversion in its owning VM. The handle does not root the object, and any
35/// collection or destruction that frees it invalidates every copy.
36pub struct GcObject {
37    raw: NonNull<RawGcObject>,
38}
39
40/// Closed conversion from a generic GC object to a known VM object kind.
41///
42/// # Safety
43///
44/// Implementations must use the exact GC tag and representation for `Self`.
45/// The source allocation must remain live and correctly tagged.
46#[allow(
47    clippy::missing_safety_doc,
48    reason = "the sole method uses the unsafe trait's closed conversion contract"
49)]
50pub(crate) unsafe trait GcHandle: Into<GcObject> {
51    unsafe fn from_gc_object(object: GcObject) -> Self;
52}
53
54#[allow(
55    clippy::missing_safety_doc,
56    reason = "GcObject's shared raw-handle contract is documented on GcObject"
57)]
58impl GcObject {
59    pub const unsafe fn from_raw(raw: NonNull<RawGcObject>) -> Self {
60        Self { raw }
61    }
62
63    /// `luaC_init`
64    pub unsafe fn init_header(&self, thread: &Thread, type_tag: u8) {
65        unsafe {
66            let raw = self.as_ptr().as_mut().unwrap_unchecked();
67            raw.memcat = thread.as_ptr().as_ref().unwrap_unchecked().active_memcat;
68            raw.marked = thread.global().white();
69            raw.tt = type_tag;
70        }
71    }
72
73    /// `gco2ts`
74    pub unsafe fn to_string(&self) -> TString {
75        debug_assert_eq!(
76            unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt },
77            LUA_TSTRING as u8
78        );
79        unsafe { TString::from_raw(self.raw.cast()) }
80    }
81
82    /// `gco2u`
83    pub unsafe fn to_userdata(&self) -> Userdata {
84        debug_assert_eq!(
85            unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt },
86            LUA_TUSERDATA as u8
87        );
88        unsafe { Userdata::from_raw(self.raw.cast()) }
89    }
90
91    /// `gco2cl`
92    pub unsafe fn to_closure(&self) -> Closure {
93        debug_assert_eq!(
94            unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt },
95            LUA_TFUNCTION as u8
96        );
97        unsafe { Closure::from_raw(self.raw.cast()) }
98    }
99
100    /// `gco2h`
101    pub unsafe fn to_table(&self) -> Table {
102        debug_assert_eq!(
103            unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt },
104            LUA_TTABLE as u8
105        );
106        unsafe { Table::from_raw(self.raw.cast()) }
107    }
108
109    /// `gco2p`
110    pub unsafe fn to_proto(&self) -> Proto {
111        debug_assert_eq!(
112            unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt },
113            LUA_TPROTO as u8
114        );
115        unsafe { Proto::from_raw(self.raw.cast()) }
116    }
117
118    /// `gco2uv`
119    pub unsafe fn to_upvalue(&self) -> UpVal {
120        debug_assert_eq!(
121            unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt },
122            LUA_TUPVALUE as u8
123        );
124        unsafe { UpVal::from_raw(self.raw.cast()) }
125    }
126
127    /// `gco2th`
128    pub unsafe fn to_state(&self) -> Thread {
129        debug_assert_eq!(
130            unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt },
131            LUA_TTHREAD as u8
132        );
133        unsafe { Thread::from_raw(self.raw.cast()) }
134    }
135
136    /// `gco2buf`
137    pub unsafe fn to_buffer(&self) -> Buffer {
138        debug_assert_eq!(
139            unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt },
140            LUA_TBUFFER as u8
141        );
142        unsafe { Buffer::from_raw(self.raw.cast()) }
143    }
144
145    /// `gco2class`
146    pub unsafe fn to_class(&self) -> Class {
147        debug_assert_eq!(
148            unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt },
149            LUA_TCLASS as u8
150        );
151        unsafe { Class::from_raw(self.raw.cast()) }
152    }
153
154    /// `gco2object`
155    pub unsafe fn to_object(&self) -> Object {
156        debug_assert_eq!(
157            unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt },
158            LUA_TOBJECT as u8
159        );
160        unsafe { Object::from_raw(self.raw.cast()) }
161    }
162}
163
164impl From<TValue> for GcObject {
165    fn from(value: TValue) -> Self {
166        value.gc_value()
167    }
168}
169
170impl From<TString> for GcObject {
171    fn from(value: TString) -> Self {
172        unsafe { Self::from_raw(NonNull::new_unchecked(value.as_ptr().cast())) }
173    }
174}
175
176impl From<Table> for GcObject {
177    fn from(value: Table) -> Self {
178        unsafe { Self::from_raw(NonNull::new_unchecked(value.as_ptr().cast())) }
179    }
180}
181
182impl From<LuaNode> for GcObject {
183    fn from(value: LuaNode) -> Self {
184        unsafe { Self::from_raw(NonNull::new_unchecked(value.as_ptr().cast())) }
185    }
186}
187
188impl From<Proto> for GcObject {
189    fn from(value: Proto) -> Self {
190        unsafe { Self::from_raw(NonNull::new_unchecked(value.as_ptr().cast())) }
191    }
192}
193
194impl From<Closure> for GcObject {
195    fn from(value: Closure) -> Self {
196        unsafe { Self::from_raw(NonNull::new_unchecked(value.as_ptr().cast())) }
197    }
198}
199
200impl From<UpVal> for GcObject {
201    fn from(value: UpVal) -> Self {
202        unsafe { Self::from_raw(NonNull::new_unchecked(value.as_ptr().cast())) }
203    }
204}
205
206impl From<Userdata> for GcObject {
207    fn from(value: Userdata) -> Self {
208        unsafe { Self::from_raw(NonNull::new_unchecked(value.as_ptr().cast())) }
209    }
210}
211
212impl From<Buffer> for GcObject {
213    fn from(value: Buffer) -> Self {
214        unsafe { Self::from_raw(NonNull::new_unchecked(value.as_ptr().cast())) }
215    }
216}
217
218impl From<Thread> for GcObject {
219    fn from(value: Thread) -> Self {
220        unsafe { Self::from_raw(NonNull::new_unchecked(value.as_ptr().cast())) }
221    }
222}
223
224impl From<&Thread> for GcObject {
225    fn from(value: &Thread) -> Self {
226        unsafe { Self::from_raw(NonNull::new_unchecked(value.as_ptr().cast())) }
227    }
228}
229
230impl From<Class> for GcObject {
231    fn from(value: Class) -> Self {
232        unsafe { Self::from_raw(NonNull::new_unchecked(value.as_ptr().cast())) }
233    }
234}
235
236impl From<Object> for GcObject {
237    fn from(value: Object) -> Self {
238        unsafe { Self::from_raw(NonNull::new_unchecked(value.as_ptr().cast())) }
239    }
240}
241
242unsafe impl GcHandle for TString {
243    unsafe fn from_gc_object(object: GcObject) -> Self {
244        unsafe { Self::from_raw(object.raw.cast()) }
245    }
246}
247
248unsafe impl GcHandle for Table {
249    unsafe fn from_gc_object(object: GcObject) -> Self {
250        unsafe { Self::from_raw(object.raw.cast()) }
251    }
252}
253
254unsafe impl GcHandle for Proto {
255    unsafe fn from_gc_object(object: GcObject) -> Self {
256        unsafe { Self::from_raw(object.raw.cast()) }
257    }
258}
259
260unsafe impl GcHandle for Closure {
261    unsafe fn from_gc_object(object: GcObject) -> Self {
262        unsafe { Self::from_raw(object.raw.cast()) }
263    }
264}
265
266unsafe impl GcHandle for UpVal {
267    unsafe fn from_gc_object(object: GcObject) -> Self {
268        unsafe { Self::from_raw(object.raw.cast()) }
269    }
270}
271
272unsafe impl GcHandle for Userdata {
273    unsafe fn from_gc_object(object: GcObject) -> Self {
274        unsafe { Self::from_raw(object.raw.cast()) }
275    }
276}
277
278unsafe impl GcHandle for Buffer {
279    unsafe fn from_gc_object(object: GcObject) -> Self {
280        unsafe { Self::from_raw(object.raw.cast()) }
281    }
282}
283
284unsafe impl GcHandle for Thread {
285    unsafe fn from_gc_object(object: GcObject) -> Self {
286        unsafe { Self::from_raw(object.raw.cast()) }
287    }
288}
289
290unsafe impl GcHandle for Class {
291    unsafe fn from_gc_object(object: GcObject) -> Self {
292        unsafe { Self::from_raw(object.raw.cast()) }
293    }
294}
295
296unsafe impl GcHandle for Object {
297    unsafe fn from_gc_object(object: GcObject) -> Self {
298        unsafe { Self::from_raw(object.raw.cast()) }
299    }
300}
301impl crate::handle::sealed::Sealed for GcObject {}
302impl RawHandle for GcObject {
303    type Raw = RawGcObject;
304
305    fn as_ptr(&self) -> *mut Self::Raw {
306        self.raw.as_ptr()
307    }
308}
309
310impl AsRef<GcObject> for GcObject {
311    fn as_ref(&self) -> &GcObject {
312        self
313    }
314}