1use super::support::{
2 BytecodeBuilderConstant, BytecodeBuilderFunction, BytecodeBuilderScratch, TableShapeCacheKey,
3};
4use super::*;
5use crate::function::BytecodeStringTable;
6use crate::model::{
7 BytecodeClass, BytecodeFeedbackSlot, BytecodeFeedbackType, BytecodeImportId, BytecodeString,
8 BytecodeTypedLocal, BytecodeUserdataType, BytecodeVector, BytecodeVectorDouble, ClosureIndex,
9 ConstantIndex, Register, TableShape,
10};
11use crate::opcodes::{
12 BYTECODE_TYPE_VERSION_TARGET, BYTECODE_VERSION_CLASSES, BYTECODE_VERSION_TARGET,
13 BytecodeConstantTag, FEEDBACK_TYPE_CALLTARGET, PROTO_FLAG_INLINABLE,
14};
15use crate::wire::BytecodeWriter;
16use luau_common::flags;
17use std::borrow::Cow;
18
19impl<'src> BytecodeBuilder<'src> {
20 fn add_bytecode_constant(&mut self, value: BytecodeBuilderConstant) -> ConstantIndex {
21 let key = value.cache_key();
22 let proto = self.current_function();
23
24 if let Some(index) = proto.constant_index.get(&key) {
25 return *index;
26 }
27
28 if proto.constants.len() >= MAX_CONSTANT_COUNT {
29 return -1;
30 }
31
32 let index = proto.constants.len() as ConstantIndex;
33 proto.constants.push(value);
34 let (_, fresh) = proto.constant_index.insert(key, index);
35 debug_assert!(fresh);
36 index
37 }
38
39 pub fn add_constant_nil(&mut self) -> ConstantIndex {
40 self.add_bytecode_constant(BytecodeBuilderConstant::Nil)
41 }
42
43 pub fn add_constant_boolean(&mut self, value: bool) -> ConstantIndex {
44 self.add_bytecode_constant(BytecodeBuilderConstant::Boolean(value))
45 }
46
47 pub fn add_constant_number(&mut self, value: f64) -> ConstantIndex {
48 self.add_bytecode_constant(BytecodeBuilderConstant::Number(value))
49 }
50
51 pub fn add_constant_integer(&mut self, value: i64) -> ConstantIndex {
52 self.add_bytecode_constant(BytecodeBuilderConstant::Integer64(value))
53 }
54
55 pub fn add_constant_string(
56 &mut self,
57 value: impl Into<BytecodeStringRef<'src>>,
58 ) -> ConstantIndex {
59 let value = value.into();
60 let index = self.add_string_table_entry(&value);
61 self.add_bytecode_constant(BytecodeBuilderConstant::String(index))
62 }
63
64 pub fn add_import(&mut self, import_id: BytecodeImportId) -> ConstantIndex {
65 self.add_bytecode_constant(BytecodeBuilderConstant::Import(import_id))
66 }
67
68 pub fn add_constant_closure(&mut self, function_id: u32) -> ConstantIndex {
69 self.add_bytecode_constant(BytecodeBuilderConstant::Closure(ClosureIndex::new(
70 function_id,
71 )))
72 }
73
74 pub fn add_constant_table(&mut self, shape: &TableShape) -> ConstantIndex {
75 let proto = self.current_function();
76 let key = TableShapeCacheKey::new(shape.clone());
77
78 if let Some(index) = proto.table_shape_index.get(&key) {
79 return *index;
80 }
81
82 if proto.constants.len() >= MAX_CONSTANT_COUNT {
83 return -1;
84 }
85
86 let index = proto.constants.len() as ConstantIndex;
87 let table_shape_index = proto.table_shapes.len() as u32;
88 proto.table_shapes.push(shape.clone());
89 let (_, fresh) = proto.table_shape_index.insert(key, index);
90 debug_assert!(fresh);
91 proto
92 .constants
93 .push(BytecodeBuilderConstant::Table(table_shape_index));
94 index
95 }
96
97 pub fn add_constant_vector(&mut self, x: f32, y: f32, z: f32, w: f32) -> ConstantIndex {
98 self.add_bytecode_constant(BytecodeBuilderConstant::Vector(BytecodeVector::new(
99 x, y, z, w,
100 )))
101 }
102
103 pub fn add_constant_vector_double(&mut self, x: f64, y: f64, z: f64, w: f64) -> ConstantIndex {
104 self.add_bytecode_constant(BytecodeBuilderConstant::VectorDouble(
105 BytecodeVectorDouble::new(x, y, z, w),
106 ))
107 }
108
109 pub fn add_class_shape(&mut self, shape: BytecodeClass) -> ConstantIndex {
110 if self.scratch.constants.len() >= MAX_CONSTANT_COUNT {
111 return -1;
112 }
113
114 let index = self.scratch.constants.len() as ConstantIndex;
115 let class_shape_index = self.class_shapes.len() as u32;
116 self.class_shapes.push(shape);
117 self.scratch
118 .constants
119 .push(BytecodeBuilderConstant::Class(class_shape_index));
120 index
121 }
122
123 pub fn add_fb_slot(&mut self, ty: BytecodeFeedbackType) -> u32 {
124 debug_assert_eq!(ty, BytecodeFeedbackType::CallTarget);
125 let pc = self.current_function().code.len() as u32;
126 let proto = self.current_function();
127 proto.feedback_slots.push(BytecodeFeedbackSlot { pc });
128 proto.feedback_slots.len() as u32 - 1
129 }
130
131 pub fn set_function_type_info(&mut self, value: Vec<u8>) {
132 self.current_function_meta().type_info = value;
133 }
134
135 pub fn push_local_type_info(&mut self, ty: u8, register: Register, start_pc: u32, end_pc: u32) {
136 self.current_function()
137 .local_types
138 .push(BytecodeTypedLocal {
139 ty,
140 register,
141 start_pc,
142 end_pc,
143 });
144 }
145
146 pub fn push_upvalue_type_info(&mut self, ty: u8) {
147 self.current_function().upvalue_types.push(ty);
148 }
149
150 pub fn add_userdata_type(&mut self, name: impl Into<BytecodeString>) -> u32 {
151 let index = self.userdata_types.len();
152 self.userdata_types.push(BytecodeUserdataType {
153 name: name.into(),
154 name_ref: 0,
155 used: false,
156 });
157 u32::try_from(index).expect("userdata type index must fit bytecode varint")
158 }
159
160 pub fn use_userdata_type(&mut self, index: u32) {
161 self.userdata_types[index as usize].used = true;
162 }
163
164 pub fn finalize(&mut self) {
165 debug_assert!(
166 self.bytecode.is_empty(),
167 "BytecodeBuilder::finalize requires bytecode to be empty"
168 );
169 let main = u32::try_from(
170 self.main
171 .expect("main function must be set before finalize"),
172 )
173 .expect("main function id must fit varint");
174 self.assign_userdata_type_name_refs();
175 self.bytecode = self.finish_bytecode(main);
176 }
177
178 pub fn get_bytecode(&self) -> &[u8] {
179 debug_assert!(
180 !self.bytecode.is_empty(),
181 "BytecodeBuilder::get_bytecode requires finalize first"
182 );
183 &self.bytecode
184 }
185
186 pub fn get_error(message: impl AsRef<[u8]>) -> Vec<u8> {
189 let message = message.as_ref();
190 let mut result = Vec::with_capacity(message.len() + 1);
191 result.push(0);
192 result.extend_from_slice(message);
193 result
194 }
195
196 pub fn get_string_table(&self) -> BytecodeStringTable<'_> {
197 let mut strings = vec![Cow::Borrowed(&[][..]); self.string_index.len()];
198 for (value, index) in &self.string_index {
199 debug_assert!(*index > 0 && (*index as usize) <= strings.len());
200 strings[*index as usize - 1] = Cow::Borrowed(value.as_bytes());
201 }
202 BytecodeStringTable::new(strings)
203 }
204
205 pub fn get_function_data(&self, id: usize) -> Vec<u8> {
206 self.functions[id].data.clone()
207 }
208
209 pub(super) fn function_data(&self, id: usize) -> Vec<u8> {
210 let mut writer = BytecodeWriter::new();
211 self.write_function(&mut writer, &self.functions[id], &self.scratch);
212 writer.into_bytes()
213 }
214
215 fn finish_bytecode(&self, main: u32) -> Vec<u8> {
216 let version = self.version();
217 let mut writer = BytecodeWriter::new();
218
219 writer.write_u8(version);
220 writer.write_u8(BYTECODE_TYPE_VERSION_TARGET);
221 self.write_finalized_string_table(&mut writer);
222 self.write_userdata_remapping(&mut writer);
223 writer.write_varint(self.functions.len() as u32);
224
225 for function in &self.functions {
226 if version >= 12 {
227 writer.write_varint(function.data.len() as u32);
228 }
229 writer.write_bytes(&function.data);
230 }
231
232 writer.write_varint(main);
233 writer.into_bytes()
234 }
235
236 fn version(&self) -> u8 {
237 if flags::DebugLuauUserDefinedClasses.get() {
238 return BYTECODE_VERSION_CLASSES;
239 }
240
241 if flags::LuauCompileEmitVectorDouble.get() {
242 return 13;
243 }
244
245 if flags::LuauBytecodeCostModel.get() {
246 return 12;
247 }
248
249 if flags::LuauEmitCallFeedback.get() {
250 return 11;
251 }
252
253 BYTECODE_VERSION_TARGET
254 }
255
256 fn write_function(
257 &self,
258 writer: &mut BytecodeWriter,
259 function: &BytecodeBuilderFunction,
260 scratch: &BytecodeBuilderScratch<'src>,
261 ) {
262 writer.write_u8(function.max_stack_size);
263 writer.write_u8(function.num_params);
264 writer.write_u8(function.upvalue_count);
265 writer.write_u8(u8::from(function.is_vararg));
266 writer.write_u8(function.flags);
267
268 if function.type_info.is_empty()
269 && scratch.upvalue_types.is_empty()
270 && scratch.local_types.is_empty()
271 {
272 writer.write_varint(0);
273 } else {
274 let mut types = BytecodeWriter::new();
275 types.write_varint(function.type_info.len() as u32);
276 types.write_varint(scratch.upvalue_types.len() as u32);
277 types.write_varint(scratch.local_types.len() as u32);
278 types.write_bytes(&function.type_info);
279
280 for ty in &scratch.upvalue_types {
281 types.write_u8(*ty);
282 }
283
284 for local in &scratch.local_types {
285 types.write_u8(local.ty);
286 types.write_u8(local.register);
287 types.write_varint(local.start_pc);
288 debug_assert!(local.end_pc >= local.start_pc);
289 types.write_varint(local.end_pc - local.start_pc);
290 }
291
292 let types = types.into_bytes();
293 writer.write_varint(types.len() as u32);
294 writer.write_bytes(&types);
295 }
296
297 writer.write_varint(scratch.code.len() as u32);
298 for instruction in &scratch.code {
299 writer.write_u32(instruction.word());
300 }
301
302 writer.write_varint(scratch.constants.len() as u32);
303 for constant in &scratch.constants {
304 self.write_function_constant(writer, scratch, constant);
305 }
306
307 writer.write_varint(scratch.child_functions.len() as u32);
308 for child in &scratch.child_functions {
309 writer.write_varint(*child);
310 }
311
312 writer.write_varint(function.line_defined as u32);
313 writer.write_varint(function.debug_name.as_ref().copied().unwrap_or(0));
314
315 if scratch.lines.is_empty() || scratch.lines.contains(&0) {
316 writer.write_u8(0);
317 } else {
318 writer.write_u8(1);
319 Self::write_line_info(writer, &scratch.lines);
320 }
321
322 if scratch.local_vars.is_empty() && scratch.upvalues.is_empty() {
323 writer.write_u8(0);
324 } else {
325 writer.write_u8(1);
326 writer.write_varint(scratch.local_vars.len() as u32);
327 for local in &scratch.local_vars {
328 writer.write_varint(local.name);
329 writer.write_varint(local.start_pc);
330 writer.write_varint(local.end_pc);
331 writer.write_u8(local.register);
332 }
333
334 writer.write_varint(scratch.upvalues.len() as u32);
335 for upvalue in &scratch.upvalues {
336 writer.write_varint(*upvalue);
337 }
338 }
339
340 if flags::LuauEmitCallFeedback.get() {
341 writer.write_varint(scratch.feedback_slots.len() as u32);
342 for slot in &scratch.feedback_slots {
343 writer.write_u8(FEEDBACK_TYPE_CALLTARGET);
344 writer.write_varint(slot.pc);
345 }
346 } else if self.version() >= 12 {
347 writer.write_varint(0);
348 }
349
350 if self.version() >= 12 && function.flags & PROTO_FLAG_INLINABLE != 0 {
351 writer.write_varint64(function.cost);
352 }
353 }
354
355 fn write_function_constant(
356 &self,
357 writer: &mut BytecodeWriter,
358 scratch: &BytecodeBuilderScratch<'src>,
359 constant: &BytecodeBuilderConstant,
360 ) {
361 match constant {
362 BytecodeBuilderConstant::Nil => writer.write_u8(BytecodeConstantTag::Nil as u8),
363 BytecodeBuilderConstant::Boolean(value) => {
364 writer.write_u8(BytecodeConstantTag::Boolean as u8);
365 writer.write_u8(u8::from(*value));
366 }
367 BytecodeBuilderConstant::Number(value) => {
368 writer.write_u8(BytecodeConstantTag::Number as u8);
369 writer.write_f64(*value);
370 }
371 BytecodeBuilderConstant::Integer64(value) => {
372 writer.write_u8(BytecodeConstantTag::Integer as u8);
373 writer.write_integer_constant(*value);
374 }
375 BytecodeBuilderConstant::Vector(value) => {
376 writer.write_u8(BytecodeConstantTag::Vector as u8);
377 writer.write_f32(value.x());
378 writer.write_f32(value.y());
379 writer.write_f32(value.z());
380 writer.write_f32(value.w());
381 }
382 BytecodeBuilderConstant::VectorDouble(value) => {
383 if flags::LuauCompileEmitVectorDouble.get() {
384 writer.write_u8(BytecodeConstantTag::VectorDouble as u8);
385 writer.write_f64(value.x());
386 writer.write_f64(value.y());
387 writer.write_f64(value.z());
388 writer.write_f64(value.w());
389 } else {
390 writer.write_u8(BytecodeConstantTag::Vector as u8);
391 writer.write_f32(value.x() as f32);
392 writer.write_f32(value.y() as f32);
393 writer.write_f32(value.z() as f32);
394 writer.write_f32(value.w() as f32);
395 }
396 }
397 BytecodeBuilderConstant::String(value) => {
398 writer.write_u8(BytecodeConstantTag::String as u8);
399 writer.write_varint(*value);
400 }
401 BytecodeBuilderConstant::Import(value) => {
402 writer.write_u8(BytecodeConstantTag::Import as u8);
403 writer.write_u32(value.raw());
404 }
405 BytecodeBuilderConstant::Table(shape_index) => {
406 let shape = &scratch.table_shapes[*shape_index as usize];
407 let write_constants = shape.has_constants();
408 writer.write_u8(if write_constants {
409 BytecodeConstantTag::TableWithConstants as u8
410 } else {
411 BytecodeConstantTag::Table as u8
412 });
413 writer.write_varint(shape.len() as u32);
414 for entry in shape.entries() {
415 writer
416 .write_varint(u32::try_from(entry.key).expect("table key must fit varint"));
417 if write_constants {
418 writer.write_i32(entry.value.unwrap_or(-1));
419 }
420 }
421 }
422 BytecodeBuilderConstant::Closure(id) => {
423 writer.write_u8(BytecodeConstantTag::Closure as u8);
424 writer.write_varint(id.get());
425 }
426 BytecodeBuilderConstant::Class(class_index) => {
427 let class = &self.class_shapes[*class_index as usize];
428 writer.write_u8(BytecodeConstantTag::ClassShape as u8);
429 writer.write_varint(
430 u32::try_from(class.class_name).expect("class name must fit varint"),
431 );
432 writer.write_varint(class.property_names.len() as u32);
433 writer.write_varint(class.method_names.len() as u32);
434 for prop in &class.property_names {
435 writer
436 .write_varint(u32::try_from(*prop).expect("property name must fit varint"));
437 }
438 for method in &class.method_names {
439 writer
440 .write_varint(u32::try_from(*method).expect("method name must fit varint"));
441 }
442 }
443 }
444 }
445
446 fn write_line_info(writer: &mut BytecodeWriter, lines: &[i32]) {
447 debug_assert!(!lines.is_empty());
448
449 let mut span = 1usize << 24;
450
451 let mut offset = 0usize;
452 while offset < lines.len() {
453 let mut next = offset;
454 let mut min = lines[offset];
455 let mut max = lines[offset];
456
457 while next < lines.len() && next < offset + span {
458 min = min.min(lines[next]);
459 max = max.max(lines[next]);
460
461 if max - min > 255 {
462 break;
463 }
464
465 next += 1;
466 }
467
468 if next < lines.len() && next - offset < span {
469 span = 1usize << (next - offset).ilog2();
470 } else {
471 offset += span;
472 }
473 }
474
475 let baseline_size = (lines.len() - 1) / span + 1;
476 let mut baseline = vec![0i32; baseline_size];
477 for offset in (0..lines.len()).step_by(span) {
478 let end = (offset + span).min(lines.len());
479 baseline[offset / span] = *lines[offset..end]
480 .iter()
481 .min()
482 .expect("line range must be non-empty");
483 }
484
485 let log_span = span.ilog2() as u8;
486 writer.write_u8(log_span);
487
488 let mut last_offset = 0u8;
489 for (index, line) in lines.iter().copied().enumerate() {
490 let delta = line - baseline[index >> usize::from(log_span)];
491 debug_assert!((0..=255).contains(&delta));
492 let delta = delta as u8;
493 writer.write_u8(delta.wrapping_sub(last_offset));
494 last_offset = delta;
495 }
496
497 let mut last_line = 0i32;
498 for line in baseline {
499 writer.write_i32(line.wrapping_sub(last_line));
500 last_line = line;
501 }
502 }
503
504 pub(super) fn add_string_table_entry(&mut self, value: &BytecodeStringRef<'src>) -> u32 {
505 let next_index = self.string_index.len() as u32 + 1;
506 let index = self.string_index.get_or_insert_default(*value);
507
508 if *index == 0 {
511 *index = next_index;
512
513 if self.dump_flags.code() {
514 self.debug_strings.push(*value);
515 }
516 }
517
518 *index
519 }
520
521 fn base_string_table(&self) -> Vec<&[u8]> {
522 let mut strings = vec![None; self.string_index.len()];
523 for (value, index) in &self.string_index {
524 debug_assert!(*index > 0 && (*index as usize) <= strings.len());
525 strings[*index as usize - 1] = Some(value.as_bytes());
526 }
527 strings
528 .into_iter()
529 .map(|string| string.expect("base string table entry must exist"))
530 .collect()
531 }
532
533 fn assign_userdata_type_name_refs(&mut self) {
534 let base_string_refs = self
535 .string_index
536 .iter()
537 .map(|(value, index)| (value.as_bytes(), *index))
538 .collect::<Vec<_>>();
539 let mut next_index = self.string_index.len() as u32 + 1;
540
541 for index in 0..self.userdata_types.len() {
542 let (previous, current_and_rest) = self.userdata_types.split_at_mut(index);
543 let current = &mut current_and_rest[0];
544
545 if !current.used {
546 current.name_ref = 0;
547 continue;
548 }
549
550 if let Some((_, name_ref)) = base_string_refs
551 .iter()
552 .find(|(name, _)| *name == current.name.as_bytes())
553 {
554 current.name_ref = *name_ref;
555 continue;
556 }
557
558 if let Some(name_ref) = previous
559 .iter()
560 .find(|userdata_type| {
561 userdata_type.used && userdata_type.name.as_bytes() == current.name.as_bytes()
562 })
563 .map(|userdata_type| userdata_type.name_ref)
564 {
565 current.name_ref = name_ref;
566 continue;
567 }
568
569 current.name_ref = next_index;
570 next_index += 1;
571 }
572 }
573
574 fn finalized_string_table(&self) -> Vec<&[u8]> {
575 let base_count = self.string_index.len();
576 let mut strings = self
577 .base_string_table()
578 .into_iter()
579 .map(Some)
580 .collect::<Vec<_>>();
581 let final_len = self
582 .userdata_types
583 .iter()
584 .map(|userdata_type| userdata_type.name_ref as usize)
585 .max()
586 .unwrap_or(strings.len())
587 .max(strings.len());
588 strings.resize(final_len, None);
589
590 for userdata_type in &self.userdata_types {
591 if userdata_type.used {
592 let slot = &mut strings[userdata_type.name_ref as usize - 1];
593
594 if slot.is_none() && userdata_type.name_ref as usize > base_count {
595 *slot = Some(userdata_type.name.as_bytes());
596 } else {
597 debug_assert_eq!(
598 slot.expect("userdata string table entry must exist"),
599 userdata_type.name.as_bytes()
600 );
601 }
602 }
603 }
604
605 strings
606 .into_iter()
607 .map(|string| string.expect("finalized string table entry must exist"))
608 .collect()
609 }
610
611 fn write_finalized_string_table(&self, writer: &mut BytecodeWriter) {
612 let strings = self.finalized_string_table();
613
614 writer.write_varint(strings.len() as u32);
615 for string in strings {
616 writer.write_varint(string.len() as u32);
617 writer.write_bytes(string);
618 }
619 }
620
621 fn write_userdata_remapping(&self, writer: &mut BytecodeWriter) {
622 for (index, userdata_type) in self.userdata_types.iter().enumerate() {
623 if userdata_type.used {
624 let bytecode_index = u8::try_from(index + 1)
625 .expect("userdata type remapping index must fit bytecode byte");
626 writer.write_u8(bytecode_index);
627 writer.write_varint(userdata_type.name_ref);
628 }
629 }
630
631 writer.write_u8(0);
632 }
633
634 pub fn get_string_hash(key: impl AsRef<[u8]>) -> u32 {
635 let bytes = key.as_ref();
636 let mut hash = bytes.len() as u32;
637
638 for byte in bytes.iter().rev() {
639 hash ^= (hash << 5)
640 .wrapping_add(hash >> 2)
641 .wrapping_add(u32::from(*byte));
642 }
643
644 hash
645 }
646}