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