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 is_stream: bool,
495 pub has_rest_param: bool,
497}
498
499impl Chunk {
500 pub fn new() -> Self {
501 Self {
502 code: Vec::new(),
503 constants: Vec::new(),
504 lines: Vec::new(),
505 columns: Vec::new(),
506 source_file: None,
507 current_col: 0,
508 functions: Vec::new(),
509 inline_cache_slots: BTreeMap::new(),
510 inline_caches: Rc::new(RefCell::new(Vec::new())),
511 local_slots: Vec::new(),
512 }
513 }
514
515 pub fn set_column(&mut self, col: u32) {
517 self.current_col = col;
518 }
519
520 pub fn add_constant(&mut self, constant: Constant) -> u16 {
522 for (i, c) in self.constants.iter().enumerate() {
523 if c == &constant {
524 return i as u16;
525 }
526 }
527 let idx = self.constants.len();
528 self.constants.push(constant);
529 idx as u16
530 }
531
532 pub fn emit(&mut self, op: Op, line: u32) {
534 let col = self.current_col;
535 self.code.push(op as u8);
536 self.lines.push(line);
537 self.columns.push(col);
538 }
539
540 pub fn emit_u16(&mut self, op: Op, arg: u16, line: u32) {
542 let col = self.current_col;
543 let op_offset = self.code.len();
544 self.code.push(op as u8);
545 self.code.push((arg >> 8) as u8);
546 self.code.push((arg & 0xFF) as u8);
547 self.lines.push(line);
548 self.lines.push(line);
549 self.lines.push(line);
550 self.columns.push(col);
551 self.columns.push(col);
552 self.columns.push(col);
553 if matches!(
554 op,
555 Op::GetProperty | Op::GetPropertyOpt | Op::MethodCallSpread
556 ) {
557 self.register_inline_cache(op_offset);
558 }
559 }
560
561 pub fn emit_u8(&mut self, op: Op, arg: u8, line: u32) {
563 let col = self.current_col;
564 self.code.push(op as u8);
565 self.code.push(arg);
566 self.lines.push(line);
567 self.lines.push(line);
568 self.columns.push(col);
569 self.columns.push(col);
570 }
571
572 pub fn emit_call_builtin(
574 &mut self,
575 id: crate::BuiltinId,
576 name_idx: u16,
577 arg_count: u8,
578 line: u32,
579 ) {
580 let col = self.current_col;
581 self.code.push(Op::CallBuiltin as u8);
582 self.code.extend_from_slice(&id.raw().to_be_bytes());
583 self.code.push((name_idx >> 8) as u8);
584 self.code.push((name_idx & 0xFF) as u8);
585 self.code.push(arg_count);
586 for _ in 0..12 {
587 self.lines.push(line);
588 self.columns.push(col);
589 }
590 }
591
592 pub fn emit_call_builtin_spread(&mut self, id: crate::BuiltinId, name_idx: u16, line: u32) {
594 let col = self.current_col;
595 self.code.push(Op::CallBuiltinSpread as u8);
596 self.code.extend_from_slice(&id.raw().to_be_bytes());
597 self.code.push((name_idx >> 8) as u8);
598 self.code.push((name_idx & 0xFF) as u8);
599 for _ in 0..11 {
600 self.lines.push(line);
601 self.columns.push(col);
602 }
603 }
604
605 pub fn emit_method_call(&mut self, name_idx: u16, arg_count: u8, line: u32) {
607 self.emit_method_call_inner(Op::MethodCall, name_idx, arg_count, line);
608 }
609
610 pub fn emit_method_call_opt(&mut self, name_idx: u16, arg_count: u8, line: u32) {
612 self.emit_method_call_inner(Op::MethodCallOpt, name_idx, arg_count, line);
613 }
614
615 fn emit_method_call_inner(&mut self, op: Op, name_idx: u16, arg_count: u8, line: u32) {
616 let col = self.current_col;
617 let op_offset = self.code.len();
618 self.code.push(op as u8);
619 self.code.push((name_idx >> 8) as u8);
620 self.code.push((name_idx & 0xFF) as u8);
621 self.code.push(arg_count);
622 self.lines.push(line);
623 self.lines.push(line);
624 self.lines.push(line);
625 self.lines.push(line);
626 self.columns.push(col);
627 self.columns.push(col);
628 self.columns.push(col);
629 self.columns.push(col);
630 self.register_inline_cache(op_offset);
631 }
632
633 pub fn current_offset(&self) -> usize {
635 self.code.len()
636 }
637
638 pub fn emit_jump(&mut self, op: Op, line: u32) -> usize {
640 let col = self.current_col;
641 self.code.push(op as u8);
642 let patch_pos = self.code.len();
643 self.code.push(0xFF);
644 self.code.push(0xFF);
645 self.lines.push(line);
646 self.lines.push(line);
647 self.lines.push(line);
648 self.columns.push(col);
649 self.columns.push(col);
650 self.columns.push(col);
651 patch_pos
652 }
653
654 pub fn patch_jump(&mut self, patch_pos: usize) {
656 let target = self.code.len() as u16;
657 self.code[patch_pos] = (target >> 8) as u8;
658 self.code[patch_pos + 1] = (target & 0xFF) as u8;
659 }
660
661 pub fn patch_jump_to(&mut self, patch_pos: usize, target: usize) {
663 let target = target as u16;
664 self.code[patch_pos] = (target >> 8) as u8;
665 self.code[patch_pos + 1] = (target & 0xFF) as u8;
666 }
667
668 pub fn read_u16(&self, pos: usize) -> u16 {
670 ((self.code[pos] as u16) << 8) | (self.code[pos + 1] as u16)
671 }
672
673 fn register_inline_cache(&mut self, op_offset: usize) {
674 if self.inline_cache_slots.contains_key(&op_offset) {
675 return;
676 }
677 let mut entries = self.inline_caches.borrow_mut();
678 let slot = entries.len();
679 entries.push(InlineCacheEntry::Empty);
680 self.inline_cache_slots.insert(op_offset, slot);
681 }
682
683 pub(crate) fn inline_cache_slot(&self, op_offset: usize) -> Option<usize> {
684 self.inline_cache_slots.get(&op_offset).copied()
685 }
686
687 pub(crate) fn inline_cache_entry(&self, slot: usize) -> InlineCacheEntry {
688 self.inline_caches
689 .borrow()
690 .get(slot)
691 .cloned()
692 .unwrap_or(InlineCacheEntry::Empty)
693 }
694
695 pub(crate) fn set_inline_cache_entry(&self, slot: usize, entry: InlineCacheEntry) {
696 if let Some(existing) = self.inline_caches.borrow_mut().get_mut(slot) {
697 *existing = entry;
698 }
699 }
700
701 pub(crate) fn add_local_slot(
702 &mut self,
703 name: String,
704 mutable: bool,
705 scope_depth: usize,
706 ) -> u16 {
707 let idx = self.local_slots.len();
708 self.local_slots.push(LocalSlotInfo {
709 name,
710 mutable,
711 scope_depth,
712 });
713 idx as u16
714 }
715
716 #[cfg(test)]
717 pub(crate) fn inline_cache_entries(&self) -> Vec<InlineCacheEntry> {
718 self.inline_caches.borrow().clone()
719 }
720
721 pub fn read_u64(&self, pos: usize) -> u64 {
723 u64::from_be_bytes([
724 self.code[pos],
725 self.code[pos + 1],
726 self.code[pos + 2],
727 self.code[pos + 3],
728 self.code[pos + 4],
729 self.code[pos + 5],
730 self.code[pos + 6],
731 self.code[pos + 7],
732 ])
733 }
734
735 pub fn disassemble(&self, name: &str) -> String {
737 let mut out = format!("== {name} ==\n");
738 let mut ip = 0;
739 while ip < self.code.len() {
740 let op = self.code[ip];
741 let line = self.lines.get(ip).copied().unwrap_or(0);
742 out.push_str(&format!("{:04} [{:>4}] ", ip, line));
743 ip += 1;
744
745 match op {
746 x if x == Op::Constant as u8 => {
747 let idx = self.read_u16(ip);
748 ip += 2;
749 let val = &self.constants[idx as usize];
750 out.push_str(&format!("CONSTANT {:>4} ({})\n", idx, val));
751 }
752 x if x == Op::Nil as u8 => out.push_str("NIL\n"),
753 x if x == Op::True as u8 => out.push_str("TRUE\n"),
754 x if x == Op::False as u8 => out.push_str("FALSE\n"),
755 x if x == Op::GetVar as u8 => {
756 let idx = self.read_u16(ip);
757 ip += 2;
758 out.push_str(&format!(
759 "GET_VAR {:>4} ({})\n",
760 idx, self.constants[idx as usize]
761 ));
762 }
763 x if x == Op::DefLet as u8 => {
764 let idx = self.read_u16(ip);
765 ip += 2;
766 out.push_str(&format!(
767 "DEF_LET {:>4} ({})\n",
768 idx, self.constants[idx as usize]
769 ));
770 }
771 x if x == Op::DefVar as u8 => {
772 let idx = self.read_u16(ip);
773 ip += 2;
774 out.push_str(&format!(
775 "DEF_VAR {:>4} ({})\n",
776 idx, self.constants[idx as usize]
777 ));
778 }
779 x if x == Op::SetVar as u8 => {
780 let idx = self.read_u16(ip);
781 ip += 2;
782 out.push_str(&format!(
783 "SET_VAR {:>4} ({})\n",
784 idx, self.constants[idx as usize]
785 ));
786 }
787 x if x == Op::GetLocalSlot as u8 => {
788 let slot = self.read_u16(ip);
789 ip += 2;
790 out.push_str(&format!("GET_LOCAL_SLOT {:>4}", slot));
791 if let Some(info) = self.local_slots.get(slot as usize) {
792 out.push_str(&format!(" ({})", info.name));
793 }
794 out.push('\n');
795 }
796 x if x == Op::DefLocalSlot as u8 => {
797 let slot = self.read_u16(ip);
798 ip += 2;
799 out.push_str(&format!("DEF_LOCAL_SLOT {:>4}", slot));
800 if let Some(info) = self.local_slots.get(slot as usize) {
801 out.push_str(&format!(" ({})", info.name));
802 }
803 out.push('\n');
804 }
805 x if x == Op::SetLocalSlot as u8 => {
806 let slot = self.read_u16(ip);
807 ip += 2;
808 out.push_str(&format!("SET_LOCAL_SLOT {:>4}", slot));
809 if let Some(info) = self.local_slots.get(slot as usize) {
810 out.push_str(&format!(" ({})", info.name));
811 }
812 out.push('\n');
813 }
814 x if x == Op::PushScope as u8 => out.push_str("PUSH_SCOPE\n"),
815 x if x == Op::PopScope as u8 => out.push_str("POP_SCOPE\n"),
816 x if x == Op::Add as u8 => out.push_str("ADD\n"),
817 x if x == Op::Sub as u8 => out.push_str("SUB\n"),
818 x if x == Op::Mul as u8 => out.push_str("MUL\n"),
819 x if x == Op::Div as u8 => out.push_str("DIV\n"),
820 x if x == Op::Mod as u8 => out.push_str("MOD\n"),
821 x if x == Op::Pow as u8 => out.push_str("POW\n"),
822 x if x == Op::Negate as u8 => out.push_str("NEGATE\n"),
823 x if x == Op::Equal as u8 => out.push_str("EQUAL\n"),
824 x if x == Op::NotEqual as u8 => out.push_str("NOT_EQUAL\n"),
825 x if x == Op::Less as u8 => out.push_str("LESS\n"),
826 x if x == Op::Greater as u8 => out.push_str("GREATER\n"),
827 x if x == Op::LessEqual as u8 => out.push_str("LESS_EQUAL\n"),
828 x if x == Op::GreaterEqual as u8 => out.push_str("GREATER_EQUAL\n"),
829 x if x == Op::Contains as u8 => out.push_str("CONTAINS\n"),
830 x if x == Op::Not as u8 => out.push_str("NOT\n"),
831 x if x == Op::Jump as u8 => {
832 let target = self.read_u16(ip);
833 ip += 2;
834 out.push_str(&format!("JUMP {:>4}\n", target));
835 }
836 x if x == Op::JumpIfFalse as u8 => {
837 let target = self.read_u16(ip);
838 ip += 2;
839 out.push_str(&format!("JUMP_IF_FALSE {:>4}\n", target));
840 }
841 x if x == Op::JumpIfTrue as u8 => {
842 let target = self.read_u16(ip);
843 ip += 2;
844 out.push_str(&format!("JUMP_IF_TRUE {:>4}\n", target));
845 }
846 x if x == Op::Pop as u8 => out.push_str("POP\n"),
847 x if x == Op::Call as u8 => {
848 let argc = self.code[ip];
849 ip += 1;
850 out.push_str(&format!("CALL {:>4}\n", argc));
851 }
852 x if x == Op::TailCall as u8 => {
853 let argc = self.code[ip];
854 ip += 1;
855 out.push_str(&format!("TAIL_CALL {:>4}\n", argc));
856 }
857 x if x == Op::Return as u8 => out.push_str("RETURN\n"),
858 x if x == Op::Closure as u8 => {
859 let idx = self.read_u16(ip);
860 ip += 2;
861 out.push_str(&format!("CLOSURE {:>4}\n", idx));
862 }
863 x if x == Op::BuildList as u8 => {
864 let count = self.read_u16(ip);
865 ip += 2;
866 out.push_str(&format!("BUILD_LIST {:>4}\n", count));
867 }
868 x if x == Op::BuildDict as u8 => {
869 let count = self.read_u16(ip);
870 ip += 2;
871 out.push_str(&format!("BUILD_DICT {:>4}\n", count));
872 }
873 x if x == Op::Subscript as u8 => out.push_str("SUBSCRIPT\n"),
874 x if x == Op::SubscriptOpt as u8 => out.push_str("SUBSCRIPT_OPT\n"),
875 x if x == Op::Slice as u8 => out.push_str("SLICE\n"),
876 x if x == Op::GetProperty as u8 => {
877 let idx = self.read_u16(ip);
878 ip += 2;
879 out.push_str(&format!(
880 "GET_PROPERTY {:>4} ({})\n",
881 idx, self.constants[idx as usize]
882 ));
883 }
884 x if x == Op::GetPropertyOpt as u8 => {
885 let idx = self.read_u16(ip);
886 ip += 2;
887 out.push_str(&format!(
888 "GET_PROPERTY_OPT {:>4} ({})\n",
889 idx, self.constants[idx as usize]
890 ));
891 }
892 x if x == Op::SetProperty as u8 => {
893 let idx = self.read_u16(ip);
894 ip += 2;
895 out.push_str(&format!(
896 "SET_PROPERTY {:>4} ({})\n",
897 idx, self.constants[idx as usize]
898 ));
899 }
900 x if x == Op::SetSubscript as u8 => {
901 let idx = self.read_u16(ip);
902 ip += 2;
903 out.push_str(&format!(
904 "SET_SUBSCRIPT {:>4} ({})\n",
905 idx, self.constants[idx as usize]
906 ));
907 }
908 x if x == Op::MethodCall as u8 => {
909 let idx = self.read_u16(ip);
910 ip += 2;
911 let argc = self.code[ip];
912 ip += 1;
913 out.push_str(&format!(
914 "METHOD_CALL {:>4} ({}) argc={}\n",
915 idx, self.constants[idx as usize], argc
916 ));
917 }
918 x if x == Op::MethodCallOpt as u8 => {
919 let idx = self.read_u16(ip);
920 ip += 2;
921 let argc = self.code[ip];
922 ip += 1;
923 out.push_str(&format!(
924 "METHOD_CALL_OPT {:>4} ({}) argc={}\n",
925 idx, self.constants[idx as usize], argc
926 ));
927 }
928 x if x == Op::Concat as u8 => {
929 let count = self.read_u16(ip);
930 ip += 2;
931 out.push_str(&format!("CONCAT {:>4}\n", count));
932 }
933 x if x == Op::IterInit as u8 => out.push_str("ITER_INIT\n"),
934 x if x == Op::IterNext as u8 => {
935 let target = self.read_u16(ip);
936 ip += 2;
937 out.push_str(&format!("ITER_NEXT {:>4}\n", target));
938 }
939 x if x == Op::Throw as u8 => out.push_str("THROW\n"),
940 x if x == Op::TryCatchSetup as u8 => {
941 let target = self.read_u16(ip);
942 ip += 2;
943 out.push_str(&format!("TRY_CATCH_SETUP {:>4}\n", target));
944 }
945 x if x == Op::PopHandler as u8 => out.push_str("POP_HANDLER\n"),
946 x if x == Op::Pipe as u8 => out.push_str("PIPE\n"),
947 x if x == Op::Parallel as u8 => out.push_str("PARALLEL\n"),
948 x if x == Op::ParallelMap as u8 => out.push_str("PARALLEL_MAP\n"),
949 x if x == Op::ParallelSettle as u8 => out.push_str("PARALLEL_SETTLE\n"),
950 x if x == Op::Spawn as u8 => out.push_str("SPAWN\n"),
951 x if x == Op::Import as u8 => {
952 let idx = self.read_u16(ip);
953 ip += 2;
954 out.push_str(&format!(
955 "IMPORT {:>4} ({})\n",
956 idx, self.constants[idx as usize]
957 ));
958 }
959 x if x == Op::SelectiveImport as u8 => {
960 let path_idx = self.read_u16(ip);
961 ip += 2;
962 let names_idx = self.read_u16(ip);
963 ip += 2;
964 out.push_str(&format!(
965 "SELECTIVE_IMPORT {:>4} ({}) names: {:>4} ({})\n",
966 path_idx,
967 self.constants[path_idx as usize],
968 names_idx,
969 self.constants[names_idx as usize]
970 ));
971 }
972 x if x == Op::SyncMutexEnter as u8 => {
973 let idx = self.read_u16(ip);
974 ip += 2;
975 out.push_str(&format!(
976 "SYNC_MUTEX_ENTER {:>4} ({})\n",
977 idx, self.constants[idx as usize]
978 ));
979 }
980 x if x == Op::DeadlineSetup as u8 => out.push_str("DEADLINE_SETUP\n"),
981 x if x == Op::DeadlineEnd as u8 => out.push_str("DEADLINE_END\n"),
982 x if x == Op::BuildEnum as u8 => {
983 let enum_idx = self.read_u16(ip);
984 ip += 2;
985 let variant_idx = self.read_u16(ip);
986 ip += 2;
987 let field_count = self.read_u16(ip);
988 ip += 2;
989 out.push_str(&format!(
990 "BUILD_ENUM {:>4} ({}) {:>4} ({}) fields={}\n",
991 enum_idx,
992 self.constants[enum_idx as usize],
993 variant_idx,
994 self.constants[variant_idx as usize],
995 field_count
996 ));
997 }
998 x if x == Op::MatchEnum as u8 => {
999 let enum_idx = self.read_u16(ip);
1000 ip += 2;
1001 let variant_idx = self.read_u16(ip);
1002 ip += 2;
1003 out.push_str(&format!(
1004 "MATCH_ENUM {:>4} ({}) {:>4} ({})\n",
1005 enum_idx,
1006 self.constants[enum_idx as usize],
1007 variant_idx,
1008 self.constants[variant_idx as usize]
1009 ));
1010 }
1011 x if x == Op::PopIterator as u8 => out.push_str("POP_ITERATOR\n"),
1012 x if x == Op::TryUnwrap as u8 => out.push_str("TRY_UNWRAP\n"),
1013 x if x == Op::TryWrapOk as u8 => out.push_str("TRY_WRAP_OK\n"),
1014 x if x == Op::CallSpread as u8 => out.push_str("CALL_SPREAD\n"),
1015 x if x == Op::CallBuiltin as u8 => {
1016 let id = self.read_u64(ip);
1017 ip += 8;
1018 let idx = self.read_u16(ip);
1019 ip += 2;
1020 let argc = self.code[ip];
1021 ip += 1;
1022 out.push_str(&format!(
1023 "CALL_BUILTIN {id:#018x} {:>4} ({}) argc={}\n",
1024 idx, self.constants[idx as usize], argc
1025 ));
1026 }
1027 x if x == Op::CallBuiltinSpread as u8 => {
1028 let id = self.read_u64(ip);
1029 ip += 8;
1030 let idx = self.read_u16(ip);
1031 ip += 2;
1032 out.push_str(&format!(
1033 "CALL_BUILTIN_SPREAD {id:#018x} {:>4} ({})\n",
1034 idx, self.constants[idx as usize]
1035 ));
1036 }
1037 x if x == Op::MethodCallSpread as u8 => {
1038 let idx = self.read_u16(ip + 1);
1039 ip += 2;
1040 out.push_str(&format!("METHOD_CALL_SPREAD {idx}\n"));
1041 }
1042 x if x == Op::Dup as u8 => out.push_str("DUP\n"),
1043 x if x == Op::Swap as u8 => out.push_str("SWAP\n"),
1044 x if x == Op::AddInt as u8 => out.push_str("ADD_INT\n"),
1045 x if x == Op::SubInt as u8 => out.push_str("SUB_INT\n"),
1046 x if x == Op::MulInt as u8 => out.push_str("MUL_INT\n"),
1047 x if x == Op::DivInt as u8 => out.push_str("DIV_INT\n"),
1048 x if x == Op::ModInt as u8 => out.push_str("MOD_INT\n"),
1049 x if x == Op::AddFloat as u8 => out.push_str("ADD_FLOAT\n"),
1050 x if x == Op::SubFloat as u8 => out.push_str("SUB_FLOAT\n"),
1051 x if x == Op::MulFloat as u8 => out.push_str("MUL_FLOAT\n"),
1052 x if x == Op::DivFloat as u8 => out.push_str("DIV_FLOAT\n"),
1053 x if x == Op::ModFloat as u8 => out.push_str("MOD_FLOAT\n"),
1054 x if x == Op::EqualInt as u8 => out.push_str("EQUAL_INT\n"),
1055 x if x == Op::NotEqualInt as u8 => out.push_str("NOT_EQUAL_INT\n"),
1056 x if x == Op::LessInt as u8 => out.push_str("LESS_INT\n"),
1057 x if x == Op::GreaterInt as u8 => out.push_str("GREATER_INT\n"),
1058 x if x == Op::LessEqualInt as u8 => out.push_str("LESS_EQUAL_INT\n"),
1059 x if x == Op::GreaterEqualInt as u8 => out.push_str("GREATER_EQUAL_INT\n"),
1060 x if x == Op::EqualFloat as u8 => out.push_str("EQUAL_FLOAT\n"),
1061 x if x == Op::NotEqualFloat as u8 => out.push_str("NOT_EQUAL_FLOAT\n"),
1062 x if x == Op::LessFloat as u8 => out.push_str("LESS_FLOAT\n"),
1063 x if x == Op::GreaterFloat as u8 => out.push_str("GREATER_FLOAT\n"),
1064 x if x == Op::LessEqualFloat as u8 => out.push_str("LESS_EQUAL_FLOAT\n"),
1065 x if x == Op::GreaterEqualFloat as u8 => out.push_str("GREATER_EQUAL_FLOAT\n"),
1066 x if x == Op::EqualBool as u8 => out.push_str("EQUAL_BOOL\n"),
1067 x if x == Op::NotEqualBool as u8 => out.push_str("NOT_EQUAL_BOOL\n"),
1068 x if x == Op::EqualString as u8 => out.push_str("EQUAL_STRING\n"),
1069 x if x == Op::NotEqualString as u8 => out.push_str("NOT_EQUAL_STRING\n"),
1070 x if x == Op::Yield as u8 => out.push_str("YIELD\n"),
1071 _ => {
1072 out.push_str(&format!("UNKNOWN(0x{:02x})\n", op));
1073 }
1074 }
1075 }
1076 out
1077 }
1078}
1079
1080impl Default for Chunk {
1081 fn default() -> Self {
1082 Self::new()
1083 }
1084}
1085
1086#[cfg(test)]
1087mod tests {
1088 use super::Op;
1089
1090 #[test]
1091 fn op_from_byte_matches_repr_order() {
1092 for (byte, op) in Op::ALL.iter().copied().enumerate() {
1093 assert_eq!(byte as u8, op as u8);
1094 assert_eq!(Op::from_byte(byte as u8), Some(op));
1095 }
1096 assert_eq!(Op::from_byte(Op::ALL.len() as u8), None);
1097 }
1098}