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