luau_vm/function/
proto.rs1use 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)]
81pub struct Proto {
89 raw: NonNull<RawProto>,
90}
91
92#[derive(Clone, Copy, PartialEq, Eq)]
93#[repr(transparent)]
94pub 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 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 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 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 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 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}