1use std::cell::RefCell;
2use std::collections::BTreeMap;
3use std::fmt;
4use std::rc::Rc;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8#[repr(u8)]
9pub enum Op {
10 Constant, Nil,
14 True,
16 False,
18
19 GetVar, DefLet, DefVar, SetVar, PushScope,
30 PopScope,
32
33 Add,
35 Sub,
36 Mul,
37 Div,
38 Mod,
39 Pow,
40 Negate,
41
42 Equal,
44 NotEqual,
45 Less,
46 Greater,
47 LessEqual,
48 GreaterEqual,
49
50 Not,
52
53 Jump,
56 JumpIfFalse,
58 JumpIfTrue,
60 Pop,
62
63 Call,
66 TailCall,
70 Return,
72 Closure,
74
75 BuildList,
78 BuildDict,
80 Subscript,
82 SubscriptOpt,
85 Slice,
87
88 GetProperty,
91 GetPropertyOpt,
94 SetProperty,
97 SetSubscript,
100 MethodCall,
102 MethodCallOpt,
105
106 Concat,
109
110 IterInit,
113 IterNext,
116
117 Pipe,
120
121 Throw,
124 TryCatchSetup,
126 PopHandler,
128
129 Parallel,
133 ParallelMap,
136 ParallelSettle,
139 Spawn,
142 SyncMutexEnter,
145
146 Import,
149 SelectiveImport,
151
152 DeadlineSetup,
155 DeadlineEnd,
157
158 BuildEnum,
163
164 MatchEnum,
169
170 PopIterator,
173
174 GetArgc,
177
178 CheckType,
184
185 TryUnwrap,
188
189 CallSpread,
192 CallBuiltin,
195 CallBuiltinSpread,
198 MethodCallSpread,
201
202 Dup,
205 Swap,
207 Contains,
210
211 AddInt,
213 SubInt,
214 MulInt,
215 DivInt,
216 ModInt,
217 AddFloat,
218 SubFloat,
219 MulFloat,
220 DivFloat,
221 ModFloat,
222 EqualInt,
223 NotEqualInt,
224 LessInt,
225 GreaterInt,
226 LessEqualInt,
227 GreaterEqualInt,
228 EqualFloat,
229 NotEqualFloat,
230 LessFloat,
231 GreaterFloat,
232 LessEqualFloat,
233 GreaterEqualFloat,
234 EqualBool,
235 NotEqualBool,
236 EqualString,
237 NotEqualString,
238
239 Yield,
241
242 GetLocalSlot,
245 DefLocalSlot,
247 SetLocalSlot,
249}
250
251impl Op {
252 pub(crate) const ALL: &'static [Self] = &[
253 Op::Constant,
254 Op::Nil,
255 Op::True,
256 Op::False,
257 Op::GetVar,
258 Op::DefLet,
259 Op::DefVar,
260 Op::SetVar,
261 Op::PushScope,
262 Op::PopScope,
263 Op::Add,
264 Op::Sub,
265 Op::Mul,
266 Op::Div,
267 Op::Mod,
268 Op::Pow,
269 Op::Negate,
270 Op::Equal,
271 Op::NotEqual,
272 Op::Less,
273 Op::Greater,
274 Op::LessEqual,
275 Op::GreaterEqual,
276 Op::Not,
277 Op::Jump,
278 Op::JumpIfFalse,
279 Op::JumpIfTrue,
280 Op::Pop,
281 Op::Call,
282 Op::TailCall,
283 Op::Return,
284 Op::Closure,
285 Op::BuildList,
286 Op::BuildDict,
287 Op::Subscript,
288 Op::SubscriptOpt,
289 Op::Slice,
290 Op::GetProperty,
291 Op::GetPropertyOpt,
292 Op::SetProperty,
293 Op::SetSubscript,
294 Op::MethodCall,
295 Op::MethodCallOpt,
296 Op::Concat,
297 Op::IterInit,
298 Op::IterNext,
299 Op::Pipe,
300 Op::Throw,
301 Op::TryCatchSetup,
302 Op::PopHandler,
303 Op::Parallel,
304 Op::ParallelMap,
305 Op::ParallelSettle,
306 Op::Spawn,
307 Op::SyncMutexEnter,
308 Op::Import,
309 Op::SelectiveImport,
310 Op::DeadlineSetup,
311 Op::DeadlineEnd,
312 Op::BuildEnum,
313 Op::MatchEnum,
314 Op::PopIterator,
315 Op::GetArgc,
316 Op::CheckType,
317 Op::TryUnwrap,
318 Op::CallSpread,
319 Op::CallBuiltin,
320 Op::CallBuiltinSpread,
321 Op::MethodCallSpread,
322 Op::Dup,
323 Op::Swap,
324 Op::Contains,
325 Op::AddInt,
326 Op::SubInt,
327 Op::MulInt,
328 Op::DivInt,
329 Op::ModInt,
330 Op::AddFloat,
331 Op::SubFloat,
332 Op::MulFloat,
333 Op::DivFloat,
334 Op::ModFloat,
335 Op::EqualInt,
336 Op::NotEqualInt,
337 Op::LessInt,
338 Op::GreaterInt,
339 Op::LessEqualInt,
340 Op::GreaterEqualInt,
341 Op::EqualFloat,
342 Op::NotEqualFloat,
343 Op::LessFloat,
344 Op::GreaterFloat,
345 Op::LessEqualFloat,
346 Op::GreaterEqualFloat,
347 Op::EqualBool,
348 Op::NotEqualBool,
349 Op::EqualString,
350 Op::NotEqualString,
351 Op::Yield,
352 Op::GetLocalSlot,
353 Op::DefLocalSlot,
354 Op::SetLocalSlot,
355 ];
356
357 pub(crate) fn from_byte(byte: u8) -> Option<Self> {
358 Self::ALL.get(byte as usize).copied()
359 }
360}
361
362#[derive(Debug, Clone, PartialEq)]
364pub enum Constant {
365 Int(i64),
366 Float(f64),
367 String(String),
368 Bool(bool),
369 Nil,
370 Duration(u64),
371}
372
373#[derive(Debug, Clone, PartialEq, Eq)]
381pub(crate) enum InlineCacheEntry {
382 Empty,
383 Property {
384 name_idx: u16,
385 target: PropertyCacheTarget,
386 },
387 Method {
388 name_idx: u16,
389 argc: usize,
390 target: MethodCacheTarget,
391 },
392}
393
394#[derive(Debug, Clone, Copy, PartialEq, Eq)]
395pub(crate) enum PropertyCacheTarget {
396 ListCount,
397 ListEmpty,
398 ListFirst,
399 ListLast,
400 StringCount,
401 StringEmpty,
402 PairFirst,
403 PairSecond,
404 EnumVariant,
405 EnumFields,
406}
407
408#[derive(Debug, Clone, Copy, PartialEq, Eq)]
409pub(crate) enum MethodCacheTarget {
410 ListCount,
411 ListEmpty,
412 StringCount,
413 StringEmpty,
414 DictCount,
415 RangeCount,
416 RangeLen,
417 RangeEmpty,
418 RangeFirst,
419 RangeLast,
420 SetCount,
421 SetLen,
422 SetEmpty,
423}
424
425#[derive(Debug, Clone, PartialEq, Eq)]
427pub struct LocalSlotInfo {
428 pub name: String,
429 pub mutable: bool,
430 pub scope_depth: usize,
431}
432
433impl fmt::Display for Constant {
434 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
435 match self {
436 Constant::Int(n) => write!(f, "{n}"),
437 Constant::Float(n) => write!(f, "{n}"),
438 Constant::String(s) => write!(f, "\"{s}\""),
439 Constant::Bool(b) => write!(f, "{b}"),
440 Constant::Nil => write!(f, "nil"),
441 Constant::Duration(ms) => write!(f, "{ms}ms"),
442 }
443 }
444}
445
446#[derive(Debug, Clone)]
448pub struct Chunk {
449 pub code: Vec<u8>,
451 pub constants: Vec<Constant>,
453 pub lines: Vec<u32>,
455 pub columns: Vec<u32>,
458 pub source_file: Option<String>,
463 current_col: u32,
465 pub functions: Vec<CompiledFunctionRef>,
467 inline_cache_slots: BTreeMap<usize, usize>,
470 inline_caches: Rc<RefCell<Vec<InlineCacheEntry>>>,
473 pub(crate) local_slots: Vec<LocalSlotInfo>,
475}
476
477pub type ChunkRef = Rc<Chunk>;
478pub type CompiledFunctionRef = Rc<CompiledFunction>;
479
480#[derive(Debug, Clone)]
482pub struct CompiledFunction {
483 pub name: String,
484 pub params: Vec<String>,
485 pub default_start: Option<usize>,
487 pub chunk: ChunkRef,
488 pub is_generator: bool,
490 pub has_rest_param: bool,
492}
493
494impl Chunk {
495 pub fn new() -> Self {
496 Self {
497 code: Vec::new(),
498 constants: Vec::new(),
499 lines: Vec::new(),
500 columns: Vec::new(),
501 source_file: None,
502 current_col: 0,
503 functions: Vec::new(),
504 inline_cache_slots: BTreeMap::new(),
505 inline_caches: Rc::new(RefCell::new(Vec::new())),
506 local_slots: Vec::new(),
507 }
508 }
509
510 pub fn set_column(&mut self, col: u32) {
512 self.current_col = col;
513 }
514
515 pub fn add_constant(&mut self, constant: Constant) -> u16 {
517 for (i, c) in self.constants.iter().enumerate() {
518 if c == &constant {
519 return i as u16;
520 }
521 }
522 let idx = self.constants.len();
523 self.constants.push(constant);
524 idx as u16
525 }
526
527 pub fn emit(&mut self, op: Op, line: u32) {
529 let col = self.current_col;
530 self.code.push(op as u8);
531 self.lines.push(line);
532 self.columns.push(col);
533 }
534
535 pub fn emit_u16(&mut self, op: Op, arg: u16, line: u32) {
537 let col = self.current_col;
538 let op_offset = self.code.len();
539 self.code.push(op as u8);
540 self.code.push((arg >> 8) as u8);
541 self.code.push((arg & 0xFF) as u8);
542 self.lines.push(line);
543 self.lines.push(line);
544 self.lines.push(line);
545 self.columns.push(col);
546 self.columns.push(col);
547 self.columns.push(col);
548 if matches!(
549 op,
550 Op::GetProperty | Op::GetPropertyOpt | Op::MethodCallSpread
551 ) {
552 self.register_inline_cache(op_offset);
553 }
554 }
555
556 pub fn emit_u8(&mut self, op: Op, arg: u8, line: u32) {
558 let col = self.current_col;
559 self.code.push(op as u8);
560 self.code.push(arg);
561 self.lines.push(line);
562 self.lines.push(line);
563 self.columns.push(col);
564 self.columns.push(col);
565 }
566
567 pub fn emit_call_builtin(
569 &mut self,
570 id: crate::BuiltinId,
571 name_idx: u16,
572 arg_count: u8,
573 line: u32,
574 ) {
575 let col = self.current_col;
576 self.code.push(Op::CallBuiltin as u8);
577 self.code.extend_from_slice(&id.raw().to_be_bytes());
578 self.code.push((name_idx >> 8) as u8);
579 self.code.push((name_idx & 0xFF) as u8);
580 self.code.push(arg_count);
581 for _ in 0..12 {
582 self.lines.push(line);
583 self.columns.push(col);
584 }
585 }
586
587 pub fn emit_call_builtin_spread(&mut self, id: crate::BuiltinId, name_idx: u16, line: u32) {
589 let col = self.current_col;
590 self.code.push(Op::CallBuiltinSpread as u8);
591 self.code.extend_from_slice(&id.raw().to_be_bytes());
592 self.code.push((name_idx >> 8) as u8);
593 self.code.push((name_idx & 0xFF) as u8);
594 for _ in 0..11 {
595 self.lines.push(line);
596 self.columns.push(col);
597 }
598 }
599
600 pub fn emit_method_call(&mut self, name_idx: u16, arg_count: u8, line: u32) {
602 self.emit_method_call_inner(Op::MethodCall, name_idx, arg_count, line);
603 }
604
605 pub fn emit_method_call_opt(&mut self, name_idx: u16, arg_count: u8, line: u32) {
607 self.emit_method_call_inner(Op::MethodCallOpt, name_idx, arg_count, line);
608 }
609
610 fn emit_method_call_inner(&mut self, op: Op, name_idx: u16, arg_count: u8, line: u32) {
611 let col = self.current_col;
612 let op_offset = self.code.len();
613 self.code.push(op as u8);
614 self.code.push((name_idx >> 8) as u8);
615 self.code.push((name_idx & 0xFF) as u8);
616 self.code.push(arg_count);
617 self.lines.push(line);
618 self.lines.push(line);
619 self.lines.push(line);
620 self.lines.push(line);
621 self.columns.push(col);
622 self.columns.push(col);
623 self.columns.push(col);
624 self.columns.push(col);
625 self.register_inline_cache(op_offset);
626 }
627
628 pub fn current_offset(&self) -> usize {
630 self.code.len()
631 }
632
633 pub fn emit_jump(&mut self, op: Op, line: u32) -> usize {
635 let col = self.current_col;
636 self.code.push(op as u8);
637 let patch_pos = self.code.len();
638 self.code.push(0xFF);
639 self.code.push(0xFF);
640 self.lines.push(line);
641 self.lines.push(line);
642 self.lines.push(line);
643 self.columns.push(col);
644 self.columns.push(col);
645 self.columns.push(col);
646 patch_pos
647 }
648
649 pub fn patch_jump(&mut self, patch_pos: usize) {
651 let target = self.code.len() as u16;
652 self.code[patch_pos] = (target >> 8) as u8;
653 self.code[patch_pos + 1] = (target & 0xFF) as u8;
654 }
655
656 pub fn patch_jump_to(&mut self, patch_pos: usize, target: usize) {
658 let target = target as u16;
659 self.code[patch_pos] = (target >> 8) as u8;
660 self.code[patch_pos + 1] = (target & 0xFF) as u8;
661 }
662
663 pub fn read_u16(&self, pos: usize) -> u16 {
665 ((self.code[pos] as u16) << 8) | (self.code[pos + 1] as u16)
666 }
667
668 fn register_inline_cache(&mut self, op_offset: usize) {
669 if self.inline_cache_slots.contains_key(&op_offset) {
670 return;
671 }
672 let mut entries = self.inline_caches.borrow_mut();
673 let slot = entries.len();
674 entries.push(InlineCacheEntry::Empty);
675 self.inline_cache_slots.insert(op_offset, slot);
676 }
677
678 pub(crate) fn inline_cache_slot(&self, op_offset: usize) -> Option<usize> {
679 self.inline_cache_slots.get(&op_offset).copied()
680 }
681
682 pub(crate) fn inline_cache_entry(&self, slot: usize) -> InlineCacheEntry {
683 self.inline_caches
684 .borrow()
685 .get(slot)
686 .cloned()
687 .unwrap_or(InlineCacheEntry::Empty)
688 }
689
690 pub(crate) fn set_inline_cache_entry(&self, slot: usize, entry: InlineCacheEntry) {
691 if let Some(existing) = self.inline_caches.borrow_mut().get_mut(slot) {
692 *existing = entry;
693 }
694 }
695
696 pub(crate) fn add_local_slot(
697 &mut self,
698 name: String,
699 mutable: bool,
700 scope_depth: usize,
701 ) -> u16 {
702 let idx = self.local_slots.len();
703 self.local_slots.push(LocalSlotInfo {
704 name,
705 mutable,
706 scope_depth,
707 });
708 idx as u16
709 }
710
711 #[cfg(test)]
712 pub(crate) fn inline_cache_entries(&self) -> Vec<InlineCacheEntry> {
713 self.inline_caches.borrow().clone()
714 }
715
716 pub fn read_u64(&self, pos: usize) -> u64 {
718 u64::from_be_bytes([
719 self.code[pos],
720 self.code[pos + 1],
721 self.code[pos + 2],
722 self.code[pos + 3],
723 self.code[pos + 4],
724 self.code[pos + 5],
725 self.code[pos + 6],
726 self.code[pos + 7],
727 ])
728 }
729
730 pub fn disassemble(&self, name: &str) -> String {
732 let mut out = format!("== {name} ==\n");
733 let mut ip = 0;
734 while ip < self.code.len() {
735 let op = self.code[ip];
736 let line = self.lines.get(ip).copied().unwrap_or(0);
737 out.push_str(&format!("{:04} [{:>4}] ", ip, line));
738 ip += 1;
739
740 match op {
741 x if x == Op::Constant as u8 => {
742 let idx = self.read_u16(ip);
743 ip += 2;
744 let val = &self.constants[idx as usize];
745 out.push_str(&format!("CONSTANT {:>4} ({})\n", idx, val));
746 }
747 x if x == Op::Nil as u8 => out.push_str("NIL\n"),
748 x if x == Op::True as u8 => out.push_str("TRUE\n"),
749 x if x == Op::False as u8 => out.push_str("FALSE\n"),
750 x if x == Op::GetVar as u8 => {
751 let idx = self.read_u16(ip);
752 ip += 2;
753 out.push_str(&format!(
754 "GET_VAR {:>4} ({})\n",
755 idx, self.constants[idx as usize]
756 ));
757 }
758 x if x == Op::DefLet as u8 => {
759 let idx = self.read_u16(ip);
760 ip += 2;
761 out.push_str(&format!(
762 "DEF_LET {:>4} ({})\n",
763 idx, self.constants[idx as usize]
764 ));
765 }
766 x if x == Op::DefVar as u8 => {
767 let idx = self.read_u16(ip);
768 ip += 2;
769 out.push_str(&format!(
770 "DEF_VAR {:>4} ({})\n",
771 idx, self.constants[idx as usize]
772 ));
773 }
774 x if x == Op::SetVar as u8 => {
775 let idx = self.read_u16(ip);
776 ip += 2;
777 out.push_str(&format!(
778 "SET_VAR {:>4} ({})\n",
779 idx, self.constants[idx as usize]
780 ));
781 }
782 x if x == Op::GetLocalSlot as u8 => {
783 let slot = self.read_u16(ip);
784 ip += 2;
785 out.push_str(&format!("GET_LOCAL_SLOT {:>4}", slot));
786 if let Some(info) = self.local_slots.get(slot as usize) {
787 out.push_str(&format!(" ({})", info.name));
788 }
789 out.push('\n');
790 }
791 x if x == Op::DefLocalSlot as u8 => {
792 let slot = self.read_u16(ip);
793 ip += 2;
794 out.push_str(&format!("DEF_LOCAL_SLOT {:>4}", slot));
795 if let Some(info) = self.local_slots.get(slot as usize) {
796 out.push_str(&format!(" ({})", info.name));
797 }
798 out.push('\n');
799 }
800 x if x == Op::SetLocalSlot as u8 => {
801 let slot = self.read_u16(ip);
802 ip += 2;
803 out.push_str(&format!("SET_LOCAL_SLOT {:>4}", slot));
804 if let Some(info) = self.local_slots.get(slot as usize) {
805 out.push_str(&format!(" ({})", info.name));
806 }
807 out.push('\n');
808 }
809 x if x == Op::PushScope as u8 => out.push_str("PUSH_SCOPE\n"),
810 x if x == Op::PopScope as u8 => out.push_str("POP_SCOPE\n"),
811 x if x == Op::Add as u8 => out.push_str("ADD\n"),
812 x if x == Op::Sub as u8 => out.push_str("SUB\n"),
813 x if x == Op::Mul as u8 => out.push_str("MUL\n"),
814 x if x == Op::Div as u8 => out.push_str("DIV\n"),
815 x if x == Op::Mod as u8 => out.push_str("MOD\n"),
816 x if x == Op::Pow as u8 => out.push_str("POW\n"),
817 x if x == Op::Negate as u8 => out.push_str("NEGATE\n"),
818 x if x == Op::Equal as u8 => out.push_str("EQUAL\n"),
819 x if x == Op::NotEqual as u8 => out.push_str("NOT_EQUAL\n"),
820 x if x == Op::Less as u8 => out.push_str("LESS\n"),
821 x if x == Op::Greater as u8 => out.push_str("GREATER\n"),
822 x if x == Op::LessEqual as u8 => out.push_str("LESS_EQUAL\n"),
823 x if x == Op::GreaterEqual as u8 => out.push_str("GREATER_EQUAL\n"),
824 x if x == Op::Contains as u8 => out.push_str("CONTAINS\n"),
825 x if x == Op::Not as u8 => out.push_str("NOT\n"),
826 x if x == Op::Jump as u8 => {
827 let target = self.read_u16(ip);
828 ip += 2;
829 out.push_str(&format!("JUMP {:>4}\n", target));
830 }
831 x if x == Op::JumpIfFalse as u8 => {
832 let target = self.read_u16(ip);
833 ip += 2;
834 out.push_str(&format!("JUMP_IF_FALSE {:>4}\n", target));
835 }
836 x if x == Op::JumpIfTrue as u8 => {
837 let target = self.read_u16(ip);
838 ip += 2;
839 out.push_str(&format!("JUMP_IF_TRUE {:>4}\n", target));
840 }
841 x if x == Op::Pop as u8 => out.push_str("POP\n"),
842 x if x == Op::Call as u8 => {
843 let argc = self.code[ip];
844 ip += 1;
845 out.push_str(&format!("CALL {:>4}\n", argc));
846 }
847 x if x == Op::TailCall as u8 => {
848 let argc = self.code[ip];
849 ip += 1;
850 out.push_str(&format!("TAIL_CALL {:>4}\n", argc));
851 }
852 x if x == Op::Return as u8 => out.push_str("RETURN\n"),
853 x if x == Op::Closure as u8 => {
854 let idx = self.read_u16(ip);
855 ip += 2;
856 out.push_str(&format!("CLOSURE {:>4}\n", idx));
857 }
858 x if x == Op::BuildList as u8 => {
859 let count = self.read_u16(ip);
860 ip += 2;
861 out.push_str(&format!("BUILD_LIST {:>4}\n", count));
862 }
863 x if x == Op::BuildDict as u8 => {
864 let count = self.read_u16(ip);
865 ip += 2;
866 out.push_str(&format!("BUILD_DICT {:>4}\n", count));
867 }
868 x if x == Op::Subscript as u8 => out.push_str("SUBSCRIPT\n"),
869 x if x == Op::SubscriptOpt as u8 => out.push_str("SUBSCRIPT_OPT\n"),
870 x if x == Op::Slice as u8 => out.push_str("SLICE\n"),
871 x if x == Op::GetProperty as u8 => {
872 let idx = self.read_u16(ip);
873 ip += 2;
874 out.push_str(&format!(
875 "GET_PROPERTY {:>4} ({})\n",
876 idx, self.constants[idx as usize]
877 ));
878 }
879 x if x == Op::GetPropertyOpt as u8 => {
880 let idx = self.read_u16(ip);
881 ip += 2;
882 out.push_str(&format!(
883 "GET_PROPERTY_OPT {:>4} ({})\n",
884 idx, self.constants[idx as usize]
885 ));
886 }
887 x if x == Op::SetProperty as u8 => {
888 let idx = self.read_u16(ip);
889 ip += 2;
890 out.push_str(&format!(
891 "SET_PROPERTY {:>4} ({})\n",
892 idx, self.constants[idx as usize]
893 ));
894 }
895 x if x == Op::SetSubscript as u8 => {
896 let idx = self.read_u16(ip);
897 ip += 2;
898 out.push_str(&format!(
899 "SET_SUBSCRIPT {:>4} ({})\n",
900 idx, self.constants[idx as usize]
901 ));
902 }
903 x if x == Op::MethodCall as u8 => {
904 let idx = self.read_u16(ip);
905 ip += 2;
906 let argc = self.code[ip];
907 ip += 1;
908 out.push_str(&format!(
909 "METHOD_CALL {:>4} ({}) argc={}\n",
910 idx, self.constants[idx as usize], argc
911 ));
912 }
913 x if x == Op::MethodCallOpt as u8 => {
914 let idx = self.read_u16(ip);
915 ip += 2;
916 let argc = self.code[ip];
917 ip += 1;
918 out.push_str(&format!(
919 "METHOD_CALL_OPT {:>4} ({}) argc={}\n",
920 idx, self.constants[idx as usize], argc
921 ));
922 }
923 x if x == Op::Concat as u8 => {
924 let count = self.read_u16(ip);
925 ip += 2;
926 out.push_str(&format!("CONCAT {:>4}\n", count));
927 }
928 x if x == Op::IterInit as u8 => out.push_str("ITER_INIT\n"),
929 x if x == Op::IterNext as u8 => {
930 let target = self.read_u16(ip);
931 ip += 2;
932 out.push_str(&format!("ITER_NEXT {:>4}\n", target));
933 }
934 x if x == Op::Throw as u8 => out.push_str("THROW\n"),
935 x if x == Op::TryCatchSetup as u8 => {
936 let target = self.read_u16(ip);
937 ip += 2;
938 out.push_str(&format!("TRY_CATCH_SETUP {:>4}\n", target));
939 }
940 x if x == Op::PopHandler as u8 => out.push_str("POP_HANDLER\n"),
941 x if x == Op::Pipe as u8 => out.push_str("PIPE\n"),
942 x if x == Op::Parallel as u8 => out.push_str("PARALLEL\n"),
943 x if x == Op::ParallelMap as u8 => out.push_str("PARALLEL_MAP\n"),
944 x if x == Op::ParallelSettle as u8 => out.push_str("PARALLEL_SETTLE\n"),
945 x if x == Op::Spawn as u8 => out.push_str("SPAWN\n"),
946 x if x == Op::Import as u8 => {
947 let idx = self.read_u16(ip);
948 ip += 2;
949 out.push_str(&format!(
950 "IMPORT {:>4} ({})\n",
951 idx, self.constants[idx as usize]
952 ));
953 }
954 x if x == Op::SelectiveImport as u8 => {
955 let path_idx = self.read_u16(ip);
956 ip += 2;
957 let names_idx = self.read_u16(ip);
958 ip += 2;
959 out.push_str(&format!(
960 "SELECTIVE_IMPORT {:>4} ({}) names: {:>4} ({})\n",
961 path_idx,
962 self.constants[path_idx as usize],
963 names_idx,
964 self.constants[names_idx as usize]
965 ));
966 }
967 x if x == Op::SyncMutexEnter as u8 => {
968 let idx = self.read_u16(ip);
969 ip += 2;
970 out.push_str(&format!(
971 "SYNC_MUTEX_ENTER {:>4} ({})\n",
972 idx, self.constants[idx as usize]
973 ));
974 }
975 x if x == Op::DeadlineSetup as u8 => out.push_str("DEADLINE_SETUP\n"),
976 x if x == Op::DeadlineEnd as u8 => out.push_str("DEADLINE_END\n"),
977 x if x == Op::BuildEnum as u8 => {
978 let enum_idx = self.read_u16(ip);
979 ip += 2;
980 let variant_idx = self.read_u16(ip);
981 ip += 2;
982 let field_count = self.read_u16(ip);
983 ip += 2;
984 out.push_str(&format!(
985 "BUILD_ENUM {:>4} ({}) {:>4} ({}) fields={}\n",
986 enum_idx,
987 self.constants[enum_idx as usize],
988 variant_idx,
989 self.constants[variant_idx as usize],
990 field_count
991 ));
992 }
993 x if x == Op::MatchEnum as u8 => {
994 let enum_idx = self.read_u16(ip);
995 ip += 2;
996 let variant_idx = self.read_u16(ip);
997 ip += 2;
998 out.push_str(&format!(
999 "MATCH_ENUM {:>4} ({}) {:>4} ({})\n",
1000 enum_idx,
1001 self.constants[enum_idx as usize],
1002 variant_idx,
1003 self.constants[variant_idx as usize]
1004 ));
1005 }
1006 x if x == Op::PopIterator as u8 => out.push_str("POP_ITERATOR\n"),
1007 x if x == Op::TryUnwrap as u8 => out.push_str("TRY_UNWRAP\n"),
1008 x if x == Op::CallSpread as u8 => out.push_str("CALL_SPREAD\n"),
1009 x if x == Op::CallBuiltin as u8 => {
1010 let id = self.read_u64(ip);
1011 ip += 8;
1012 let idx = self.read_u16(ip);
1013 ip += 2;
1014 let argc = self.code[ip];
1015 ip += 1;
1016 out.push_str(&format!(
1017 "CALL_BUILTIN {id:#018x} {:>4} ({}) argc={}\n",
1018 idx, self.constants[idx as usize], argc
1019 ));
1020 }
1021 x if x == Op::CallBuiltinSpread as u8 => {
1022 let id = self.read_u64(ip);
1023 ip += 8;
1024 let idx = self.read_u16(ip);
1025 ip += 2;
1026 out.push_str(&format!(
1027 "CALL_BUILTIN_SPREAD {id:#018x} {:>4} ({})\n",
1028 idx, self.constants[idx as usize]
1029 ));
1030 }
1031 x if x == Op::MethodCallSpread as u8 => {
1032 let idx = self.read_u16(ip + 1);
1033 ip += 2;
1034 out.push_str(&format!("METHOD_CALL_SPREAD {idx}\n"));
1035 }
1036 x if x == Op::Dup as u8 => out.push_str("DUP\n"),
1037 x if x == Op::Swap as u8 => out.push_str("SWAP\n"),
1038 x if x == Op::AddInt as u8 => out.push_str("ADD_INT\n"),
1039 x if x == Op::SubInt as u8 => out.push_str("SUB_INT\n"),
1040 x if x == Op::MulInt as u8 => out.push_str("MUL_INT\n"),
1041 x if x == Op::DivInt as u8 => out.push_str("DIV_INT\n"),
1042 x if x == Op::ModInt as u8 => out.push_str("MOD_INT\n"),
1043 x if x == Op::AddFloat as u8 => out.push_str("ADD_FLOAT\n"),
1044 x if x == Op::SubFloat as u8 => out.push_str("SUB_FLOAT\n"),
1045 x if x == Op::MulFloat as u8 => out.push_str("MUL_FLOAT\n"),
1046 x if x == Op::DivFloat as u8 => out.push_str("DIV_FLOAT\n"),
1047 x if x == Op::ModFloat as u8 => out.push_str("MOD_FLOAT\n"),
1048 x if x == Op::EqualInt as u8 => out.push_str("EQUAL_INT\n"),
1049 x if x == Op::NotEqualInt as u8 => out.push_str("NOT_EQUAL_INT\n"),
1050 x if x == Op::LessInt as u8 => out.push_str("LESS_INT\n"),
1051 x if x == Op::GreaterInt as u8 => out.push_str("GREATER_INT\n"),
1052 x if x == Op::LessEqualInt as u8 => out.push_str("LESS_EQUAL_INT\n"),
1053 x if x == Op::GreaterEqualInt as u8 => out.push_str("GREATER_EQUAL_INT\n"),
1054 x if x == Op::EqualFloat as u8 => out.push_str("EQUAL_FLOAT\n"),
1055 x if x == Op::NotEqualFloat as u8 => out.push_str("NOT_EQUAL_FLOAT\n"),
1056 x if x == Op::LessFloat as u8 => out.push_str("LESS_FLOAT\n"),
1057 x if x == Op::GreaterFloat as u8 => out.push_str("GREATER_FLOAT\n"),
1058 x if x == Op::LessEqualFloat as u8 => out.push_str("LESS_EQUAL_FLOAT\n"),
1059 x if x == Op::GreaterEqualFloat as u8 => out.push_str("GREATER_EQUAL_FLOAT\n"),
1060 x if x == Op::EqualBool as u8 => out.push_str("EQUAL_BOOL\n"),
1061 x if x == Op::NotEqualBool as u8 => out.push_str("NOT_EQUAL_BOOL\n"),
1062 x if x == Op::EqualString as u8 => out.push_str("EQUAL_STRING\n"),
1063 x if x == Op::NotEqualString as u8 => out.push_str("NOT_EQUAL_STRING\n"),
1064 x if x == Op::Yield as u8 => out.push_str("YIELD\n"),
1065 _ => {
1066 out.push_str(&format!("UNKNOWN(0x{:02x})\n", op));
1067 }
1068 }
1069 }
1070 out
1071 }
1072}
1073
1074impl Default for Chunk {
1075 fn default() -> Self {
1076 Self::new()
1077 }
1078}
1079
1080#[cfg(test)]
1081mod tests {
1082 use super::Op;
1083
1084 #[test]
1085 fn op_from_byte_matches_repr_order() {
1086 for (byte, op) in Op::ALL.iter().copied().enumerate() {
1087 assert_eq!(byte as u8, op as u8);
1088 assert_eq!(Op::from_byte(byte as u8), Some(op));
1089 }
1090 assert_eq!(Op::from_byte(Op::ALL.len() as u8), None);
1091 }
1092}