Skip to main content

luau_vm/
value.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_EXTRA_SIZE, LUA_TBOOLEAN, LUA_TBUFFER, LUA_TCLASS, LUA_TFUNCTION, LUA_TINTEGER,
12    LUA_TLIGHTUSERDATA, LUA_TNIL, LUA_TNUMBER, LUA_TOBJECT, LUA_TPROTO, LUA_TSTRING, LUA_TTABLE,
13    LUA_TTHREAD, LUA_TUPVALUE, LUA_TUSERDATA, LUA_TVECTOR, LUA_VECTOR_SIZE,
14};
15use crate::userdata::Userdata;
16use crate::{Class, Object};
17
18use crate::gc::{GcObject, RawGcObject};
19
20#[derive(Clone, Copy)]
21#[repr(C)]
22pub union RawValue {
23    pub gc: *mut RawGcObject,
24    pub pointer: *mut (),
25    pub number: f64,
26    pub boolean: i32,
27    pub integer: i64,
28    pub vector: [f32; 2],
29}
30
31#[derive(Clone, Copy)]
32#[repr(C)]
33pub struct RawTValue {
34    pub value: RawValue,
35    pub extra: [i32; LUA_EXTRA_SIZE],
36    pub tt: i32,
37}
38
39impl RawTValue {
40    pub const fn nil() -> Self {
41        Self {
42            value: RawValue {
43                pointer: core::ptr::null_mut(),
44            },
45            extra: [0; LUA_EXTRA_SIZE],
46            tt: LUA_TNIL,
47        }
48    }
49
50    pub const fn number(value: f64) -> Self {
51        Self {
52            value: RawValue { number: value },
53            extra: [0; LUA_EXTRA_SIZE],
54            tt: LUA_TNUMBER,
55        }
56    }
57
58    pub fn string(value: TString) -> Self {
59        Self {
60            value: RawValue {
61                gc: GcObject::from(value).as_ptr(),
62            },
63            extra: [0; LUA_EXTRA_SIZE],
64            tt: LUA_TSTRING,
65        }
66    }
67
68    pub const fn light_userdata(pointer: *mut (), tag: i32) -> Self {
69        let mut extra = [0; LUA_EXTRA_SIZE];
70        extra[0] = tag;
71        Self {
72            value: RawValue { pointer },
73            extra,
74            tt: LUA_TLIGHTUSERDATA,
75        }
76    }
77}
78
79pub const RAW_TVALUE_NIL: RawTValue = RawTValue::nil();
80
81#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
82#[repr(transparent)]
83/// Nullable traversal position in a VM value array.
84///
85/// Unsafe access and navigation require a live owning array, an in-bounds
86/// non-null position where applicable, and cursors from the same allocation.
87/// Stack or array relocation invalidates every derived cursor.
88pub struct TValueCursor(*mut RawTValue);
89
90#[allow(
91    clippy::missing_safety_doc,
92    reason = "TValueCursor navigation is governed by its documented non-owning cursor contract"
93)]
94impl TValueCursor {
95    /// Returns the current traversal address, which may be null.
96    ///
97    /// Stack growth and relocation invalidate cursors into the old stack.
98    pub const fn as_ptr(&self) -> *mut RawTValue {
99        self.0
100    }
101
102    pub const fn from_ptr(raw: *mut RawTValue) -> Self {
103        Self(raw)
104    }
105
106    pub const fn is_null(&self) -> bool {
107        self.0.is_null()
108    }
109
110    pub unsafe fn is_nil_unchecked(&self) -> bool {
111        debug_assert!(!self.is_null());
112        unsafe { (*self.as_ptr()).tt == LUA_TNIL }
113    }
114
115    pub unsafe fn value_unchecked(&self) -> TValue {
116        debug_assert!(!self.is_null());
117        unsafe { TValue::from_raw(NonNull::new_unchecked(self.0)) }
118    }
119
120    pub fn value(&self) -> Option<TValue> {
121        NonNull::new(self.0).map(|raw| unsafe { TValue::from_raw(raw) })
122    }
123
124    pub unsafe fn add(self, count: usize) -> Self {
125        unsafe { Self::from_ptr(self.0.add(count)) }
126    }
127
128    pub unsafe fn sub(self, count: usize) -> Self {
129        unsafe { Self::from_ptr(self.0.sub(count)) }
130    }
131
132    pub unsafe fn offset(self, count: isize) -> Self {
133        unsafe { Self::from_ptr(self.0.offset(count)) }
134    }
135
136    pub unsafe fn offset_from(self, other: Self) -> isize {
137        unsafe { self.0.offset_from(other.0) }
138    }
139
140    pub fn addr_offset_from(self, other: Self) -> isize {
141        let byte_offset = self.0 as isize - other.0 as isize;
142        debug_assert_eq!(byte_offset % core::mem::size_of::<RawTValue>() as isize, 0);
143        byte_offset / core::mem::size_of::<RawTValue>() as isize
144    }
145}
146
147impl AsRef<TValueCursor> for TValueCursor {
148    fn as_ref(&self) -> &TValueCursor {
149        self
150    }
151}
152
153#[derive(Clone, Copy, PartialEq, Eq)]
154#[repr(transparent)]
155/// Non-owning view of a live tagged VM value slot.
156///
157/// # Safety model for unsafe constructors
158///
159/// The source slot must remain live for every use of the copied view. It must
160/// contain a valid VM tag/payload pair, and referenced collectable values must
161/// belong to the same VM and remain rooted as required by the caller.
162pub struct TValue {
163    raw: NonNull<RawTValue>,
164}
165
166#[repr(transparent)]
167pub struct NilObject(RawTValue);
168
169unsafe impl Sync for NilObject {}
170
171pub static LUA_O_NIL_OBJECT: NilObject = NilObject(RAW_TVALUE_NIL);
172#[allow(
173    clippy::missing_safety_doc,
174    reason = "TValue's shared raw-view contract is documented on TValue"
175)]
176impl TValue {
177    pub const unsafe fn from_raw(raw: NonNull<RawTValue>) -> Self {
178        Self { raw }
179    }
180
181    pub unsafe fn from_ref(raw: &RawTValue) -> Self {
182        Self {
183            raw: NonNull::from(raw),
184        }
185    }
186
187    pub unsafe fn from_mut(raw: &mut RawTValue) -> Self {
188        Self {
189            raw: NonNull::from(raw),
190        }
191    }
192
193    /// `ttisnil`
194    pub fn is_nil(&self) -> bool {
195        unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TNIL }
196    }
197
198    /// `ttisnumber`
199    pub fn is_number(&self) -> bool {
200        unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TNUMBER }
201    }
202
203    /// `ttisinteger`
204    pub fn is_integer(&self) -> bool {
205        unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TINTEGER }
206    }
207
208    /// `ttisstring`
209    pub fn is_string(&self) -> bool {
210        unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TSTRING }
211    }
212
213    /// `ttistable`
214    pub fn is_table(&self) -> bool {
215        unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TTABLE }
216    }
217
218    /// `ttisfunction`
219    pub fn is_function(&self) -> bool {
220        unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TFUNCTION }
221    }
222
223    /// `iscfunction`
224    pub fn is_native_function(&self) -> bool {
225        if !self.is_function() {
226            return false;
227        }
228
229        let closure = self.closure_value();
230        unsafe { closure.is_native() }
231    }
232
233    /// `isLfunction`
234    pub fn is_lua_function(&self) -> bool {
235        if !self.is_function() {
236            return false;
237        }
238
239        let closure = self.closure_value();
240        unsafe { closure.is_lua() }
241    }
242
243    /// `ttisboolean`
244    pub fn is_boolean(&self) -> bool {
245        unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TBOOLEAN }
246    }
247
248    /// `ttisuserdata`
249    pub fn is_userdata(&self) -> bool {
250        unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TUSERDATA }
251    }
252
253    /// `ttisthread`
254    pub fn is_thread(&self) -> bool {
255        unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TTHREAD }
256    }
257
258    /// `ttisbuffer`
259    pub fn is_buffer(&self) -> bool {
260        unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TBUFFER }
261    }
262
263    /// `ttislightuserdata`
264    pub fn is_light_userdata(&self) -> bool {
265        unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TLIGHTUSERDATA }
266    }
267
268    /// `ttisvector`
269    pub fn is_vector(&self) -> bool {
270        unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TVECTOR }
271    }
272
273    /// `ttisclass`
274    pub fn is_class(&self) -> bool {
275        unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TCLASS }
276    }
277
278    /// `ttisobject`
279    pub fn is_object(&self) -> bool {
280        unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TOBJECT }
281    }
282
283    /// `ttisupval`
284    pub fn is_upvalue(&self) -> bool {
285        unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TUPVALUE }
286    }
287
288    pub fn is_proto(&self) -> bool {
289        unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TPROTO }
290    }
291
292    /// `iscollectable`
293    pub fn is_collectable(&self) -> bool {
294        unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt >= LUA_TSTRING }
295    }
296
297    /// `ttype`
298    pub fn tt(&self) -> i32 {
299        unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt }
300    }
301
302    /// `l_isfalse`
303    pub fn is_false(&self) -> bool {
304        let value = unsafe { self.as_ptr().as_ref().unwrap_unchecked() };
305        value.tt == LUA_TNIL || (value.tt == LUA_TBOOLEAN && unsafe { value.value.boolean } == 0)
306    }
307
308    /// `gcvalue`
309    pub fn gc_value(&self) -> GcObject {
310        debug_assert!(self.is_collectable());
311        unsafe {
312            GcObject::from_raw(NonNull::new_unchecked(
313                self.as_ptr().as_ref().unwrap_unchecked().value.gc,
314            ))
315        }
316    }
317
318    /// `tsvalue`
319    pub fn string_value(&self) -> TString {
320        debug_assert!(self.is_string());
321        unsafe {
322            TString::from_raw(NonNull::new_unchecked(
323                self.as_ptr().as_ref().unwrap_unchecked().value.gc.cast(),
324            ))
325        }
326    }
327
328    /// `hvalue`
329    pub fn table_value(&self) -> Table {
330        debug_assert!(self.is_table());
331        unsafe {
332            Table::from_raw(NonNull::new_unchecked(
333                self.as_ptr().as_ref().unwrap_unchecked().value.gc.cast(),
334            ))
335        }
336    }
337
338    /// `uvalue`
339    pub fn userdata_value(&self) -> Userdata {
340        debug_assert!(self.is_userdata());
341        unsafe {
342            Userdata::from_raw(NonNull::new_unchecked(
343                self.as_ptr().as_ref().unwrap_unchecked().value.gc.cast(),
344            ))
345        }
346    }
347
348    /// `clvalue`
349    pub fn closure_value(&self) -> Closure {
350        debug_assert!(self.is_function());
351        unsafe {
352            Closure::from_raw(NonNull::new_unchecked(
353                self.as_ptr().as_ref().unwrap_unchecked().value.gc.cast(),
354            ))
355        }
356    }
357
358    /// `pvalue`
359    pub fn pointer_value(&self) -> *mut () {
360        debug_assert!(self.is_light_userdata());
361        unsafe { self.as_ptr().as_ref().unwrap_unchecked().value.pointer }
362    }
363
364    /// `nvalue`
365    pub fn number_value(&self) -> f64 {
366        debug_assert!(self.is_number());
367        unsafe { self.as_ptr().as_ref().unwrap_unchecked().value.number }
368    }
369
370    /// `lvalue`
371    pub fn integer_value(&self) -> i64 {
372        debug_assert!(self.is_integer());
373        unsafe { self.as_ptr().as_ref().unwrap_unchecked().value.integer }
374    }
375
376    /// `bvalue`
377    pub fn boolean_value(&self) -> i32 {
378        debug_assert!(self.is_boolean());
379        unsafe { self.as_ptr().as_ref().unwrap_unchecked().value.boolean }
380    }
381
382    /// `thvalue`
383    pub fn thread_value(&self) -> Thread {
384        debug_assert!(self.is_thread());
385        unsafe {
386            Thread::from_raw(NonNull::new_unchecked(
387                self.as_ptr().as_ref().unwrap_unchecked().value.gc.cast(),
388            ))
389        }
390    }
391
392    /// `bufvalue`
393    pub fn buffer_value(&self) -> Buffer {
394        debug_assert!(self.is_buffer());
395        unsafe {
396            Buffer::from_raw(NonNull::new_unchecked(
397                self.as_ptr().as_ref().unwrap_unchecked().value.gc.cast(),
398            ))
399        }
400    }
401
402    /// `upvalue`
403    pub fn upvalue_value(&self) -> UpVal {
404        debug_assert!(self.is_upvalue());
405        unsafe {
406            UpVal::from_raw(NonNull::new_unchecked(
407                self.as_ptr().as_ref().unwrap_unchecked().value.gc.cast(),
408            ))
409        }
410    }
411
412    /// `classvalue`
413    pub fn class_value(&self) -> Class {
414        debug_assert!(self.is_class());
415        unsafe {
416            Class::from_raw(NonNull::new_unchecked(
417                self.as_ptr().as_ref().unwrap_unchecked().value.gc.cast(),
418            ))
419        }
420    }
421
422    /// `objectvalue`
423    pub fn object_value(&self) -> Object {
424        debug_assert!(self.is_object());
425        unsafe {
426            Object::from_raw(NonNull::new_unchecked(
427                self.as_ptr().as_ref().unwrap_unchecked().value.gc.cast(),
428            ))
429        }
430    }
431
432    /// `pvalue`
433    pub fn proto_value(&self) -> Proto {
434        debug_assert!(self.is_proto());
435        unsafe { self.gc_value().to_proto() }
436    }
437
438    /// `lightuserdatatag`
439    pub fn light_userdata_tag(&self) -> i32 {
440        debug_assert!(self.is_light_userdata());
441        unsafe { self.as_ptr().as_ref().unwrap_unchecked().extra[0] }
442    }
443
444    /// `vvalue`
445    pub fn vector_value(&self) -> [f32; LUA_VECTOR_SIZE] {
446        debug_assert!(self.is_vector());
447        let vector = self.as_ptr().cast::<f32>();
448        #[cfg(not(feature = "vector4"))]
449        unsafe {
450            [*vector, *vector.add(1), *vector.add(2)]
451        }
452        #[cfg(feature = "vector4")]
453        unsafe {
454            [*vector, *vector.add(1), *vector.add(2), *vector.add(3)]
455        }
456    }
457
458    /// `setnilvalue`
459    pub fn set_nil(&self) {
460        unsafe {
461            self.as_ptr().as_mut().unwrap_unchecked().tt = LUA_TNIL;
462        }
463    }
464
465    /// `setnvalue`
466    pub fn set_number(&self, value: f64) {
467        unsafe {
468            let raw = self.as_ptr().as_mut().unwrap_unchecked();
469            raw.value = RawValue { number: value };
470            raw.tt = LUA_TNUMBER;
471        }
472    }
473
474    /// `setlvalue`
475    pub fn set_integer(&self, value: i64) {
476        unsafe {
477            let raw = self.as_ptr().as_mut().unwrap_unchecked();
478            raw.value = RawValue { integer: value };
479            raw.tt = LUA_TINTEGER;
480        }
481    }
482
483    /// `setbvalue`
484    pub fn set_boolean(&self, value: i32) {
485        unsafe {
486            let raw = self.as_ptr().as_mut().unwrap_unchecked();
487            raw.value = RawValue { boolean: value };
488            raw.tt = LUA_TBOOLEAN;
489        }
490    }
491
492    /// `setpvalue`
493    pub fn set_light_userdata(&self, pointer: *mut (), tag: i32) {
494        unsafe {
495            let raw = self.as_ptr().as_mut().unwrap_unchecked();
496            raw.value = RawValue { pointer };
497            raw.extra[0] = tag;
498            raw.tt = LUA_TLIGHTUSERDATA;
499        }
500    }
501
502    /// `setsvalue`
503    pub fn set_string_value(&self, value: TString) {
504        unsafe {
505            let raw = self.as_ptr().as_mut().unwrap_unchecked();
506            raw.value = RawValue {
507                gc: GcObject::from(value).as_ptr(),
508            };
509            raw.tt = LUA_TSTRING;
510        }
511    }
512
513    /// `setvvalue`
514    pub fn set_vector(&self, value: [f32; LUA_VECTOR_SIZE]) {
515        let vector = self.as_ptr().cast::<f32>();
516        unsafe {
517            *vector = value[0];
518            *vector.add(1) = value[1];
519            *vector.add(2) = value[2];
520        }
521        #[cfg(feature = "vector4")]
522        unsafe {
523            *vector.add(3) = value[3];
524        }
525        unsafe {
526            (*self.as_ptr()).tt = LUA_TVECTOR;
527        }
528    }
529
530    /// `setuvalue`
531    pub fn set_userdata_value(&self, value: Userdata) {
532        unsafe {
533            let raw = self.as_ptr().as_mut().unwrap_unchecked();
534            raw.value = RawValue {
535                gc: GcObject::from(value).as_ptr(),
536            };
537            raw.tt = LUA_TUSERDATA;
538        }
539    }
540
541    /// `setthvalue`
542    pub fn set_thread_value(&self, value: &Thread) {
543        unsafe {
544            let raw = self.as_ptr().as_mut().unwrap_unchecked();
545            raw.value = RawValue {
546                gc: GcObject::from(value).as_ptr(),
547            };
548            raw.tt = LUA_TTHREAD;
549        }
550    }
551
552    /// `setbufvalue`
553    pub fn set_buffer_value(&self, value: Buffer) {
554        unsafe {
555            let raw = self.as_ptr().as_mut().unwrap_unchecked();
556            raw.value = RawValue {
557                gc: GcObject::from(value).as_ptr(),
558            };
559            raw.tt = LUA_TBUFFER;
560        }
561    }
562
563    /// `setclvalue`
564    pub fn set_closure_value(&self, value: Closure) {
565        unsafe {
566            let raw = self.as_ptr().as_mut().unwrap_unchecked();
567            raw.value = RawValue {
568                gc: GcObject::from(value).as_ptr(),
569            };
570            raw.tt = LUA_TFUNCTION;
571        }
572    }
573
574    /// `sethvalue`
575    pub fn set_table_value(&self, value: Table) {
576        unsafe {
577            let raw = self.as_ptr().as_mut().unwrap_unchecked();
578            raw.value = RawValue {
579                gc: GcObject::from(value).as_ptr(),
580            };
581            raw.tt = LUA_TTABLE;
582        }
583    }
584
585    /// `setptvalue`
586    pub fn set_proto_value(&self, value: Proto) {
587        unsafe {
588            let raw = self.as_ptr().as_mut().unwrap_unchecked();
589            raw.value = RawValue {
590                gc: GcObject::from(value).as_ptr(),
591            };
592            raw.tt = LUA_TPROTO;
593        }
594    }
595
596    /// `setupvalue`
597    pub fn set_upvalue_value(&self, value: UpVal) {
598        unsafe {
599            let raw = self.as_ptr().as_mut().unwrap_unchecked();
600            raw.value = RawValue {
601                gc: GcObject::from(value).as_ptr(),
602            };
603            raw.tt = LUA_TUPVALUE;
604        }
605    }
606
607    /// `setclassvalue`
608    pub fn set_class_value(&self, value: Class) {
609        unsafe {
610            let raw = self.as_ptr().as_mut().unwrap_unchecked();
611            raw.value = RawValue {
612                gc: GcObject::from(value).as_ptr(),
613            };
614            raw.tt = LUA_TCLASS;
615        }
616    }
617
618    /// `setobjectvalue`
619    pub fn set_object_value(&self, value: Object) {
620        unsafe {
621            let raw = self.as_ptr().as_mut().unwrap_unchecked();
622            raw.value = RawValue {
623                gc: GcObject::from(value).as_ptr(),
624            };
625            raw.tt = LUA_TOBJECT;
626        }
627    }
628
629    /// `setobj`
630    pub fn set_obj(&self, other: impl Into<TValue>) {
631        let other = other.into();
632        unsafe {
633            core::ptr::copy(other.as_ptr(), self.as_ptr(), 1);
634        }
635    }
636
637    /// `luaO_rawequalObj`
638    pub fn raw_equal(&self, other: impl Into<TValue>) -> bool {
639        let other = other.into();
640        if self.tt() != other.tt() {
641            return false;
642        }
643
644        match self.tt() {
645            x if x == LUA_TNIL => true,
646            x if x == LUA_TNUMBER => self.number_value() == other.number_value(),
647            x if x == LUA_TINTEGER => self.integer_value() == other.integer_value(),
648            x if x == LUA_TVECTOR => self.vector_value() == other.vector_value(),
649            x if x == LUA_TBOOLEAN => self.boolean_value() == other.boolean_value(),
650            x if x == LUA_TLIGHTUSERDATA => {
651                self.pointer_value() == other.pointer_value()
652                    && self.light_userdata_tag() == other.light_userdata_tag()
653            }
654            _ => {
655                debug_assert!(self.is_collectable());
656                self.gc_value() == other.gc_value()
657            }
658        }
659    }
660}
661
662/// `luaO_nilobject`
663pub fn nil_object() -> TValue {
664    unsafe { TValue::from_ref(&LUA_O_NIL_OBJECT.0) }
665}
666impl crate::handle::sealed::Sealed for TValue {}
667impl RawHandle for TValue {
668    type Raw = RawTValue;
669
670    fn as_ptr(&self) -> *mut Self::Raw {
671        self.raw.as_ptr()
672    }
673}
674
675impl AsRef<TValue> for TValue {
676    fn as_ref(&self) -> &TValue {
677        self
678    }
679}