1use std::cell::RefCell;
2use std::collections::BTreeMap;
3use std::fmt;
4use std::rc::Rc;
5
6use harn_parser::TypeExpr;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10#[repr(u8)]
11pub enum Op {
12 Constant, Nil,
16 True,
18 False,
20
21 GetVar, DefLet, DefVar, SetVar, PushScope,
32 PopScope,
34
35 Add,
37 Sub,
38 Mul,
39 Div,
40 Mod,
41 Pow,
42 Negate,
43
44 Equal,
46 NotEqual,
47 Less,
48 Greater,
49 LessEqual,
50 GreaterEqual,
51
52 Not,
54
55 Jump,
58 JumpIfFalse,
60 JumpIfTrue,
62 Pop,
64
65 Call,
68 TailCall,
72 Return,
74 Closure,
76
77 BuildList,
80 BuildDict,
82 Subscript,
84 SubscriptOpt,
87 Slice,
89
90 GetProperty,
93 GetPropertyOpt,
96 SetProperty,
99 SetSubscript,
102 MethodCall,
104 MethodCallOpt,
107
108 Concat,
111
112 IterInit,
115 IterNext,
118
119 Pipe,
122
123 Throw,
126 TryCatchSetup,
128 PopHandler,
130
131 Parallel,
135 ParallelMap,
138 ParallelMapStream,
141 ParallelSettle,
144 Spawn,
147 SyncMutexEnter,
150
151 Import,
154 SelectiveImport,
156
157 DeadlineSetup,
160 DeadlineEnd,
162
163 BuildEnum,
168
169 MatchEnum,
174
175 PopIterator,
178
179 GetArgc,
182
183 CheckType,
189
190 TryUnwrap,
193 TryWrapOk,
195
196 CallSpread,
199 CallBuiltin,
202 CallBuiltinSpread,
205 MethodCallSpread,
208
209 Dup,
212 Swap,
214 Contains,
217
218 AddInt,
220 SubInt,
221 MulInt,
222 DivInt,
223 ModInt,
224 AddFloat,
225 SubFloat,
226 MulFloat,
227 DivFloat,
228 ModFloat,
229 EqualInt,
230 NotEqualInt,
231 LessInt,
232 GreaterInt,
233 LessEqualInt,
234 GreaterEqualInt,
235 EqualFloat,
236 NotEqualFloat,
237 LessFloat,
238 GreaterFloat,
239 LessEqualFloat,
240 GreaterEqualFloat,
241 EqualBool,
242 NotEqualBool,
243 EqualString,
244 NotEqualString,
245
246 Yield,
248
249 GetLocalSlot,
252 DefLocalSlot,
254 SetLocalSlot,
256}
257
258impl Op {
259 pub(crate) const ALL: &'static [Self] = &[
260 Op::Constant,
261 Op::Nil,
262 Op::True,
263 Op::False,
264 Op::GetVar,
265 Op::DefLet,
266 Op::DefVar,
267 Op::SetVar,
268 Op::PushScope,
269 Op::PopScope,
270 Op::Add,
271 Op::Sub,
272 Op::Mul,
273 Op::Div,
274 Op::Mod,
275 Op::Pow,
276 Op::Negate,
277 Op::Equal,
278 Op::NotEqual,
279 Op::Less,
280 Op::Greater,
281 Op::LessEqual,
282 Op::GreaterEqual,
283 Op::Not,
284 Op::Jump,
285 Op::JumpIfFalse,
286 Op::JumpIfTrue,
287 Op::Pop,
288 Op::Call,
289 Op::TailCall,
290 Op::Return,
291 Op::Closure,
292 Op::BuildList,
293 Op::BuildDict,
294 Op::Subscript,
295 Op::SubscriptOpt,
296 Op::Slice,
297 Op::GetProperty,
298 Op::GetPropertyOpt,
299 Op::SetProperty,
300 Op::SetSubscript,
301 Op::MethodCall,
302 Op::MethodCallOpt,
303 Op::Concat,
304 Op::IterInit,
305 Op::IterNext,
306 Op::Pipe,
307 Op::Throw,
308 Op::TryCatchSetup,
309 Op::PopHandler,
310 Op::Parallel,
311 Op::ParallelMap,
312 Op::ParallelMapStream,
313 Op::ParallelSettle,
314 Op::Spawn,
315 Op::SyncMutexEnter,
316 Op::Import,
317 Op::SelectiveImport,
318 Op::DeadlineSetup,
319 Op::DeadlineEnd,
320 Op::BuildEnum,
321 Op::MatchEnum,
322 Op::PopIterator,
323 Op::GetArgc,
324 Op::CheckType,
325 Op::TryUnwrap,
326 Op::TryWrapOk,
327 Op::CallSpread,
328 Op::CallBuiltin,
329 Op::CallBuiltinSpread,
330 Op::MethodCallSpread,
331 Op::Dup,
332 Op::Swap,
333 Op::Contains,
334 Op::AddInt,
335 Op::SubInt,
336 Op::MulInt,
337 Op::DivInt,
338 Op::ModInt,
339 Op::AddFloat,
340 Op::SubFloat,
341 Op::MulFloat,
342 Op::DivFloat,
343 Op::ModFloat,
344 Op::EqualInt,
345 Op::NotEqualInt,
346 Op::LessInt,
347 Op::GreaterInt,
348 Op::LessEqualInt,
349 Op::GreaterEqualInt,
350 Op::EqualFloat,
351 Op::NotEqualFloat,
352 Op::LessFloat,
353 Op::GreaterFloat,
354 Op::LessEqualFloat,
355 Op::GreaterEqualFloat,
356 Op::EqualBool,
357 Op::NotEqualBool,
358 Op::EqualString,
359 Op::NotEqualString,
360 Op::Yield,
361 Op::GetLocalSlot,
362 Op::DefLocalSlot,
363 Op::SetLocalSlot,
364 ];
365
366 pub(crate) fn from_byte(byte: u8) -> Option<Self> {
367 Self::ALL.get(byte as usize).copied()
368 }
369}
370
371#[derive(Debug, Clone, PartialEq)]
373pub enum Constant {
374 Int(i64),
375 Float(f64),
376 String(String),
377 Bool(bool),
378 Nil,
379 Duration(i64),
380}
381
382#[derive(Debug, Clone, PartialEq, Eq)]
390pub(crate) enum InlineCacheEntry {
391 Empty,
392 Property {
393 name_idx: u16,
394 target: PropertyCacheTarget,
395 },
396 Method {
397 name_idx: u16,
398 argc: usize,
399 target: MethodCacheTarget,
400 },
401}
402
403#[derive(Debug, Clone, PartialEq, Eq)]
404pub(crate) enum PropertyCacheTarget {
405 DictField(Rc<str>),
406 StructField { field_name: Rc<str>, index: usize },
407 ListCount,
408 ListEmpty,
409 ListFirst,
410 ListLast,
411 StringCount,
412 StringEmpty,
413 PairFirst,
414 PairSecond,
415 EnumVariant,
416 EnumFields,
417}
418
419#[derive(Debug, Clone, Copy, PartialEq, Eq)]
420pub(crate) enum MethodCacheTarget {
421 ListCount,
422 ListEmpty,
423 StringCount,
424 StringEmpty,
425 DictCount,
426 RangeCount,
427 RangeLen,
428 RangeEmpty,
429 RangeFirst,
430 RangeLast,
431 SetCount,
432 SetLen,
433 SetEmpty,
434}
435
436#[derive(Debug, Clone, PartialEq, Eq)]
438pub struct LocalSlotInfo {
439 pub name: String,
440 pub mutable: bool,
441 pub scope_depth: usize,
442}
443
444impl fmt::Display for Constant {
445 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
446 match self {
447 Constant::Int(n) => write!(f, "{n}"),
448 Constant::Float(n) => write!(f, "{n}"),
449 Constant::String(s) => write!(f, "\"{s}\""),
450 Constant::Bool(b) => write!(f, "{b}"),
451 Constant::Nil => write!(f, "nil"),
452 Constant::Duration(ms) => write!(f, "{ms}ms"),
453 }
454 }
455}
456
457#[derive(Debug, Clone)]
459pub struct Chunk {
460 pub code: Vec<u8>,
462 pub constants: Vec<Constant>,
464 pub lines: Vec<u32>,
466 pub columns: Vec<u32>,
469 pub source_file: Option<String>,
474 current_col: u32,
476 pub functions: Vec<CompiledFunctionRef>,
478 inline_cache_slots: BTreeMap<usize, usize>,
481 inline_caches: Rc<RefCell<Vec<InlineCacheEntry>>>,
484 pub(crate) local_slots: Vec<LocalSlotInfo>,
486}
487
488pub type ChunkRef = Rc<Chunk>;
489pub type CompiledFunctionRef = Rc<CompiledFunction>;
490
491#[derive(Debug, Clone)]
492pub(crate) struct CachedChunk {
493 code: Vec<u8>,
494 constants: Vec<Constant>,
495 lines: Vec<u32>,
496 columns: Vec<u32>,
497 source_file: Option<String>,
498 current_col: u32,
499 functions: Vec<CachedCompiledFunction>,
500 inline_cache_slots: BTreeMap<usize, usize>,
501 local_slots: Vec<LocalSlotInfo>,
502}
503
504#[derive(Debug, Clone)]
505pub(crate) struct CachedCompiledFunction {
506 name: String,
507 type_params: Vec<String>,
508 nominal_type_names: Vec<String>,
509 params: Vec<ParamSlot>,
510 default_start: Option<usize>,
511 chunk: CachedChunk,
512 is_generator: bool,
513 is_stream: bool,
514 has_rest_param: bool,
515}
516
517#[derive(Debug, Clone)]
523pub struct ParamSlot {
524 pub name: String,
525 pub type_expr: Option<TypeExpr>,
528 pub has_default: bool,
532}
533
534impl ParamSlot {
535 pub fn from_typed_param(param: &harn_parser::TypedParam) -> Self {
538 Self {
539 name: param.name.clone(),
540 type_expr: param.type_expr.clone(),
541 has_default: param.default_value.is_some(),
542 }
543 }
544
545 pub fn vec_from_typed(params: &[harn_parser::TypedParam]) -> Vec<Self> {
550 params.iter().map(Self::from_typed_param).collect()
551 }
552}
553
554#[derive(Debug, Clone)]
556pub struct CompiledFunction {
557 pub name: String,
558 pub type_params: Vec<String>,
562 pub nominal_type_names: Vec<String>,
566 pub params: Vec<ParamSlot>,
567 pub default_start: Option<usize>,
569 pub chunk: ChunkRef,
570 pub is_generator: bool,
572 pub is_stream: bool,
574 pub has_rest_param: bool,
576}
577
578impl CompiledFunction {
579 pub fn param_names(&self) -> impl Iterator<Item = &str> {
582 self.params.iter().map(|p| p.name.as_str())
583 }
584
585 pub fn required_param_count(&self) -> usize {
587 self.default_start.unwrap_or(self.params.len())
588 }
589
590 pub fn declares_type_param(&self, name: &str) -> bool {
591 self.type_params.iter().any(|param| param == name)
592 }
593
594 pub fn has_nominal_type(&self, name: &str) -> bool {
595 self.nominal_type_names.iter().any(|ty| ty == name)
596 }
597
598 pub(crate) fn freeze_for_cache(&self) -> CachedCompiledFunction {
599 CachedCompiledFunction {
600 name: self.name.clone(),
601 type_params: self.type_params.clone(),
602 nominal_type_names: self.nominal_type_names.clone(),
603 params: self.params.clone(),
604 default_start: self.default_start,
605 chunk: self.chunk.freeze_for_cache(),
606 is_generator: self.is_generator,
607 is_stream: self.is_stream,
608 has_rest_param: self.has_rest_param,
609 }
610 }
611
612 pub(crate) fn from_cached(cached: &CachedCompiledFunction) -> Self {
613 Self {
614 name: cached.name.clone(),
615 type_params: cached.type_params.clone(),
616 nominal_type_names: cached.nominal_type_names.clone(),
617 params: cached.params.clone(),
618 default_start: cached.default_start,
619 chunk: Rc::new(Chunk::from_cached(&cached.chunk)),
620 is_generator: cached.is_generator,
621 is_stream: cached.is_stream,
622 has_rest_param: cached.has_rest_param,
623 }
624 }
625}
626
627impl Chunk {
628 pub fn new() -> Self {
629 Self {
630 code: Vec::new(),
631 constants: Vec::new(),
632 lines: Vec::new(),
633 columns: Vec::new(),
634 source_file: None,
635 current_col: 0,
636 functions: Vec::new(),
637 inline_cache_slots: BTreeMap::new(),
638 inline_caches: Rc::new(RefCell::new(Vec::new())),
639 local_slots: Vec::new(),
640 }
641 }
642
643 pub fn set_column(&mut self, col: u32) {
645 self.current_col = col;
646 }
647
648 pub fn add_constant(&mut self, constant: Constant) -> u16 {
650 for (i, c) in self.constants.iter().enumerate() {
651 if c == &constant {
652 return i as u16;
653 }
654 }
655 let idx = self.constants.len();
656 self.constants.push(constant);
657 idx as u16
658 }
659
660 pub fn emit(&mut self, op: Op, line: u32) {
662 let col = self.current_col;
663 self.code.push(op as u8);
664 self.lines.push(line);
665 self.columns.push(col);
666 }
667
668 pub fn emit_u16(&mut self, op: Op, arg: u16, line: u32) {
670 let col = self.current_col;
671 let op_offset = self.code.len();
672 self.code.push(op as u8);
673 self.code.push((arg >> 8) as u8);
674 self.code.push((arg & 0xFF) as u8);
675 self.lines.push(line);
676 self.lines.push(line);
677 self.lines.push(line);
678 self.columns.push(col);
679 self.columns.push(col);
680 self.columns.push(col);
681 if matches!(
682 op,
683 Op::GetProperty | Op::GetPropertyOpt | Op::MethodCallSpread
684 ) {
685 self.register_inline_cache(op_offset);
686 }
687 }
688
689 pub fn emit_u8(&mut self, op: Op, arg: u8, line: u32) {
691 let col = self.current_col;
692 self.code.push(op as u8);
693 self.code.push(arg);
694 self.lines.push(line);
695 self.lines.push(line);
696 self.columns.push(col);
697 self.columns.push(col);
698 }
699
700 pub fn emit_call_builtin(
702 &mut self,
703 id: crate::BuiltinId,
704 name_idx: u16,
705 arg_count: u8,
706 line: u32,
707 ) {
708 let col = self.current_col;
709 self.code.push(Op::CallBuiltin as u8);
710 self.code.extend_from_slice(&id.raw().to_be_bytes());
711 self.code.push((name_idx >> 8) as u8);
712 self.code.push((name_idx & 0xFF) as u8);
713 self.code.push(arg_count);
714 for _ in 0..12 {
715 self.lines.push(line);
716 self.columns.push(col);
717 }
718 }
719
720 pub fn emit_call_builtin_spread(&mut self, id: crate::BuiltinId, name_idx: u16, line: u32) {
722 let col = self.current_col;
723 self.code.push(Op::CallBuiltinSpread as u8);
724 self.code.extend_from_slice(&id.raw().to_be_bytes());
725 self.code.push((name_idx >> 8) as u8);
726 self.code.push((name_idx & 0xFF) as u8);
727 for _ in 0..11 {
728 self.lines.push(line);
729 self.columns.push(col);
730 }
731 }
732
733 pub fn emit_method_call(&mut self, name_idx: u16, arg_count: u8, line: u32) {
735 self.emit_method_call_inner(Op::MethodCall, name_idx, arg_count, line);
736 }
737
738 pub fn emit_method_call_opt(&mut self, name_idx: u16, arg_count: u8, line: u32) {
740 self.emit_method_call_inner(Op::MethodCallOpt, name_idx, arg_count, line);
741 }
742
743 fn emit_method_call_inner(&mut self, op: Op, name_idx: u16, arg_count: u8, line: u32) {
744 let col = self.current_col;
745 let op_offset = self.code.len();
746 self.code.push(op as u8);
747 self.code.push((name_idx >> 8) as u8);
748 self.code.push((name_idx & 0xFF) as u8);
749 self.code.push(arg_count);
750 self.lines.push(line);
751 self.lines.push(line);
752 self.lines.push(line);
753 self.lines.push(line);
754 self.columns.push(col);
755 self.columns.push(col);
756 self.columns.push(col);
757 self.columns.push(col);
758 self.register_inline_cache(op_offset);
759 }
760
761 pub fn current_offset(&self) -> usize {
763 self.code.len()
764 }
765
766 pub fn emit_jump(&mut self, op: Op, line: u32) -> usize {
768 let col = self.current_col;
769 self.code.push(op as u8);
770 let patch_pos = self.code.len();
771 self.code.push(0xFF);
772 self.code.push(0xFF);
773 self.lines.push(line);
774 self.lines.push(line);
775 self.lines.push(line);
776 self.columns.push(col);
777 self.columns.push(col);
778 self.columns.push(col);
779 patch_pos
780 }
781
782 pub fn patch_jump(&mut self, patch_pos: usize) {
784 let target = self.code.len() as u16;
785 self.code[patch_pos] = (target >> 8) as u8;
786 self.code[patch_pos + 1] = (target & 0xFF) as u8;
787 }
788
789 pub fn patch_jump_to(&mut self, patch_pos: usize, target: usize) {
791 let target = target as u16;
792 self.code[patch_pos] = (target >> 8) as u8;
793 self.code[patch_pos + 1] = (target & 0xFF) as u8;
794 }
795
796 pub fn read_u16(&self, pos: usize) -> u16 {
798 ((self.code[pos] as u16) << 8) | (self.code[pos + 1] as u16)
799 }
800
801 fn register_inline_cache(&mut self, op_offset: usize) {
802 if self.inline_cache_slots.contains_key(&op_offset) {
803 return;
804 }
805 let mut entries = self.inline_caches.borrow_mut();
806 let slot = entries.len();
807 entries.push(InlineCacheEntry::Empty);
808 self.inline_cache_slots.insert(op_offset, slot);
809 }
810
811 pub(crate) fn inline_cache_slot(&self, op_offset: usize) -> Option<usize> {
812 self.inline_cache_slots.get(&op_offset).copied()
813 }
814
815 pub(crate) fn inline_cache_entry(&self, slot: usize) -> InlineCacheEntry {
816 self.inline_caches
817 .borrow()
818 .get(slot)
819 .cloned()
820 .unwrap_or(InlineCacheEntry::Empty)
821 }
822
823 pub(crate) fn set_inline_cache_entry(&self, slot: usize, entry: InlineCacheEntry) {
824 if let Some(existing) = self.inline_caches.borrow_mut().get_mut(slot) {
825 *existing = entry;
826 }
827 }
828
829 pub(crate) fn freeze_for_cache(&self) -> CachedChunk {
830 CachedChunk {
831 code: self.code.clone(),
832 constants: self.constants.clone(),
833 lines: self.lines.clone(),
834 columns: self.columns.clone(),
835 source_file: self.source_file.clone(),
836 current_col: self.current_col,
837 functions: self
838 .functions
839 .iter()
840 .map(|function| function.freeze_for_cache())
841 .collect(),
842 inline_cache_slots: self.inline_cache_slots.clone(),
843 local_slots: self.local_slots.clone(),
844 }
845 }
846
847 pub(crate) fn from_cached(cached: &CachedChunk) -> Self {
848 let inline_cache_count = cached.inline_cache_slots.len();
849 Self {
850 code: cached.code.clone(),
851 constants: cached.constants.clone(),
852 lines: cached.lines.clone(),
853 columns: cached.columns.clone(),
854 source_file: cached.source_file.clone(),
855 current_col: cached.current_col,
856 functions: cached
857 .functions
858 .iter()
859 .map(|function| Rc::new(CompiledFunction::from_cached(function)))
860 .collect(),
861 inline_cache_slots: cached.inline_cache_slots.clone(),
862 inline_caches: Rc::new(RefCell::new(vec![
863 InlineCacheEntry::Empty;
864 inline_cache_count
865 ])),
866 local_slots: cached.local_slots.clone(),
867 }
868 }
869
870 pub(crate) fn add_local_slot(
871 &mut self,
872 name: String,
873 mutable: bool,
874 scope_depth: usize,
875 ) -> u16 {
876 let idx = self.local_slots.len();
877 self.local_slots.push(LocalSlotInfo {
878 name,
879 mutable,
880 scope_depth,
881 });
882 idx as u16
883 }
884
885 #[cfg(test)]
886 pub(crate) fn inline_cache_entries(&self) -> Vec<InlineCacheEntry> {
887 self.inline_caches.borrow().clone()
888 }
889
890 pub fn read_u64(&self, pos: usize) -> u64 {
892 u64::from_be_bytes([
893 self.code[pos],
894 self.code[pos + 1],
895 self.code[pos + 2],
896 self.code[pos + 3],
897 self.code[pos + 4],
898 self.code[pos + 5],
899 self.code[pos + 6],
900 self.code[pos + 7],
901 ])
902 }
903
904 pub fn disassemble(&self, name: &str) -> String {
906 let mut out = format!("== {name} ==\n");
907 let mut ip = 0;
908 while ip < self.code.len() {
909 let op = self.code[ip];
910 let line = self.lines.get(ip).copied().unwrap_or(0);
911 out.push_str(&format!("{:04} [{:>4}] ", ip, line));
912 ip += 1;
913
914 match op {
915 x if x == Op::Constant as u8 => {
916 let idx = self.read_u16(ip);
917 ip += 2;
918 let val = &self.constants[idx as usize];
919 out.push_str(&format!("CONSTANT {:>4} ({})\n", idx, val));
920 }
921 x if x == Op::Nil as u8 => out.push_str("NIL\n"),
922 x if x == Op::True as u8 => out.push_str("TRUE\n"),
923 x if x == Op::False as u8 => out.push_str("FALSE\n"),
924 x if x == Op::GetVar as u8 => {
925 let idx = self.read_u16(ip);
926 ip += 2;
927 out.push_str(&format!(
928 "GET_VAR {:>4} ({})\n",
929 idx, self.constants[idx as usize]
930 ));
931 }
932 x if x == Op::DefLet as u8 => {
933 let idx = self.read_u16(ip);
934 ip += 2;
935 out.push_str(&format!(
936 "DEF_LET {:>4} ({})\n",
937 idx, self.constants[idx as usize]
938 ));
939 }
940 x if x == Op::DefVar as u8 => {
941 let idx = self.read_u16(ip);
942 ip += 2;
943 out.push_str(&format!(
944 "DEF_VAR {:>4} ({})\n",
945 idx, self.constants[idx as usize]
946 ));
947 }
948 x if x == Op::SetVar as u8 => {
949 let idx = self.read_u16(ip);
950 ip += 2;
951 out.push_str(&format!(
952 "SET_VAR {:>4} ({})\n",
953 idx, self.constants[idx as usize]
954 ));
955 }
956 x if x == Op::GetLocalSlot as u8 => {
957 let slot = self.read_u16(ip);
958 ip += 2;
959 out.push_str(&format!("GET_LOCAL_SLOT {:>4}", slot));
960 if let Some(info) = self.local_slots.get(slot as usize) {
961 out.push_str(&format!(" ({})", info.name));
962 }
963 out.push('\n');
964 }
965 x if x == Op::DefLocalSlot as u8 => {
966 let slot = self.read_u16(ip);
967 ip += 2;
968 out.push_str(&format!("DEF_LOCAL_SLOT {:>4}", slot));
969 if let Some(info) = self.local_slots.get(slot as usize) {
970 out.push_str(&format!(" ({})", info.name));
971 }
972 out.push('\n');
973 }
974 x if x == Op::SetLocalSlot as u8 => {
975 let slot = self.read_u16(ip);
976 ip += 2;
977 out.push_str(&format!("SET_LOCAL_SLOT {:>4}", slot));
978 if let Some(info) = self.local_slots.get(slot as usize) {
979 out.push_str(&format!(" ({})", info.name));
980 }
981 out.push('\n');
982 }
983 x if x == Op::PushScope as u8 => out.push_str("PUSH_SCOPE\n"),
984 x if x == Op::PopScope as u8 => out.push_str("POP_SCOPE\n"),
985 x if x == Op::Add as u8 => out.push_str("ADD\n"),
986 x if x == Op::Sub as u8 => out.push_str("SUB\n"),
987 x if x == Op::Mul as u8 => out.push_str("MUL\n"),
988 x if x == Op::Div as u8 => out.push_str("DIV\n"),
989 x if x == Op::Mod as u8 => out.push_str("MOD\n"),
990 x if x == Op::Pow as u8 => out.push_str("POW\n"),
991 x if x == Op::Negate as u8 => out.push_str("NEGATE\n"),
992 x if x == Op::Equal as u8 => out.push_str("EQUAL\n"),
993 x if x == Op::NotEqual as u8 => out.push_str("NOT_EQUAL\n"),
994 x if x == Op::Less as u8 => out.push_str("LESS\n"),
995 x if x == Op::Greater as u8 => out.push_str("GREATER\n"),
996 x if x == Op::LessEqual as u8 => out.push_str("LESS_EQUAL\n"),
997 x if x == Op::GreaterEqual as u8 => out.push_str("GREATER_EQUAL\n"),
998 x if x == Op::Contains as u8 => out.push_str("CONTAINS\n"),
999 x if x == Op::Not as u8 => out.push_str("NOT\n"),
1000 x if x == Op::Jump as u8 => {
1001 let target = self.read_u16(ip);
1002 ip += 2;
1003 out.push_str(&format!("JUMP {:>4}\n", target));
1004 }
1005 x if x == Op::JumpIfFalse as u8 => {
1006 let target = self.read_u16(ip);
1007 ip += 2;
1008 out.push_str(&format!("JUMP_IF_FALSE {:>4}\n", target));
1009 }
1010 x if x == Op::JumpIfTrue as u8 => {
1011 let target = self.read_u16(ip);
1012 ip += 2;
1013 out.push_str(&format!("JUMP_IF_TRUE {:>4}\n", target));
1014 }
1015 x if x == Op::Pop as u8 => out.push_str("POP\n"),
1016 x if x == Op::Call as u8 => {
1017 let argc = self.code[ip];
1018 ip += 1;
1019 out.push_str(&format!("CALL {:>4}\n", argc));
1020 }
1021 x if x == Op::TailCall as u8 => {
1022 let argc = self.code[ip];
1023 ip += 1;
1024 out.push_str(&format!("TAIL_CALL {:>4}\n", argc));
1025 }
1026 x if x == Op::Return as u8 => out.push_str("RETURN\n"),
1027 x if x == Op::Closure as u8 => {
1028 let idx = self.read_u16(ip);
1029 ip += 2;
1030 out.push_str(&format!("CLOSURE {:>4}\n", idx));
1031 }
1032 x if x == Op::BuildList as u8 => {
1033 let count = self.read_u16(ip);
1034 ip += 2;
1035 out.push_str(&format!("BUILD_LIST {:>4}\n", count));
1036 }
1037 x if x == Op::BuildDict as u8 => {
1038 let count = self.read_u16(ip);
1039 ip += 2;
1040 out.push_str(&format!("BUILD_DICT {:>4}\n", count));
1041 }
1042 x if x == Op::Subscript as u8 => out.push_str("SUBSCRIPT\n"),
1043 x if x == Op::SubscriptOpt as u8 => out.push_str("SUBSCRIPT_OPT\n"),
1044 x if x == Op::Slice as u8 => out.push_str("SLICE\n"),
1045 x if x == Op::GetProperty as u8 => {
1046 let idx = self.read_u16(ip);
1047 ip += 2;
1048 out.push_str(&format!(
1049 "GET_PROPERTY {:>4} ({})\n",
1050 idx, self.constants[idx as usize]
1051 ));
1052 }
1053 x if x == Op::GetPropertyOpt as u8 => {
1054 let idx = self.read_u16(ip);
1055 ip += 2;
1056 out.push_str(&format!(
1057 "GET_PROPERTY_OPT {:>4} ({})\n",
1058 idx, self.constants[idx as usize]
1059 ));
1060 }
1061 x if x == Op::SetProperty as u8 => {
1062 let idx = self.read_u16(ip);
1063 ip += 2;
1064 out.push_str(&format!(
1065 "SET_PROPERTY {:>4} ({})\n",
1066 idx, self.constants[idx as usize]
1067 ));
1068 }
1069 x if x == Op::SetSubscript as u8 => {
1070 let idx = self.read_u16(ip);
1071 ip += 2;
1072 out.push_str(&format!(
1073 "SET_SUBSCRIPT {:>4} ({})\n",
1074 idx, self.constants[idx as usize]
1075 ));
1076 }
1077 x if x == Op::MethodCall as u8 => {
1078 let idx = self.read_u16(ip);
1079 ip += 2;
1080 let argc = self.code[ip];
1081 ip += 1;
1082 out.push_str(&format!(
1083 "METHOD_CALL {:>4} ({}) argc={}\n",
1084 idx, self.constants[idx as usize], argc
1085 ));
1086 }
1087 x if x == Op::MethodCallOpt as u8 => {
1088 let idx = self.read_u16(ip);
1089 ip += 2;
1090 let argc = self.code[ip];
1091 ip += 1;
1092 out.push_str(&format!(
1093 "METHOD_CALL_OPT {:>4} ({}) argc={}\n",
1094 idx, self.constants[idx as usize], argc
1095 ));
1096 }
1097 x if x == Op::Concat as u8 => {
1098 let count = self.read_u16(ip);
1099 ip += 2;
1100 out.push_str(&format!("CONCAT {:>4}\n", count));
1101 }
1102 x if x == Op::IterInit as u8 => out.push_str("ITER_INIT\n"),
1103 x if x == Op::IterNext as u8 => {
1104 let target = self.read_u16(ip);
1105 ip += 2;
1106 out.push_str(&format!("ITER_NEXT {:>4}\n", target));
1107 }
1108 x if x == Op::Throw as u8 => out.push_str("THROW\n"),
1109 x if x == Op::TryCatchSetup as u8 => {
1110 let target = self.read_u16(ip);
1111 ip += 2;
1112 out.push_str(&format!("TRY_CATCH_SETUP {:>4}\n", target));
1113 }
1114 x if x == Op::PopHandler as u8 => out.push_str("POP_HANDLER\n"),
1115 x if x == Op::Pipe as u8 => out.push_str("PIPE\n"),
1116 x if x == Op::Parallel as u8 => out.push_str("PARALLEL\n"),
1117 x if x == Op::ParallelMap as u8 => out.push_str("PARALLEL_MAP\n"),
1118 x if x == Op::ParallelMapStream as u8 => out.push_str("PARALLEL_MAP_STREAM\n"),
1119 x if x == Op::ParallelSettle as u8 => out.push_str("PARALLEL_SETTLE\n"),
1120 x if x == Op::Spawn as u8 => out.push_str("SPAWN\n"),
1121 x if x == Op::Import as u8 => {
1122 let idx = self.read_u16(ip);
1123 ip += 2;
1124 out.push_str(&format!(
1125 "IMPORT {:>4} ({})\n",
1126 idx, self.constants[idx as usize]
1127 ));
1128 }
1129 x if x == Op::SelectiveImport as u8 => {
1130 let path_idx = self.read_u16(ip);
1131 ip += 2;
1132 let names_idx = self.read_u16(ip);
1133 ip += 2;
1134 out.push_str(&format!(
1135 "SELECTIVE_IMPORT {:>4} ({}) names: {:>4} ({})\n",
1136 path_idx,
1137 self.constants[path_idx as usize],
1138 names_idx,
1139 self.constants[names_idx as usize]
1140 ));
1141 }
1142 x if x == Op::SyncMutexEnter as u8 => {
1143 let idx = self.read_u16(ip);
1144 ip += 2;
1145 out.push_str(&format!(
1146 "SYNC_MUTEX_ENTER {:>4} ({})\n",
1147 idx, self.constants[idx as usize]
1148 ));
1149 }
1150 x if x == Op::DeadlineSetup as u8 => out.push_str("DEADLINE_SETUP\n"),
1151 x if x == Op::DeadlineEnd as u8 => out.push_str("DEADLINE_END\n"),
1152 x if x == Op::BuildEnum as u8 => {
1153 let enum_idx = self.read_u16(ip);
1154 ip += 2;
1155 let variant_idx = self.read_u16(ip);
1156 ip += 2;
1157 let field_count = self.read_u16(ip);
1158 ip += 2;
1159 out.push_str(&format!(
1160 "BUILD_ENUM {:>4} ({}) {:>4} ({}) fields={}\n",
1161 enum_idx,
1162 self.constants[enum_idx as usize],
1163 variant_idx,
1164 self.constants[variant_idx as usize],
1165 field_count
1166 ));
1167 }
1168 x if x == Op::MatchEnum as u8 => {
1169 let enum_idx = self.read_u16(ip);
1170 ip += 2;
1171 let variant_idx = self.read_u16(ip);
1172 ip += 2;
1173 out.push_str(&format!(
1174 "MATCH_ENUM {:>4} ({}) {:>4} ({})\n",
1175 enum_idx,
1176 self.constants[enum_idx as usize],
1177 variant_idx,
1178 self.constants[variant_idx as usize]
1179 ));
1180 }
1181 x if x == Op::PopIterator as u8 => out.push_str("POP_ITERATOR\n"),
1182 x if x == Op::TryUnwrap as u8 => out.push_str("TRY_UNWRAP\n"),
1183 x if x == Op::TryWrapOk as u8 => out.push_str("TRY_WRAP_OK\n"),
1184 x if x == Op::CallSpread as u8 => out.push_str("CALL_SPREAD\n"),
1185 x if x == Op::CallBuiltin as u8 => {
1186 let id = self.read_u64(ip);
1187 ip += 8;
1188 let idx = self.read_u16(ip);
1189 ip += 2;
1190 let argc = self.code[ip];
1191 ip += 1;
1192 out.push_str(&format!(
1193 "CALL_BUILTIN {id:#018x} {:>4} ({}) argc={}\n",
1194 idx, self.constants[idx as usize], argc
1195 ));
1196 }
1197 x if x == Op::CallBuiltinSpread as u8 => {
1198 let id = self.read_u64(ip);
1199 ip += 8;
1200 let idx = self.read_u16(ip);
1201 ip += 2;
1202 out.push_str(&format!(
1203 "CALL_BUILTIN_SPREAD {id:#018x} {:>4} ({})\n",
1204 idx, self.constants[idx as usize]
1205 ));
1206 }
1207 x if x == Op::MethodCallSpread as u8 => {
1208 let idx = self.read_u16(ip + 1);
1209 ip += 2;
1210 out.push_str(&format!("METHOD_CALL_SPREAD {idx}\n"));
1211 }
1212 x if x == Op::Dup as u8 => out.push_str("DUP\n"),
1213 x if x == Op::Swap as u8 => out.push_str("SWAP\n"),
1214 x if x == Op::AddInt as u8 => out.push_str("ADD_INT\n"),
1215 x if x == Op::SubInt as u8 => out.push_str("SUB_INT\n"),
1216 x if x == Op::MulInt as u8 => out.push_str("MUL_INT\n"),
1217 x if x == Op::DivInt as u8 => out.push_str("DIV_INT\n"),
1218 x if x == Op::ModInt as u8 => out.push_str("MOD_INT\n"),
1219 x if x == Op::AddFloat as u8 => out.push_str("ADD_FLOAT\n"),
1220 x if x == Op::SubFloat as u8 => out.push_str("SUB_FLOAT\n"),
1221 x if x == Op::MulFloat as u8 => out.push_str("MUL_FLOAT\n"),
1222 x if x == Op::DivFloat as u8 => out.push_str("DIV_FLOAT\n"),
1223 x if x == Op::ModFloat as u8 => out.push_str("MOD_FLOAT\n"),
1224 x if x == Op::EqualInt as u8 => out.push_str("EQUAL_INT\n"),
1225 x if x == Op::NotEqualInt as u8 => out.push_str("NOT_EQUAL_INT\n"),
1226 x if x == Op::LessInt as u8 => out.push_str("LESS_INT\n"),
1227 x if x == Op::GreaterInt as u8 => out.push_str("GREATER_INT\n"),
1228 x if x == Op::LessEqualInt as u8 => out.push_str("LESS_EQUAL_INT\n"),
1229 x if x == Op::GreaterEqualInt as u8 => out.push_str("GREATER_EQUAL_INT\n"),
1230 x if x == Op::EqualFloat as u8 => out.push_str("EQUAL_FLOAT\n"),
1231 x if x == Op::NotEqualFloat as u8 => out.push_str("NOT_EQUAL_FLOAT\n"),
1232 x if x == Op::LessFloat as u8 => out.push_str("LESS_FLOAT\n"),
1233 x if x == Op::GreaterFloat as u8 => out.push_str("GREATER_FLOAT\n"),
1234 x if x == Op::LessEqualFloat as u8 => out.push_str("LESS_EQUAL_FLOAT\n"),
1235 x if x == Op::GreaterEqualFloat as u8 => out.push_str("GREATER_EQUAL_FLOAT\n"),
1236 x if x == Op::EqualBool as u8 => out.push_str("EQUAL_BOOL\n"),
1237 x if x == Op::NotEqualBool as u8 => out.push_str("NOT_EQUAL_BOOL\n"),
1238 x if x == Op::EqualString as u8 => out.push_str("EQUAL_STRING\n"),
1239 x if x == Op::NotEqualString as u8 => out.push_str("NOT_EQUAL_STRING\n"),
1240 x if x == Op::Yield as u8 => out.push_str("YIELD\n"),
1241 _ => {
1242 out.push_str(&format!("UNKNOWN(0x{:02x})\n", op));
1243 }
1244 }
1245 }
1246 out
1247 }
1248}
1249
1250impl Default for Chunk {
1251 fn default() -> Self {
1252 Self::new()
1253 }
1254}
1255
1256#[cfg(test)]
1257mod tests {
1258 use super::Op;
1259
1260 #[test]
1261 fn op_from_byte_matches_repr_order() {
1262 for (byte, op) in Op::ALL.iter().copied().enumerate() {
1263 assert_eq!(byte as u8, op as u8);
1264 assert_eq!(Op::from_byte(byte as u8), Some(op));
1265 }
1266 assert_eq!(Op::from_byte(Op::ALL.len() as u8), None);
1267 }
1268}