1use std::collections::HashMap;
10use std::convert::{TryFrom, TryInto};
11use std::rc::Rc;
12
13use object::builtins::BuiltIns;
14use object::{CompiledFunction, Object};
15use parser::lexer::token::Span;
16use strum::IntoEnumIterator;
17
18use crate::compiler::{BindingDebugInfo, Bytecode, DebugInfo, PcSpan};
19use crate::op_code::{read_operands, Instructions, Opcode, DEFINITIONS};
20
21pub const FORMAT_VERSION: u8 = 2;
25
26pub(crate) const MAGIC: [u8; 4] = *b"MBC\0";
27pub(crate) const FLAG_HAS_DEBUG_INFO: u8 = 0b0000_0001;
28
29pub(crate) const TAG_INTEGER: u8 = 1;
30pub(crate) const TAG_STRING: u8 = 2;
31pub(crate) const TAG_FUNCTION: u8 = 3;
32
33#[derive(Debug, PartialEq)]
34pub enum SnapshotWriteError {
35 UnsupportedConstant { index: usize, kind: String },
38}
39
40#[derive(Debug, PartialEq)]
41pub enum SnapshotError {
42 BadMagic,
43 UnsupportedVersion {
44 found: u8,
45 expected: u8,
46 },
47 AbiFingerprintMismatch {
48 found: u32,
49 expected: u32,
50 },
51 UnexpectedEof,
52 InvalidLeb128,
53 IntegerOverflow,
54 LimitExceeded,
56 BadTag(u8),
57 BadUtf8,
58 BadFlags(u8),
59 TrailingBytes,
60 InvalidInstruction(String),
62 DuplicateDebugEntry(usize),
63 DebugPcNotIncreasing {
64 pc: usize,
65 },
66 DebugIndexNotFunction(usize),
68 DebugPcOutOfRange {
69 pc: usize,
70 len: usize,
71 },
72 DebugSlotNotIncreasing {
74 slot: usize,
75 },
76 DebugSlotOutOfRange {
78 slot: usize,
79 num_locals: usize,
80 },
81 DebugMainBindingsNotEmpty,
84 MissingFunctionDebugInfo(usize),
87 DebugFreeCountMismatch {
90 constant_index: usize,
91 operand: usize,
92 free_names: usize,
93 },
94}
95
96lazy_static! {
97 static ref ABI_FINGERPRINT: u32 = compute_abi_fingerprint();
98}
99
100pub fn bytecode_abi_fingerprint() -> u32 {
106 *ABI_FINGERPRINT
107}
108
109fn compute_abi_fingerprint() -> u32 {
110 let mut hash = Fnv1a::new();
111 for opcode in Opcode::iter() {
112 let definition = DEFINITIONS
113 .get(&opcode)
114 .unwrap_or_else(|| panic!("opcode {:?} missing from DEFINITIONS", opcode));
115 hash.absorb_u64(opcode as u64);
116 hash.absorb_bytes(definition.name().as_bytes());
117 for &width in definition.operand_widths() {
118 hash.absorb_u64(width as u64);
119 }
120 }
121 for (index, builtin) in BuiltIns.iter().enumerate() {
122 hash.absorb_u64(index as u64);
123 hash.absorb_bytes(builtin.name.as_bytes());
124 }
125 hash.finish()
126}
127
128struct Fnv1a(u32);
130
131impl Fnv1a {
132 fn new() -> Self {
133 Fnv1a(0x811c_9dc5)
134 }
135
136 fn write(&mut self, bytes: &[u8]) {
137 for &byte in bytes {
138 self.0 ^= u32::from(byte);
139 self.0 = self.0.wrapping_mul(0x0100_0193);
140 }
141 }
142
143 fn absorb_bytes(&mut self, bytes: &[u8]) {
146 let mut length = Vec::new();
147 write_uleb128(&mut length, bytes.len() as u64);
148 self.write(&length);
149 self.write(bytes);
150 }
151
152 fn absorb_u64(&mut self, value: u64) {
153 let mut encoded = Vec::new();
154 write_uleb128(&mut encoded, value);
155 self.absorb_bytes(&encoded);
156 }
157
158 fn finish(&self) -> u32 {
159 self.0
160 }
161}
162
163pub fn write_bytecode(
169 bytecode: &Bytecode,
170 strip_debug: bool,
171) -> Result<Vec<u8>, SnapshotWriteError> {
172 let mut out = Vec::new();
173 out.extend_from_slice(&MAGIC);
174 out.push(FORMAT_VERSION);
175 out.extend_from_slice(&bytecode_abi_fingerprint().to_le_bytes());
176 out.push(if strip_debug { 0 } else { FLAG_HAS_DEBUG_INFO });
177
178 write_bytes(&mut out, &bytecode.instructions.data);
179 write_uleb128(&mut out, bytecode.constants.len() as u64);
180 for (index, constant) in bytecode.constants.iter().enumerate() {
181 write_constant(&mut out, index, constant)?;
182 }
183
184 if !strip_debug {
185 write_debug_info(&mut out, &bytecode.debug_info);
186 let mut entries: Vec<_> = bytecode.function_debug_info.iter().collect();
187 entries.sort_by_key(|(index, _)| **index);
188 write_uleb128(&mut out, entries.len() as u64);
189 for (index, debug_info) in entries {
190 write_uleb128(&mut out, *index as u64);
191 write_debug_info(&mut out, debug_info);
192 }
193 }
194 Ok(out)
195}
196
197fn write_constant(
198 out: &mut Vec<u8>,
199 index: usize,
200 constant: &Object,
201) -> Result<(), SnapshotWriteError> {
202 match constant {
203 Object::Integer(value) => {
204 out.push(TAG_INTEGER);
205 write_sleb128(out, *value);
206 }
207 Object::String(value) => {
208 out.push(TAG_STRING);
209 write_string(out, value);
210 }
211 Object::CompiledFunction(function) => {
212 out.push(TAG_FUNCTION);
213 write_string(out, &function.name);
214 write_uleb128(out, function.num_locals as u64);
215 write_uleb128(out, function.num_parameters as u64);
216 write_bytes(out, &function.instructions);
217 }
218 other => {
219 return Err(SnapshotWriteError::UnsupportedConstant {
220 index,
221 kind: object_kind(other).to_string(),
222 })
223 }
224 }
225 Ok(())
226}
227
228fn write_debug_info(out: &mut Vec<u8>, debug_info: &DebugInfo) {
229 write_uleb128(out, debug_info.pc_spans.len() as u64);
230 for pc_span in &debug_info.pc_spans {
231 write_uleb128(out, pc_span.pc as u64);
232 write_uleb128(out, pc_span.span.start as u64);
233 write_uleb128(out, pc_span.span.end as u64);
234 }
235 write_uleb128(out, debug_info.local_bindings.len() as u64);
236 for binding in &debug_info.local_bindings {
237 write_uleb128(out, binding.slot as u64);
238 write_string(out, &binding.name);
239 }
240 write_uleb128(out, debug_info.free_names.len() as u64);
241 for name in &debug_info.free_names {
242 write_string(out, name);
243 }
244}
245
246fn write_string(out: &mut Vec<u8>, value: &str) {
247 write_bytes(out, value.as_bytes());
248}
249
250fn write_bytes(out: &mut Vec<u8>, bytes: &[u8]) {
251 write_uleb128(out, bytes.len() as u64);
252 out.extend_from_slice(bytes);
253}
254
255pub(crate) fn write_uleb128(out: &mut Vec<u8>, mut value: u64) {
256 loop {
257 let byte = (value & 0x7f) as u8;
258 value >>= 7;
259 if value == 0 {
260 out.push(byte);
261 return;
262 }
263 out.push(byte | 0x80);
264 }
265}
266
267pub(crate) fn write_sleb128(out: &mut Vec<u8>, mut value: i64) {
268 loop {
269 let byte = (value & 0x7f) as u8;
270 value >>= 7;
271 let sign_bit_clear = byte & 0x40 == 0;
272 if (value == 0 && sign_bit_clear) || (value == -1 && !sign_bit_clear) {
273 out.push(byte);
274 return;
275 }
276 out.push(byte | 0x80);
277 }
278}
279
280fn object_kind(object: &Object) -> &'static str {
281 match object {
282 Object::Integer(_) => "Integer",
283 Object::Boolean(_) => "Boolean",
284 Object::String(_) => "String",
285 Object::Array(_) => "Array",
286 Object::Hash(_) => "Hash",
287 Object::Null => "Null",
288 Object::ReturnValue(_) => "ReturnValue",
289 Object::Function(..) => "Function",
290 Object::Builtin(_) => "Builtin",
291 Object::Error(_) => "Error",
292 Object::CompiledFunction(_) => "CompiledFunction",
293 Object::ClosureObj(_) => "Closure",
294 Object::Class(_) => "Class",
295 Object::Instance(_) => "Instance",
296 Object::BoundMethod(_) => "BoundMethod",
297 }
298}
299
300pub fn read_bytecode(buf: &[u8]) -> Result<Bytecode, SnapshotError> {
304 let mut reader = Reader::new(buf);
305
306 let magic = reader.read_exact(MAGIC.len())?;
307 if magic != MAGIC {
308 return Err(SnapshotError::BadMagic);
309 }
310 let version = reader.read_u8()?;
311 if version != FORMAT_VERSION {
312 return Err(SnapshotError::UnsupportedVersion {
313 found: version,
314 expected: FORMAT_VERSION,
315 });
316 }
317 let found = u32::from_le_bytes(reader.read_exact(4)?.try_into().unwrap());
318 let expected = bytecode_abi_fingerprint();
319 if found != expected {
320 return Err(SnapshotError::AbiFingerprintMismatch {
321 found,
322 expected,
323 });
324 }
325 let flags = reader.read_u8()?;
326 if flags & !FLAG_HAS_DEBUG_INFO != 0 {
327 return Err(SnapshotError::BadFlags(flags));
328 }
329 let has_debug = flags & FLAG_HAS_DEBUG_INFO != 0;
330
331 let main_instructions = reader.read_length_prefixed_bytes()?.to_vec();
332 let constant_count = reader.read_count()?;
333 let mut constants: Vec<Rc<Object>> = Vec::with_capacity(constant_count);
334 for _ in 0..constant_count {
335 constants.push(Rc::new(read_constant(&mut reader)?));
336 }
337
338 let (debug_info, function_debug_info) = if has_debug {
339 read_debug_section(&mut reader, &constants, main_instructions.len())?
340 } else {
341 (DebugInfo::default(), HashMap::new())
342 };
343
344 if reader.remaining() != 0 {
345 return Err(SnapshotError::TrailingBytes);
346 }
347
348 let closure_debug = has_debug.then_some(&function_debug_info);
349 validate_instruction_stream("main", &main_instructions, &constants, closure_debug)?;
350 for (index, constant) in constants.iter().enumerate() {
351 if let Object::CompiledFunction(function) = constant.as_ref() {
352 validate_instruction_stream(
353 &format!("constant {}", index),
354 &function.instructions,
355 &constants,
356 closure_debug,
357 )?;
358 }
359 }
360
361 Ok(Bytecode {
362 instructions: Instructions {
363 data: main_instructions,
364 },
365 constants,
366 debug_info,
367 function_debug_info,
368 })
369}
370
371fn read_constant(reader: &mut Reader) -> Result<Object, SnapshotError> {
372 let tag = reader.read_u8()?;
373 match tag {
374 TAG_INTEGER => Ok(Object::Integer(reader.read_sleb128()?)),
375 TAG_STRING => Ok(Object::String(reader.read_string()?)),
376 TAG_FUNCTION => {
377 let name = reader.read_string()?;
378 let num_locals = reader.read_usize()?;
379 let num_parameters = reader.read_usize()?;
380 let instructions = reader.read_length_prefixed_bytes()?.to_vec();
381 Ok(Object::CompiledFunction(Rc::new(CompiledFunction {
382 name,
383 instructions,
384 num_locals,
385 num_parameters,
386 })))
387 }
388 other => Err(SnapshotError::BadTag(other)),
389 }
390}
391
392fn read_debug_section(
393 reader: &mut Reader,
394 constants: &[Rc<Object>],
395 main_len: usize,
396) -> Result<(DebugInfo, HashMap<usize, DebugInfo>), SnapshotError> {
397 let main_debug = read_debug_info(reader, main_len)?;
398 if !main_debug.local_bindings.is_empty() || !main_debug.free_names.is_empty() {
399 return Err(SnapshotError::DebugMainBindingsNotEmpty);
400 }
401 let entry_count = reader.read_count()?;
402 let mut function_debug_info = HashMap::with_capacity(entry_count);
403 for _ in 0..entry_count {
404 let constant_index = reader.read_usize()?;
405 let (function_len, num_locals) = match constants.get(constant_index).map(Rc::as_ref) {
406 Some(Object::CompiledFunction(function)) => {
407 (function.instructions.len(), function.num_locals)
408 }
409 _ => return Err(SnapshotError::DebugIndexNotFunction(constant_index)),
410 };
411 let debug_info = read_debug_info(reader, function_len)?;
412 validate_local_bindings(&debug_info.local_bindings, num_locals)?;
413 if function_debug_info
414 .insert(constant_index, debug_info)
415 .is_some()
416 {
417 return Err(SnapshotError::DuplicateDebugEntry(constant_index));
418 }
419 }
420 Ok((main_debug, function_debug_info))
421}
422
423fn validate_local_bindings(
427 bindings: &[BindingDebugInfo],
428 num_locals: usize,
429) -> Result<(), SnapshotError> {
430 let mut previous: Option<usize> = None;
431 for binding in bindings {
432 if previous.is_some_and(|previous| binding.slot <= previous) {
433 return Err(SnapshotError::DebugSlotNotIncreasing {
434 slot: binding.slot,
435 });
436 }
437 if binding.slot >= num_locals {
438 return Err(SnapshotError::DebugSlotOutOfRange {
439 slot: binding.slot,
440 num_locals,
441 });
442 }
443 previous = Some(binding.slot);
444 }
445 Ok(())
446}
447
448fn read_debug_info(
449 reader: &mut Reader,
450 instruction_len: usize,
451) -> Result<DebugInfo, SnapshotError> {
452 let count = reader.read_count()?;
453 let mut pc_spans = Vec::with_capacity(count);
454 let mut previous: Option<usize> = None;
455 for _ in 0..count {
456 let pc = reader.read_usize()?;
457 if let Some(previous) = previous {
458 if pc <= previous {
459 return Err(SnapshotError::DebugPcNotIncreasing {
460 pc,
461 });
462 }
463 }
464 if pc > instruction_len {
465 return Err(SnapshotError::DebugPcOutOfRange {
466 pc,
467 len: instruction_len,
468 });
469 }
470 let start = reader.read_usize()?;
471 let end = reader.read_usize()?;
472 pc_spans.push(PcSpan {
473 pc,
474 span: Span {
475 start,
476 end,
477 },
478 });
479 previous = Some(pc);
480 }
481 let binding_count = reader.read_count()?;
482 let mut local_bindings = Vec::with_capacity(binding_count);
483 for _ in 0..binding_count {
484 let slot = reader.read_usize()?;
485 let name = reader.read_string()?;
486 local_bindings.push(BindingDebugInfo {
487 name,
488 slot,
489 });
490 }
491 let free_count = reader.read_count()?;
492 let mut free_names = Vec::with_capacity(free_count);
493 for _ in 0..free_count {
494 free_names.push(reader.read_string()?);
495 }
496 Ok(DebugInfo {
497 pc_spans,
498 local_bindings,
499 free_names,
500 })
501}
502
503fn validate_instruction_stream(
516 stream: &str,
517 instructions: &[u8],
518 constants: &[Rc<Object>],
519 debug: Option<&HashMap<usize, DebugInfo>>,
520) -> Result<(), SnapshotError> {
521 let len = instructions.len();
522 let mut is_boundary = vec![false; len + 1];
523 let mut jumps: Vec<(usize, usize)> = Vec::new();
524 let mut offset = 0;
525 while offset < len {
526 is_boundary[offset] = true;
527 let byte = instructions[offset];
528 let opcode = Opcode::from_repr(byte)
529 .ok_or_else(|| invalid(stream, offset, format!("unknown opcode 0x{:02x}", byte)))?;
530 let definition = DEFINITIONS.get(&opcode).expect("missing opcode definition");
531 let operand_len: usize = definition
532 .operand_widths()
533 .iter()
534 .map(|w| *w as usize)
535 .sum();
536 if offset + 1 + operand_len > len {
537 return Err(invalid(
538 stream,
539 offset,
540 format!("truncated operands for {}", definition.name()),
541 ));
542 }
543 let (operands, _) = read_operands(definition, &instructions[offset + 1..]);
544 match opcode {
545 Opcode::OpJump | Opcode::OpJumpNotTruthy => jumps.push((offset, operands[0])),
546 Opcode::OpConst => {
547 if operands[0] >= constants.len() {
548 return Err(invalid(
549 stream,
550 offset,
551 format!("constant index {} out of range", operands[0]),
552 ));
553 }
554 }
555 Opcode::OpClosure => {
556 let index = operands[0];
557 if !matches!(
558 constants.get(index).map(Rc::as_ref),
559 Some(Object::CompiledFunction(_))
560 ) {
561 return Err(invalid(
562 stream,
563 offset,
564 format!("OpClosure needs a function constant at index {}", index),
565 ));
566 }
567 if let Some(debug) = debug {
568 let info = debug
569 .get(&index)
570 .ok_or(SnapshotError::MissingFunctionDebugInfo(index))?;
571 if operands[1] != info.free_names.len() {
572 return Err(SnapshotError::DebugFreeCountMismatch {
573 constant_index: index,
574 operand: operands[1],
575 free_names: info.free_names.len(),
576 });
577 }
578 }
579 }
580 Opcode::OpClass | Opcode::OpMethod | Opcode::OpGetProperty | Opcode::OpSetProperty => {
581 let index = operands[0];
582 if !matches!(constants.get(index).map(Rc::as_ref), Some(Object::String(_))) {
583 return Err(invalid(
584 stream,
585 offset,
586 format!("{} needs a string constant at index {}", definition.name(), index),
587 ));
588 }
589 }
590 Opcode::OpGetBuiltin => {
591 if operands[0] >= BuiltIns.len() {
592 return Err(invalid(
593 stream,
594 offset,
595 format!("builtin index {} out of range", operands[0]),
596 ));
597 }
598 }
599 Opcode::OpHash if operands[0] % 2 != 0 => {
600 return Err(invalid(
601 stream,
602 offset,
603 format!("OpHash needs an even element count, got {}", operands[0]),
604 ));
605 }
606 _ => {}
607 }
608 offset += 1 + operand_len;
609 }
610 is_boundary[len] = true;
611 for (offset, target) in jumps {
612 if target > len || !is_boundary[target] {
613 return Err(invalid(
614 stream,
615 offset,
616 format!("jump target {} is not an instruction boundary", target),
617 ));
618 }
619 }
620 Ok(())
621}
622
623fn invalid(stream: &str, offset: usize, message: String) -> SnapshotError {
624 SnapshotError::InvalidInstruction(format!("{} (stream {}, offset {})", message, stream, offset))
625}
626
627pub(crate) struct Reader<'a> {
628 buf: &'a [u8],
629 pos: usize,
630}
631
632impl<'a> Reader<'a> {
633 pub(crate) fn new(buf: &'a [u8]) -> Self {
634 Reader {
635 buf,
636 pos: 0,
637 }
638 }
639
640 pub(crate) fn position(&self) -> usize {
643 self.pos
644 }
645
646 fn remaining(&self) -> usize {
647 self.buf.len() - self.pos
648 }
649
650 pub(crate) fn read_u8(&mut self) -> Result<u8, SnapshotError> {
651 let byte = *self.buf.get(self.pos).ok_or(SnapshotError::UnexpectedEof)?;
652 self.pos += 1;
653 Ok(byte)
654 }
655
656 pub(crate) fn read_exact(&mut self, len: usize) -> Result<&'a [u8], SnapshotError> {
657 if len > self.remaining() {
658 return Err(SnapshotError::UnexpectedEof);
659 }
660 let slice = &self.buf[self.pos..self.pos + len];
661 self.pos += len;
662 Ok(slice)
663 }
664
665 pub(crate) fn read_uleb128(&mut self) -> Result<u64, SnapshotError> {
668 let mut result: u64 = 0;
669 let mut shift = 0u32;
670 for _ in 0..10 {
671 let byte = self.read_u8()?;
672 let bits = u64::from(byte & 0x7f);
673 if shift == 63 && bits > 1 {
674 return Err(SnapshotError::InvalidLeb128);
675 }
676 result |= bits << shift;
677 if byte & 0x80 == 0 {
678 return Ok(result);
679 }
680 shift += 7;
681 }
682 Err(SnapshotError::InvalidLeb128)
683 }
684
685 pub(crate) fn read_sleb128(&mut self) -> Result<i64, SnapshotError> {
686 let mut result: i64 = 0;
687 let mut shift = 0u32;
688 for _ in 0..10 {
689 let byte = self.read_u8()?;
690 let bits = i64::from(byte & 0x7f);
691 if shift == 63 {
692 if byte & 0x80 != 0 || (bits != 0 && bits != 0x7f) {
695 return Err(SnapshotError::InvalidLeb128);
696 }
697 return Ok(result | bits.wrapping_shl(63));
698 }
699 result |= bits << shift;
700 if byte & 0x80 == 0 {
701 if byte & 0x40 != 0 {
702 result |= -1i64 << (shift + 7);
703 }
704 return Ok(result);
705 }
706 shift += 7;
707 }
708 Err(SnapshotError::InvalidLeb128)
709 }
710
711 pub(crate) fn read_usize(&mut self) -> Result<usize, SnapshotError> {
713 let value = self.read_uleb128()?;
714 usize::try_from(value).map_err(|_| SnapshotError::IntegerOverflow)
715 }
716
717 fn read_count(&mut self) -> Result<usize, SnapshotError> {
721 let count = self.read_usize()?;
722 if count > self.remaining() {
723 return Err(SnapshotError::LimitExceeded);
724 }
725 Ok(count)
726 }
727
728 fn read_length_prefixed_bytes(&mut self) -> Result<&'a [u8], SnapshotError> {
729 let len = self.read_usize()?;
730 if len > self.remaining() {
731 return Err(SnapshotError::LimitExceeded);
732 }
733 self.read_exact(len)
734 }
735
736 fn read_string(&mut self) -> Result<String, SnapshotError> {
737 let bytes = self.read_length_prefixed_bytes()?;
738 String::from_utf8(bytes.to_vec()).map_err(|_| SnapshotError::BadUtf8)
739 }
740}