Skip to main content

luau_vm/state/
call_info.rs

1use core::ptr::NonNull;
2
3use crate::function::RawProto;
4use crate::function::{Closure, Proto};
5use crate::handle::RawHandle;
6use crate::handle::sealed::Sealed;
7use crate::value::{RawTValue, TValueCursor};
8
9#[repr(C)]
10pub struct RawCallInfo {
11    pub base: *mut RawTValue,
12    pub function: *mut RawTValue,
13    pub top: *mut RawTValue,
14    pub proto: *mut RawProto,
15    pub saved: RawCallInfoSaved,
16    pub n_results: i32,
17    pub flags: u32,
18}
19
20#[repr(C)]
21pub union RawCallInfoSaved {
22    pub saved_pc: *const u32,
23    pub errfunc: i32,
24}
25
26#[derive(Clone, Copy, PartialEq, Eq)]
27#[repr(transparent)]
28/// Non-owning view of a live VM call-frame record.
29///
30/// # Safety model for unsafe methods
31///
32/// The owning thread and call-info array must remain live. Stack pointers,
33/// saved PCs, flags, and function variants must match the active frame, and
34/// call-info or stack relocation invalidates derived views.
35pub struct CallInfo {
36    raw: NonNull<RawCallInfo>,
37}
38
39#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
40#[repr(transparent)]
41/// Nullable traversal position in a thread's call-info array.
42///
43/// Unsafe navigation requires both cursors to belong to the same live array;
44/// reallocation or thread destruction invalidates every cursor.
45pub struct CallInfoCursor(*mut RawCallInfo);
46
47#[allow(
48    clippy::missing_safety_doc,
49    reason = "CallInfo's shared raw-view contract is documented on CallInfo"
50)]
51impl CallInfo {
52    pub const unsafe fn from_raw(raw: NonNull<RawCallInfo>) -> Self {
53        Self { raw }
54    }
55
56    pub fn base(&self) -> TValueCursor {
57        unsafe { TValueCursor::from_ptr(self.as_ptr().as_ref().unwrap_unchecked().base) }
58    }
59
60    pub fn function(&self) -> TValueCursor {
61        unsafe { TValueCursor::from_ptr(self.as_ptr().as_ref().unwrap_unchecked().function) }
62    }
63
64    pub fn top(&self) -> TValueCursor {
65        unsafe { TValueCursor::from_ptr(self.as_ptr().as_ref().unwrap_unchecked().top) }
66    }
67
68    pub fn set_base(&self, base: TValueCursor) {
69        unsafe {
70            self.as_ptr().as_mut().unwrap_unchecked().base = base.as_ptr();
71        }
72    }
73
74    pub fn set_function(&self, function: TValueCursor) {
75        unsafe {
76            self.as_ptr().as_mut().unwrap_unchecked().function = function.as_ptr();
77        }
78    }
79
80    pub fn set_top(&self, top: TValueCursor) {
81        unsafe {
82            self.as_ptr().as_mut().unwrap_unchecked().top = top.as_ptr();
83        }
84    }
85
86    pub fn proto(&self) -> Option<Proto> {
87        let proto = unsafe { self.as_ptr().as_ref().unwrap_unchecked().proto };
88        NonNull::new(proto).map(|raw| unsafe { Proto::from_raw(raw) })
89    }
90
91    pub fn set_proto(&self, proto: Option<Proto>) {
92        unsafe {
93            self.as_ptr().as_mut().unwrap_unchecked().proto =
94                proto.map_or(core::ptr::null_mut(), |proto| proto.as_ptr());
95        }
96    }
97
98    pub unsafe fn saved_pc(&self) -> *const u32 {
99        unsafe { self.as_ptr().as_ref().unwrap_unchecked().saved.saved_pc }
100    }
101
102    pub unsafe fn set_saved_pc(&self, saved_pc: *const u32) {
103        unsafe {
104            self.as_ptr().as_mut().unwrap_unchecked().saved.saved_pc = saved_pc;
105        }
106    }
107
108    pub unsafe fn errfunc(&self) -> i32 {
109        unsafe { self.as_ptr().as_ref().unwrap_unchecked().saved.errfunc }
110    }
111
112    pub unsafe fn set_errfunc(&self, errfunc: i32) {
113        unsafe {
114            self.as_ptr().as_mut().unwrap_unchecked().saved.errfunc = errfunc;
115        }
116    }
117
118    pub unsafe fn init_call(
119        &self,
120        function: TValueCursor,
121        top: TValueCursor,
122        n_results: i32,
123        proto: Option<Proto>,
124    ) {
125        unsafe {
126            self.set_function(function);
127            self.set_base(function.add(1));
128            self.set_top(top);
129            self.set_proto(proto);
130            self.set_saved_pc(core::ptr::null());
131            self.as_ptr().as_mut().unwrap_unchecked().flags = 0;
132            self.as_ptr().as_mut().unwrap_unchecked().n_results = n_results;
133        }
134    }
135
136    pub unsafe fn rebase_stack(&self, old_stack: TValueCursor, new_stack: TValueCursor) {
137        unsafe {
138            self.set_top(new_stack.add(self.top().addr_offset_from(old_stack) as usize));
139            self.set_base(new_stack.add(self.base().addr_offset_from(old_stack) as usize));
140            self.set_function(new_stack.add(self.function().addr_offset_from(old_stack) as usize));
141        }
142    }
143
144    /// `ci_func`
145    pub unsafe fn function_closure(&self) -> Closure {
146        unsafe { self.function().value_unchecked().closure_value() }
147    }
148
149    /// `f_isLua`
150    pub unsafe fn is_lua_function(&self) -> bool {
151        unsafe { self.function_closure().is_lua() }
152    }
153
154    /// `isLua`
155    pub unsafe fn is_lua(&self) -> bool {
156        unsafe { self.function().value_unchecked().is_function() && self.is_lua_function() }
157    }
158}
159
160#[allow(
161    clippy::missing_safety_doc,
162    reason = "CallInfoCursor's navigation contract is documented on CallInfoCursor"
163)]
164impl CallInfoCursor {
165    /// Returns the current traversal address, which may be null.
166    ///
167    /// CallInfo array growth invalidates cursors into the old allocation.
168    pub const fn as_ptr(&self) -> *mut RawCallInfo {
169        self.0
170    }
171
172    pub const fn from_ptr(raw: *mut RawCallInfo) -> Self {
173        Self(raw)
174    }
175
176    pub const fn is_null(&self) -> bool {
177        self.0.is_null()
178    }
179
180    pub unsafe fn call_info_unchecked(&self) -> CallInfo {
181        debug_assert!(!self.is_null());
182        unsafe { CallInfo::from_raw(NonNull::new_unchecked(self.0)) }
183    }
184
185    pub fn call_info(&self) -> Option<CallInfo> {
186        NonNull::new(self.0).map(|raw| unsafe { CallInfo::from_raw(raw) })
187    }
188
189    pub unsafe fn add(self, count: usize) -> Self {
190        unsafe { Self::from_ptr(self.0.add(count)) }
191    }
192
193    pub unsafe fn sub(self, count: usize) -> Self {
194        unsafe { Self::from_ptr(self.0.sub(count)) }
195    }
196
197    pub unsafe fn offset(self, count: isize) -> Self {
198        unsafe { Self::from_ptr(self.0.offset(count)) }
199    }
200
201    pub unsafe fn offset_from(self, other: Self) -> isize {
202        unsafe { self.0.offset_from(other.0) }
203    }
204}
205
206impl Sealed for CallInfo {}
207
208impl RawHandle for CallInfo {
209    type Raw = RawCallInfo;
210
211    fn as_ptr(&self) -> *mut Self::Raw {
212        self.raw.as_ptr()
213    }
214}
215
216impl AsRef<CallInfo> for CallInfo {
217    fn as_ref(&self) -> &CallInfo {
218        self
219    }
220}
221
222impl AsRef<CallInfoCursor> for CallInfoCursor {
223    fn as_ref(&self) -> &CallInfoCursor {
224        self
225    }
226}