Skip to main content

pipa/compiler/
opcode.rs

1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2#[repr(u8)]
3pub enum Opcode {
4    Nop = 0,
5    End = 1,
6    Return = 2,
7
8    LoadConst = 10,
9    LoadInt = 11,
10    LoadInt8 = 12,
11    LoadTrue = 13,
12    LoadFalse = 14,
13    LoadNull = 15,
14    LoadUndefined = 16,
15    LoadTdz = 17,
16    CheckTdz = 18,
17    CheckRef = 19,
18    CheckObjectCoercible = 91,
19
20    Add = 20,
21    Sub = 21,
22    SubImm8 = 34,
23    AddNum = 35,
24    SubNum = 36,
25    MulNum = 37,
26    DivNum = 38,
27    AddImm8 = 39,
28    Mul = 22,
29    Div = 23,
30    Mod = 24,
31    Pow = 25,
32    Neg = 26,
33    BitAnd = 27,
34    BitOr = 28,
35    BitXor = 29,
36    BitNot = 30,
37    Shl = 31,
38    Shr = 32,
39    UShr = 33,
40
41    Lt = 50,
42    Lte = 51,
43    LteImm8 = 60,
44    Gt = 52,
45    Gte = 53,
46    Eq = 54,
47    Neq = 55,
48    StrictEq = 56,
49    StrictNeq = 57,
50    InstanceOf = 58,
51    NewRegExp = 59,
52
53    Not = 70,
54    TypeOf = 71,
55
56    Jump = 80,
57    JumpIf = 81,
58    JumpIfNot = 82,
59    JumpBreak = 199,
60    EndFinally = 200,
61    ResetPerIterVar = 201,
62    JumpIfNullish = 176,
63    Throw = 83,
64    Try = 84,
65    Catch = 85,
66    Finally = 86,
67    Jump8 = 87,
68    JumpIf8 = 88,
69    JumpIfNot8 = 89,
70
71    Move = 90,
72
73    GetLocal = 100,
74    SetLocal = 101,
75    GetGlobal = 102,
76    SetGlobal = 103,
77    GetUpvalue = 104,
78    SetUpvalue = 105,
79    IncLocal = 106,
80    DecLocal = 107,
81
82    NewObject = 120,
83    NewArray = 121,
84    GetField = 122,
85    SetField = 123,
86    GetProp = 124,
87    SetProp = 125,
88    Call = 126,
89    CallMethod = 127,
90    NewFunction = 128,
91    CallNew = 129,
92    DeleteProp = 130,
93    HasProperty = 131,
94    GatherRest = 132,
95    ObjectSpread = 133,
96    GetPropertyNames = 134,
97    ArrayExtend = 135,
98    SetProto = 136,
99    GetSuper = 137,
100    ArrayPush = 138,
101    CallSpread = 139,
102    CallMethodSpread = 140,
103    CallNewSpread = 141,
104    GetPrivate = 142,
105    SetPrivate = 143,
106    HasPrivate = 144,
107    Yield = 145,
108    NewGeneratorFunction = 146,
109    NewAsyncFunction = 147,
110    Await = 148,
111    NewAsyncGeneratorFunction = 149,
112    GetIterator = 150,
113    IteratorNext = 151,
114    GetArguments = 152,
115    GetCurrentFunction = 153,
116    CallCurrent = 154,
117    CallCurrent1 = 155,
118    GetNamedProp = 156,
119    SetNamedProp = 157,
120    Call0 = 174,
121    Call1 = 175,
122    Call2 = 177,
123    Call3 = 178,
124    CallNamedMethod = 179,
125
126    MathSin = 180,
127    MathCos = 181,
128    MathSqrt = 182,
129    MathAbs = 183,
130    MathFloor = 184,
131    MathCeil = 185,
132    MathRound = 186,
133    MathPow = 187,
134    MathMin = 188,
135    MathMax = 189,
136
137    DefineAccessor = 190,
138
139    DefinePrivateAccessor = 191,
140
141    SetMethodProp = 192,
142
143    Pos = 193,
144    DefineGlobal = 194,
145    DeleteGlobal = 195,
146    ThrowReferenceError = 196,
147    SetGlobalVar = 197,
148    InitGlobalVar = 198,
149
150    LtJumpIfNot = 158,
151    LtJumpIf = 159,
152    LteJumpIfNot = 160,
153    LteJumpIf = 161,
154    GtJumpIfNot = 162,
155    GtJumpIf = 163,
156    GteJumpIfNot = 164,
157    GteJumpIf = 165,
158    EqJumpIfNot = 166,
159    EqJumpIf = 167,
160    NeqJumpIfNot = 168,
161    NeqJumpIf = 169,
162    StrictEqJumpIfNot = 170,
163    StrictEqJumpIf = 171,
164    StrictNeqJumpIfNot = 172,
165    StrictNeqJumpIf = 173,
166}
167
168impl Opcode {
169    #[inline(always)]
170    pub fn from_u8(v: u8) -> Option<Self> {
171        match v {
172            0 => Some(Opcode::Nop),
173            1 => Some(Opcode::End),
174            2 => Some(Opcode::Return),
175            10 => Some(Opcode::LoadConst),
176            11 => Some(Opcode::LoadInt),
177            12 => Some(Opcode::LoadInt8),
178            13 => Some(Opcode::LoadTrue),
179            14 => Some(Opcode::LoadFalse),
180            15 => Some(Opcode::LoadNull),
181            16 => Some(Opcode::LoadUndefined),
182            17 => Some(Opcode::LoadTdz),
183            18 => Some(Opcode::CheckTdz),
184            19 => Some(Opcode::CheckRef),
185            91 => Some(Opcode::CheckObjectCoercible),
186            20 => Some(Opcode::Add),
187            21 => Some(Opcode::Sub),
188            34 => Some(Opcode::SubImm8),
189            35 => Some(Opcode::AddNum),
190            36 => Some(Opcode::SubNum),
191            37 => Some(Opcode::MulNum),
192            38 => Some(Opcode::DivNum),
193            39 => Some(Opcode::AddImm8),
194            22 => Some(Opcode::Mul),
195            23 => Some(Opcode::Div),
196            24 => Some(Opcode::Mod),
197            25 => Some(Opcode::Pow),
198            26 => Some(Opcode::Neg),
199            27 => Some(Opcode::BitAnd),
200            28 => Some(Opcode::BitOr),
201            29 => Some(Opcode::BitXor),
202            30 => Some(Opcode::BitNot),
203            31 => Some(Opcode::Shl),
204            32 => Some(Opcode::Shr),
205            33 => Some(Opcode::UShr),
206            50 => Some(Opcode::Lt),
207            51 => Some(Opcode::Lte),
208            60 => Some(Opcode::LteImm8),
209            52 => Some(Opcode::Gt),
210            53 => Some(Opcode::Gte),
211            54 => Some(Opcode::Eq),
212            55 => Some(Opcode::Neq),
213            56 => Some(Opcode::StrictEq),
214            57 => Some(Opcode::StrictNeq),
215            58 => Some(Opcode::InstanceOf),
216            59 => Some(Opcode::NewRegExp),
217            70 => Some(Opcode::Not),
218            71 => Some(Opcode::TypeOf),
219            80 => Some(Opcode::Jump),
220            81 => Some(Opcode::JumpIf),
221            82 => Some(Opcode::JumpIfNot),
222            199 => Some(Opcode::JumpBreak),
223            200 => Some(Opcode::EndFinally),
224            201 => Some(Opcode::ResetPerIterVar),
225            176 => Some(Opcode::JumpIfNullish),
226            83 => Some(Opcode::Throw),
227            84 => Some(Opcode::Try),
228            85 => Some(Opcode::Catch),
229            86 => Some(Opcode::Finally),
230            87 => Some(Opcode::Jump8),
231            88 => Some(Opcode::JumpIf8),
232            89 => Some(Opcode::JumpIfNot8),
233            90 => Some(Opcode::Move),
234            100 => Some(Opcode::GetLocal),
235            101 => Some(Opcode::SetLocal),
236            102 => Some(Opcode::GetGlobal),
237            103 => Some(Opcode::SetGlobal),
238            104 => Some(Opcode::GetUpvalue),
239            105 => Some(Opcode::SetUpvalue),
240            106 => Some(Opcode::IncLocal),
241            107 => Some(Opcode::DecLocal),
242            120 => Some(Opcode::NewObject),
243            121 => Some(Opcode::NewArray),
244            122 => Some(Opcode::GetField),
245            123 => Some(Opcode::SetField),
246            124 => Some(Opcode::GetProp),
247            125 => Some(Opcode::SetProp),
248            126 => Some(Opcode::Call),
249            127 => Some(Opcode::CallMethod),
250            128 => Some(Opcode::NewFunction),
251            129 => Some(Opcode::CallNew),
252            130 => Some(Opcode::DeleteProp),
253            131 => Some(Opcode::HasProperty),
254            132 => Some(Opcode::GatherRest),
255            133 => Some(Opcode::ObjectSpread),
256            134 => Some(Opcode::GetPropertyNames),
257            135 => Some(Opcode::ArrayExtend),
258            136 => Some(Opcode::SetProto),
259            137 => Some(Opcode::GetSuper),
260            138 => Some(Opcode::ArrayPush),
261            139 => Some(Opcode::CallSpread),
262            140 => Some(Opcode::CallMethodSpread),
263            141 => Some(Opcode::CallNewSpread),
264            142 => Some(Opcode::GetPrivate),
265            143 => Some(Opcode::SetPrivate),
266            144 => Some(Opcode::HasPrivate),
267            145 => Some(Opcode::Yield),
268            146 => Some(Opcode::NewGeneratorFunction),
269            147 => Some(Opcode::NewAsyncFunction),
270            148 => Some(Opcode::Await),
271            149 => Some(Opcode::NewAsyncGeneratorFunction),
272            150 => Some(Opcode::GetIterator),
273            151 => Some(Opcode::IteratorNext),
274            152 => Some(Opcode::GetArguments),
275            153 => Some(Opcode::GetCurrentFunction),
276            154 => Some(Opcode::CallCurrent),
277            155 => Some(Opcode::CallCurrent1),
278            156 => Some(Opcode::GetNamedProp),
279            157 => Some(Opcode::SetNamedProp),
280            174 => Some(Opcode::Call0),
281            175 => Some(Opcode::Call1),
282            177 => Some(Opcode::Call2),
283            178 => Some(Opcode::Call3),
284            179 => Some(Opcode::CallNamedMethod),
285            180 => Some(Opcode::MathSin),
286            181 => Some(Opcode::MathCos),
287            182 => Some(Opcode::MathSqrt),
288            183 => Some(Opcode::MathAbs),
289            184 => Some(Opcode::MathFloor),
290            185 => Some(Opcode::MathCeil),
291            186 => Some(Opcode::MathRound),
292            187 => Some(Opcode::MathPow),
293            188 => Some(Opcode::MathMin),
294            189 => Some(Opcode::MathMax),
295            190 => Some(Opcode::DefineAccessor),
296            191 => Some(Opcode::DefinePrivateAccessor),
297            192 => Some(Opcode::SetMethodProp),
298            193 => Some(Opcode::Pos),
299            194 => Some(Opcode::DefineGlobal),
300            195 => Some(Opcode::DeleteGlobal),
301            196 => Some(Opcode::ThrowReferenceError),
302            197 => Some(Opcode::SetGlobalVar),
303            198 => Some(Opcode::InitGlobalVar),
304            158 => Some(Opcode::LtJumpIfNot),
305            159 => Some(Opcode::LtJumpIf),
306            160 => Some(Opcode::LteJumpIfNot),
307            161 => Some(Opcode::LteJumpIf),
308            162 => Some(Opcode::GtJumpIfNot),
309            163 => Some(Opcode::GtJumpIf),
310            164 => Some(Opcode::GteJumpIfNot),
311            165 => Some(Opcode::GteJumpIf),
312            166 => Some(Opcode::EqJumpIfNot),
313            167 => Some(Opcode::EqJumpIf),
314            168 => Some(Opcode::NeqJumpIfNot),
315            169 => Some(Opcode::NeqJumpIf),
316            170 => Some(Opcode::StrictEqJumpIfNot),
317            171 => Some(Opcode::StrictEqJumpIf),
318            172 => Some(Opcode::StrictNeqJumpIfNot),
319            173 => Some(Opcode::StrictNeqJumpIf),
320            _ => None,
321        }
322    }
323
324    #[inline(always)]
325    pub fn from_u8_unchecked(v: u8) -> Self {
326        Self::from_u8(v).unwrap_or(Opcode::Nop)
327    }
328
329    pub fn instruction_size(op: Opcode) -> usize {
330        match op {
331            Opcode::Nop | Opcode::End | Opcode::Catch | Opcode::Finally | Opcode::EndFinally => 1,
332            Opcode::ResetPerIterVar => 3,
333            Opcode::Return => 3,
334            Opcode::LoadTrue
335            | Opcode::LoadFalse
336            | Opcode::LoadNull
337            | Opcode::LoadUndefined
338            | Opcode::LoadTdz
339            | Opcode::CheckTdz
340            | Opcode::IncLocal
341            | Opcode::DecLocal
342            | Opcode::Throw
343            | Opcode::GatherRest
344            | Opcode::GetSuper
345            | Opcode::Yield
346            | Opcode::NewObject
347            | Opcode::GetArguments
348            | Opcode::GetCurrentFunction
349            | Opcode::CheckObjectCoercible => 3,
350            Opcode::Neg | Opcode::BitNot | Opcode::Not | Opcode::TypeOf | Opcode::Move => 5,
351            Opcode::Add
352            | Opcode::AddNum
353            | Opcode::SubNum
354            | Opcode::MulNum
355            | Opcode::DivNum
356            | Opcode::Sub
357            | Opcode::Mul
358            | Opcode::Div
359            | Opcode::Mod
360            | Opcode::Pow
361            | Opcode::BitAnd
362            | Opcode::BitOr
363            | Opcode::BitXor
364            | Opcode::Shl
365            | Opcode::Shr
366            | Opcode::UShr
367            | Opcode::Lt
368            | Opcode::Lte
369            | Opcode::Gt
370            | Opcode::Gte
371            | Opcode::Eq
372            | Opcode::Neq
373            | Opcode::StrictEq
374            | Opcode::StrictNeq
375            | Opcode::InstanceOf => 7,
376            Opcode::SubImm8 | Opcode::AddImm8 | Opcode::LteImm8 => 6,
377            Opcode::NewRegExp => 11,
378            Opcode::LoadConst | Opcode::LoadInt => 7,
379            Opcode::LoadInt8 => 4,
380            Opcode::Jump | Opcode::JumpBreak => 5,
381            Opcode::JumpIf | Opcode::JumpIfNot | Opcode::JumpIfNullish => 7,
382            Opcode::Jump8 => 2,
383            Opcode::JumpIf8 | Opcode::JumpIfNot8 => 4,
384            Opcode::Try => 9,
385            Opcode::GetLocal
386            | Opcode::SetLocal
387            | Opcode::GetGlobal
388            | Opcode::SetGlobal
389            | Opcode::CheckRef => 7,
390            Opcode::GetUpvalue | Opcode::SetUpvalue => 5,
391            Opcode::NewArray => 5,
392            Opcode::GetField | Opcode::GetProp => 7,
393            Opcode::SetField | Opcode::SetProp => 7,
394            Opcode::GetNamedProp | Opcode::SetNamedProp => 9,
395
396            Opcode::LtJumpIfNot
397            | Opcode::LtJumpIf
398            | Opcode::LteJumpIfNot
399            | Opcode::LteJumpIf
400            | Opcode::GtJumpIfNot
401            | Opcode::GtJumpIf
402            | Opcode::GteJumpIfNot
403            | Opcode::GteJumpIf
404            | Opcode::EqJumpIfNot
405            | Opcode::EqJumpIf
406            | Opcode::NeqJumpIfNot
407            | Opcode::NeqJumpIf
408            | Opcode::StrictEqJumpIfNot
409            | Opcode::StrictEqJumpIf
410            | Opcode::StrictNeqJumpIfNot
411            | Opcode::StrictNeqJumpIf => 9,
412            Opcode::Call | Opcode::CallNew => 7,
413            Opcode::Call0 => 5,
414            Opcode::Call1 => 7,
415            Opcode::Call2 => 9,
416            Opcode::Call3 => 11,
417            Opcode::CallCurrent => 5,
418            Opcode::CallCurrent1 => 5,
419            Opcode::CallMethod => 9,
420            Opcode::CallNamedMethod => 11,
421            Opcode::MathSin
422            | Opcode::MathCos
423            | Opcode::MathSqrt
424            | Opcode::MathAbs
425            | Opcode::MathFloor
426            | Opcode::MathCeil
427            | Opcode::MathRound => 5,
428            Opcode::MathPow | Opcode::MathMin | Opcode::MathMax => 7,
429            Opcode::DefineAccessor => 9,
430            Opcode::DefinePrivateAccessor => 9,
431            Opcode::SetMethodProp => 7,
432            Opcode::Pos => 5,
433            Opcode::DefineGlobal => 7,
434            Opcode::DeleteGlobal => 7,
435            Opcode::ThrowReferenceError => 5,
436            Opcode::SetGlobalVar => 7,
437            Opcode::InitGlobalVar => 7,
438            Opcode::NewFunction => 1,
439            Opcode::DeleteProp => 7,
440            Opcode::HasProperty => 7,
441            Opcode::ObjectSpread => 5,
442            Opcode::GetPropertyNames => 5,
443            Opcode::ArrayExtend => 5,
444            Opcode::SetProto => 5,
445            Opcode::ArrayPush => 5,
446            Opcode::CallSpread => 7,
447            Opcode::CallMethodSpread => 9,
448            Opcode::CallNewSpread => 7,
449            Opcode::GetPrivate => 7,
450            Opcode::SetPrivate => 7,
451            Opcode::HasPrivate => 7,
452            Opcode::NewGeneratorFunction => 1,
453            Opcode::NewAsyncFunction => 1,
454            Opcode::NewAsyncGeneratorFunction => 1,
455            Opcode::Await => 3,
456            Opcode::GetIterator => 5,
457            Opcode::IteratorNext => 7,
458        }
459    }
460}
461
462#[derive(Debug, Clone)]
463pub struct Bytecode {
464    pub code: Vec<u8>,
465    pub constants: Vec<crate::value::JSValue>,
466    pub locals_count: u32,
467    pub param_count: u16,
468    pub line_number_table: Option<std::sync::Arc<crate::compiler::location::LineNumberTable>>,
469    pub ic_table: crate::compiler::InlineCacheTable,
470
471    pub shared_ic_table_ptr: *mut crate::compiler::InlineCacheTable,
472
473    pub shared_nested_bytecodes_ptr:
474        *mut std::cell::UnsafeCell<Vec<(u32, std::sync::Arc<NestedBytecode>)>>,
475
476    pub shared_code_ptr: *const u8,
477    pub shared_code_len: usize,
478    pub shared_const_ptr: *const crate::value::JSValue,
479    pub shared_const_len: usize,
480    pub uses_arguments: bool,
481    pub is_strict: bool,
482    pub var_name_to_slot: std::rc::Rc<Vec<(u32, u16)>>,
483    pub nested_bytecodes: Vec<(u32, std::sync::Arc<NestedBytecode>)>,
484    pub is_simple_constructor: bool,
485    pub simple_constructor_props: Vec<(crate::runtime::atom::Atom, u16, u16)>,
486    pub cached_constructor_atoms: Vec<crate::runtime::atom::Atom>,
487    pub cached_constructor_final_shape: Option<std::ptr::NonNull<crate::object::shape::Shape>>,
488}
489
490unsafe impl Send for Bytecode {}
491unsafe impl Sync for Bytecode {}
492
493pub struct NestedBytecode {
494    pub code: Vec<u8>,
495    pub constants: Vec<crate::value::JSValue>,
496    pub locals_count: u32,
497    pub param_count: u16,
498    pub uses_arguments: bool,
499    pub is_strict: bool,
500    pub var_name_to_slot: std::rc::Rc<Vec<(u32, u16)>>,
501    pub line_number_table: Option<std::sync::Arc<crate::compiler::location::LineNumberTable>>,
502
503    pub parent_bytecode_span: u32,
504
505    pub upvalue_count: u32,
506
507    pub upvalue_descs: Vec<(u32, u32)>,
508
509    pub func_name_atom: u32,
510
511    pub source_text: Option<String>,
512
513    pub ic_table: std::cell::UnsafeCell<crate::compiler::InlineCacheTable>,
514
515    pub nested_bytecodes: std::cell::UnsafeCell<Vec<(u32, std::sync::Arc<NestedBytecode>)>>,
516}
517
518unsafe impl Sync for NestedBytecode {}
519unsafe impl Send for NestedBytecode {}
520
521impl std::fmt::Debug for NestedBytecode {
522    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
523        f.debug_struct("NestedBytecode")
524            .field("code_len", &self.code.len())
525            .field("locals_count", &self.locals_count)
526            .field("param_count", &self.param_count)
527            .finish()
528    }
529}
530
531impl Bytecode {
532    pub fn new() -> Self {
533        Self {
534            code: Vec::new(),
535            constants: Vec::new(),
536            locals_count: 0,
537            param_count: 0,
538            uses_arguments: false,
539            is_strict: false,
540            var_name_to_slot: std::rc::Rc::new(Vec::new()),
541            line_number_table: None,
542            ic_table: crate::compiler::InlineCacheTable::new(),
543            shared_ic_table_ptr: std::ptr::null_mut(),
544            shared_nested_bytecodes_ptr: std::ptr::null_mut(),
545            shared_code_ptr: std::ptr::null(),
546            shared_code_len: 0,
547            shared_const_ptr: std::ptr::null(),
548            shared_const_len: 0,
549            nested_bytecodes: Vec::new(),
550            is_simple_constructor: false,
551            simple_constructor_props: Vec::new(),
552            cached_constructor_final_shape: None,
553            cached_constructor_atoms: Vec::new(),
554        }
555    }
556
557    #[inline(always)]
558    pub fn effective_ic_table_ptr(&self) -> *mut crate::compiler::InlineCacheTable {
559        if !self.shared_ic_table_ptr.is_null() {
560            self.shared_ic_table_ptr
561        } else {
562            &self.ic_table as *const crate::compiler::InlineCacheTable
563                as *mut crate::compiler::InlineCacheTable
564        }
565    }
566
567    #[inline(always)]
568    pub fn effective_code_ptr(&self) -> *const u8 {
569        if !self.shared_code_ptr.is_null() {
570            self.shared_code_ptr
571        } else {
572            self.code.as_ptr()
573        }
574    }
575
576    #[inline(always)]
577    pub fn effective_code_len(&self) -> usize {
578        if !self.shared_code_ptr.is_null() {
579            self.shared_code_len
580        } else {
581            self.code.len()
582        }
583    }
584
585    #[inline(always)]
586    pub fn effective_const_ptr(&self) -> *const crate::value::JSValue {
587        if !self.shared_const_ptr.is_null() {
588            self.shared_const_ptr
589        } else {
590            self.constants.as_ptr()
591        }
592    }
593
594    #[inline(always)]
595    pub fn effective_const_len(&self) -> usize {
596        if !self.shared_const_ptr.is_null() {
597            self.shared_const_len
598        } else {
599            self.constants.len()
600        }
601    }
602
603    pub fn disassemble(&self) -> String {
604        use std::fmt::Write;
605        let mut out = String::new();
606        writeln!(out, "=== Register Bytecode ===").unwrap();
607        writeln!(
608            out,
609            "locals_count: {}, param_count: {}",
610            self.locals_count, self.param_count
611        )
612        .unwrap();
613        let mut pc = 0usize;
614        while pc < self.code.len() {
615            let op_val = self.code[pc];
616            let op = Opcode::from_u8_unchecked(op_val);
617            let size = Opcode::instruction_size(op);
618            let remaining = self.code.len().saturating_sub(pc + 1);
619
620            let read_u8 = |offset: usize| -> u8 {
621                if offset <= remaining {
622                    self.code.get(pc + offset).copied().unwrap_or(0)
623                } else {
624                    0
625                }
626            };
627            let read_i32 = |offset: usize| -> i32 {
628                let bytes = [
629                    read_u8(offset),
630                    read_u8(offset + 1),
631                    read_u8(offset + 2),
632                    read_u8(offset + 3),
633                ];
634                i32::from_le_bytes(bytes)
635            };
636            let read_u32 = |offset: usize| -> u32 { read_i32(offset) as u32 };
637            let read_u16 = |offset: usize| -> u16 {
638                u16::from_le_bytes([read_u8(offset), read_u8(offset + 1)])
639            };
640            let read_i8 = |offset: usize| -> i8 { read_u8(offset) as i8 };
641
642            let mut operands = String::new();
643            match op {
644                Opcode::Nop
645                | Opcode::End
646                | Opcode::Catch
647                | Opcode::Finally
648                | Opcode::EndFinally => {}
649                Opcode::ResetPerIterVar => {
650                    let a = read_u16(1);
651                    write!(operands, " r{}", a).unwrap();
652                }
653                Opcode::Return
654                | Opcode::LoadTrue
655                | Opcode::LoadFalse
656                | Opcode::LoadNull
657                | Opcode::LoadUndefined
658                | Opcode::IncLocal
659                | Opcode::DecLocal => {
660                    let a = read_u16(1);
661                    write!(operands, " r{}", a).unwrap();
662                }
663                Opcode::LoadConst => {
664                    let dst = read_u16(1);
665                    let idx = read_u32(3);
666                    write!(operands, " r{}, const[{}]", dst, idx).unwrap();
667                }
668                Opcode::LoadInt => {
669                    let dst = read_u16(1);
670                    let val = read_i32(3);
671                    write!(operands, " r{}, {}", dst, val).unwrap();
672                }
673                Opcode::LoadInt8 => {
674                    let dst = read_u16(1);
675                    let val = read_u8(3) as i8;
676                    write!(operands, " r{}, {}", dst, val).unwrap();
677                }
678                Opcode::Neg
679                | Opcode::BitNot
680                | Opcode::Not
681                | Opcode::TypeOf
682                | Opcode::Throw
683                | Opcode::GatherRest
684                | Opcode::GetSuper
685                | Opcode::Yield
686                | Opcode::NewObject
687                | Opcode::GetArguments
688                | Opcode::GetCurrentFunction => {
689                    let a = read_u16(1);
690                    write!(operands, " r{}", a).unwrap();
691                }
692                Opcode::Add
693                | Opcode::AddNum
694                | Opcode::SubNum
695                | Opcode::MulNum
696                | Opcode::DivNum
697                | Opcode::Sub
698                | Opcode::Mul
699                | Opcode::Div
700                | Opcode::Mod
701                | Opcode::Pow
702                | Opcode::BitAnd
703                | Opcode::BitOr
704                | Opcode::BitXor
705                | Opcode::Shl
706                | Opcode::Shr
707                | Opcode::UShr
708                | Opcode::Lt
709                | Opcode::Lte
710                | Opcode::Gt
711                | Opcode::Gte
712                | Opcode::Eq
713                | Opcode::Neq
714                | Opcode::StrictEq
715                | Opcode::StrictNeq => {
716                    let dst = read_u16(1);
717                    let src1 = read_u16(3);
718                    let src2 = read_u16(5);
719                    write!(operands, " r{}, r{}, r{}", dst, src1, src2).unwrap();
720                }
721                Opcode::InstanceOf => {
722                    let dst = read_u16(1);
723                    let obj = read_u16(3);
724                    let ctor = read_u16(5);
725                    write!(operands, " r{}, r{}, r{}", dst, obj, ctor).unwrap();
726                }
727                Opcode::NewRegExp => {
728                    let dst = read_u16(1);
729                    let pattern_idx = read_u32(3);
730                    let flags_idx = read_u32(7);
731                    write!(
732                        operands,
733                        " r{}, pattern={}, flags={}",
734                        dst, pattern_idx, flags_idx
735                    )
736                    .unwrap();
737                }
738                Opcode::SubImm8 | Opcode::AddImm8 | Opcode::LteImm8 => {
739                    let dst = read_u16(1);
740                    let src = read_u16(3);
741                    let imm = read_i8(5);
742                    write!(operands, " r{}, r{}, {}", dst, src, imm).unwrap();
743                }
744                Opcode::Jump | Opcode::JumpBreak => {
745                    let off = read_i32(1);
746                    let size = Opcode::instruction_size(Opcode::Jump) as i32;
747                    write!(operands, " {} (pc {})", off, pc as i32 + size + off).unwrap();
748                }
749                Opcode::JumpIf | Opcode::JumpIfNot | Opcode::JumpIfNullish => {
750                    let src = read_u16(1);
751                    let off = read_i32(3);
752                    let size = Opcode::instruction_size(Opcode::JumpIf) as i32;
753                    write!(
754                        operands,
755                        " r{}, {} (pc {})",
756                        src,
757                        off,
758                        pc as i32 + size + off
759                    )
760                    .unwrap();
761                }
762                Opcode::Jump8 => {
763                    let off = read_i8(1);
764                    let size = Opcode::instruction_size(Opcode::Jump8) as i32;
765                    write!(operands, " {} (pc {})", off, pc as i32 + size + off as i32).unwrap();
766                }
767                Opcode::JumpIf8 | Opcode::JumpIfNot8 => {
768                    let src = read_u16(1);
769                    let off = read_i8(3);
770                    let size = Opcode::instruction_size(Opcode::JumpIf8) as i32;
771                    write!(
772                        operands,
773                        " r{}, {} (pc {})",
774                        src,
775                        off,
776                        pc as i32 + size + off as i32
777                    )
778                    .unwrap();
779                }
780                Opcode::Try => {
781                    let catch = read_i32(1);
782                    let finally = read_i32(5);
783                    write!(operands, " catch={} finally={}", catch, finally).unwrap();
784                }
785                Opcode::Move => {
786                    let dst = read_u16(1);
787                    let src = read_u16(3);
788                    write!(operands, " r{}, r{}", dst, src).unwrap();
789                }
790                Opcode::GetLocal | Opcode::GetGlobal => {
791                    let dst = read_u16(1);
792                    let idx = read_u32(3);
793                    write!(operands, " r{}, {}", dst, idx).unwrap();
794                }
795                Opcode::SetLocal | Opcode::SetGlobal => {
796                    let idx = read_u32(1);
797                    let src = read_u16(5);
798                    write!(operands, " {}, r{}", idx, src).unwrap();
799                }
800                Opcode::GetUpvalue | Opcode::SetUpvalue => {
801                    let a = read_u16(1);
802                    let b = read_u16(3);
803                    write!(operands, " r{}, r{}", a, b).unwrap();
804                }
805                Opcode::NewArray => {
806                    let dst = read_u16(1);
807                    let count = read_u16(3);
808                    write!(operands, " r{}, {}", dst, count).unwrap();
809                }
810                Opcode::GetField
811                | Opcode::GetProp
812                | Opcode::DeleteProp
813                | Opcode::HasProperty
814                | Opcode::GetPrivate
815                | Opcode::HasPrivate => {
816                    let dst = read_u16(1);
817                    let obj = read_u16(3);
818                    let key = read_u16(5);
819                    write!(operands, " r{}, r{}, r{}", dst, obj, key).unwrap();
820                }
821                Opcode::GetNamedProp => {
822                    let dst = read_u16(1);
823                    let obj = read_u16(3);
824                    let atom = read_u32(5);
825                    write!(operands, " r{}, r{}, atom({})", dst, obj, atom).unwrap();
826                }
827                Opcode::SetField | Opcode::SetProp | Opcode::SetPrivate => {
828                    let obj = read_u16(1);
829                    let key = read_u16(3);
830                    let val = read_u16(5);
831                    write!(operands, " r{}, r{}, r{}", obj, key, val).unwrap();
832                }
833                Opcode::SetNamedProp => {
834                    let obj = read_u16(1);
835                    let val = read_u16(3);
836                    let atom = read_u32(5);
837                    write!(operands, " r{}, r{}, atom({})", obj, val, atom).unwrap();
838                }
839                Opcode::LtJumpIfNot
840                | Opcode::LtJumpIf
841                | Opcode::LteJumpIfNot
842                | Opcode::LteJumpIf
843                | Opcode::GtJumpIfNot
844                | Opcode::GtJumpIf
845                | Opcode::GteJumpIfNot
846                | Opcode::GteJumpIf
847                | Opcode::EqJumpIfNot
848                | Opcode::EqJumpIf
849                | Opcode::NeqJumpIfNot
850                | Opcode::NeqJumpIf
851                | Opcode::StrictEqJumpIfNot
852                | Opcode::StrictEqJumpIf
853                | Opcode::StrictNeqJumpIfNot
854                | Opcode::StrictNeqJumpIf => {
855                    let a = read_u16(3);
856                    let b = read_u16(5);
857                    let offset = read_i32(10);
858                    let target = (pc as i64 + 14 + offset as i64) as usize;
859                    write!(operands, " r{}, r{} -> {}", a, b, target).unwrap();
860                }
861                Opcode::Call | Opcode::CallNew => {
862                    let dst = read_u16(1);
863                    let func = read_u16(3);
864                    let argc = read_u16(5);
865                    write!(operands, " r{}, r{}, argc={}", dst, func, argc).unwrap();
866                }
867                Opcode::Call0 => {
868                    let dst = read_u16(1);
869                    let func = read_u16(3);
870                    write!(operands, " r{}, r{}", dst, func).unwrap();
871                }
872                Opcode::Call1 => {
873                    let dst = read_u16(1);
874                    let func = read_u16(3);
875                    let arg = read_u16(5);
876                    write!(operands, " r{}, r{}, r{}", dst, func, arg).unwrap();
877                }
878                Opcode::Call2 => {
879                    let dst = read_u16(1);
880                    let func = read_u16(3);
881                    let arg0 = read_u16(5);
882                    let arg1 = read_u16(7);
883                    write!(operands, " r{}, r{}, r{}, r{}", dst, func, arg0, arg1).unwrap();
884                }
885                Opcode::Call3 => {
886                    let dst = read_u16(1);
887                    let func = read_u16(3);
888                    let arg0 = read_u16(5);
889                    let arg1 = read_u16(7);
890                    let arg2 = read_u16(9);
891                    write!(
892                        operands,
893                        " r{}, r{}, r{}, r{}, r{}",
894                        dst, func, arg0, arg1, arg2
895                    )
896                    .unwrap();
897                }
898                Opcode::CallCurrent => {
899                    let dst = read_u16(1);
900                    let argc = read_u16(3);
901                    write!(operands, " r{}, argc={}", dst, argc).unwrap();
902                }
903                Opcode::CallCurrent1 => {
904                    let dst = read_u16(1);
905                    let arg = read_u16(3);
906                    write!(operands, " r{}, r{}", dst, arg).unwrap();
907                }
908                Opcode::CallMethod => {
909                    let dst = read_u16(1);
910                    let obj = read_u16(3);
911                    let func = read_u16(5);
912                    let argc = read_u16(7);
913                    write!(operands, " r{}, r{}, r{}, argc={}", dst, obj, func, argc).unwrap();
914                }
915                Opcode::CallNamedMethod => {
916                    let dst = read_u16(1);
917                    let obj = read_u16(3);
918                    let atom = read_u32(5);
919                    let argc = read_u16(9);
920                    write!(
921                        operands,
922                        " r{}, r{}, atom={}, argc={}",
923                        dst, obj, atom, argc
924                    )
925                    .unwrap();
926                }
927                Opcode::NewFunction
928                | Opcode::NewGeneratorFunction
929                | Opcode::NewAsyncFunction
930                | Opcode::NewAsyncGeneratorFunction => {
931                    write!(operands, " <embedded>").unwrap();
932                }
933                Opcode::ObjectSpread
934                | Opcode::GetPropertyNames
935                | Opcode::ArrayExtend
936                | Opcode::SetProto
937                | Opcode::ArrayPush => {
938                    let a = read_u16(1);
939                    let b = read_u16(3);
940                    write!(operands, " r{}, r{}", a, b).unwrap();
941                }
942                Opcode::CallSpread | Opcode::CallNewSpread => {
943                    let dst = read_u16(1);
944                    let func = read_u16(3);
945                    let arr = read_u16(5);
946                    write!(operands, " r{}, r{}, r{}", dst, func, arr).unwrap();
947                }
948                Opcode::CallMethodSpread => {
949                    let dst = read_u16(1);
950                    let obj = read_u16(3);
951                    let func = read_u16(5);
952                    let arr = read_u16(7);
953                    write!(operands, " r{}, r{}, r{}, r{}", dst, obj, func, arr).unwrap();
954                }
955                Opcode::MathSin
956                | Opcode::MathCos
957                | Opcode::MathSqrt
958                | Opcode::MathAbs
959                | Opcode::MathFloor
960                | Opcode::MathCeil
961                | Opcode::MathRound => {
962                    let dst = read_u16(1);
963                    let src = read_u16(3);
964                    write!(operands, " r{}, r{}", dst, src).unwrap();
965                }
966                Opcode::MathPow | Opcode::MathMin | Opcode::MathMax => {
967                    let dst = read_u16(1);
968                    let a = read_u16(3);
969                    let b = read_u16(5);
970                    write!(operands, " r{}, r{}, r{}", dst, a, b).unwrap();
971                }
972                Opcode::DefineAccessor => {
973                    let obj = read_u16(1);
974                    let key = read_u16(3);
975                    let getter = read_u16(5);
976                    let setter = read_u16(7);
977                    write!(operands, " r{}, r{}, r{}, r{}", obj, key, getter, setter).unwrap();
978                }
979                Opcode::DefinePrivateAccessor => {
980                    let obj = read_u16(1);
981                    let key = read_u16(3);
982                    let getter = read_u16(5);
983                    let setter = read_u16(7);
984                    write!(operands, " r{}, r{}, r{}, r{}", obj, key, getter, setter).unwrap();
985                }
986                Opcode::SetMethodProp => {
987                    let obj = read_u16(1);
988                    let key = read_u16(3);
989                    let val = read_u16(5);
990                    write!(operands, " r{}, r{}, r{}", obj, key, val).unwrap();
991                }
992                Opcode::Pos => {
993                    let dst = read_u16(1);
994                    let src = read_u16(3);
995                    write!(operands, " r{}, r{}", dst, src).unwrap();
996                }
997                Opcode::DefineGlobal => {
998                    let idx = read_u32(1);
999                    let src = read_u16(5);
1000                    write!(operands, " {}, r{}", idx, src).unwrap();
1001                }
1002                Opcode::DeleteGlobal => {
1003                    let dst = read_u16(1);
1004                    let idx = read_u32(3);
1005                    write!(operands, " r{}, {}", dst, idx).unwrap();
1006                }
1007                Opcode::ThrowReferenceError => {
1008                    let idx = read_u32(1);
1009                    write!(operands, " const[{}]", idx).unwrap();
1010                }
1011                Opcode::SetGlobalVar => {
1012                    let idx = read_u32(1);
1013                    let src = read_u16(5);
1014                    write!(operands, " {}, r{}", idx, src).unwrap();
1015                }
1016                Opcode::InitGlobalVar => {
1017                    let idx = read_u32(1);
1018                    let src = read_u16(5);
1019                    write!(operands, " {}, r{}", idx, src).unwrap();
1020                }
1021                Opcode::Await => {
1022                    let src = read_u16(1);
1023                    write!(operands, " r{}", src).unwrap();
1024                }
1025                Opcode::LoadTdz | Opcode::CheckTdz => {
1026                    let a = read_u16(1);
1027                    write!(operands, " r{}", a).unwrap();
1028                }
1029                Opcode::CheckRef => {
1030                    let a = read_u16(1);
1031                    let idx = read_u32(3);
1032                    write!(operands, " r{}, const[{}]", a, idx).unwrap();
1033                }
1034                Opcode::CheckObjectCoercible => {
1035                    let a = read_u16(1);
1036                    write!(operands, " r{}", a).unwrap();
1037                }
1038                Opcode::GetIterator => {
1039                    let dst = read_u16(1);
1040                    let src = read_u16(3);
1041                    write!(operands, " r{}, r{}", dst, src).unwrap();
1042                }
1043                Opcode::IteratorNext => {
1044                    let dst_val = read_u16(1);
1045                    let dst_done = read_u16(3);
1046                    let iter = read_u16(5);
1047                    write!(operands, " r{}, r{}, r{}", dst_val, dst_done, iter).unwrap();
1048                }
1049            }
1050            writeln!(out, "{:04}  {:?}{}", pc, op, operands).unwrap();
1051            pc += size.max(1);
1052        }
1053        out
1054    }
1055
1056    pub fn serialize(&self) -> Vec<u8> {
1057        let mut out = Vec::new();
1058
1059        out.extend_from_slice(b"SAFC");
1060
1061        out.extend_from_slice(&1u32.to_le_bytes());
1062
1063        out.extend_from_slice(&self.locals_count.to_le_bytes());
1064
1065        out.extend_from_slice(&self.param_count.to_le_bytes());
1066
1067        out.push(self.uses_arguments as u8);
1068
1069        out.extend_from_slice(&(self.code.len() as u64).to_le_bytes());
1070
1071        out.extend_from_slice(&self.code);
1072
1073        out.extend_from_slice(&(self.constants.len() as u64).to_le_bytes());
1074
1075        for c in &self.constants {
1076            out.extend_from_slice(&c.raw_bits().to_le_bytes());
1077        }
1078        out
1079    }
1080
1081    pub fn deserialize(data: &[u8]) -> Result<Self, String> {
1082        if data.len() < 8 {
1083            return Err("Invalid .jsc: too short".to_string());
1084        }
1085        if &data[0..4] != b"SAFC" {
1086            return Err("Invalid .jsc: bad magic".to_string());
1087        }
1088        let version = u32::from_le_bytes([data[4], data[5], data[6], data[7]]);
1089        if version != 1 {
1090            return Err(format!("Unsupported .jsc version: {}", version));
1091        }
1092        let mut offset = 8usize;
1093        let read_u32 = |o: &mut usize| -> u32 {
1094            let v = u32::from_le_bytes([data[*o], data[*o + 1], data[*o + 2], data[*o + 3]]);
1095            *o += 4;
1096            v
1097        };
1098        let read_u16 = |o: &mut usize| -> u16 {
1099            let v = u16::from_le_bytes([data[*o], data[*o + 1]]);
1100            *o += 2;
1101            v
1102        };
1103        let read_u64 = |o: &mut usize| -> u64 {
1104            let v = u64::from_le_bytes([
1105                data[*o],
1106                data[*o + 1],
1107                data[*o + 2],
1108                data[*o + 3],
1109                data[*o + 4],
1110                data[*o + 5],
1111                data[*o + 6],
1112                data[*o + 7],
1113            ]);
1114            *o += 8;
1115            v
1116        };
1117        let locals_count = read_u32(&mut offset);
1118        let param_count = read_u16(&mut offset);
1119        let uses_arguments = data[offset] != 0;
1120        offset += 1;
1121        let code_len = read_u64(&mut offset) as usize;
1122        if offset + code_len > data.len() {
1123            return Err("Invalid .jsc: code truncated".to_string());
1124        }
1125        let code = data[offset..offset + code_len].to_vec();
1126        offset += code_len;
1127        let constants_count = read_u64(&mut offset) as usize;
1128        if offset + constants_count * 8 > data.len() {
1129            return Err("Invalid .jsc: constants truncated".to_string());
1130        }
1131        let mut constants = Vec::with_capacity(constants_count);
1132        for _ in 0..constants_count {
1133            let bits = read_u64(&mut offset);
1134            constants.push(crate::value::JSValue::from_raw_bits(bits));
1135        }
1136        Ok(Self {
1137            code,
1138            constants,
1139            locals_count,
1140            param_count,
1141            uses_arguments,
1142            is_strict: false,
1143            var_name_to_slot: std::rc::Rc::new(Vec::new()),
1144            line_number_table: None,
1145            ic_table: crate::compiler::InlineCacheTable::new(),
1146            shared_ic_table_ptr: std::ptr::null_mut(),
1147            shared_nested_bytecodes_ptr: std::ptr::null_mut(),
1148            shared_code_ptr: std::ptr::null(),
1149            shared_code_len: 0,
1150            shared_const_ptr: std::ptr::null(),
1151            shared_const_len: 0,
1152            nested_bytecodes: Vec::new(),
1153            is_simple_constructor: false,
1154            simple_constructor_props: Vec::new(),
1155            cached_constructor_final_shape: None,
1156            cached_constructor_atoms: Vec::new(),
1157        })
1158    }
1159}
1160
1161#[cfg(test)]
1162mod tests {
1163    use super::*;
1164
1165    #[test]
1166    fn test_opcode_roundtrip() {
1167        let ops = [
1168            Opcode::Nop,
1169            Opcode::End,
1170            Opcode::Return,
1171            Opcode::LoadConst,
1172            Opcode::LoadInt,
1173            Opcode::LoadTrue,
1174            Opcode::Add,
1175            Opcode::Sub,
1176            Opcode::Mul,
1177            Opcode::Div,
1178            Opcode::Jump,
1179            Opcode::JumpBreak,
1180            Opcode::JumpIf,
1181            Opcode::JumpIfNot,
1182            Opcode::GetLocal,
1183            Opcode::SetLocal,
1184            Opcode::IncLocal,
1185            Opcode::DecLocal,
1186            Opcode::Call,
1187            Opcode::CallMethod,
1188            Opcode::NewFunction,
1189            Opcode::JumpIfNullish,
1190            Opcode::Call2,
1191            Opcode::Call3,
1192            Opcode::Try,
1193        ];
1194        for op in ops {
1195            let byte = op as u8;
1196            let recovered = Opcode::from_u8(byte).expect("roundtrip failed");
1197            assert_eq!(op, recovered, "Opcode {:?} did not round-trip", op);
1198        }
1199    }
1200
1201    #[test]
1202    fn test_disassemble_basic_program() {
1203        let bytecode = Bytecode {
1204            code: vec![
1205                Opcode::LoadInt as u8,
1206                0x00,
1207                0x00,
1208                0x0A,
1209                0x00,
1210                0x00,
1211                0x00,
1212                Opcode::LoadInt as u8,
1213                0x01,
1214                0x00,
1215                0x14,
1216                0x00,
1217                0x00,
1218                0x00,
1219                Opcode::Add as u8,
1220                0x02,
1221                0x00,
1222                0x00,
1223                0x00,
1224                0x01,
1225                0x00,
1226                Opcode::Return as u8,
1227                0x02,
1228                0x00,
1229            ],
1230            constants: vec![],
1231            locals_count: 3,
1232            param_count: 0,
1233            line_number_table: None,
1234            ic_table: crate::compiler::InlineCacheTable::new(),
1235            shared_ic_table_ptr: std::ptr::null_mut(),
1236            uses_arguments: false,
1237            is_strict: false,
1238            var_name_to_slot: std::rc::Rc::new(Vec::new()),
1239            nested_bytecodes: Vec::new(),
1240            is_simple_constructor: false,
1241            simple_constructor_props: Vec::new(),
1242            cached_constructor_final_shape: None,
1243            cached_constructor_atoms: Vec::new(),
1244            shared_code_ptr: std::ptr::null(),
1245            shared_code_len: 0,
1246            shared_const_ptr: std::ptr::null(),
1247            shared_const_len: 0,
1248            shared_nested_bytecodes_ptr: std::ptr::null_mut(),
1249        };
1250
1251        let output = bytecode.disassemble();
1252        assert!(output.contains("=== Register Bytecode ==="));
1253        assert!(
1254            output.contains("LoadInt r0, 10"),
1255            "Basic disasm wrong: {}",
1256            output
1257        );
1258        assert!(
1259            output.contains("LoadInt r1, 20"),
1260            "Basic disasm wrong: {}",
1261            output
1262        );
1263        assert!(
1264            output.contains("Add r2, r0, r1"),
1265            "Basic disasm wrong: {}",
1266            output
1267        );
1268        assert!(output.contains("Return"), "Basic disasm wrong: {}", output);
1269    }
1270
1271    #[test]
1272    fn test_disassemble_jump_offsets() {
1273        let bytecode = Bytecode {
1274            code: vec![
1275                Opcode::LoadFalse as u8,
1276                0x00,
1277                0x00,
1278                Opcode::JumpIfNot as u8,
1279                0x00,
1280                0x00,
1281                0x05,
1282                0x00,
1283                0x00,
1284                0x00,
1285                Opcode::LoadTrue as u8,
1286                0x01,
1287                0x00,
1288                Opcode::Jump as u8,
1289                0x00,
1290                0x00,
1291                0x00,
1292                0x00,
1293            ],
1294            constants: vec![],
1295            locals_count: 1,
1296            param_count: 0,
1297            line_number_table: None,
1298            ic_table: crate::compiler::InlineCacheTable::new(),
1299            shared_ic_table_ptr: std::ptr::null_mut(),
1300            uses_arguments: false,
1301            is_strict: false,
1302            var_name_to_slot: std::rc::Rc::new(Vec::new()),
1303            nested_bytecodes: Vec::new(),
1304            is_simple_constructor: false,
1305            simple_constructor_props: Vec::new(),
1306            cached_constructor_final_shape: None,
1307            cached_constructor_atoms: Vec::new(),
1308            shared_code_ptr: std::ptr::null(),
1309            shared_code_len: 0,
1310            shared_const_ptr: std::ptr::null(),
1311            shared_const_len: 0,
1312            shared_nested_bytecodes_ptr: std::ptr::null_mut(),
1313        };
1314
1315        let output = bytecode.disassemble();
1316
1317        assert!(
1318            output.contains("JumpIfNot r0, 5 (pc 15)"),
1319            "JumpIfNot disasm wrong: {}",
1320            output
1321        );
1322
1323        assert!(
1324            output.contains("Jump 0 (pc 18)"),
1325            "Jump disasm wrong: {}",
1326            output
1327        );
1328    }
1329
1330    #[test]
1331    fn test_disassemble_call_and_object_ops() {
1332        let bytecode = Bytecode {
1333            code: vec![
1334                Opcode::NewObject as u8,
1335                0x00,
1336                0x00,
1337                Opcode::Call as u8,
1338                0x01,
1339                0x00,
1340                0x02,
1341                0x00,
1342                0x00,
1343                0x00,
1344                Opcode::SetField as u8,
1345                0x00,
1346                0x00,
1347                0x01,
1348                0x00,
1349                0x02,
1350                0x00,
1351            ],
1352            constants: vec![],
1353            locals_count: 3,
1354            param_count: 0,
1355            line_number_table: None,
1356            ic_table: crate::compiler::InlineCacheTable::new(),
1357            shared_ic_table_ptr: std::ptr::null_mut(),
1358            uses_arguments: false,
1359            is_strict: false,
1360            var_name_to_slot: std::rc::Rc::new(Vec::new()),
1361            nested_bytecodes: Vec::new(),
1362            is_simple_constructor: false,
1363            simple_constructor_props: Vec::new(),
1364            cached_constructor_final_shape: None,
1365            cached_constructor_atoms: Vec::new(),
1366            shared_code_ptr: std::ptr::null(),
1367            shared_code_len: 0,
1368            shared_const_ptr: std::ptr::null(),
1369            shared_const_len: 0,
1370            shared_nested_bytecodes_ptr: std::ptr::null_mut(),
1371        };
1372
1373        let output = bytecode.disassemble();
1374        assert!(
1375            output.contains("NewObject r0"),
1376            "NewObject disasm wrong: {}",
1377            output
1378        );
1379        assert!(
1380            output.contains("Call r1, r2, argc=0"),
1381            "Call disasm wrong: {}",
1382            output
1383        );
1384        assert!(
1385            output.contains("SetField r0, r1, r2"),
1386            "SetField disasm wrong: {}",
1387            output
1388        );
1389    }
1390}