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}