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, Copy, PartialEq, Eq)]
404pub(crate) enum PropertyCacheTarget {
405 ListCount,
406 ListEmpty,
407 ListFirst,
408 ListLast,
409 StringCount,
410 StringEmpty,
411 PairFirst,
412 PairSecond,
413 EnumVariant,
414 EnumFields,
415}
416
417#[derive(Debug, Clone, Copy, PartialEq, Eq)]
418pub(crate) enum MethodCacheTarget {
419 ListCount,
420 ListEmpty,
421 StringCount,
422 StringEmpty,
423 DictCount,
424 RangeCount,
425 RangeLen,
426 RangeEmpty,
427 RangeFirst,
428 RangeLast,
429 SetCount,
430 SetLen,
431 SetEmpty,
432}
433
434#[derive(Debug, Clone, PartialEq, Eq)]
436pub struct LocalSlotInfo {
437 pub name: String,
438 pub mutable: bool,
439 pub scope_depth: usize,
440}
441
442impl fmt::Display for Constant {
443 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
444 match self {
445 Constant::Int(n) => write!(f, "{n}"),
446 Constant::Float(n) => write!(f, "{n}"),
447 Constant::String(s) => write!(f, "\"{s}\""),
448 Constant::Bool(b) => write!(f, "{b}"),
449 Constant::Nil => write!(f, "nil"),
450 Constant::Duration(ms) => write!(f, "{ms}ms"),
451 }
452 }
453}
454
455#[derive(Debug, Clone)]
457pub struct Chunk {
458 pub code: Vec<u8>,
460 pub constants: Vec<Constant>,
462 pub lines: Vec<u32>,
464 pub columns: Vec<u32>,
467 pub source_file: Option<String>,
472 current_col: u32,
474 pub functions: Vec<CompiledFunctionRef>,
476 inline_cache_slots: BTreeMap<usize, usize>,
479 inline_caches: Rc<RefCell<Vec<InlineCacheEntry>>>,
482 pub(crate) local_slots: Vec<LocalSlotInfo>,
484}
485
486pub type ChunkRef = Rc<Chunk>;
487pub type CompiledFunctionRef = Rc<CompiledFunction>;
488
489#[derive(Debug, Clone)]
490pub(crate) struct CachedChunk {
491 code: Vec<u8>,
492 constants: Vec<Constant>,
493 lines: Vec<u32>,
494 columns: Vec<u32>,
495 source_file: Option<String>,
496 current_col: u32,
497 functions: Vec<CachedCompiledFunction>,
498 inline_cache_slots: BTreeMap<usize, usize>,
499 local_slots: Vec<LocalSlotInfo>,
500}
501
502#[derive(Debug, Clone)]
503pub(crate) struct CachedCompiledFunction {
504 name: String,
505 type_params: Vec<String>,
506 nominal_type_names: Vec<String>,
507 params: Vec<ParamSlot>,
508 default_start: Option<usize>,
509 chunk: CachedChunk,
510 is_generator: bool,
511 is_stream: bool,
512 has_rest_param: bool,
513}
514
515#[derive(Debug, Clone)]
521pub struct ParamSlot {
522 pub name: String,
523 pub type_expr: Option<TypeExpr>,
526 pub has_default: bool,
530}
531
532impl ParamSlot {
533 pub fn from_typed_param(param: &harn_parser::TypedParam) -> Self {
536 Self {
537 name: param.name.clone(),
538 type_expr: param.type_expr.clone(),
539 has_default: param.default_value.is_some(),
540 }
541 }
542
543 pub fn vec_from_typed(params: &[harn_parser::TypedParam]) -> Vec<Self> {
548 params.iter().map(Self::from_typed_param).collect()
549 }
550}
551
552#[derive(Debug, Clone)]
554pub struct CompiledFunction {
555 pub name: String,
556 pub type_params: Vec<String>,
560 pub nominal_type_names: Vec<String>,
564 pub params: Vec<ParamSlot>,
565 pub default_start: Option<usize>,
567 pub chunk: ChunkRef,
568 pub is_generator: bool,
570 pub is_stream: bool,
572 pub has_rest_param: bool,
574}
575
576impl CompiledFunction {
577 pub fn param_names(&self) -> impl Iterator<Item = &str> {
580 self.params.iter().map(|p| p.name.as_str())
581 }
582
583 pub fn required_param_count(&self) -> usize {
585 self.default_start.unwrap_or(self.params.len())
586 }
587
588 pub fn declares_type_param(&self, name: &str) -> bool {
589 self.type_params.iter().any(|param| param == name)
590 }
591
592 pub fn has_nominal_type(&self, name: &str) -> bool {
593 self.nominal_type_names.iter().any(|ty| ty == name)
594 }
595
596 pub(crate) fn freeze_for_cache(&self) -> CachedCompiledFunction {
597 CachedCompiledFunction {
598 name: self.name.clone(),
599 type_params: self.type_params.clone(),
600 nominal_type_names: self.nominal_type_names.clone(),
601 params: self.params.clone(),
602 default_start: self.default_start,
603 chunk: self.chunk.freeze_for_cache(),
604 is_generator: self.is_generator,
605 is_stream: self.is_stream,
606 has_rest_param: self.has_rest_param,
607 }
608 }
609
610 pub(crate) fn from_cached(cached: &CachedCompiledFunction) -> Self {
611 Self {
612 name: cached.name.clone(),
613 type_params: cached.type_params.clone(),
614 nominal_type_names: cached.nominal_type_names.clone(),
615 params: cached.params.clone(),
616 default_start: cached.default_start,
617 chunk: Rc::new(Chunk::from_cached(&cached.chunk)),
618 is_generator: cached.is_generator,
619 is_stream: cached.is_stream,
620 has_rest_param: cached.has_rest_param,
621 }
622 }
623}
624
625impl Chunk {
626 pub fn new() -> Self {
627 Self {
628 code: Vec::new(),
629 constants: Vec::new(),
630 lines: Vec::new(),
631 columns: Vec::new(),
632 source_file: None,
633 current_col: 0,
634 functions: Vec::new(),
635 inline_cache_slots: BTreeMap::new(),
636 inline_caches: Rc::new(RefCell::new(Vec::new())),
637 local_slots: Vec::new(),
638 }
639 }
640
641 pub fn set_column(&mut self, col: u32) {
643 self.current_col = col;
644 }
645
646 pub fn add_constant(&mut self, constant: Constant) -> u16 {
648 for (i, c) in self.constants.iter().enumerate() {
649 if c == &constant {
650 return i as u16;
651 }
652 }
653 let idx = self.constants.len();
654 self.constants.push(constant);
655 idx as u16
656 }
657
658 pub fn emit(&mut self, op: Op, line: u32) {
660 let col = self.current_col;
661 self.code.push(op as u8);
662 self.lines.push(line);
663 self.columns.push(col);
664 }
665
666 pub fn emit_u16(&mut self, op: Op, arg: u16, line: u32) {
668 let col = self.current_col;
669 let op_offset = self.code.len();
670 self.code.push(op as u8);
671 self.code.push((arg >> 8) as u8);
672 self.code.push((arg & 0xFF) as u8);
673 self.lines.push(line);
674 self.lines.push(line);
675 self.lines.push(line);
676 self.columns.push(col);
677 self.columns.push(col);
678 self.columns.push(col);
679 if matches!(
680 op,
681 Op::GetProperty | Op::GetPropertyOpt | Op::MethodCallSpread
682 ) {
683 self.register_inline_cache(op_offset);
684 }
685 }
686
687 pub fn emit_u8(&mut self, op: Op, arg: u8, line: u32) {
689 let col = self.current_col;
690 self.code.push(op as u8);
691 self.code.push(arg);
692 self.lines.push(line);
693 self.lines.push(line);
694 self.columns.push(col);
695 self.columns.push(col);
696 }
697
698 pub fn emit_call_builtin(
700 &mut self,
701 id: crate::BuiltinId,
702 name_idx: u16,
703 arg_count: u8,
704 line: u32,
705 ) {
706 let col = self.current_col;
707 self.code.push(Op::CallBuiltin as u8);
708 self.code.extend_from_slice(&id.raw().to_be_bytes());
709 self.code.push((name_idx >> 8) as u8);
710 self.code.push((name_idx & 0xFF) as u8);
711 self.code.push(arg_count);
712 for _ in 0..12 {
713 self.lines.push(line);
714 self.columns.push(col);
715 }
716 }
717
718 pub fn emit_call_builtin_spread(&mut self, id: crate::BuiltinId, name_idx: u16, line: u32) {
720 let col = self.current_col;
721 self.code.push(Op::CallBuiltinSpread as u8);
722 self.code.extend_from_slice(&id.raw().to_be_bytes());
723 self.code.push((name_idx >> 8) as u8);
724 self.code.push((name_idx & 0xFF) as u8);
725 for _ in 0..11 {
726 self.lines.push(line);
727 self.columns.push(col);
728 }
729 }
730
731 pub fn emit_method_call(&mut self, name_idx: u16, arg_count: u8, line: u32) {
733 self.emit_method_call_inner(Op::MethodCall, name_idx, arg_count, line);
734 }
735
736 pub fn emit_method_call_opt(&mut self, name_idx: u16, arg_count: u8, line: u32) {
738 self.emit_method_call_inner(Op::MethodCallOpt, name_idx, arg_count, line);
739 }
740
741 fn emit_method_call_inner(&mut self, op: Op, name_idx: u16, arg_count: u8, line: u32) {
742 let col = self.current_col;
743 let op_offset = self.code.len();
744 self.code.push(op as u8);
745 self.code.push((name_idx >> 8) as u8);
746 self.code.push((name_idx & 0xFF) as u8);
747 self.code.push(arg_count);
748 self.lines.push(line);
749 self.lines.push(line);
750 self.lines.push(line);
751 self.lines.push(line);
752 self.columns.push(col);
753 self.columns.push(col);
754 self.columns.push(col);
755 self.columns.push(col);
756 self.register_inline_cache(op_offset);
757 }
758
759 pub fn current_offset(&self) -> usize {
761 self.code.len()
762 }
763
764 pub fn emit_jump(&mut self, op: Op, line: u32) -> usize {
766 let col = self.current_col;
767 self.code.push(op as u8);
768 let patch_pos = self.code.len();
769 self.code.push(0xFF);
770 self.code.push(0xFF);
771 self.lines.push(line);
772 self.lines.push(line);
773 self.lines.push(line);
774 self.columns.push(col);
775 self.columns.push(col);
776 self.columns.push(col);
777 patch_pos
778 }
779
780 pub fn patch_jump(&mut self, patch_pos: usize) {
782 let target = self.code.len() as u16;
783 self.code[patch_pos] = (target >> 8) as u8;
784 self.code[patch_pos + 1] = (target & 0xFF) as u8;
785 }
786
787 pub fn patch_jump_to(&mut self, patch_pos: usize, target: usize) {
789 let target = target as u16;
790 self.code[patch_pos] = (target >> 8) as u8;
791 self.code[patch_pos + 1] = (target & 0xFF) as u8;
792 }
793
794 pub fn read_u16(&self, pos: usize) -> u16 {
796 ((self.code[pos] as u16) << 8) | (self.code[pos + 1] as u16)
797 }
798
799 fn register_inline_cache(&mut self, op_offset: usize) {
800 if self.inline_cache_slots.contains_key(&op_offset) {
801 return;
802 }
803 let mut entries = self.inline_caches.borrow_mut();
804 let slot = entries.len();
805 entries.push(InlineCacheEntry::Empty);
806 self.inline_cache_slots.insert(op_offset, slot);
807 }
808
809 pub(crate) fn inline_cache_slot(&self, op_offset: usize) -> Option<usize> {
810 self.inline_cache_slots.get(&op_offset).copied()
811 }
812
813 pub(crate) fn inline_cache_entry(&self, slot: usize) -> InlineCacheEntry {
814 self.inline_caches
815 .borrow()
816 .get(slot)
817 .cloned()
818 .unwrap_or(InlineCacheEntry::Empty)
819 }
820
821 pub(crate) fn set_inline_cache_entry(&self, slot: usize, entry: InlineCacheEntry) {
822 if let Some(existing) = self.inline_caches.borrow_mut().get_mut(slot) {
823 *existing = entry;
824 }
825 }
826
827 pub(crate) fn freeze_for_cache(&self) -> CachedChunk {
828 CachedChunk {
829 code: self.code.clone(),
830 constants: self.constants.clone(),
831 lines: self.lines.clone(),
832 columns: self.columns.clone(),
833 source_file: self.source_file.clone(),
834 current_col: self.current_col,
835 functions: self
836 .functions
837 .iter()
838 .map(|function| function.freeze_for_cache())
839 .collect(),
840 inline_cache_slots: self.inline_cache_slots.clone(),
841 local_slots: self.local_slots.clone(),
842 }
843 }
844
845 pub(crate) fn from_cached(cached: &CachedChunk) -> Self {
846 let inline_cache_count = cached.inline_cache_slots.len();
847 Self {
848 code: cached.code.clone(),
849 constants: cached.constants.clone(),
850 lines: cached.lines.clone(),
851 columns: cached.columns.clone(),
852 source_file: cached.source_file.clone(),
853 current_col: cached.current_col,
854 functions: cached
855 .functions
856 .iter()
857 .map(|function| Rc::new(CompiledFunction::from_cached(function)))
858 .collect(),
859 inline_cache_slots: cached.inline_cache_slots.clone(),
860 inline_caches: Rc::new(RefCell::new(vec![
861 InlineCacheEntry::Empty;
862 inline_cache_count
863 ])),
864 local_slots: cached.local_slots.clone(),
865 }
866 }
867
868 pub(crate) fn add_local_slot(
869 &mut self,
870 name: String,
871 mutable: bool,
872 scope_depth: usize,
873 ) -> u16 {
874 let idx = self.local_slots.len();
875 self.local_slots.push(LocalSlotInfo {
876 name,
877 mutable,
878 scope_depth,
879 });
880 idx as u16
881 }
882
883 #[cfg(test)]
884 pub(crate) fn inline_cache_entries(&self) -> Vec<InlineCacheEntry> {
885 self.inline_caches.borrow().clone()
886 }
887
888 pub fn read_u64(&self, pos: usize) -> u64 {
890 u64::from_be_bytes([
891 self.code[pos],
892 self.code[pos + 1],
893 self.code[pos + 2],
894 self.code[pos + 3],
895 self.code[pos + 4],
896 self.code[pos + 5],
897 self.code[pos + 6],
898 self.code[pos + 7],
899 ])
900 }
901
902 pub fn disassemble(&self, name: &str) -> String {
904 let mut out = format!("== {name} ==\n");
905 let mut ip = 0;
906 while ip < self.code.len() {
907 let op = self.code[ip];
908 let line = self.lines.get(ip).copied().unwrap_or(0);
909 out.push_str(&format!("{:04} [{:>4}] ", ip, line));
910 ip += 1;
911
912 match op {
913 x if x == Op::Constant as u8 => {
914 let idx = self.read_u16(ip);
915 ip += 2;
916 let val = &self.constants[idx as usize];
917 out.push_str(&format!("CONSTANT {:>4} ({})\n", idx, val));
918 }
919 x if x == Op::Nil as u8 => out.push_str("NIL\n"),
920 x if x == Op::True as u8 => out.push_str("TRUE\n"),
921 x if x == Op::False as u8 => out.push_str("FALSE\n"),
922 x if x == Op::GetVar as u8 => {
923 let idx = self.read_u16(ip);
924 ip += 2;
925 out.push_str(&format!(
926 "GET_VAR {:>4} ({})\n",
927 idx, self.constants[idx as usize]
928 ));
929 }
930 x if x == Op::DefLet as u8 => {
931 let idx = self.read_u16(ip);
932 ip += 2;
933 out.push_str(&format!(
934 "DEF_LET {:>4} ({})\n",
935 idx, self.constants[idx as usize]
936 ));
937 }
938 x if x == Op::DefVar as u8 => {
939 let idx = self.read_u16(ip);
940 ip += 2;
941 out.push_str(&format!(
942 "DEF_VAR {:>4} ({})\n",
943 idx, self.constants[idx as usize]
944 ));
945 }
946 x if x == Op::SetVar as u8 => {
947 let idx = self.read_u16(ip);
948 ip += 2;
949 out.push_str(&format!(
950 "SET_VAR {:>4} ({})\n",
951 idx, self.constants[idx as usize]
952 ));
953 }
954 x if x == Op::GetLocalSlot as u8 => {
955 let slot = self.read_u16(ip);
956 ip += 2;
957 out.push_str(&format!("GET_LOCAL_SLOT {:>4}", slot));
958 if let Some(info) = self.local_slots.get(slot as usize) {
959 out.push_str(&format!(" ({})", info.name));
960 }
961 out.push('\n');
962 }
963 x if x == Op::DefLocalSlot as u8 => {
964 let slot = self.read_u16(ip);
965 ip += 2;
966 out.push_str(&format!("DEF_LOCAL_SLOT {:>4}", slot));
967 if let Some(info) = self.local_slots.get(slot as usize) {
968 out.push_str(&format!(" ({})", info.name));
969 }
970 out.push('\n');
971 }
972 x if x == Op::SetLocalSlot as u8 => {
973 let slot = self.read_u16(ip);
974 ip += 2;
975 out.push_str(&format!("SET_LOCAL_SLOT {:>4}", slot));
976 if let Some(info) = self.local_slots.get(slot as usize) {
977 out.push_str(&format!(" ({})", info.name));
978 }
979 out.push('\n');
980 }
981 x if x == Op::PushScope as u8 => out.push_str("PUSH_SCOPE\n"),
982 x if x == Op::PopScope as u8 => out.push_str("POP_SCOPE\n"),
983 x if x == Op::Add as u8 => out.push_str("ADD\n"),
984 x if x == Op::Sub as u8 => out.push_str("SUB\n"),
985 x if x == Op::Mul as u8 => out.push_str("MUL\n"),
986 x if x == Op::Div as u8 => out.push_str("DIV\n"),
987 x if x == Op::Mod as u8 => out.push_str("MOD\n"),
988 x if x == Op::Pow as u8 => out.push_str("POW\n"),
989 x if x == Op::Negate as u8 => out.push_str("NEGATE\n"),
990 x if x == Op::Equal as u8 => out.push_str("EQUAL\n"),
991 x if x == Op::NotEqual as u8 => out.push_str("NOT_EQUAL\n"),
992 x if x == Op::Less as u8 => out.push_str("LESS\n"),
993 x if x == Op::Greater as u8 => out.push_str("GREATER\n"),
994 x if x == Op::LessEqual as u8 => out.push_str("LESS_EQUAL\n"),
995 x if x == Op::GreaterEqual as u8 => out.push_str("GREATER_EQUAL\n"),
996 x if x == Op::Contains as u8 => out.push_str("CONTAINS\n"),
997 x if x == Op::Not as u8 => out.push_str("NOT\n"),
998 x if x == Op::Jump as u8 => {
999 let target = self.read_u16(ip);
1000 ip += 2;
1001 out.push_str(&format!("JUMP {:>4}\n", target));
1002 }
1003 x if x == Op::JumpIfFalse as u8 => {
1004 let target = self.read_u16(ip);
1005 ip += 2;
1006 out.push_str(&format!("JUMP_IF_FALSE {:>4}\n", target));
1007 }
1008 x if x == Op::JumpIfTrue as u8 => {
1009 let target = self.read_u16(ip);
1010 ip += 2;
1011 out.push_str(&format!("JUMP_IF_TRUE {:>4}\n", target));
1012 }
1013 x if x == Op::Pop as u8 => out.push_str("POP\n"),
1014 x if x == Op::Call as u8 => {
1015 let argc = self.code[ip];
1016 ip += 1;
1017 out.push_str(&format!("CALL {:>4}\n", argc));
1018 }
1019 x if x == Op::TailCall as u8 => {
1020 let argc = self.code[ip];
1021 ip += 1;
1022 out.push_str(&format!("TAIL_CALL {:>4}\n", argc));
1023 }
1024 x if x == Op::Return as u8 => out.push_str("RETURN\n"),
1025 x if x == Op::Closure as u8 => {
1026 let idx = self.read_u16(ip);
1027 ip += 2;
1028 out.push_str(&format!("CLOSURE {:>4}\n", idx));
1029 }
1030 x if x == Op::BuildList as u8 => {
1031 let count = self.read_u16(ip);
1032 ip += 2;
1033 out.push_str(&format!("BUILD_LIST {:>4}\n", count));
1034 }
1035 x if x == Op::BuildDict as u8 => {
1036 let count = self.read_u16(ip);
1037 ip += 2;
1038 out.push_str(&format!("BUILD_DICT {:>4}\n", count));
1039 }
1040 x if x == Op::Subscript as u8 => out.push_str("SUBSCRIPT\n"),
1041 x if x == Op::SubscriptOpt as u8 => out.push_str("SUBSCRIPT_OPT\n"),
1042 x if x == Op::Slice as u8 => out.push_str("SLICE\n"),
1043 x if x == Op::GetProperty as u8 => {
1044 let idx = self.read_u16(ip);
1045 ip += 2;
1046 out.push_str(&format!(
1047 "GET_PROPERTY {:>4} ({})\n",
1048 idx, self.constants[idx as usize]
1049 ));
1050 }
1051 x if x == Op::GetPropertyOpt as u8 => {
1052 let idx = self.read_u16(ip);
1053 ip += 2;
1054 out.push_str(&format!(
1055 "GET_PROPERTY_OPT {:>4} ({})\n",
1056 idx, self.constants[idx as usize]
1057 ));
1058 }
1059 x if x == Op::SetProperty as u8 => {
1060 let idx = self.read_u16(ip);
1061 ip += 2;
1062 out.push_str(&format!(
1063 "SET_PROPERTY {:>4} ({})\n",
1064 idx, self.constants[idx as usize]
1065 ));
1066 }
1067 x if x == Op::SetSubscript as u8 => {
1068 let idx = self.read_u16(ip);
1069 ip += 2;
1070 out.push_str(&format!(
1071 "SET_SUBSCRIPT {:>4} ({})\n",
1072 idx, self.constants[idx as usize]
1073 ));
1074 }
1075 x if x == Op::MethodCall as u8 => {
1076 let idx = self.read_u16(ip);
1077 ip += 2;
1078 let argc = self.code[ip];
1079 ip += 1;
1080 out.push_str(&format!(
1081 "METHOD_CALL {:>4} ({}) argc={}\n",
1082 idx, self.constants[idx as usize], argc
1083 ));
1084 }
1085 x if x == Op::MethodCallOpt as u8 => {
1086 let idx = self.read_u16(ip);
1087 ip += 2;
1088 let argc = self.code[ip];
1089 ip += 1;
1090 out.push_str(&format!(
1091 "METHOD_CALL_OPT {:>4} ({}) argc={}\n",
1092 idx, self.constants[idx as usize], argc
1093 ));
1094 }
1095 x if x == Op::Concat as u8 => {
1096 let count = self.read_u16(ip);
1097 ip += 2;
1098 out.push_str(&format!("CONCAT {:>4}\n", count));
1099 }
1100 x if x == Op::IterInit as u8 => out.push_str("ITER_INIT\n"),
1101 x if x == Op::IterNext as u8 => {
1102 let target = self.read_u16(ip);
1103 ip += 2;
1104 out.push_str(&format!("ITER_NEXT {:>4}\n", target));
1105 }
1106 x if x == Op::Throw as u8 => out.push_str("THROW\n"),
1107 x if x == Op::TryCatchSetup as u8 => {
1108 let target = self.read_u16(ip);
1109 ip += 2;
1110 out.push_str(&format!("TRY_CATCH_SETUP {:>4}\n", target));
1111 }
1112 x if x == Op::PopHandler as u8 => out.push_str("POP_HANDLER\n"),
1113 x if x == Op::Pipe as u8 => out.push_str("PIPE\n"),
1114 x if x == Op::Parallel as u8 => out.push_str("PARALLEL\n"),
1115 x if x == Op::ParallelMap as u8 => out.push_str("PARALLEL_MAP\n"),
1116 x if x == Op::ParallelMapStream as u8 => out.push_str("PARALLEL_MAP_STREAM\n"),
1117 x if x == Op::ParallelSettle as u8 => out.push_str("PARALLEL_SETTLE\n"),
1118 x if x == Op::Spawn as u8 => out.push_str("SPAWN\n"),
1119 x if x == Op::Import as u8 => {
1120 let idx = self.read_u16(ip);
1121 ip += 2;
1122 out.push_str(&format!(
1123 "IMPORT {:>4} ({})\n",
1124 idx, self.constants[idx as usize]
1125 ));
1126 }
1127 x if x == Op::SelectiveImport as u8 => {
1128 let path_idx = self.read_u16(ip);
1129 ip += 2;
1130 let names_idx = self.read_u16(ip);
1131 ip += 2;
1132 out.push_str(&format!(
1133 "SELECTIVE_IMPORT {:>4} ({}) names: {:>4} ({})\n",
1134 path_idx,
1135 self.constants[path_idx as usize],
1136 names_idx,
1137 self.constants[names_idx as usize]
1138 ));
1139 }
1140 x if x == Op::SyncMutexEnter as u8 => {
1141 let idx = self.read_u16(ip);
1142 ip += 2;
1143 out.push_str(&format!(
1144 "SYNC_MUTEX_ENTER {:>4} ({})\n",
1145 idx, self.constants[idx as usize]
1146 ));
1147 }
1148 x if x == Op::DeadlineSetup as u8 => out.push_str("DEADLINE_SETUP\n"),
1149 x if x == Op::DeadlineEnd as u8 => out.push_str("DEADLINE_END\n"),
1150 x if x == Op::BuildEnum as u8 => {
1151 let enum_idx = self.read_u16(ip);
1152 ip += 2;
1153 let variant_idx = self.read_u16(ip);
1154 ip += 2;
1155 let field_count = self.read_u16(ip);
1156 ip += 2;
1157 out.push_str(&format!(
1158 "BUILD_ENUM {:>4} ({}) {:>4} ({}) fields={}\n",
1159 enum_idx,
1160 self.constants[enum_idx as usize],
1161 variant_idx,
1162 self.constants[variant_idx as usize],
1163 field_count
1164 ));
1165 }
1166 x if x == Op::MatchEnum as u8 => {
1167 let enum_idx = self.read_u16(ip);
1168 ip += 2;
1169 let variant_idx = self.read_u16(ip);
1170 ip += 2;
1171 out.push_str(&format!(
1172 "MATCH_ENUM {:>4} ({}) {:>4} ({})\n",
1173 enum_idx,
1174 self.constants[enum_idx as usize],
1175 variant_idx,
1176 self.constants[variant_idx as usize]
1177 ));
1178 }
1179 x if x == Op::PopIterator as u8 => out.push_str("POP_ITERATOR\n"),
1180 x if x == Op::TryUnwrap as u8 => out.push_str("TRY_UNWRAP\n"),
1181 x if x == Op::TryWrapOk as u8 => out.push_str("TRY_WRAP_OK\n"),
1182 x if x == Op::CallSpread as u8 => out.push_str("CALL_SPREAD\n"),
1183 x if x == Op::CallBuiltin as u8 => {
1184 let id = self.read_u64(ip);
1185 ip += 8;
1186 let idx = self.read_u16(ip);
1187 ip += 2;
1188 let argc = self.code[ip];
1189 ip += 1;
1190 out.push_str(&format!(
1191 "CALL_BUILTIN {id:#018x} {:>4} ({}) argc={}\n",
1192 idx, self.constants[idx as usize], argc
1193 ));
1194 }
1195 x if x == Op::CallBuiltinSpread as u8 => {
1196 let id = self.read_u64(ip);
1197 ip += 8;
1198 let idx = self.read_u16(ip);
1199 ip += 2;
1200 out.push_str(&format!(
1201 "CALL_BUILTIN_SPREAD {id:#018x} {:>4} ({})\n",
1202 idx, self.constants[idx as usize]
1203 ));
1204 }
1205 x if x == Op::MethodCallSpread as u8 => {
1206 let idx = self.read_u16(ip + 1);
1207 ip += 2;
1208 out.push_str(&format!("METHOD_CALL_SPREAD {idx}\n"));
1209 }
1210 x if x == Op::Dup as u8 => out.push_str("DUP\n"),
1211 x if x == Op::Swap as u8 => out.push_str("SWAP\n"),
1212 x if x == Op::AddInt as u8 => out.push_str("ADD_INT\n"),
1213 x if x == Op::SubInt as u8 => out.push_str("SUB_INT\n"),
1214 x if x == Op::MulInt as u8 => out.push_str("MUL_INT\n"),
1215 x if x == Op::DivInt as u8 => out.push_str("DIV_INT\n"),
1216 x if x == Op::ModInt as u8 => out.push_str("MOD_INT\n"),
1217 x if x == Op::AddFloat as u8 => out.push_str("ADD_FLOAT\n"),
1218 x if x == Op::SubFloat as u8 => out.push_str("SUB_FLOAT\n"),
1219 x if x == Op::MulFloat as u8 => out.push_str("MUL_FLOAT\n"),
1220 x if x == Op::DivFloat as u8 => out.push_str("DIV_FLOAT\n"),
1221 x if x == Op::ModFloat as u8 => out.push_str("MOD_FLOAT\n"),
1222 x if x == Op::EqualInt as u8 => out.push_str("EQUAL_INT\n"),
1223 x if x == Op::NotEqualInt as u8 => out.push_str("NOT_EQUAL_INT\n"),
1224 x if x == Op::LessInt as u8 => out.push_str("LESS_INT\n"),
1225 x if x == Op::GreaterInt as u8 => out.push_str("GREATER_INT\n"),
1226 x if x == Op::LessEqualInt as u8 => out.push_str("LESS_EQUAL_INT\n"),
1227 x if x == Op::GreaterEqualInt as u8 => out.push_str("GREATER_EQUAL_INT\n"),
1228 x if x == Op::EqualFloat as u8 => out.push_str("EQUAL_FLOAT\n"),
1229 x if x == Op::NotEqualFloat as u8 => out.push_str("NOT_EQUAL_FLOAT\n"),
1230 x if x == Op::LessFloat as u8 => out.push_str("LESS_FLOAT\n"),
1231 x if x == Op::GreaterFloat as u8 => out.push_str("GREATER_FLOAT\n"),
1232 x if x == Op::LessEqualFloat as u8 => out.push_str("LESS_EQUAL_FLOAT\n"),
1233 x if x == Op::GreaterEqualFloat as u8 => out.push_str("GREATER_EQUAL_FLOAT\n"),
1234 x if x == Op::EqualBool as u8 => out.push_str("EQUAL_BOOL\n"),
1235 x if x == Op::NotEqualBool as u8 => out.push_str("NOT_EQUAL_BOOL\n"),
1236 x if x == Op::EqualString as u8 => out.push_str("EQUAL_STRING\n"),
1237 x if x == Op::NotEqualString as u8 => out.push_str("NOT_EQUAL_STRING\n"),
1238 x if x == Op::Yield as u8 => out.push_str("YIELD\n"),
1239 _ => {
1240 out.push_str(&format!("UNKNOWN(0x{:02x})\n", op));
1241 }
1242 }
1243 }
1244 out
1245 }
1246}
1247
1248impl Default for Chunk {
1249 fn default() -> Self {
1250 Self::new()
1251 }
1252}
1253
1254#[cfg(test)]
1255mod tests {
1256 use super::Op;
1257
1258 #[test]
1259 fn op_from_byte_matches_repr_order() {
1260 for (byte, op) in Op::ALL.iter().copied().enumerate() {
1261 assert_eq!(byte as u8, op as u8);
1262 assert_eq!(Op::from_byte(byte as u8), Some(op));
1263 }
1264 assert_eq!(Op::from_byte(Op::ALL.len() as u8), None);
1265 }
1266}