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