1use luau_common::{BString, ByteSlice};
2use std::borrow::Borrow;
3
4use crate::opcodes::{InvalidOpcode, Opcode};
5
6pub type Register = u8;
8
9pub type ConstantIndex = i32;
11
12pub type PackedTableValue = i32;
14
15pub type InstructionWord = u32;
17
18#[derive(Debug, Clone, PartialEq, Eq, Hash)]
19pub struct BytecodeClass {
20 pub class_name: ConstantIndex,
21 pub property_names: Vec<ConstantIndex>,
22 pub method_names: Vec<ConstantIndex>,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
26pub struct BytecodeImportId(u32);
27
28impl BytecodeImportId {
29 pub fn new(id0: ConstantIndex) -> Self {
30 debug_assert!((0..1024).contains(&id0));
31 Self((1 << 30) | ((id0 as u32) << 20))
32 }
33
34 pub fn from_two(id0: ConstantIndex, id1: ConstantIndex) -> Self {
35 debug_assert!((0..1024).contains(&id0));
36 debug_assert!((0..1024).contains(&id1));
37 Self((2 << 30) | ((id0 as u32) << 20) | ((id1 as u32) << 10))
38 }
39
40 pub fn from_three(id0: ConstantIndex, id1: ConstantIndex, id2: ConstantIndex) -> Self {
41 debug_assert!((0..1024).contains(&id0));
42 debug_assert!((0..1024).contains(&id1));
43 debug_assert!((0..1024).contains(&id2));
44 Self((3 << 30) | ((id0 as u32) << 20) | ((id1 as u32) << 10) | id2 as u32)
45 }
46
47 pub fn from_raw(value: u32) -> Self {
48 Self(value)
49 }
50
51 pub fn raw(self) -> u32 {
52 self.0
53 }
54
55 pub fn components(self) -> Vec<ConstantIndex> {
56 let count = self.0 >> 30;
57 let mut result = Vec::with_capacity(count as usize);
58 if count > 0 {
59 result.push(((self.0 >> 20) & 1023) as ConstantIndex);
60 }
61 if count > 1 {
62 result.push(((self.0 >> 10) & 1023) as ConstantIndex);
63 }
64 if count > 2 {
65 result.push((self.0 & 1023) as ConstantIndex);
66 }
67 result
68 }
69}
70
71#[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
72pub struct BytecodeString(BString);
73
74impl BytecodeString {
75 pub fn as_bytes(&self) -> &[u8] {
76 self.0.as_bytes()
77 }
78
79 pub fn len(&self) -> usize {
80 self.0.len()
81 }
82
83 pub fn is_empty(&self) -> bool {
84 self.0.is_empty()
85 }
86}
87
88impl Borrow<[u8]> for BytecodeString {
89 fn borrow(&self) -> &[u8] {
90 self.as_bytes()
91 }
92}
93
94impl From<&[u8]> for BytecodeString {
95 fn from(value: &[u8]) -> Self {
96 Self(BString::from(value))
97 }
98}
99
100impl<const N: usize> From<&[u8; N]> for BytecodeString {
101 fn from(value: &[u8; N]) -> Self {
102 Self::from(value.as_slice())
103 }
104}
105
106impl From<Vec<u8>> for BytecodeString {
107 fn from(value: Vec<u8>) -> Self {
108 Self(BString::new(value))
109 }
110}
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
113pub struct ClosureIndex(u32);
114
115impl ClosureIndex {
116 pub fn new(index: u32) -> Self {
117 Self(index)
118 }
119
120 pub fn get(self) -> u32 {
121 self.0
122 }
123
124 pub fn as_usize(self) -> usize {
125 self.0 as usize
126 }
127}
128
129#[derive(Debug, Clone, PartialEq, Eq)]
130pub struct TableShape {
131 entries: [TableShapeEntry; Self::MAX_LENGTH],
132 length: u8,
133 has_constants: bool,
134}
135
136impl TableShape {
137 pub const MAX_LENGTH: usize = 32;
138
139 pub fn new(entries: Vec<TableShapeEntry>) -> Self {
140 debug_assert!(entries.len() <= Self::MAX_LENGTH);
141 let has_constants = entries.iter().any(|entry| entry.value.is_some());
142 let mut storage = [TableShapeEntry::default(); Self::MAX_LENGTH];
143 let length = entries.len();
144 storage[..length].copy_from_slice(&entries);
145 Self {
146 entries: storage,
147 length: length as u8,
148 has_constants,
149 }
150 }
151
152 pub fn entries(&self) -> &[TableShapeEntry] {
153 &self.entries[..usize::from(self.length)]
154 }
155
156 pub fn len(&self) -> usize {
157 usize::from(self.length)
158 }
159
160 pub fn is_empty(&self) -> bool {
161 self.length == 0
162 }
163
164 pub fn has_constants(&self) -> bool {
165 self.has_constants
166 }
167}
168
169#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
170pub struct TableShapeEntry {
171 pub key: ConstantIndex,
172 pub value: Option<PackedTableValue>,
173}
174
175#[derive(Debug, Clone, Copy, PartialEq)]
176pub struct BytecodeVector {
177 values: [f32; 4],
178}
179
180impl BytecodeVector {
181 pub fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
182 Self {
183 values: [x, y, z, w],
184 }
185 }
186
187 pub fn x(self) -> f32 {
188 self.values[0]
189 }
190
191 pub fn y(self) -> f32 {
192 self.values[1]
193 }
194
195 pub fn z(self) -> f32 {
196 self.values[2]
197 }
198
199 pub fn w(self) -> f32 {
200 self.values[3]
201 }
202
203 pub fn to_bits(self) -> [u32; 4] {
204 self.values.map(f32::to_bits)
205 }
206}
207
208#[derive(Debug, Clone, Copy, PartialEq)]
209pub struct BytecodeVectorDouble {
210 values: [f64; 4],
211}
212
213impl BytecodeVectorDouble {
214 pub fn new(x: f64, y: f64, z: f64, w: f64) -> Self {
215 Self {
216 values: [x, y, z, w],
217 }
218 }
219
220 pub fn x(self) -> f64 {
221 self.values[0]
222 }
223
224 pub fn y(self) -> f64 {
225 self.values[1]
226 }
227
228 pub fn z(self) -> f64 {
229 self.values[2]
230 }
231
232 pub fn w(self) -> f64 {
233 self.values[3]
234 }
235
236 pub fn to_bits(self) -> [u64; 4] {
237 self.values.map(f64::to_bits)
238 }
239}
240
241#[derive(Debug, Clone, Copy, PartialEq, Eq)]
242pub struct BytecodeTypedLocal {
243 pub ty: u8,
244 pub register: Register,
245 pub start_pc: u32,
246 pub end_pc: u32,
247}
248
249#[derive(Debug, Clone, Copy, PartialEq, Eq)]
250pub struct BytecodeFeedbackSlot {
251 pub pc: u32,
252}
253
254#[derive(Debug, Clone, Copy, PartialEq, Eq)]
255pub enum BytecodeFeedbackType {
256 CallTarget,
257}
258
259#[derive(Debug, Clone, PartialEq, Eq)]
260pub struct BytecodeUserdataType {
261 pub name: BytecodeString,
262 pub name_ref: u32,
263 pub used: bool,
264}
265
266#[derive(Debug, Clone, Copy, PartialEq, Eq)]
267#[repr(transparent)]
268pub struct Instruction(InstructionWord);
269
270#[derive(Debug, Clone, Copy, PartialEq, Eq)]
271#[repr(transparent)]
272pub struct InstructionAux(InstructionWord);
273
274impl Instruction {
275 pub fn new(word: InstructionWord) -> Self {
276 Self(word)
277 }
278
279 pub fn abc(opcode: Opcode, a: u8, b: u8, c: u8) -> Self {
280 Self(opcode.word() | ((a as u32) << 8) | ((b as u32) << 16) | ((c as u32) << 24))
281 }
282
283 pub fn ad(opcode: Opcode, a: u8, d: i16) -> Self {
284 Self(opcode.word() | ((a as u32) << 8) | (((d as u16) as u32) << 16))
285 }
286
287 pub fn ae(opcode: Opcode, e: i32) -> Self {
288 Self(opcode.word() | (((e as u32) & 0x00ff_ffff) << 8))
289 }
290
291 pub fn word(self) -> InstructionWord {
292 self.0
293 }
294
295 pub fn with_d(self, d: i16) -> Self {
296 Self((self.0 & 0x0000_ffff) | (((d as u16) as u32) << 16))
297 }
298
299 pub fn with_c(self, c: u8) -> Self {
300 Self((self.0 & 0x00ff_ffff) | ((c as u32) << 24))
301 }
302
303 pub fn try_opcode(self) -> Result<Opcode, InvalidOpcode> {
305 let opcode = (self.0 & 0xff) as u8;
306 Opcode::from_byte(opcode).ok_or(InvalidOpcode::new(opcode))
307 }
308
309 pub unsafe fn opcode_unchecked(self) -> Opcode {
317 unsafe { self.try_opcode().unwrap_unchecked() }
318 }
319
320 pub fn a(self) -> u8 {
321 ((self.0 >> 8) & 0xff) as u8
322 }
323
324 pub fn b(self) -> u8 {
325 ((self.0 >> 16) & 0xff) as u8
326 }
327
328 pub fn c(self) -> u8 {
329 ((self.0 >> 24) & 0xff) as u8
330 }
331
332 pub fn d(self) -> i16 {
333 (self.0 >> 16) as u16 as i16
334 }
335
336 pub fn e(self) -> i32 {
337 (self.0 as i32) >> 8
338 }
339
340 pub fn try_jump_target(self, pc: u32) -> Result<Option<i32>, InvalidOpcode> {
342 Ok(self.try_opcode()?.jump_target(self, pc))
343 }
344
345 pub unsafe fn jump_target_unchecked(self, pc: u32) -> Option<i32> {
352 unsafe { self.opcode_unchecked().jump_target(self, pc) }
353 }
354}
355
356impl InstructionAux {
357 pub fn new(word: InstructionWord) -> Self {
358 Self(word)
359 }
360
361 pub fn word(self) -> InstructionWord {
362 self.0
363 }
364
365 pub fn a(self) -> u8 {
366 (self.0 & 0xff) as u8
367 }
368
369 pub fn b(self) -> u8 {
370 ((self.0 >> 8) & 0xff) as u8
371 }
372
373 pub fn kv(self) -> u32 {
374 self.0 & 0x00ff_ffff
375 }
376
377 pub fn kb(self) -> bool {
378 (self.0 & 1) != 0
379 }
380
381 pub fn is_negated(self) -> bool {
382 (self.0 >> 31) != 0
383 }
384
385 pub fn kv16(self) -> u16 {
386 (self.0 & 0xffff) as u16
387 }
388
389 pub fn slot(self) -> u16 {
390 (self.0 >> 16) as u16
391 }
392}