Skip to main content

luau_vm/thread/
stack.rs

1use core::ptr;
2
3use luau_common::{BStr, ByteSlice, flags};
4
5use super::{
6    LUA_ENVIRON_INDEX, LUA_GLOBALS_INDEX, LUA_REGISTRY_INDEX, LUA_TNONE, LUAI_MAX_C_STACK, Thread,
7    is_pseudo,
8};
9use crate::buffer::BufferRuntime;
10use crate::call::{ProtectedCall, ThreadStack};
11use crate::debug::DebugRuntime;
12use crate::gc::{GcBarrier, GcRuntime};
13use crate::handle::RawHandle;
14use crate::handle::sealed::Sealed;
15use crate::state::ThreadState;
16use crate::string::{LuaString, StringFormatting, StringRuntime, TString};
17use crate::types::{
18    LUA_T_COUNT, LUA_TBOOLEAN, LUA_TBUFFER, LUA_TCLASS, LUA_TFUNCTION, LUA_TLIGHTUSERDATA,
19    LUA_TNIL, LUA_TNUMBER, LUA_TOBJECT, LUA_TSTRING, LUA_TTABLE, LUA_TTHREAD, LUA_TUSERDATA,
20    LUA_TVECTOR,
21};
22use crate::value::{RAW_TVALUE_NIL, TValue, TValueCursor, nil_object};
23use crate::vm::{VmConversions, VmOperations};
24use crate::{Class, Table, VmErrorResult, VmResult};
25
26// Raw stack capability
27/// Unstable raw stack-address conversion capability.
28///
29/// # Safety
30///
31/// Indices must be valid for this thread under the corresponding Luau stack
32/// rule. Returned views and cursors borrow no storage and are invalidated by
33/// stack relocation, stack mutation, reset, close, or VM destruction.
34#[allow(
35    clippy::missing_safety_doc,
36    reason = "all methods share the capability-level safety contract"
37)]
38pub trait RawStackAccess: Sealed {
39    /// `luaA_toobject`
40    unsafe fn to_object(&self, index: i32) -> Option<TValue>;
41
42    /// `luaA_pushvalue`
43    unsafe fn push_value_internal(&self, value: TValue) -> VmErrorResult;
44
45    unsafe fn push_table(&self, table: Table) -> VmErrorResult;
46
47    /// `luaA_pushclass`
48    unsafe fn push_class(&self, class: Class) -> VmErrorResult;
49}
50
51// Stack addressing and movement
52impl RawStackAccess for Thread {
53    /// `luaA_toobject`
54    unsafe fn to_object(&self, index: i32) -> Option<TValue> {
55        let object = unsafe { self.index_to_addr(index) };
56        if object == nil_object() {
57            None
58        } else {
59            Some(object)
60        }
61    }
62
63    /// `luaA_pushvalue`
64    unsafe fn push_value_internal(&self, value: TValue) -> VmErrorResult {
65        unsafe {
66            self.ensure_stack(self, 1)?;
67            let top = self.stack_top();
68            top.value_unchecked().set_obj(value);
69            debug_assert!(top.offset_from(self.current_call_info().top()) < 0);
70            self.set_stack_top(top.add(1));
71        }
72        Ok(())
73    }
74
75    unsafe fn push_table(&self, table: Table) -> VmErrorResult {
76        unsafe {
77            self.ensure_stack(self, 1)?;
78            let top = self.stack_top();
79            top.value_unchecked().set_table_value(table);
80            debug_assert!(top.offset_from(self.current_call_info().top()) < 0);
81            self.set_stack_top(top.add(1));
82        }
83        Ok(())
84    }
85
86    /// `luaA_pushclass`
87    unsafe fn push_class(&self, class: Class) -> VmErrorResult {
88        unsafe {
89            self.ensure_stack(self, 1)?;
90            let top = self.stack_top();
91            top.value_unchecked().set_class_value(class);
92            debug_assert!(top.offset_from(self.current_call_info().top()) < 0);
93            self.set_stack_top(top.add(1));
94        }
95        Ok(())
96    }
97}
98
99impl Thread {
100    /// `pseudo2addr`
101    unsafe fn pseudo_to_addr(&self, index: i32) -> TValue {
102        debug_assert!(is_pseudo(index));
103
104        unsafe {
105            let global = self.global();
106
107            match index {
108                LUA_REGISTRY_INDEX => global.registry(),
109                LUA_ENVIRON_INDEX => {
110                    let pseudo_temp = global.pseudo_temp();
111                    pseudo_temp.set_table_value(self.current_env());
112                    pseudo_temp
113                }
114                LUA_GLOBALS_INDEX => {
115                    let pseudo_temp = global.pseudo_temp();
116                    pseudo_temp.set_table_value(self.globals());
117                    pseudo_temp
118                }
119                _ => {
120                    let closure = self.current_function();
121                    let upvalue_index = (LUA_GLOBALS_INDEX - index) as usize;
122
123                    if upvalue_index
124                        <= closure.as_ptr().as_ref().unwrap_unchecked().n_upvalues as usize
125                    {
126                        closure.native_upvalue(upvalue_index - 1)
127                    } else {
128                        nil_object()
129                    }
130                }
131            }
132        }
133    }
134
135    pub(crate) unsafe fn stack_index_cursor(&self, index: i32) -> Option<TValueCursor> {
136        debug_assert!(index != 0);
137
138        unsafe {
139            if index > 0 {
140                let object = self.stack_base().add((index - 1) as usize);
141                debug_assert!(
142                    index
143                        <= self
144                            .current_call_info()
145                            .top()
146                            .offset_from(self.stack_base()) as i32
147                );
148                Some(object)
149            } else if index > LUA_REGISTRY_INDEX {
150                debug_assert!(-index <= self.stack_top().offset_from(self.stack_base()) as i32);
151                Some(self.stack_top().offset(index as isize))
152            } else {
153                None
154            }
155        }
156    }
157
158    pub(crate) unsafe fn stack_index_to_cursor(&self, index: i32) -> TValueCursor {
159        debug_assert!(!is_pseudo(index));
160        unsafe { self.stack_index_cursor(index).unwrap_unchecked() }
161    }
162
163    /// `index2addr`
164    pub(crate) unsafe fn index_to_addr(&self, index: i32) -> TValue {
165        unsafe {
166            if let Some(object) = self.stack_index_cursor(index) {
167                if index > 0 && object.offset_from(self.stack_top()) >= 0 {
168                    nil_object()
169                } else {
170                    object.value_unchecked()
171                }
172            } else {
173                self.pseudo_to_addr(index)
174            }
175        }
176    }
177
178    /// `ensure_stack_impl`
179    pub(crate) unsafe fn ensure_stack(&self, error_thread: &Thread, size: i32) -> VmErrorResult {
180        unsafe {
181            let available = self.current_call_info().top().offset_from(self.stack_top()) as i32;
182            if flags::LuauAutoStack.get()
183                && size > 0
184                && size > available
185                && self.check_stack(size) == 0
186            {
187                return crate::run_error!(error_thread, "stack overflow");
188            }
189        }
190        Ok(())
191    }
192
193    /// `lua_absindex`
194    pub unsafe fn abs_index(&self, index: i32) -> i32 {
195        unsafe {
196            debug_assert!(
197                (index > 0 && index <= self.get_top())
198                    || (index < 0 && -index <= self.get_top())
199                    || is_pseudo(index)
200            );
201            if index > 0 || is_pseudo(index) {
202                index
203            } else {
204                self.get_top() + index + 1
205            }
206        }
207    }
208
209    /// `lua_gettop`
210    pub unsafe fn get_top(&self) -> i32 {
211        unsafe { self.stack_top().offset_from(self.stack_base()) as i32 }
212    }
213
214    /// `lua_checkstack`
215    pub unsafe fn check_stack(&self, size: i32) -> i32 {
216        debug_assert!(size >= 0);
217        unsafe {
218            let top_offset = self.stack_top().offset_from(self.stack_base()) as i32;
219            if size > LUAI_MAX_C_STACK || top_offset + size > LUAI_MAX_C_STACK {
220                return 0;
221            }
222
223            if size > 0 {
224                if self.stack_limit_reached(size) {
225                    #[repr(C)]
226                    struct GrowStackContext {
227                        size: i32,
228                    }
229
230                    unsafe fn grow_stack_callback(
231                        thread: &Thread,
232                        context: &mut GrowStackContext,
233                    ) -> VmResult {
234                        unsafe { thread.grow_stack(context.size) }?;
235                        Ok(())
236                    }
237
238                    let mut context = GrowStackContext { size };
239                    if self
240                        .raw_run_protected(grow_stack_callback, &mut context)
241                        .is_err()
242                    {
243                        return 0;
244                    }
245                }
246
247                let pointer = self.stack_top().add(size as usize);
248                self.expand_stack_limit(pointer);
249            }
250
251            1
252        }
253    }
254
255    /// `lua_rawcheckstack`
256    pub unsafe fn raw_check_stack(&self, size: i32) -> VmErrorResult {
257        debug_assert!(size >= 0);
258        unsafe {
259            self.check_stack_internal(size)?;
260            let pointer = self.stack_top().add(size as usize);
261            self.expand_stack_limit(pointer);
262        }
263        Ok(())
264    }
265
266    /// `lua_settop`
267    pub unsafe fn set_top(&self, index: i32) -> VmErrorResult {
268        debug_assert!(index >= 0);
269        unsafe {
270            let base = self.stack_base();
271            let mut top = self.stack_top();
272            self.ensure_stack(self, index - top.offset_from(base) as i32)?;
273            debug_assert!(index <= self.stack_last().offset_from(base) as i32);
274            let target = base.add(index as usize);
275            while top < target {
276                top.value_unchecked().set_nil();
277                top = top.add(1);
278            }
279            self.set_stack_top(target);
280        }
281        Ok(())
282    }
283
284    /// Restores the stack top to a previous absolute top.
285    pub unsafe fn restore_top(&self, top: i32) {
286        unsafe {
287            let current_top = self.get_top();
288            debug_assert!((0..=current_top).contains(&top));
289            self.set_stack_top(self.stack_base().add(top as usize));
290        }
291    }
292
293    /// `lua_pop`
294    pub unsafe fn pop(&self, count: i32) {
295        debug_assert!(count >= 0);
296        unsafe {
297            let current_top = self.get_top();
298            debug_assert!(count <= current_top);
299            self.restore_top(current_top - count);
300        }
301    }
302
303    /// `lua_pushvalue`
304    pub unsafe fn push_value(&self, index: i32) -> VmErrorResult {
305        unsafe {
306            self.thread_barrier();
307            self.ensure_stack(self, 1)?;
308
309            let top = self.stack_top();
310            let object = self.index_to_addr(index);
311            top.value_unchecked().set_obj(object);
312            debug_assert!(top.offset_from(self.current_call_info().top()) < 0);
313            self.set_stack_top(top.add(1));
314        }
315        Ok(())
316    }
317
318    /// `lua_remove`
319    pub unsafe fn remove(&self, index: i32) {
320        unsafe {
321            let object_cursor = self.stack_index_to_cursor(index);
322            let next = object_cursor.add(1);
323            ptr::copy(
324                next.as_ptr(),
325                object_cursor.as_ptr(),
326                self.stack_top().offset_from(next) as usize,
327            );
328            self.set_stack_top(self.stack_top().sub(1));
329        }
330    }
331
332    /// `lua_insert`
333    pub unsafe fn insert(&self, index: i32) {
334        unsafe {
335            self.thread_barrier();
336
337            let object_cursor = self.stack_index_to_cursor(index);
338
339            let top = self.stack_top().as_ptr();
340            let mut slot = top;
341            while slot > object_cursor.as_ptr() {
342                ptr::copy(slot.sub(1), slot, 1);
343                slot = slot.sub(1);
344            }
345
346            ptr::copy(top, object_cursor.as_ptr(), 1);
347        }
348    }
349
350    /// `lua_replace`
351    pub unsafe fn replace(&self, index: i32) {
352        unsafe {
353            let top = self.stack_top();
354            debug_assert!(top.offset_from(self.stack_base()) > 0);
355
356            self.thread_barrier();
357
358            let object = self.index_to_addr(index);
359            debug_assert!(object != nil_object());
360
361            let value = top.sub(1);
362            if index == LUA_ENVIRON_INDEX {
363                debug_assert!(self.current_call_info() != self.base_call_info());
364                debug_assert!(value.value_unchecked().is_table());
365
366                let function = self.current_function();
367                function.set_env(value.value_unchecked().table_value());
368                self.barrier_value(function.into(), value.value_unchecked());
369            } else if index == LUA_GLOBALS_INDEX {
370                debug_assert!(value.value_unchecked().is_table());
371                self.set_globals(value.value_unchecked().table_value());
372            } else {
373                object.set_obj(value.value_unchecked());
374
375                if index < LUA_GLOBALS_INDEX {
376                    let function = self.current_function();
377                    self.barrier_value(function.into(), value.value_unchecked());
378                }
379            }
380
381            self.set_stack_top(top.sub(1));
382        }
383    }
384
385    /// `lua_xmove`
386    pub unsafe fn x_move(&self, to: &Thread, count: i32) -> VmErrorResult {
387        unsafe {
388            debug_assert!(count >= 0);
389
390            if *self == *to {
391                return Ok(());
392            }
393
394            debug_assert!(count <= self.stack_top().offset_from(self.stack_base()) as i32);
395            debug_assert!(self.global() == to.global());
396
397            to.thread_barrier();
398            to.ensure_stack(self, count)?;
399
400            let to_top = to.stack_top();
401            let from_top = self.stack_top().sub(count as usize);
402            for index in 0..count as usize {
403                to_top
404                    .add(index)
405                    .value_unchecked()
406                    .set_obj(from_top.add(index).value_unchecked());
407            }
408
409            self.set_stack_top(from_top);
410            to.set_stack_top(to_top.add(count as usize));
411        }
412        Ok(())
413    }
414
415    /// `lua_xpush`
416    pub unsafe fn x_push(&self, to: &Thread, index: i32) -> VmErrorResult {
417        unsafe {
418            debug_assert!(self.global() == to.global());
419
420            to.thread_barrier();
421            to.ensure_stack(self, 1)?;
422
423            let top = to.stack_top();
424            let object = self.index_to_addr(index);
425            top.value_unchecked().set_obj(object);
426            debug_assert!(top < to.current_call_info().top());
427            to.set_stack_top(top.add(1));
428        }
429        Ok(())
430    }
431}
432
433// Value pushes
434impl Thread {
435    /// `lua_pushnil`
436    pub unsafe fn push_nil(&self) -> VmErrorResult {
437        unsafe {
438            self.ensure_stack(self, 1)?;
439            let top = self.stack_top();
440            debug_assert!(top < self.current_call_info().top());
441            top.value_unchecked().set_nil();
442            self.set_stack_top(top.add(1));
443        }
444        Ok(())
445    }
446
447    /// `lua_pushnumber`
448    pub unsafe fn push_number(&self, value: f64) -> VmErrorResult {
449        unsafe {
450            self.ensure_stack(self, 1)?;
451            let top = self.stack_top();
452            debug_assert!(top < self.current_call_info().top());
453            top.value_unchecked().set_number(value);
454            self.set_stack_top(top.add(1));
455        }
456        Ok(())
457    }
458
459    /// `lua_pushinteger`
460    pub unsafe fn push_integer(&self, value: i32) -> VmErrorResult {
461        unsafe {
462            self.ensure_stack(self, 1)?;
463            let top = self.stack_top();
464            debug_assert!(top < self.current_call_info().top());
465            top.value_unchecked().set_number(value as f64);
466            self.set_stack_top(top.add(1));
467        }
468        Ok(())
469    }
470
471    /// `lua_pushinteger64`
472    pub unsafe fn push_integer64(&self, value: i64) -> VmErrorResult {
473        unsafe {
474            self.ensure_stack(self, 1)?;
475            let top = self.stack_top();
476            debug_assert!(top < self.current_call_info().top());
477            top.value_unchecked().set_integer(value);
478            self.set_stack_top(top.add(1));
479        }
480        Ok(())
481    }
482
483    /// `lua_pushunsigned`
484    pub unsafe fn push_unsigned(&self, value: u32) -> VmErrorResult {
485        unsafe {
486            self.ensure_stack(self, 1)?;
487            let top = self.stack_top();
488            debug_assert!(top < self.current_call_info().top());
489            top.value_unchecked().set_number(value as f64);
490            self.set_stack_top(top.add(1));
491        }
492        Ok(())
493    }
494
495    /// `lua_pushvector`
496    pub unsafe fn push_vector(
497        &self,
498        components: [f32; crate::types::LUA_VECTOR_SIZE],
499    ) -> VmErrorResult {
500        unsafe {
501            self.ensure_stack(self, 1)?;
502            let top = self.stack_top();
503            debug_assert!(top < self.current_call_info().top());
504            top.value_unchecked().set_vector(components);
505            self.set_stack_top(top.add(1));
506        }
507        Ok(())
508    }
509
510    /// `lua_pushboolean`
511    pub unsafe fn push_boolean(&self, value: i32) -> VmErrorResult {
512        unsafe {
513            self.ensure_stack(self, 1)?;
514            let top = self.stack_top();
515            debug_assert!(top < self.current_call_info().top());
516            top.value_unchecked().set_boolean(i32::from(value != 0));
517            self.set_stack_top(top.add(1));
518        }
519        Ok(())
520    }
521
522    /// `lua_pushlightuserdatatagged`
523    pub unsafe fn push_light_userdata_tagged(&self, pointer: *mut (), tag: i32) -> VmErrorResult {
524        unsafe {
525            self.ensure_stack(self, 1)?;
526            let top = self.stack_top();
527            debug_assert!(top < self.current_call_info().top());
528            top.value_unchecked().set_light_userdata(pointer, tag);
529            self.set_stack_top(top.add(1));
530        }
531        Ok(())
532    }
533
534    /// `lua_pushlightuserdata`
535    pub unsafe fn push_light_userdata(&self, pointer: *mut ()) -> VmErrorResult {
536        unsafe { self.push_light_userdata_tagged(pointer, 0) }
537    }
538
539    /// `lua_pushlstring`
540    pub unsafe fn push_string(&self, bytes: impl AsRef<[u8]>) -> VmErrorResult {
541        let bytes = bytes.as_ref();
542        unsafe {
543            self.check_gc()?;
544            self.thread_barrier();
545            self.ensure_stack(self, 1)?;
546
547            let interned = self.intern_string(bytes.as_bstr())?;
548            let top = self.stack_top();
549            top.value_unchecked().set_string_value(interned);
550            debug_assert!(top < self.current_call_info().top());
551            self.set_stack_top(top.add(1));
552        }
553        Ok(())
554    }
555
556    /// `lua_pushstring`
557    pub unsafe fn push_optional_string(&self, bytes: Option<impl AsRef<[u8]>>) -> VmErrorResult {
558        if let Some(bytes) = bytes {
559            unsafe { self.push_string(bytes) }
560        } else {
561            unsafe { self.push_nil() }
562        }
563    }
564
565    /// `lua_pushvfstring`
566    pub unsafe fn push_vfstring<'a>(
567        &self,
568        format: &str,
569        args: impl AsMut<[luau_printf::Arg<'a>]>,
570    ) -> VmErrorResult<LuaString> {
571        unsafe {
572            self.check_gc()?;
573            self.thread_barrier();
574            self.push_vfstring_internal(format, args)
575        }
576    }
577
578    /// `lua_pushfstring`
579    pub unsafe fn push_fstring<'a>(
580        &self,
581        format: &str,
582        args: impl AsMut<[luau_printf::Arg<'a>]>,
583    ) -> VmErrorResult<LuaString> {
584        unsafe {
585            self.check_gc()?;
586            self.thread_barrier();
587            self.push_fstring_internal(format, args)
588        }
589    }
590}
591
592// Value queries and conversions
593impl Thread {
594    /// Borrows bytes from an interned string owned by this thread's VM.
595    ///
596    /// The result is tied to the thread borrow rather than to the copied raw
597    /// string handle. Callers of the surrounding unsafe API remain
598    /// responsible for preventing collection while the borrow is live.
599    unsafe fn borrow_interned_string(&self, string: TString) -> &BStr {
600        unsafe {
601            let raw = string.as_ptr().as_ref().unwrap_unchecked();
602            core::slice::from_raw_parts(string.data_ptr(), raw.len as usize).as_bstr()
603        }
604    }
605
606    /// `lua_isnumber`
607    pub unsafe fn is_number(&self, index: i32) -> i32 {
608        unsafe {
609            let object = self.index_to_addr(index);
610            let mut converted = RAW_TVALUE_NIL;
611            let converted = TValue::from_mut(&mut converted);
612            self.to_number_internal(object, converted).is_some() as i32
613        }
614    }
615
616    /// `lua_isstring`
617    pub unsafe fn is_string(&self, index: i32) -> i32 {
618        let tag = unsafe { self.type_of(index) };
619        (tag == LUA_TSTRING || tag == LUA_TNUMBER) as i32
620    }
621
622    /// `lua_islightuserdata`
623    pub unsafe fn is_light_userdata(&self, index: i32) -> i32 {
624        (unsafe { self.type_of(index) } == LUA_TLIGHTUSERDATA) as i32
625    }
626
627    /// `lua_isfunction`
628    pub unsafe fn is_function(&self, index: i32) -> i32 {
629        (unsafe { self.type_of(index) } == LUA_TFUNCTION) as i32
630    }
631
632    /// `lua_istable`
633    pub unsafe fn is_table(&self, index: i32) -> i32 {
634        (unsafe { self.type_of(index) } == LUA_TTABLE) as i32
635    }
636
637    /// `lua_isnil`
638    pub unsafe fn is_nil(&self, index: i32) -> i32 {
639        (unsafe { self.type_of(index) } == LUA_TNIL) as i32
640    }
641
642    /// `lua_isboolean`
643    pub unsafe fn is_boolean(&self, index: i32) -> i32 {
644        (unsafe { self.type_of(index) } == LUA_TBOOLEAN) as i32
645    }
646
647    /// `lua_isvector`
648    pub unsafe fn is_vector(&self, index: i32) -> i32 {
649        (unsafe { self.type_of(index) } == LUA_TVECTOR) as i32
650    }
651
652    /// `lua_isthread`
653    pub unsafe fn is_thread(&self, index: i32) -> i32 {
654        (unsafe { self.type_of(index) } == LUA_TTHREAD) as i32
655    }
656
657    /// `lua_isbuffer`
658    pub unsafe fn is_buffer(&self, index: i32) -> i32 {
659        (unsafe { self.type_of(index) } == LUA_TBUFFER) as i32
660    }
661
662    /// `lua_isnone`
663    pub unsafe fn is_none(&self, index: i32) -> i32 {
664        (unsafe { self.type_of(index) } == LUA_TNONE) as i32
665    }
666
667    /// `lua_isnoneornil`
668    pub unsafe fn is_none_or_nil(&self, index: i32) -> i32 {
669        (unsafe { self.type_of(index) } <= LUA_TNIL) as i32
670    }
671
672    /// `lua_isclass`
673    pub unsafe fn is_class(&self, index: i32) -> i32 {
674        (unsafe { self.type_of(index) } == LUA_TCLASS) as i32
675    }
676
677    /// `lua_isobject`
678    pub unsafe fn is_object(&self, index: i32) -> i32 {
679        (unsafe { self.type_of(index) } == LUA_TOBJECT) as i32
680    }
681
682    /// `lua_isinteger64`
683    pub unsafe fn is_integer64(&self, index: i32) -> i32 {
684        let object = unsafe { self.index_to_addr(index) };
685        (object != nil_object() && object.is_integer()) as i32
686    }
687
688    /// `lua_iscfunction`
689    pub unsafe fn is_native_function(&self, index: i32) -> i32 {
690        let object = unsafe { self.index_to_addr(index) };
691        (object != nil_object()
692            && object.is_function()
693            && unsafe { object.closure_value().is_native() }) as i32
694    }
695
696    /// `lua_isLfunction`
697    pub unsafe fn is_lua_function(&self, index: i32) -> i32 {
698        let object = unsafe { self.index_to_addr(index) };
699        (object != nil_object()
700            && object.is_function()
701            && unsafe { object.closure_value().is_lua() }) as i32
702    }
703
704    /// `lua_isuserdata`
705    pub unsafe fn is_userdata(&self, index: i32) -> i32 {
706        let object = unsafe { self.index_to_addr(index) };
707        (object != nil_object() && (object.is_userdata() || object.is_light_userdata())) as i32
708    }
709
710    /// `lua_type`
711    pub unsafe fn type_of(&self, index: i32) -> i32 {
712        let object = unsafe { self.index_to_addr(index) };
713        if object == nil_object() {
714            LUA_TNONE
715        } else {
716            object.tt()
717        }
718    }
719
720    /// `lua_typename`
721    pub unsafe fn type_name(&self, tag: i32) -> LuaString {
722        debug_assert!((LUA_TNONE..LUA_T_COUNT as i32).contains(&tag));
723
724        if tag == LUA_TNONE {
725            LuaString::from_static(b"no value".as_bstr())
726        } else {
727            LuaString::from_interned(unsafe { self.global().type_name(tag as usize) })
728        }
729    }
730
731    /// `lua_equal`
732    pub unsafe fn equal(&self, left: i32, right: i32) -> VmResult<i32> {
733        unsafe {
734            let left_object = self.index_to_addr(left);
735            let right_object = self.index_to_addr(right);
736
737            if left_object == nil_object()
738                || right_object == nil_object()
739                || left_object.tt() != right_object.tt()
740            {
741                Ok(0)
742            } else {
743                self.equal_value(left_object, right_object)
744            }
745        }
746    }
747
748    /// `lua_rawequal`
749    pub unsafe fn raw_equal(&self, left: i32, right: i32) -> i32 {
750        let left_object = unsafe { self.index_to_addr(left) };
751        let right_object = unsafe { self.index_to_addr(right) };
752
753        if left_object == nil_object() || right_object == nil_object() {
754            0
755        } else {
756            left_object.raw_equal(right_object) as i32
757        }
758    }
759
760    /// `lua_lessthan`
761    pub unsafe fn less_than(&self, left: i32, right: i32) -> VmResult<i32> {
762        unsafe {
763            let left_object = self.index_to_addr(left);
764            let right_object = self.index_to_addr(right);
765
766            if left_object == nil_object() || right_object == nil_object() {
767                Ok(0)
768            } else {
769                self.less_than_internal(left_object, right_object)
770            }
771        }
772    }
773
774    /// `lua_tonumberx`
775    pub unsafe fn to_number(&self, index: i32) -> Option<f64> {
776        unsafe {
777            let object = self.index_to_addr(index);
778            let mut converted = RAW_TVALUE_NIL;
779            let converted = TValue::from_mut(&mut converted);
780            self.to_number_internal(object, converted)
781                .map(|value| value.number_value())
782        }
783    }
784
785    /// `lua_tointegerx`
786    pub unsafe fn to_integer(&self, index: i32) -> Option<i32> {
787        unsafe {
788            let object = self.index_to_addr(index);
789            let mut converted = RAW_TVALUE_NIL;
790            let converted = TValue::from_mut(&mut converted);
791            self.to_number_internal(object, converted)
792                .map(|value| value.number_value())
793                .map(crate::number::num_to_int)
794        }
795    }
796
797    /// `lua_tounsignedx`
798    pub unsafe fn to_unsigned(&self, index: i32) -> Option<u32> {
799        unsafe {
800            let object = self.index_to_addr(index);
801            let mut converted = RAW_TVALUE_NIL;
802            let converted = TValue::from_mut(&mut converted);
803            self.to_number_internal(object, converted)
804                .map(|value| value.number_value())
805                .map(crate::number::num_to_unsigned)
806        }
807    }
808
809    /// `lua_tovector`
810    pub unsafe fn to_vector(&self, index: i32) -> Option<[f32; crate::types::LUA_VECTOR_SIZE]> {
811        let object = unsafe { self.index_to_addr(index) };
812        if object == nil_object() || !object.is_vector() {
813            None
814        } else {
815            Some(object.vector_value())
816        }
817    }
818
819    /// `lua_toboolean`
820    pub unsafe fn to_boolean(&self, index: i32) -> i32 {
821        let object = unsafe { self.index_to_addr(index) };
822        (!object.is_false()) as i32
823    }
824
825    /// `lua_tointeger64`
826    pub unsafe fn to_integer64(&self, index: i32) -> Option<i64> {
827        unsafe {
828            let object = self.index_to_addr(index);
829
830            if object != nil_object() && object.is_integer() {
831                Some(object.integer_value())
832            } else {
833                None
834            }
835        }
836    }
837
838    /// `lua_tostring`
839    pub unsafe fn to_string(&self, index: i32) -> VmErrorResult<Option<&BStr>> {
840        unsafe {
841            let mut object = self.index_to_addr(index);
842            if !object.is_string() {
843                self.thread_barrier();
844                if self.to_string_internal(object)? == 0 {
845                    return Ok(None);
846                }
847
848                self.check_gc()?;
849                object = self.index_to_addr(index);
850            }
851
852            Ok(Some(self.borrow_interned_string(object.string_value())))
853        }
854    }
855
856    /// `lua_tostringatom`
857    pub unsafe fn to_string_atom(&self, index: i32) -> Option<(&BStr, i32)> {
858        unsafe {
859            let object = self.index_to_addr(index);
860            if object == nil_object() || !object.is_string() {
861                return None;
862            }
863
864            let string = object.string_value();
865            self.update_atom(string);
866
867            Some((
868                self.borrow_interned_string(string),
869                string.as_ptr().as_ref().unwrap_unchecked().atom as i32,
870            ))
871        }
872    }
873
874    /// `lua_namecallatom`
875    pub unsafe fn namecall_atom(&self) -> Option<(&BStr, i32)> {
876        unsafe {
877            let string = self.name_call()?;
878
879            self.update_atom(string);
880
881            Some((
882                self.borrow_interned_string(string),
883                string.as_ptr().as_ref().unwrap_unchecked().atom as i32,
884            ))
885        }
886    }
887
888    /// Numeric result of the Luau length operator (`#`).
889    pub unsafe fn len(&self, index: i32) -> VmResult<f64> {
890        unsafe {
891            self.ensure_stack(self, 1)?;
892            let value = self.index_to_addr(index);
893            let result = self.stack_top();
894            self.do_len(result, value)?;
895            debug_assert!(result < self.current_call_info().top());
896            self.set_stack_top(result.add(1));
897            Ok(result.value_unchecked().number_value())
898        }
899    }
900
901    /// `lua_objlen`
902    pub unsafe fn obj_len(&self, index: i32) -> i32 {
903        unsafe {
904            let object = self.index_to_addr(index);
905            if object == nil_object() {
906                return 0;
907            }
908
909            match object.tt() {
910                x if x == LUA_TSTRING => {
911                    object
912                        .string_value()
913                        .as_ptr()
914                        .as_ref()
915                        .unwrap_unchecked()
916                        .len as i32
917                }
918                x if x == LUA_TUSERDATA => {
919                    object
920                        .userdata_value()
921                        .as_ptr()
922                        .as_ref()
923                        .unwrap_unchecked()
924                        .len
925                }
926                x if x == LUA_TBUFFER => {
927                    object
928                        .buffer_value()
929                        .as_ptr()
930                        .as_ref()
931                        .unwrap_unchecked()
932                        .len as i32
933                }
934                x if x == LUA_TTABLE => object.table_value().getn(),
935                _ => 0,
936            }
937        }
938    }
939
940    /// `lua_topointer`
941    pub unsafe fn to_pointer(&self, index: i32) -> *const () {
942        let object = unsafe { self.index_to_addr(index) };
943        if object == nil_object() {
944            return ptr::null();
945        }
946
947        match object.tt() {
948            LUA_TUSERDATA => object.userdata_value().data_ptr().cast(),
949            LUA_TLIGHTUSERDATA => object.pointer_value(),
950            _ if object.is_collectable() => object.gc_value().as_ptr().cast(),
951            _ => ptr::null(),
952        }
953    }
954}
955
956// Buffer values
957impl Thread {
958    /// `lua_newbuffer`
959    pub unsafe fn new_buffer(&self, size: usize) -> VmErrorResult<*mut u8> {
960        unsafe {
961            self.check_gc()?;
962            self.thread_barrier();
963            self.ensure_stack(self, 1)?;
964
965            let buffer = self.new_buffer_internal(size)?;
966            let data = buffer.data_mut_ptr();
967            let top = self.stack_top();
968            top.value_unchecked().set_buffer_value(buffer);
969            debug_assert!(top < self.current_call_info().top());
970            self.set_stack_top(top.add(1));
971
972            Ok(data)
973        }
974    }
975
976    /// `lua_tobuffer`
977    pub unsafe fn to_buffer(&self, index: i32) -> Option<(*mut u8, usize)> {
978        let object = unsafe { self.index_to_addr(index) };
979        if object == nil_object() || !object.is_buffer() {
980            return None;
981        }
982
983        let buffer = object.buffer_value();
984        Some((unsafe { buffer.data_mut_ptr() }, unsafe {
985            buffer.as_ptr().as_ref().unwrap_unchecked().len as usize
986        }))
987    }
988}