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