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