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