Skip to main content

luau_vm/function/
proto.rs

1use core::mem::size_of;
2use core::ptr::{self, NonNull};
3
4use crate::gc::{GcObject, RawGcObject};
5use crate::handle::RawHandle;
6use crate::string::{RawTString, TString};
7use crate::value::{RawTValue, TValue, TValueCursor};
8
9#[repr(C)]
10#[derive(Clone, Copy)]
11pub struct FeedbackVectorSlotCallTarget {
12    pub pc: u32,
13    pub proto: u32,
14    pub hits: u32,
15}
16
17#[repr(C)]
18#[derive(Clone, Copy)]
19pub union FeedbackVectorSlotData {
20    pub call_target: FeedbackVectorSlotCallTarget,
21}
22
23#[repr(C)]
24pub struct FeedbackVectorSlot {
25    pub kind: i32,
26    pub data: FeedbackVectorSlotData,
27}
28
29#[repr(C)]
30pub struct RawProto {
31    pub tt: u8,
32    pub marked: u8,
33    pub memcat: u8,
34    pub n_ups: u8,
35    pub num_params: u8,
36    pub is_vararg: u8,
37    pub max_stack_size: u8,
38    pub flags: u8,
39    pub k: *mut RawTValue,
40    pub code: *mut u32,
41    pub p: *mut *mut RawProto,
42    pub code_entry: *const u32,
43    pub exec_data: *mut (),
44    pub exec_target: usize,
45    pub line_info: *mut u8,
46    pub abs_line_info: *mut i32,
47    pub loc_vars: *mut RawLocVar,
48    pub upvalues: *mut *mut RawTString,
49    pub source: *mut RawTString,
50    pub debug_name: *mut RawTString,
51    pub debug_insn: *mut u8,
52    pub type_info: *mut u8,
53    pub userdata: *mut (),
54    pub gc_list: *mut RawGcObject,
55    pub size_code: i32,
56    pub size_p: i32,
57    pub size_loc_vars: i32,
58    pub size_upvalues: i32,
59    pub size_k: i32,
60    pub size_line_info: i32,
61    pub line_gap_log2: i32,
62    pub line_defined: i32,
63    pub bytecode_id: i32,
64    pub size_type_info: i32,
65    pub feedback_vec: *mut FeedbackVectorSlot,
66    pub feedback_vec_size: u32,
67    pub fun_id: u32,
68    pub cost: u64,
69}
70
71#[repr(C)]
72pub struct RawLocVar {
73    pub var_name: *mut RawTString,
74    pub start_pc: i32,
75    pub end_pc: i32,
76    pub reg: u8,
77}
78
79#[derive(Clone, Copy, PartialEq, Eq)]
80#[repr(transparent)]
81/// Non-owning identity of a VM prototype record.
82///
83/// # Safety model for unsafe methods
84///
85/// The proto and all referenced arrays and objects must remain live. Program
86/// counters and indices must be in bounds for their corresponding arrays, and
87/// code mutation must preserve a valid bytecode graph.
88pub struct Proto {
89    raw: NonNull<RawProto>,
90}
91
92#[derive(Clone, Copy, PartialEq, Eq)]
93#[repr(transparent)]
94/// Non-owning view of a prototype local-variable record.
95///
96/// Unsafe operations require the owning proto and local-variable array to
97/// remain live for the entire use of this copied handle.
98pub struct LocVar {
99    raw: NonNull<RawLocVar>,
100}
101
102#[allow(
103    clippy::missing_safety_doc,
104    reason = "Proto's shared raw-handle contract is documented on Proto"
105)]
106impl Proto {
107    pub const unsafe fn from_raw(raw: NonNull<RawProto>) -> Self {
108        Self { raw }
109    }
110
111    pub unsafe fn source(&self) -> Option<TString> {
112        unsafe {
113            NonNull::new(self.as_ptr().as_ref().unwrap_unchecked().source)
114                .map(|raw| TString::from_raw(raw))
115        }
116    }
117
118    pub unsafe fn debug_name(&self) -> Option<TString> {
119        unsafe {
120            NonNull::new(self.as_ptr().as_ref().unwrap_unchecked().debug_name)
121                .map(|raw| TString::from_raw(raw))
122        }
123    }
124
125    pub unsafe fn upvalue_name(&self, index: usize) -> Option<TString> {
126        let proto = unsafe { self.as_ptr().as_ref().unwrap_unchecked() };
127        if index >= proto.size_upvalues as usize {
128            None
129        } else {
130            NonNull::new(unsafe { *proto.upvalues.add(index) })
131                .map(|raw| unsafe { TString::from_raw(raw) })
132        }
133    }
134
135    pub unsafe fn child_proto(&self, index: usize) -> Option<Self> {
136        let proto = unsafe { self.as_ptr().as_ref().unwrap_unchecked() };
137        if index >= proto.size_p as usize {
138            None
139        } else {
140            NonNull::new(unsafe { *proto.p.add(index) }).map(|raw| unsafe { Self::from_raw(raw) })
141        }
142    }
143
144    pub unsafe fn gc_list(&self) -> Option<GcObject> {
145        unsafe {
146            NonNull::new(self.as_ptr().as_ref().unwrap_unchecked().gc_list)
147                .map(|raw| GcObject::from_raw(raw))
148        }
149    }
150
151    pub unsafe fn set_gc_list(&self, gc_list: Option<GcObject>) {
152        unsafe {
153            self.as_ptr().as_mut().unwrap_unchecked().gc_list =
154                gc_list.map_or(ptr::null_mut(), |object| object.as_ptr());
155        }
156    }
157
158    pub unsafe fn loc_var(&self, index: usize) -> Option<LocVar> {
159        let proto = unsafe { self.as_ptr().as_ref().unwrap_unchecked() };
160        if index >= proto.size_loc_vars as usize {
161            None
162        } else {
163            Some(unsafe { LocVar::from_raw(NonNull::new_unchecked(proto.loc_vars.add(index))) })
164        }
165    }
166
167    /// `sizeproto`
168    pub unsafe fn size(&self) -> usize {
169        let proto = unsafe { self.as_ptr().as_ref().unwrap_unchecked() };
170
171        size_of::<RawProto>()
172            + size_of::<u32>() * proto.size_code as usize
173            + size_of::<*mut RawProto>() * proto.size_p as usize
174            + size_of::<RawTValue>() * proto.size_k as usize
175            + proto.size_line_info as usize
176            + size_of::<RawLocVar>() * proto.size_loc_vars as usize
177            + size_of::<*mut RawTString>() * proto.size_upvalues as usize
178            + proto.size_type_info as usize
179    }
180
181    pub unsafe fn constants(&self) -> TValueCursor {
182        TValueCursor::from_ptr(unsafe { self.as_ptr().as_ref().unwrap_unchecked().k })
183    }
184
185    pub unsafe fn constant(&self, index: usize) -> TValue {
186        debug_assert!(index < unsafe { self.as_ptr().as_ref().unwrap_unchecked().size_k as usize });
187        unsafe { self.constants().add(index).value_unchecked() }
188    }
189
190    /// `luaG_getline`
191    pub unsafe fn get_line(&self, pc: i32) -> i32 {
192        let proto = unsafe { self.as_ptr().as_ref().unwrap_unchecked() };
193        debug_assert!(pc >= 0);
194        debug_assert!(pc < proto.size_code);
195
196        unsafe {
197            if proto.line_info.is_null() {
198                0
199            } else {
200                *proto.abs_line_info.add(pc as usize >> proto.line_gap_log2)
201                    + i32::from(*proto.line_info.add(pc as usize))
202            }
203        }
204    }
205
206    /// `pcRel`
207    pub unsafe fn pc_rel(&self, saved_pc: *const u32) -> i32 {
208        let code = unsafe { self.as_ptr().as_ref().unwrap_unchecked().code };
209        if saved_pc.is_null() || saved_pc == code {
210            0
211        } else {
212            unsafe { saved_pc.offset_from(code) as i32 - 1 }
213        }
214    }
215
216    /// `luaF_getlocal`
217    pub unsafe fn get_local(&self, mut local_number: i32, pc: i32) -> Option<LocVar> {
218        let proto = unsafe { self.as_ptr().as_ref().unwrap_unchecked() };
219
220        for index in 0..proto.size_loc_vars as usize {
221            let local = unsafe { self.loc_var(index).unwrap_unchecked() };
222            let local_ref = unsafe { local.as_ptr().as_ref().unwrap_unchecked() };
223            if pc >= local_ref.start_pc && pc < local_ref.end_pc {
224                local_number -= 1;
225                if local_number == 0 {
226                    return Some(local);
227                }
228            }
229        }
230
231        None
232    }
233
234    /// `luaF_findlocal`
235    pub unsafe fn find_local(&self, local_reg: i32, pc: i32) -> Option<LocVar> {
236        let proto = unsafe { self.as_ptr().as_ref().unwrap_unchecked() };
237
238        for index in 0..proto.size_loc_vars as usize {
239            let local = unsafe { self.loc_var(index).unwrap_unchecked() };
240            let local_ref = unsafe { local.as_ptr().as_ref().unwrap_unchecked() };
241            if i32::from(local_ref.reg) == local_reg
242                && pc >= local_ref.start_pc
243                && pc < local_ref.end_pc
244            {
245                return Some(local);
246            }
247        }
248
249        None
250    }
251
252    pub unsafe fn feedback_slot(&self, index: usize) -> Option<NonNull<FeedbackVectorSlot>> {
253        let proto = unsafe { self.as_ptr().as_ref().unwrap_unchecked() };
254        if index >= proto.feedback_vec_size as usize {
255            None
256        } else {
257            NonNull::new(unsafe { proto.feedback_vec.add(index) })
258        }
259    }
260
261    pub unsafe fn code_at(&self, index: usize) -> u32 {
262        unsafe { *self.as_ptr().as_ref().unwrap_unchecked().code.add(index) }
263    }
264
265    pub unsafe fn set_code_at(&self, index: usize, value: u32) {
266        unsafe {
267            *self.as_ptr().as_ref().unwrap_unchecked().code.add(index) = value;
268        }
269    }
270}
271
272#[allow(
273    clippy::missing_safety_doc,
274    reason = "LocVar's shared raw-view contract is documented on LocVar"
275)]
276impl LocVar {
277    pub const unsafe fn from_raw(raw: NonNull<RawLocVar>) -> Self {
278        Self { raw }
279    }
280
281    pub unsafe fn name(&self) -> Option<TString> {
282        unsafe {
283            NonNull::new(self.as_ptr().as_ref().unwrap_unchecked().var_name)
284                .map(|raw| TString::from_raw(raw))
285        }
286    }
287}
288impl crate::handle::sealed::Sealed for Proto {}
289impl crate::handle::sealed::Sealed for LocVar {}
290impl RawHandle for Proto {
291    type Raw = RawProto;
292
293    fn as_ptr(&self) -> *mut Self::Raw {
294        self.raw.as_ptr()
295    }
296}
297
298impl AsRef<Proto> for Proto {
299    fn as_ref(&self) -> &Proto {
300        self
301    }
302}
303
304impl RawHandle for LocVar {
305    type Raw = RawLocVar;
306
307    fn as_ptr(&self) -> *mut Self::Raw {
308        self.raw.as_ptr()
309    }
310}
311
312impl AsRef<LocVar> for LocVar {
313    fn as_ref(&self) -> &LocVar {
314        self
315    }
316}