1use crate::compiler::ast::*;
2use crate::compiler::location::LineNumberTable;
3use crate::compiler::opcode::{Bytecode, Opcode};
4use crate::runtime::context::JSContext;
5use crate::util::FxHashMap;
6use crate::value::JSValue;
7
8#[derive(Debug)]
9struct BreakableFrame {
10 label: Option<String>,
11 break_patches: Vec<usize>,
12 continue_patches: Vec<usize>,
13 continue_target: Option<usize>,
14}
15
16#[derive(Debug, Clone, Copy)]
17enum VarLocation {
18 Local(u16),
19 Upvalue(u16),
20}
21
22#[derive(Debug, Clone, Copy)]
23struct ParentVar {
24 local_idx: u16,
25 is_inherited: bool,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum OptLevel {
30 O0,
31 O1,
32 O2,
33 O3,
34}
35
36impl Default for OptLevel {
37 fn default() -> Self {
38 Self::O2
39 }
40}
41
42impl OptLevel {
43 pub fn from_flag(flag: &str) -> Option<Self> {
44 match flag {
45 "-O0" => Some(Self::O0),
46 "-O1" => Some(Self::O1),
47 "-O2" => Some(Self::O2),
48 "-O3" => Some(Self::O3),
49 _ => None,
50 }
51 }
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55enum P2FoldProfile {
56 Raw,
57 Ternary,
58 Logical,
59 Nullish,
60 Stable,
61 Aggressive,
62}
63
64impl P2FoldProfile {
65 fn from_env_override() -> Option<Self> {
66 let mode = std::env::var("PIPA_P2_PROFILE").ok()?;
67 match mode.as_str() {
68 "raw" => Some(Self::Raw),
69 "ternary" => Some(Self::Ternary),
70 "logical" => Some(Self::Logical),
71 "nullish" => Some(Self::Nullish),
72 "stable" => Some(Self::Stable),
73 "aggressive" => Some(Self::Aggressive),
74 _ => None,
75 }
76 }
77
78 fn allow_ternary_bool(self) -> bool {
79 matches!(
80 self,
81 Self::Ternary | Self::Logical | Self::Nullish | Self::Stable | Self::Aggressive
82 )
83 }
84
85 fn allow_logical_bool_left(self) -> bool {
86 matches!(
87 self,
88 Self::Logical | Self::Nullish | Self::Stable | Self::Aggressive
89 )
90 }
91
92 fn allow_nullish_literal_left(self) -> bool {
93 matches!(self, Self::Nullish | Self::Stable | Self::Aggressive)
94 }
95
96 fn allow_if_bool(self) -> bool {
97 matches!(self, Self::Stable | Self::Aggressive)
98 }
99
100 fn allow_aggressive_truthy(self) -> bool {
101 matches!(self, Self::Aggressive)
102 }
103}
104
105#[derive(Debug, Clone, Copy)]
106struct CodegenSettings {
107 p2_fold_profile: P2FoldProfile,
108 opt_jump_if_nullish: bool,
109 opt_call23: bool,
110 opt_fused_cmp_jump: bool,
111 opt_fused_getprop_call: bool,
112 opt_tiny_inline: bool,
113 opt_branch_result_prealloc: bool,
114}
115
116impl CodegenSettings {
117 fn for_opt_level(opt_level: OptLevel) -> Self {
118 match opt_level {
119 OptLevel::O0 => Self {
120 p2_fold_profile: P2FoldProfile::Raw,
121 opt_jump_if_nullish: false,
122 opt_call23: false,
123 opt_fused_cmp_jump: false,
124 opt_fused_getprop_call: false,
125 opt_tiny_inline: false,
126 opt_branch_result_prealloc: false,
127 },
128 OptLevel::O1 => Self {
129 p2_fold_profile: P2FoldProfile::Ternary,
130 opt_jump_if_nullish: true,
131 opt_call23: true,
132 opt_fused_cmp_jump: false,
133 opt_fused_getprop_call: true,
134 opt_tiny_inline: false,
135 opt_branch_result_prealloc: true,
136 },
137 OptLevel::O2 => Self {
138 p2_fold_profile: P2FoldProfile::Stable,
139 opt_jump_if_nullish: true,
140 opt_call23: true,
141 opt_fused_cmp_jump: false,
142 opt_fused_getprop_call: true,
143 opt_tiny_inline: true,
144 opt_branch_result_prealloc: true,
145 },
146 OptLevel::O3 => Self {
147 p2_fold_profile: P2FoldProfile::Aggressive,
148 opt_jump_if_nullish: true,
149 opt_call23: true,
150 opt_fused_cmp_jump: false,
151 opt_fused_getprop_call: true,
152 opt_tiny_inline: true,
153 opt_branch_result_prealloc: true,
154 },
155 }
156 }
157}
158
159#[derive(Debug, Clone, Copy)]
160pub(crate) struct VarEntry {
161 pub(crate) slot: u16,
162 pub(crate) kind: VariableKind,
163 pub(crate) initialized: bool,
164}
165
166pub struct CodeGenerator {
167 code: Vec<u8>,
168 constants: Vec<JSValue>,
169 next_register: u16,
170 max_register: u16,
171 free_list: Vec<u16>,
172 scope_stack: Vec<FxHashMap<String, VarEntry>>,
173 reserved_slots: std::collections::HashSet<u16>,
174 breakable_frames: Vec<BreakableFrame>,
175
176 parent_vars: FxHashMap<String, ParentVar>,
177 upvalues: Vec<(u32, u16)>,
178 upvalue_slots: FxHashMap<String, u16>,
179 line_table: LineNumberTable,
180 depth: u32,
181 is_script_level: bool,
182 scope_is_global: Vec<bool>,
183 function_uses_arguments: bool,
184 current_function_name: Option<String>,
185 tiny_inline_functions: FxHashMap<String, FunctionDeclaration>,
186 p2_fold_profile: P2FoldProfile,
187 opt_jump_if_nullish: bool,
188 opt_call23: bool,
189 opt_fused_cmp_jump: bool,
190 opt_fused_getprop_call: bool,
191 opt_tiny_inline: bool,
192 opt_branch_result_prealloc: bool,
193 suppress_ref_error: bool,
194 pub(crate) is_strict: bool,
195 pub(crate) is_eval: bool,
196 is_arrow: bool,
197 in_static_method: bool,
198 pending_label: Option<String>,
199 all_var_names: Vec<(String, u16)>,
200}
201
202impl CodeGenerator {
203 pub fn new() -> Self {
204 Self::with_opt_level(OptLevel::default())
205 }
206
207 pub fn with_opt_level(opt_level: OptLevel) -> Self {
208 let mut settings = CodegenSettings::for_opt_level(opt_level);
209 if let Some(profile) = P2FoldProfile::from_env_override() {
210 settings.p2_fold_profile = profile;
211 }
212 settings.opt_jump_if_nullish =
213 Self::env_flag("PIPA_OPT_JUMP_NULLISH", settings.opt_jump_if_nullish);
214 settings.opt_call23 = Self::env_flag("PIPA_OPT_CALL23", settings.opt_call23);
215 settings.opt_fused_cmp_jump =
216 Self::env_flag("PIPA_OPT_FUSED_CMP_JUMP", settings.opt_fused_cmp_jump);
217 settings.opt_fused_getprop_call = Self::env_flag(
218 "PIPA_OPT_FUSED_GETPROP_CALL",
219 settings.opt_fused_getprop_call,
220 );
221 settings.opt_tiny_inline = Self::env_flag("PIPA_OPT_TINY_INLINE", settings.opt_tiny_inline);
222 settings.opt_branch_result_prealloc = Self::env_flag(
223 "PIPA_OPT_BRANCH_RESULT_PREALLOC",
224 settings.opt_branch_result_prealloc,
225 );
226 Self::with_settings(settings)
227 }
228
229 fn with_settings(settings: CodegenSettings) -> Self {
230 Self {
231 code: Vec::new(),
232 constants: Vec::new(),
233 next_register: 0,
234 max_register: 0,
235 free_list: Vec::new(),
236 scope_stack: vec![FxHashMap::default()],
237 reserved_slots: std::collections::HashSet::new(),
238 breakable_frames: Vec::new(),
239 parent_vars: FxHashMap::default(),
240 upvalues: Vec::new(),
241 upvalue_slots: FxHashMap::default(),
242 line_table: LineNumberTable::new(),
243 depth: 0,
244 is_script_level: true,
245 scope_is_global: vec![false],
246 function_uses_arguments: false,
247 current_function_name: None,
248 tiny_inline_functions: FxHashMap::default(),
249 p2_fold_profile: settings.p2_fold_profile,
250 opt_jump_if_nullish: settings.opt_jump_if_nullish,
251 opt_call23: settings.opt_call23,
252 opt_fused_cmp_jump: settings.opt_fused_cmp_jump,
253 opt_fused_getprop_call: settings.opt_fused_getprop_call,
254 opt_tiny_inline: settings.opt_tiny_inline,
255 opt_branch_result_prealloc: settings.opt_branch_result_prealloc,
256 suppress_ref_error: false,
257 is_strict: false,
258 is_eval: false,
259 is_arrow: false,
260 in_static_method: false,
261 pending_label: None,
262 all_var_names: Vec::new(),
263 }
264 }
265
266 fn settings(&self) -> CodegenSettings {
267 CodegenSettings {
268 p2_fold_profile: self.p2_fold_profile,
269 opt_jump_if_nullish: self.opt_jump_if_nullish,
270 opt_call23: self.opt_call23,
271 opt_fused_cmp_jump: self.opt_fused_cmp_jump,
272 opt_fused_getprop_call: self.opt_fused_getprop_call,
273 opt_tiny_inline: self.opt_tiny_inline,
274 opt_branch_result_prealloc: self.opt_branch_result_prealloc,
275 }
276 }
277
278 fn nested_codegen(&self) -> Self {
279 let mut nested = Self::with_settings(self.settings());
280 nested.tiny_inline_functions = self.tiny_inline_functions.clone();
281 nested.is_strict = self.is_strict;
282 nested.is_eval = self.is_eval;
283 nested.in_static_method = self.in_static_method;
284 nested
285 }
286
287 fn env_flag(name: &str, default: bool) -> bool {
288 match std::env::var(name) {
289 Ok(v) => match v.to_ascii_lowercase().as_str() {
290 "1" | "true" | "yes" | "on" => true,
291 "0" | "false" | "no" | "off" => false,
292 _ => default,
293 },
294 Err(_) => default,
295 }
296 }
297
298 fn literal_truthiness(lit: &Literal) -> Option<bool> {
299 match lit {
300 Literal::Boolean(b) => Some(*b),
301 Literal::Null | Literal::Undefined => Some(false),
302 Literal::Number(n) => Some(*n != 0.0 && !n.is_nan()),
303 Literal::LegacyOctal(n) => Some(*n != 0),
304 Literal::String(s, _) => Some(!s.is_empty()),
305 Literal::BigInt(_) => None,
306 }
307 }
308
309 fn detect_tiny_inline_function(func: &FunctionDeclaration) -> bool {
310 if func.generator || func.is_async {
311 return false;
312 }
313 if func.params.len() != 1 {
314 return false;
315 }
316 let param_name = match &func.params[0] {
317 Parameter::Identifier(name) => name,
318 _ => return false,
319 };
320 if func.body.body.len() != 1 {
321 return false;
322 }
323 let ret = match &func.body.body[0] {
324 ASTNode::ReturnStatement(stmt) => stmt,
325 _ => return false,
326 };
327 let expr = match &ret.argument {
328 Some(expr) => expr,
329 None => return false,
330 };
331 match expr {
332 Expression::MemberExpression(member) => match member.object.as_ref() {
333 Expression::Identifier(id) => {
334 id.name == *param_name
335 && matches!(member.property, MemberProperty::Identifier(_))
336 }
337 Expression::MemberExpression(inner) => {
338 if inner.computed || member.computed {
339 return false;
340 }
341 match inner.object.as_ref() {
342 Expression::Identifier(id) => {
343 id.name == *param_name
344 && matches!(inner.property, MemberProperty::Identifier(_))
345 && matches!(member.property, MemberProperty::Identifier(_))
346 }
347 _ => false,
348 }
349 }
350 _ => false,
351 },
352 _ => false,
353 }
354 }
355
356 fn try_emit_tiny_inline_call(
357 &mut self,
358 call: &CallExpression,
359 ctx: &mut JSContext,
360 ) -> Result<Option<u16>, String> {
361 let callee_name = match call.callee.as_ref() {
362 Expression::Identifier(id) => &id.name,
363 _ => return Ok(None),
364 };
365 let func = match self.tiny_inline_functions.get(callee_name).cloned() {
366 Some(func) => func,
367 None => return Ok(None),
368 };
369 if call.arguments.len() != 1 {
370 return Ok(None);
371 }
372 let arg_reg = match &call.arguments[0] {
373 Argument::Expression(expr) => self.gen_expression(expr, ctx)?,
374 Argument::Spread(_) => return Ok(None),
375 };
376
377 let ret = match &func.body.body[0] {
378 ASTNode::ReturnStatement(stmt) => stmt,
379 _ => return Ok(None),
380 };
381 let expr = match &ret.argument {
382 Some(expr) => expr,
383 None => return Ok(None),
384 };
385
386 let dst = self.alloc_register();
387 match expr {
388 Expression::MemberExpression(member) => match member.object.as_ref() {
389 Expression::Identifier(_) => {
390 if let MemberProperty::Identifier(name) = &member.property {
391 let atom = ctx.intern(name);
392 self.emit(Opcode::GetNamedProp);
393 self.emit_u16(dst);
394 self.emit_u16(arg_reg);
395 self.emit_u32(atom.0);
396 } else {
397 self.free_register(arg_reg);
398 self.free_register(dst);
399 return Ok(None);
400 }
401 }
402 Expression::MemberExpression(inner) => {
403 let mid = self.alloc_register();
404 let atom1 = match &inner.property {
405 MemberProperty::Identifier(name) => ctx.intern(name),
406 _ => {
407 self.free_register(arg_reg);
408 self.free_register(mid);
409 self.free_register(dst);
410 return Ok(None);
411 }
412 };
413 let atom2 = match &member.property {
414 MemberProperty::Identifier(name) => ctx.intern(name),
415 _ => {
416 self.free_register(arg_reg);
417 self.free_register(mid);
418 self.free_register(dst);
419 return Ok(None);
420 }
421 };
422 self.emit(Opcode::GetNamedProp);
423 self.emit_u16(mid);
424 self.emit_u16(arg_reg);
425 self.emit_u32(atom1.0);
426 self.emit(Opcode::GetNamedProp);
427 self.emit_u16(dst);
428 self.emit_u16(mid);
429 self.emit_u32(atom2.0);
430 self.free_register(mid);
431 }
432 _ => {
433 self.free_register(arg_reg);
434 self.free_register(dst);
435 return Ok(None);
436 }
437 },
438 _ => {
439 self.free_register(arg_reg);
440 self.free_register(dst);
441 return Ok(None);
442 }
443 }
444
445 self.free_register(arg_reg);
446 Ok(Some(dst))
447 }
448
449 fn fused_op_from_binary_cmp(bin_op: &BinaryOp, jump_if_true: bool) -> Option<Opcode> {
450 use BinaryOp::*;
451 let op = match (bin_op, jump_if_true) {
452 (Lt, true) => Opcode::LtJumpIf,
453 (Lt, false) => Opcode::LtJumpIfNot,
454 (Lte, true) => Opcode::LteJumpIf,
455 (Lte, false) => Opcode::LteJumpIfNot,
456 (Gt, true) => Opcode::GtJumpIf,
457 (Gt, false) => Opcode::GtJumpIfNot,
458 (Gte, true) => Opcode::GteJumpIf,
459 (Gte, false) => Opcode::GteJumpIfNot,
460 (Eq, true) => Opcode::EqJumpIf,
461 (Eq, false) => Opcode::EqJumpIfNot,
462 (Neq, true) => Opcode::NeqJumpIf,
463 (Neq, false) => Opcode::NeqJumpIfNot,
464 (StrictEq, true) => Opcode::StrictEqJumpIf,
465 (StrictEq, false) => Opcode::StrictEqJumpIfNot,
466 (StrictNeq, true) => Opcode::StrictNeqJumpIf,
467 (StrictNeq, false) => Opcode::StrictNeqJumpIfNot,
468 _ => return None,
469 };
470 Some(op)
471 }
472
473 fn emit(&mut self, op: Opcode) {
474 self.code.push(op as u8);
475 }
476
477 fn emit_u8(&mut self, v: u8) {
478 self.code.push(v);
479 }
480
481 fn emit_u16(&mut self, v: u16) {
482 self.code.extend_from_slice(&v.to_le_bytes());
483 }
484
485 fn emit_i32(&mut self, v: i32) {
486 self.code.extend_from_slice(&v.to_le_bytes());
487 }
488
489 fn emit_u32(&mut self, v: u32) {
490 self.code.extend_from_slice(&v.to_le_bytes());
491 }
492
493 fn emit_int_const(&mut self, dst: u16, val: i32) {
494 if (i8::MIN as i32..=i8::MAX as i32).contains(&val) {
495 self.emit(Opcode::LoadInt8);
496 self.emit_u16(dst);
497 self.emit_u8(val as i8 as u8);
498 } else {
499 self.emit(Opcode::LoadInt);
500 self.emit_u16(dst);
501 self.emit_i32(val);
502 }
503 }
504
505 fn emit_bool_const(&mut self, dst: u16, b: bool) {
506 if b {
507 self.emit(Opcode::LoadTrue);
508 } else {
509 self.emit(Opcode::LoadFalse);
510 }
511 self.emit_u16(dst);
512 }
513
514 fn emit_backward_jump(&mut self, target: usize) {
515 let compact_offset = (target as i64 - (self.code.len() as i64 + 2)) as i32;
516 if compact_offset >= i8::MIN as i32 && compact_offset <= i8::MAX as i32 {
517 self.emit(Opcode::Jump8);
518 self.emit_u8(compact_offset as i8 as u8);
519 } else {
520 let full_offset = (target as i64 - (self.code.len() as i64 + 5)) as i32;
521 self.emit(Opcode::Jump);
522 self.emit_i32(full_offset);
523 }
524 }
525
526 fn emit_conditional_backward_jump(&mut self, src: u16, target: usize, jump_if_true: bool) {
527 let compact_offset = (target as i64 - (self.code.len() as i64 + 4)) as i32;
528 if compact_offset >= i8::MIN as i32 && compact_offset <= i8::MAX as i32 {
529 if jump_if_true {
530 self.emit(Opcode::JumpIf8);
531 } else {
532 self.emit(Opcode::JumpIfNot8);
533 }
534 self.emit_u16(src);
535 self.emit_u8(compact_offset as i8 as u8);
536 } else {
537 let full_offset = (target as i64 - (self.code.len() as i64 + 7)) as i32;
538 if jump_if_true {
539 self.emit(Opcode::JumpIf);
540 } else {
541 self.emit(Opcode::JumpIfNot);
542 }
543 self.emit_u16(src);
544 self.emit_i32(full_offset);
545 }
546 }
547
548 fn patch_jump(&mut self, patch_pos: usize, target_pos: usize) {
549 if patch_pos >= 1 {
550 let op_byte = self.code[patch_pos - 1];
551 let is_uncond = op_byte == Opcode::Jump as u8;
552 let is_cond = op_byte == Opcode::JumpIf as u8 || op_byte == Opcode::JumpIfNot as u8;
553
554 if is_uncond || is_cond {
555 let compact_offset = (target_pos as i64 - patch_pos as i64 - 1) as i32;
556 if compact_offset >= i8::MIN as i32 && compact_offset <= i8::MAX as i32 {
557 if is_uncond {
558 self.code[patch_pos - 1] = Opcode::Jump8 as u8;
559 } else {
560 self.code[patch_pos - 3] = if op_byte == Opcode::JumpIf as u8 {
561 Opcode::JumpIf8 as u8
562 } else {
563 Opcode::JumpIfNot8 as u8
564 };
565 }
566 self.code[patch_pos] = compact_offset as i8 as u8;
567
568 let fill_end = patch_pos + 4;
569 for i in (patch_pos + 1)..fill_end {
570 self.code[i] = Opcode::Nop as u8;
571 }
572 return;
573 }
574 }
575 }
576
577 if patch_pos >= 6 {
578 let fused_op = self.code[patch_pos - 5];
579 let is_fused = fused_op == Opcode::LtJumpIfNot as u8
580 || fused_op == Opcode::LtJumpIf as u8
581 || fused_op == Opcode::LteJumpIfNot as u8
582 || fused_op == Opcode::LteJumpIf as u8
583 || fused_op == Opcode::GtJumpIfNot as u8
584 || fused_op == Opcode::GtJumpIf as u8
585 || fused_op == Opcode::GteJumpIfNot as u8
586 || fused_op == Opcode::GteJumpIf as u8
587 || fused_op == Opcode::EqJumpIfNot as u8
588 || fused_op == Opcode::EqJumpIf as u8
589 || fused_op == Opcode::NeqJumpIfNot as u8
590 || fused_op == Opcode::NeqJumpIf as u8
591 || fused_op == Opcode::StrictEqJumpIfNot as u8
592 || fused_op == Opcode::StrictEqJumpIf as u8
593 || fused_op == Opcode::StrictNeqJumpIfNot as u8
594 || fused_op == Opcode::StrictNeqJumpIf as u8;
595 if is_fused {
596 let offset = (target_pos as i64 - patch_pos as i64 - 4) as i32;
597 self.code[patch_pos..patch_pos + 4].copy_from_slice(&offset.to_le_bytes());
598 return;
599 }
600 }
601
602 let full_offset = (target_pos as i64 - patch_pos as i64 - 4) as i32;
603 self.code[patch_pos..patch_pos + 4].copy_from_slice(&full_offset.to_le_bytes());
604 }
605
606 fn emit_line(&mut self, line: u32) {
607 self.line_table.add_entry(self.code.len() as u32, line);
608 }
609
610 fn add_constant(&mut self, val: JSValue) -> u32 {
611 let idx = self.constants.len() as u32;
612 self.constants.push(val);
613 idx
614 }
615
616 fn intern_and_add_const(&mut self, ctx: &mut JSContext, name: &str) -> u32 {
617 let atom = ctx.atom_table_mut().intern(name);
618 self.add_constant(JSValue::new_string(atom))
619 }
620
621 fn emit_embedded_function(
622 &mut self,
623 bytecode: &Bytecode,
624 upvalues: &[(u32, u16)],
625 func_name_atom: u32,
626 dst: u16,
627 is_generator: bool,
628 is_async: bool,
629 ) {
630 if is_generator && is_async {
631 self.emit(Opcode::NewAsyncGeneratorFunction);
632 } else if is_generator {
633 self.emit(Opcode::NewGeneratorFunction);
634 } else if is_async {
635 self.emit(Opcode::NewAsyncFunction);
636 } else {
637 self.emit(Opcode::NewFunction);
638 }
639 self.emit_u16(bytecode.param_count);
640 self.emit_u8(if bytecode.uses_arguments { 1 } else { 0 });
641 self.emit_u8(if bytecode.is_strict { 1 } else { 0 });
642 let locals_bytes = (bytecode.locals_count as u16).to_le_bytes();
643 self.code.extend_from_slice(&locals_bytes);
644 self.emit_u32(bytecode.code.len() as u32);
645 self.code.extend_from_slice(&bytecode.code);
646 self.emit_u32(bytecode.constants.len() as u32);
647 for c in &bytecode.constants {
648 if c.is_undefined() {
649 self.emit_u8(0);
650 } else if c.is_null() {
651 self.emit_u8(1);
652 } else if c.is_bool() && c.get_bool() {
653 self.emit_u8(2);
654 } else if c.is_bool() && !c.get_bool() {
655 self.emit_u8(3);
656 } else if c.is_int() {
657 self.emit_u8(4);
658 self.code.extend_from_slice(&c.get_int().to_le_bytes());
659 } else if c.is_float() {
660 self.emit_u8(5);
661 let bytes = c.get_float().to_le_bytes();
662 self.code.extend_from_slice(&bytes);
663 } else if c.is_string() {
664 self.emit_u8(6);
665 self.emit_u32(c.get_atom().0);
666 } else {
667 self.emit_u8(0);
668 }
669 }
670 self.emit_u32(upvalues.len() as u32);
671 for (atom_id, local_idx) in upvalues {
672 self.emit_u32(*atom_id);
673 self.emit_u32(*local_idx as u32);
674 }
675
676 if let Some(ref table) = bytecode.line_number_table {
677 let entries = table.entries();
678 self.emit_u32(entries.len() as u32);
679 for (off, line) in entries {
680 self.emit_u32(*off);
681 self.emit_u32(*line);
682 }
683 } else {
684 self.emit_u32(0);
685 }
686 self.emit_u32(func_name_atom);
687 self.emit_u32(bytecode.var_name_to_slot.len() as u32);
688 for &(atom_id, slot) in bytecode.var_name_to_slot.iter() {
689 self.emit_u32(atom_id);
690 self.emit_u16(slot);
691 }
692 self.emit_u16(dst);
693 }
694
695 fn alloc_register(&mut self) -> u16 {
696 while let Some(free_r) = self.free_list.pop() {
697 if !self.reserved_slots.contains(&free_r) {
698 if free_r > self.max_register {
699 self.max_register = free_r;
700 }
701 return free_r;
702 }
703 }
704
705 let mut attempts = 0u32;
706 while self.reserved_slots.contains(&self.next_register) {
707 self.next_register = self.next_register.wrapping_add(1);
708 attempts += 1;
709 if attempts >= 65536 {
710 panic!("alloc_register: all u16 slots are reserved");
711 }
712 }
713 let r = self.next_register;
714 self.next_register = self.next_register.wrapping_add(1);
715 if r > self.max_register {
716 self.max_register = r;
717 }
718 r
719 }
720
721 fn free_register(&mut self, r: u16) {
722 if !self.reserved_slots.contains(&r) && !self.free_list.contains(&r) {
723 self.free_list.push(r);
724 }
725 }
726
727 fn push_scope(&mut self) {
728 self.scope_stack.push(FxHashMap::default());
729 self.scope_is_global.push(false);
730 self.depth += 1;
731 }
732
733 fn pop_scope(&mut self) {
734 self.scope_stack.pop();
735 self.scope_is_global.pop();
736 self.depth = self.depth.saturating_sub(1);
737 }
738
739 fn declare_var(&mut self, name: &str, kind: VariableKind) -> u16 {
740 if kind == VariableKind::Var {
741 if let Some(entry) = self.scope_stack[0].get(name) {
742 return entry.slot;
743 }
744 }
745 let slot = self.alloc_register();
746 let entry = VarEntry {
747 slot,
748 kind,
749 initialized: kind == VariableKind::Var,
750 };
751 self.reserved_slots.insert(slot);
752 self.all_var_names.push((name.to_string(), slot));
753 match kind {
754 VariableKind::Var => {
755 self.scope_stack[0].insert(name.to_string(), entry);
756 }
757 VariableKind::Let | VariableKind::Const => {
758 self.scope_stack
759 .last_mut()
760 .unwrap()
761 .insert(name.to_string(), entry);
762 }
763 }
764 slot
765 }
766
767 fn is_strict_reserved_word(name: &str) -> bool {
768 matches!(
769 name,
770 "implements"
771 | "interface"
772 | "let"
773 | "package"
774 | "private"
775 | "protected"
776 | "public"
777 | "static"
778 | "yield"
779 | "eval"
780 | "arguments"
781 )
782 }
783
784 fn has_use_strict_directive(body: &BlockStatement) -> bool {
785 for stmt in &body.body {
786 match stmt {
787 ASTNode::ExpressionStatement(es) => {
788 if let Expression::Literal(Literal::String(s, has_escape)) = &es.expression {
789 if !has_escape && s == "use strict" {
790 return true;
791 }
792 } else {
793 return false;
794 }
795 }
796 _ => return false,
797 }
798 }
799 false
800 }
801
802 fn is_global_scope(&self) -> bool {
803 self.scope_is_global.first().copied().unwrap_or(false)
804 }
805
806 fn is_global_var(&self, name: &str) -> bool {
807 if !self.is_global_scope() {
808 return false;
809 }
810 self.scope_stack
811 .first()
812 .map_or(false, |s| s.contains_key(name))
813 }
814
815 fn lookup_var(&self, name: &str) -> Option<&VarEntry> {
816 for scope in self.scope_stack.iter().rev() {
817 if let Some(entry) = scope.get(name) {
818 return Some(entry);
819 }
820 }
821 None
822 }
823
824 fn lookup_var_slot(&self, name: &str) -> Option<u16> {
825 self.lookup_var(name).map(|e| e.slot)
826 }
827
828 fn resolve_var(&mut self, name: &str, ctx: &mut JSContext) -> Option<VarLocation> {
829 if let Some(entry) = self.lookup_var(name) {
830 return Some(VarLocation::Local(entry.slot));
831 }
832 if let Some(parent_var) = self.parent_vars.get(name) {
833 if let Some(&upvalue_slot) = self.upvalue_slots.get(name) {
834 return Some(VarLocation::Upvalue(upvalue_slot));
835 }
836 let upvalue_slot = self.upvalues.len() as u16;
837 self.upvalue_slots.insert(name.to_string(), upvalue_slot);
838 self.reserved_slots.insert(upvalue_slot);
839 let atom = ctx.atom_table_mut().intern(name);
840 let local_idx = if parent_var.is_inherited {
841 u16::MAX
842 } else {
843 parent_var.local_idx
844 };
845 self.upvalues.push((atom.0, local_idx));
846 return Some(VarLocation::Upvalue(upvalue_slot));
847 }
848 None
849 }
850
851 fn collect_inherited_upvalues_stmt(
852 stmt: &ASTNode,
853 locals: &mut std::collections::HashSet<String>,
854 parent_vars: &FxHashMap<String, ParentVar>,
855 result: &mut std::collections::HashSet<String>,
856 ) {
857 match stmt {
858 ASTNode::ExpressionStatement(es) => {
859 Self::collect_inherited_upvalues_expr(&es.expression, locals, parent_vars, result);
860 }
861 ASTNode::VariableDeclaration(decl) => {
862 for d in &decl.declarations {
863 if let BindingPattern::Identifier(name) = &d.id {
864 if let Some(init) = &d.init {
865 Self::collect_inherited_upvalues_expr(
866 init,
867 locals,
868 parent_vars,
869 result,
870 );
871 }
872 locals.insert(name.clone());
873 }
874 }
875 }
876 ASTNode::BlockStatement(block) => {
877 let mut block_locals = locals.clone();
878 for s in &block.body {
879 Self::collect_inherited_upvalues_stmt(
880 s,
881 &mut block_locals,
882 parent_vars,
883 result,
884 );
885 }
886 }
887 ASTNode::IfStatement(stmt) => {
888 Self::collect_inherited_upvalues_expr(&stmt.test, locals, parent_vars, result);
889 let mut cons_locals = locals.clone();
890 Self::collect_inherited_upvalues_stmt(
891 &stmt.consequent,
892 &mut cons_locals,
893 parent_vars,
894 result,
895 );
896 if let Some(alt) = &stmt.alternate {
897 let mut alt_locals = locals.clone();
898 Self::collect_inherited_upvalues_stmt(
899 alt,
900 &mut alt_locals,
901 parent_vars,
902 result,
903 );
904 }
905 }
906 ASTNode::WhileStatement(stmt) => {
907 Self::collect_inherited_upvalues_expr(&stmt.test, locals, parent_vars, result);
908 let mut loop_locals = locals.clone();
909 Self::collect_inherited_upvalues_stmt(
910 &stmt.body,
911 &mut loop_locals,
912 parent_vars,
913 result,
914 );
915 }
916 ASTNode::DoWhileStatement(stmt) => {
917 let mut loop_locals = locals.clone();
918 Self::collect_inherited_upvalues_stmt(
919 &stmt.body,
920 &mut loop_locals,
921 parent_vars,
922 result,
923 );
924 Self::collect_inherited_upvalues_expr(&stmt.test, locals, parent_vars, result);
925 }
926 ASTNode::ForStatement(stmt) => {
927 let mut for_locals = locals.clone();
928 if let Some(init) = &stmt.init {
929 match init {
930 ForInit::VariableDeclaration(decl) => {
931 for d in &decl.declarations {
932 if let BindingPattern::Identifier(name) = &d.id {
933 if let Some(init_expr) = &d.init {
934 Self::collect_inherited_upvalues_expr(
935 init_expr,
936 &mut for_locals,
937 parent_vars,
938 result,
939 );
940 }
941 for_locals.insert(name.clone());
942 }
943 }
944 }
945 ForInit::Expression(expr) => {
946 Self::collect_inherited_upvalues_expr(
947 expr,
948 &mut for_locals,
949 parent_vars,
950 result,
951 );
952 }
953 }
954 }
955 if let Some(test) = &stmt.test {
956 Self::collect_inherited_upvalues_expr(
957 test,
958 &mut for_locals,
959 parent_vars,
960 result,
961 );
962 }
963 Self::collect_inherited_upvalues_stmt(
964 &stmt.body,
965 &mut for_locals,
966 parent_vars,
967 result,
968 );
969 if let Some(update) = &stmt.update {
970 Self::collect_inherited_upvalues_expr(
971 update,
972 &mut for_locals,
973 parent_vars,
974 result,
975 );
976 }
977 }
978 ASTNode::SwitchStatement(stmt) => {
979 Self::collect_inherited_upvalues_expr(
980 &stmt.discriminant,
981 locals,
982 parent_vars,
983 result,
984 );
985 for case in &stmt.cases {
986 let mut case_locals = locals.clone();
987 if let Some(test) = &case.test {
988 Self::collect_inherited_upvalues_expr(
989 test,
990 &mut case_locals,
991 parent_vars,
992 result,
993 );
994 }
995 for s in &case.consequent {
996 Self::collect_inherited_upvalues_stmt(
997 s,
998 &mut case_locals,
999 parent_vars,
1000 result,
1001 );
1002 }
1003 }
1004 }
1005 ASTNode::ReturnStatement(stmt) => {
1006 if let Some(expr) = &stmt.argument {
1007 Self::collect_inherited_upvalues_expr(expr, locals, parent_vars, result);
1008 }
1009 }
1010 ASTNode::BreakStatement(_) | ASTNode::ContinueStatement(_) => {}
1011 ASTNode::FunctionDeclaration(func) => {
1012 let mut func_locals = std::collections::HashSet::new();
1013 func_locals.insert(func.name.clone());
1014 for param in &func.params {
1015 Self::collect_parameter_bindings(param, &mut func_locals);
1016 }
1017 for s in &func.body.body {
1018 Self::collect_inherited_upvalues_stmt(s, &mut func_locals, parent_vars, result);
1019 }
1020 }
1021 _ => {}
1022 }
1023 }
1024
1025 fn collect_binding_pattern_bindings(
1026 pattern: &BindingPattern,
1027 out: &mut std::collections::HashSet<String>,
1028 ) {
1029 match pattern {
1030 BindingPattern::Identifier(name) => {
1031 out.insert(name.clone());
1032 }
1033 BindingPattern::ArrayPattern(arr) => {
1034 for elem in &arr.elements {
1035 if let Some(elem) = elem {
1036 match elem {
1037 PatternElement::Pattern(target) => {
1038 Self::collect_assignment_target_bindings(target, out);
1039 }
1040 PatternElement::RestElement(rest) => {
1041 Self::collect_assignment_target_bindings(&rest.argument, out);
1042 }
1043 PatternElement::AssignmentPattern(ap) => {
1044 Self::collect_assignment_target_bindings(&ap.left, out);
1045 }
1046 }
1047 }
1048 }
1049 }
1050 BindingPattern::ObjectPattern(obj) => {
1051 for prop in &obj.properties {
1052 match prop {
1053 ObjectPatternProperty::Property { value, .. } => {
1054 Self::collect_assignment_target_bindings(value, out);
1055 }
1056 ObjectPatternProperty::RestElement(rest) => {
1057 Self::collect_assignment_target_bindings(&rest.argument, out);
1058 }
1059 }
1060 }
1061 }
1062 BindingPattern::AssignmentPattern(ap) => {
1063 Self::collect_assignment_target_bindings(&ap.left, out);
1064 }
1065 }
1066 }
1067
1068 fn collect_assignment_target_bindings(
1069 target: &AssignmentTarget,
1070 out: &mut std::collections::HashSet<String>,
1071 ) {
1072 match target {
1073 AssignmentTarget::Identifier(name) => {
1074 out.insert(name.clone());
1075 }
1076 AssignmentTarget::ArrayPattern(arr) => {
1077 Self::collect_binding_pattern_bindings(
1078 &BindingPattern::ArrayPattern(arr.clone()),
1079 out,
1080 );
1081 }
1082 AssignmentTarget::ObjectPattern(obj) => {
1083 Self::collect_binding_pattern_bindings(
1084 &BindingPattern::ObjectPattern(obj.clone()),
1085 out,
1086 );
1087 }
1088 AssignmentTarget::AssignmentPattern(ap) => {
1089 Self::collect_assignment_target_bindings(&ap.left, out);
1090 }
1091 AssignmentTarget::RestElement(rest) => {
1092 Self::collect_assignment_target_bindings(&rest.argument, out);
1093 }
1094 _ => {}
1095 }
1096 }
1097
1098 fn collect_parameter_bindings(param: &Parameter, out: &mut std::collections::HashSet<String>) {
1099 match param {
1100 Parameter::Identifier(name) => {
1101 out.insert(name.clone());
1102 }
1103 Parameter::Pattern(pattern) => {
1104 Self::collect_binding_pattern_bindings(pattern, out);
1105 }
1106 Parameter::AssignmentPattern(ap) => {
1107 Self::collect_assignment_target_bindings(&ap.left, out);
1108 }
1109 Parameter::RestElement(rest) => {
1110 Self::collect_assignment_target_bindings(&rest.argument, out);
1111 }
1112 }
1113 }
1114
1115 fn collect_inherited_upvalues_expr(
1116 expr: &Expression,
1117 locals: &mut std::collections::HashSet<String>,
1118 parent_vars: &FxHashMap<String, ParentVar>,
1119 result: &mut std::collections::HashSet<String>,
1120 ) {
1121 match expr {
1122 Expression::Identifier(id) => {
1123 if !locals.contains(&id.name) && parent_vars.contains_key(&id.name) {
1124 result.insert(id.name.clone());
1125 }
1126 }
1127 Expression::BinaryExpression(bin) => {
1128 Self::collect_inherited_upvalues_expr(&bin.left, locals, parent_vars, result);
1129 Self::collect_inherited_upvalues_expr(&bin.right, locals, parent_vars, result);
1130 }
1131 Expression::UnaryExpression(unary) => {
1132 Self::collect_inherited_upvalues_expr(&unary.argument, locals, parent_vars, result);
1133 }
1134 Expression::UpdateExpression(update) => {
1135 if let Expression::Identifier(id) = &*update.argument {
1136 if !locals.contains(&id.name) && parent_vars.contains_key(&id.name) {
1137 result.insert(id.name.clone());
1138 }
1139 }
1140 }
1141 Expression::AssignmentExpression(assign) => {
1142 match &assign.left {
1143 AssignmentTarget::Identifier(name) => {
1144 if !locals.contains(name) && parent_vars.contains_key(name) {
1145 result.insert(name.clone());
1146 }
1147 }
1148 AssignmentTarget::MemberExpression(member) => {
1149 Self::collect_inherited_upvalues_expr(
1150 &member.object,
1151 locals,
1152 parent_vars,
1153 result,
1154 );
1155 if let MemberProperty::Computed(e) = &member.property {
1156 Self::collect_inherited_upvalues_expr(e, locals, parent_vars, result);
1157 }
1158 }
1159 AssignmentTarget::ComputedMember(obj, key) => {
1160 Self::collect_inherited_upvalues_expr(obj, locals, parent_vars, result);
1161 Self::collect_inherited_upvalues_expr(key, locals, parent_vars, result);
1162 }
1163 _ => {}
1164 }
1165 Self::collect_inherited_upvalues_expr(&assign.right, locals, parent_vars, result);
1166 }
1167 Expression::ConditionalExpression(cond) => {
1168 Self::collect_inherited_upvalues_expr(&cond.test, locals, parent_vars, result);
1169 Self::collect_inherited_upvalues_expr(
1170 &cond.consequent,
1171 locals,
1172 parent_vars,
1173 result,
1174 );
1175 Self::collect_inherited_upvalues_expr(&cond.alternate, locals, parent_vars, result);
1176 }
1177 Expression::LogicalExpression(logical) => {
1178 Self::collect_inherited_upvalues_expr(&logical.left, locals, parent_vars, result);
1179 Self::collect_inherited_upvalues_expr(&logical.right, locals, parent_vars, result);
1180 }
1181 Expression::MemberExpression(member) => {
1182 Self::collect_inherited_upvalues_expr(&member.object, locals, parent_vars, result);
1183 if let MemberProperty::Computed(e) = &member.property {
1184 Self::collect_inherited_upvalues_expr(e, locals, parent_vars, result);
1185 }
1186 }
1187 Expression::CallExpression(call) => {
1188 Self::collect_inherited_upvalues_expr(&call.callee, locals, parent_vars, result);
1189 for arg in &call.arguments {
1190 if let Argument::Expression(e) = arg {
1191 Self::collect_inherited_upvalues_expr(e, locals, parent_vars, result);
1192 }
1193 }
1194 }
1195 Expression::NewExpression(new) => {
1196 Self::collect_inherited_upvalues_expr(&new.callee, locals, parent_vars, result);
1197 for arg in &new.arguments {
1198 if let Argument::Expression(e) = arg {
1199 Self::collect_inherited_upvalues_expr(e, locals, parent_vars, result);
1200 }
1201 }
1202 }
1203 Expression::ArrayExpression(arr) => {
1204 for elem in &arr.elements {
1205 if let Some(ArrayElement::Expression(e)) = elem {
1206 Self::collect_inherited_upvalues_expr(e, locals, parent_vars, result);
1207 }
1208 }
1209 }
1210 Expression::ObjectExpression(obj) => {
1211 for prop in &obj.properties {
1212 if let Property::Property { value, .. } = prop {
1213 Self::collect_inherited_upvalues_expr(value, locals, parent_vars, result);
1214 }
1215 }
1216 }
1217 Expression::FunctionExpression(func) => {
1218 let mut func_locals = std::collections::HashSet::new();
1219 if let Some(ref name) = func.name {
1220 func_locals.insert(name.clone());
1221 }
1222 for param in &func.params {
1223 Self::collect_parameter_bindings(param, &mut func_locals);
1224 }
1225 for s in &func.body.body {
1226 Self::collect_inherited_upvalues_stmt(s, &mut func_locals, parent_vars, result);
1227 }
1228 }
1229 Expression::ArrowFunction(arrow) => {
1230 let mut func_locals = std::collections::HashSet::new();
1231 for param in &arrow.params {
1232 Self::collect_parameter_bindings(param, &mut func_locals);
1233 }
1234 let body = match &arrow.body {
1235 ArrowBody::Expression(expr) => BlockStatement {
1236 body: vec![ASTNode::ReturnStatement(ReturnStatement {
1237 argument: Some((**expr).clone()),
1238 })],
1239 lines: vec![],
1240 },
1241 ArrowBody::Block(block) => block.clone(),
1242 };
1243 for s in &body.body {
1244 Self::collect_inherited_upvalues_stmt(s, &mut func_locals, parent_vars, result);
1245 }
1246 }
1247 Expression::SequenceExpression(seq) => {
1248 for e in &seq.expressions {
1249 Self::collect_inherited_upvalues_expr(e, locals, parent_vars, result);
1250 }
1251 }
1252 Expression::TemplateLiteral(tpl) => {
1253 for e in &tpl.expressions {
1254 Self::collect_inherited_upvalues_expr(e, locals, parent_vars, result);
1255 }
1256 }
1257 Expression::TaggedTemplateExpression(tagged) => {
1258 Self::collect_inherited_upvalues_expr(&tagged.tag, locals, parent_vars, result);
1259 for e in &tagged.quasi.expressions {
1260 Self::collect_inherited_upvalues_expr(e, locals, parent_vars, result);
1261 }
1262 }
1263 Expression::SpreadElement(spread) => {
1264 Self::collect_inherited_upvalues_expr(
1265 &spread.argument,
1266 locals,
1267 parent_vars,
1268 result,
1269 );
1270 }
1271 _ => {}
1272 }
1273 }
1274
1275 pub fn compile_expression(
1276 &mut self,
1277 expr: &Expression,
1278 ctx: &mut JSContext,
1279 ) -> Result<u16, String> {
1280 self.gen_expression(expr, ctx)
1281 }
1282
1283 pub fn compile_function(
1284 &mut self,
1285 params: &[Parameter],
1286 body: &BlockStatement,
1287 ctx: &mut JSContext,
1288 ) -> Result<(Bytecode, Vec<(u32, u16)>), String> {
1289 self.compile_function_with_parent(params, body, ctx, FxHashMap::default(), None, false)
1290 }
1291
1292 pub fn compile_script(
1293 &mut self,
1294 body: &BlockStatement,
1295 ctx: &mut JSContext,
1296 ) -> Result<(Bytecode, Vec<(u32, u16)>), String> {
1297 let saved_scope_is_global = self.scope_is_global.clone();
1298 self.scope_is_global = vec![true];
1299 let result =
1300 self.compile_function_with_parent(&[], body, ctx, FxHashMap::default(), None, true);
1301 self.scope_is_global = saved_scope_is_global;
1302 result
1303 }
1304
1305 fn compile_function_with_parent(
1306 &mut self,
1307 params: &[Parameter],
1308 body: &BlockStatement,
1309 ctx: &mut JSContext,
1310 parent_vars: FxHashMap<String, ParentVar>,
1311 function_name: Option<&str>,
1312 return_last_expr: bool,
1313 ) -> Result<(Bytecode, Vec<(u32, u16)>), String> {
1314 self.is_strict = self.is_strict || Self::has_use_strict_directive(body);
1315 let saved_is_script_level = self.is_script_level;
1316 let saved_scope_is_global = self.scope_is_global.clone();
1317 self.next_register = 0;
1318 self.max_register = 0;
1319 self.free_list.clear();
1320 self.scope_stack = vec![FxHashMap::default()];
1321
1322 if self.scope_is_global != vec![true] {
1323 self.scope_is_global = vec![false];
1324 }
1325 self.reserved_slots.clear();
1326 self.all_var_names.clear();
1327 self.code.clear();
1328 self.constants.clear();
1329 self.parent_vars = parent_vars.clone();
1330 self.upvalues.clear();
1331 self.upvalue_slots.clear();
1332 self.line_table = LineNumberTable::new();
1333 self.depth = 0;
1334 if return_last_expr {
1335 self.is_script_level = true;
1336 } else {
1337 self.is_script_level = false;
1338 }
1339 self.function_uses_arguments = false;
1340 self.current_function_name = function_name.map(|s| s.to_string());
1341
1342 let this_slot = self.alloc_register();
1343 assert_eq!(this_slot, 0);
1344 self.reserved_slots.insert(this_slot);
1345
1346 let mut fixed_param_count = 0u16;
1347 for param in params {
1348 if self.is_strict {
1349 match param {
1350 Parameter::Identifier(name) if Self::is_strict_reserved_word(name) => {
1351 return Err(format!(
1352 "SyntaxError: '{}' cannot be declared in strict mode code",
1353 name
1354 ));
1355 }
1356 _ => {}
1357 }
1358 }
1359 match param {
1360 Parameter::Identifier(name) => {
1361 self.declare_var(name, VariableKind::Var);
1362 fixed_param_count += 1;
1363 }
1364 Parameter::Pattern(pattern) => {
1365 self.predeclare_binding_pattern(pattern, VariableKind::Var);
1366 fixed_param_count += 1;
1367 }
1368 Parameter::AssignmentPattern(ap) => {
1369 self.predeclare_assignment_target(&ap.left, VariableKind::Var);
1370 fixed_param_count += 1;
1371 }
1372 Parameter::RestElement(rest) => {
1373 self.predeclare_assignment_target(&rest.argument, VariableKind::Var);
1374 }
1375 }
1376 }
1377 self.max_register = self.max_register.max(fixed_param_count);
1378
1379 for (index, param) in params.iter().enumerate() {
1380 let value_reg = (index as u16) + 1;
1381 match param {
1382 Parameter::Identifier(name) => {
1383 if let Some(slot) = self.lookup_var_slot(name) {
1384 if slot != value_reg {
1385 self.emit(Opcode::Move);
1386 self.emit_u16(slot);
1387 self.emit_u16(value_reg);
1388 }
1389 }
1390 }
1391 Parameter::Pattern(pattern) => {
1392 self.gen_binding_pattern(pattern, value_reg, VariableKind::Var, ctx)?;
1393 }
1394 Parameter::AssignmentPattern(ap) => {
1395 let binding_name = match &*ap.left {
1396 AssignmentTarget::Identifier(name) => Some(name.as_str()),
1397 _ => None,
1398 };
1399 self.gen_default_value_binding(&ap.right, value_reg, binding_name, ctx)?;
1400 self.gen_assignment_target_binding(
1401 &ap.left,
1402 value_reg,
1403 VariableKind::Var,
1404 ctx,
1405 )?;
1406 }
1407 Parameter::RestElement(_) => break,
1408 }
1409 }
1410
1411 if params
1412 .iter()
1413 .any(|p| matches!(p, Parameter::RestElement(_)))
1414 {
1415 let rest_source_reg = fixed_param_count + 1;
1416 self.emit(Opcode::GatherRest);
1417 self.emit_u16(rest_source_reg);
1418
1419 for param in params {
1420 if let Parameter::RestElement(rest) = param {
1421 self.gen_assignment_target_binding(
1422 &rest.argument,
1423 rest_source_reg,
1424 VariableKind::Var,
1425 ctx,
1426 )?;
1427 break;
1428 }
1429 }
1430 }
1431
1432 let mut locals: std::collections::HashSet<String> =
1433 self.scope_stack[0].keys().cloned().collect();
1434 let mut inherited = std::collections::HashSet::new();
1435 for stmt in &body.body {
1436 Self::collect_inherited_upvalues_stmt(stmt, &mut locals, &parent_vars, &mut inherited);
1437 }
1438 for name in inherited {
1439 if let Some(parent_var) = parent_vars.get(&name) {
1440 if self.upvalue_slots.contains_key(&name) {
1441 continue;
1442 }
1443 let upvalue_slot = self.upvalues.len() as u16;
1444 self.upvalue_slots.insert(name.clone(), upvalue_slot);
1445 self.reserved_slots.insert(upvalue_slot);
1446 let atom = ctx.atom_table_mut().intern(name.as_str());
1447 let local_idx = if parent_var.is_inherited {
1448 u16::MAX
1449 } else {
1450 parent_var.local_idx
1451 };
1452 self.upvalues.push((atom.0, local_idx));
1453 }
1454 }
1455
1456 for stmt in &body.body {
1457 if let ASTNode::FunctionDeclaration(func) = stmt {
1458 self.declare_var(&func.name, VariableKind::Var);
1459 if Self::detect_tiny_inline_function(func) {
1460 self.tiny_inline_functions
1461 .insert(func.name.clone(), func.clone());
1462 }
1463 }
1464 }
1465
1466 for stmt in &body.body {
1467 if let ASTNode::VariableDeclaration(decl) = stmt {
1468 if decl.kind == VariableKind::Var {
1469 for d in &decl.declarations {
1470 self.predeclare_binding_pattern(&d.id, VariableKind::Var);
1471 }
1472 }
1473 }
1474 }
1475
1476 self.push_scope();
1477 self.pre_scan_let_const(&body.body)?;
1478 self.emit_tdz_for_block(&body.body)?;
1479
1480 {
1481 for stmt in &body.body {
1482 if let ASTNode::FunctionDeclaration(func) = stmt {
1483 let slot = self
1484 .lookup_var_slot(&func.name)
1485 .unwrap_or_else(|| self.declare_var(&func.name, VariableKind::Var));
1486 let reg = self.gen_function_expression(
1487 &func.params,
1488 &func.body,
1489 Some(&func.name),
1490 ctx,
1491 func.generator,
1492 func.is_async,
1493 false,
1494 )?;
1495 if slot != reg {
1496 self.emit(Opcode::Move);
1497 self.emit_u16(slot);
1498 self.emit_u16(reg);
1499 self.free_register(reg);
1500 }
1501
1502 if self.is_global_scope() && !(self.is_strict && self.is_eval) {
1503 let atom = ctx.intern(&func.name);
1504 let idx = self.add_constant(JSValue::new_string(atom));
1505 if self.is_eval {
1506 self.emit(Opcode::SetGlobalVar);
1507 } else {
1508 self.emit(Opcode::SetGlobal);
1509 }
1510 self.emit_u32(idx);
1511 self.emit_u16(slot);
1512 }
1513 }
1514 }
1515 }
1516
1517 let stmt_count = body.body.len();
1518 let mut last_expr_reg: Option<u16> = None;
1519
1520 let mut last_nonempty_stmt_idx = stmt_count.wrapping_sub(1);
1521 while last_nonempty_stmt_idx < stmt_count {
1522 if !matches!(body.body[last_nonempty_stmt_idx], ASTNode::EmptyStatement) {
1523 break;
1524 }
1525 if last_nonempty_stmt_idx == 0 {
1526 last_nonempty_stmt_idx = stmt_count.wrapping_sub(1);
1527 break;
1528 }
1529 last_nonempty_stmt_idx -= 1;
1530 }
1531
1532 for (i, stmt) in body.body.iter().enumerate() {
1533 if let Some(&line) = body.lines.get(i) {
1534 self.emit_line(line);
1535 }
1536
1537 if return_last_expr && i == last_nonempty_stmt_idx {
1538 if let ASTNode::ExpressionStatement(es) = stmt {
1539 last_expr_reg = Some(self.gen_expression(&es.expression, ctx)?);
1540 } else if matches!(stmt, ASTNode::FunctionDeclaration(_)) {
1541 } else {
1542 self.gen_statement(stmt, ctx)?;
1543 }
1544 } else if matches!(stmt, ASTNode::FunctionDeclaration(_)) {
1545 } else {
1546 self.gen_statement(stmt, ctx)?;
1547 }
1548 }
1549
1550 self.pop_scope();
1551
1552 let already_returns = body
1553 .body
1554 .last()
1555 .map_or(false, |s| matches!(s, ASTNode::ReturnStatement(_)));
1556 if !already_returns {
1557 let return_reg = if let Some(reg) = last_expr_reg {
1558 reg
1559 } else {
1560 self.emit(Opcode::LoadUndefined);
1561 self.emit_u16(0);
1562 0
1563 };
1564 self.emit(Opcode::Return);
1565 self.emit_u16(return_reg);
1566 }
1567
1568 let line_table = std::mem::take(&mut self.line_table);
1569 let code = std::mem::take(&mut self.code);
1570 let mut constants = std::mem::take(&mut self.constants);
1571 if self.function_uses_arguments {
1572 let atom = if let Some(atom) = ctx.lookup_atom("__usesArguments__") {
1573 atom
1574 } else {
1575 ctx.atom_table_mut().intern("__usesArguments__")
1576 };
1577 if !constants
1578 .iter()
1579 .any(|v| v.is_string() && v.get_atom() == atom)
1580 {
1581 constants.push(JSValue::new_string(atom));
1582 }
1583 }
1584 self.is_script_level = saved_is_script_level;
1585 self.scope_is_global = saved_scope_is_global;
1586 let var_name_to_slot: std::rc::Rc<Vec<(u32, u16)>> = std::rc::Rc::new(
1587 self.all_var_names
1588 .iter()
1589 .map(|(name, slot)| {
1590 let atom = ctx.intern(name);
1591 (atom.0, *slot)
1592 })
1593 .collect(),
1594 );
1595 let mut bc = Bytecode {
1596 code,
1597 constants,
1598 locals_count: (self.max_register as u32) + 1,
1599 param_count: fixed_param_count,
1600 line_number_table: if line_table.entries().is_empty() {
1601 None
1602 } else {
1603 Some(line_table)
1604 },
1605 ic_table: crate::compiler::InlineCacheTable::new(),
1606 shared_ic_table_ptr: std::ptr::null_mut(),
1607 shared_code_ptr: std::ptr::null(),
1608 shared_code_len: 0,
1609 shared_const_ptr: std::ptr::null(),
1610 shared_const_len: 0,
1611 uses_arguments: self.function_uses_arguments,
1612 is_strict: self.is_strict,
1613 var_name_to_slot,
1614 nested_bytecodes: std::collections::HashMap::new(),
1615 is_simple_constructor: false,
1616 simple_constructor_props: Vec::new(),
1617 cached_constructor_final_shape: None,
1618 cached_constructor_atoms: Vec::new(),
1619 };
1620 bc.is_simple_constructor = detect_simple_constructor_props(&bc);
1621 if bc.is_simple_constructor {
1622 let code = &bc.code;
1623 let mut pc = 0usize;
1624 while pc < code.len() {
1625 let op = Opcode::from_u8_unchecked(code[pc]);
1626 let size = Opcode::instruction_size(op);
1627 if size == 0 || pc + size > code.len() {
1628 break;
1629 }
1630 if op == Opcode::SetNamedProp {
1631 let obj = u16::from_le_bytes([code[pc + 1], code[pc + 2]]);
1632 let val = u16::from_le_bytes([code[pc + 3], code[pc + 4]]);
1633 let atom = u32::from_le_bytes([
1634 code[pc + 5],
1635 code[pc + 6],
1636 code[pc + 7],
1637 code[pc + 8],
1638 ]);
1639 if obj == 0 && val >= 1 && val <= bc.param_count {
1640 bc.simple_constructor_props.push((
1641 crate::runtime::atom::Atom(atom),
1642 val - 1,
1643 (pc + size) as u16,
1644 ));
1645 bc.cached_constructor_atoms
1646 .push(crate::runtime::atom::Atom(atom));
1647 }
1648 }
1649 pc += size;
1650 }
1651 }
1652 Ok((bc, std::mem::take(&mut self.upvalues)))
1653 }
1654
1655 pub fn take_bytecode(&mut self) -> (Vec<u8>, Vec<JSValue>, u32, u16) {
1656 let result = (
1657 std::mem::take(&mut self.code),
1658 std::mem::take(&mut self.constants),
1659 (self.max_register as u32) + 1,
1660 0,
1661 );
1662 self.next_register = 0;
1663 self.max_register = 0;
1664 self.free_list.clear();
1665 self.scope_stack = vec![FxHashMap::default()];
1666 self.reserved_slots.clear();
1667 self.upvalues.clear();
1668 self.upvalue_slots.clear();
1669 self.depth = 0;
1670 self.function_uses_arguments = false;
1671
1672 result
1673 }
1674
1675 fn pre_scan_let_const(&mut self, body: &[ASTNode]) -> Result<(), String> {
1676 for node in body {
1677 if let ASTNode::VariableDeclaration(decl) = node {
1678 if decl.kind != VariableKind::Var {
1679 for d in &decl.declarations {
1680 self.pre_scan_binding(&d.id, decl.kind);
1681 }
1682 }
1683 } else if let ASTNode::ClassDeclaration(class) = node {
1684 if let Some(last) = self.scope_stack.last() {
1685 if !last.contains_key(&class.name) {
1686 self.declare_var(&class.name, VariableKind::Let);
1687 }
1688 }
1689 }
1690 }
1691 Ok(())
1692 }
1693
1694 fn pre_scan_binding(&mut self, pattern: &BindingPattern, kind: VariableKind) {
1695 match pattern {
1696 BindingPattern::Identifier(name) => {
1697 if let Some(last) = self.scope_stack.last() {
1698 if !last.contains_key(name) {
1699 self.declare_var(name, kind);
1700 }
1701 }
1702 }
1703 BindingPattern::ArrayPattern(arr) => {
1704 for elem in &arr.elements {
1705 if let Some(pat) = elem {
1706 match pat {
1707 PatternElement::Pattern(p) => {
1708 self.pre_scan_assignment_target_binding(p, kind)
1709 }
1710 PatternElement::RestElement(r) => {
1711 self.pre_scan_assignment_target_binding(&r.argument, kind)
1712 }
1713 PatternElement::AssignmentPattern(ap) => {
1714 self.pre_scan_assignment_target_binding(&ap.left, kind)
1715 }
1716 }
1717 }
1718 }
1719 }
1720 BindingPattern::ObjectPattern(obj) => {
1721 for prop in &obj.properties {
1722 match prop {
1723 ObjectPatternProperty::Property { value, .. } => {
1724 self.pre_scan_assignment_target_binding(value, kind);
1725 }
1726 ObjectPatternProperty::RestElement(r) => {
1727 self.pre_scan_assignment_target_binding(&r.argument, kind);
1728 }
1729 }
1730 }
1731 }
1732 BindingPattern::AssignmentPattern(ap) => {
1733 self.pre_scan_assignment_target_binding(&ap.left, kind);
1734 }
1735 }
1736 }
1737
1738 fn pre_scan_assignment_target_binding(
1739 &mut self,
1740 target: &AssignmentTarget,
1741 kind: VariableKind,
1742 ) {
1743 match target {
1744 AssignmentTarget::Identifier(name) => {
1745 if let Some(last) = self.scope_stack.last() {
1746 if !last.contains_key(name) {
1747 self.declare_var(name, kind);
1748 }
1749 }
1750 }
1751 AssignmentTarget::ArrayPattern(arr) => {
1752 for elem in &arr.elements {
1753 if let Some(pat) = elem {
1754 match pat {
1755 PatternElement::Pattern(p) => {
1756 self.pre_scan_assignment_target_binding(p, kind)
1757 }
1758 PatternElement::RestElement(r) => {
1759 self.pre_scan_assignment_target_binding(&r.argument, kind)
1760 }
1761 PatternElement::AssignmentPattern(ap) => {
1762 self.pre_scan_assignment_target_binding(&ap.left, kind)
1763 }
1764 }
1765 }
1766 }
1767 }
1768 AssignmentTarget::ObjectPattern(obj) => {
1769 for prop in &obj.properties {
1770 match prop {
1771 ObjectPatternProperty::Property { value, .. } => {
1772 self.pre_scan_assignment_target_binding(value, kind);
1773 }
1774 ObjectPatternProperty::RestElement(r) => {
1775 self.pre_scan_assignment_target_binding(&r.argument, kind);
1776 }
1777 }
1778 }
1779 }
1780 AssignmentTarget::AssignmentPattern(ap) => {
1781 self.pre_scan_assignment_target_binding(&ap.left, kind);
1782 }
1783 _ => {}
1784 }
1785 }
1786
1787 fn emit_tdz_for_block(&mut self, body: &[ASTNode]) -> Result<(), String> {
1788 for node in body {
1789 if let ASTNode::VariableDeclaration(decl) = node {
1790 if decl.kind != VariableKind::Var {
1791 for d in &decl.declarations {
1792 self.emit_tdz_for_binding(&d.id)?;
1793 }
1794 }
1795 } else if let ASTNode::ClassDeclaration(class) = node {
1796 if let Some(slot) = self.lookup_var_slot(&class.name) {
1797 self.emit(Opcode::LoadTdz);
1798 self.emit_u16(slot);
1799 }
1800 }
1801 }
1802 Ok(())
1803 }
1804
1805 fn emit_tdz_for_binding(&mut self, pattern: &BindingPattern) -> Result<(), String> {
1806 match pattern {
1807 BindingPattern::Identifier(name) => {
1808 if let Some(slot) = self.lookup_var_slot(name) {
1809 self.emit(Opcode::LoadTdz);
1810 self.emit_u16(slot);
1811 }
1812 }
1813 BindingPattern::ArrayPattern(arr) => {
1814 for elem in &arr.elements {
1815 if let Some(pat) = elem {
1816 match pat {
1817 PatternElement::Pattern(p) => self.emit_tdz_for_assignment_target(p)?,
1818 PatternElement::RestElement(r) => {
1819 self.emit_tdz_for_assignment_target(&r.argument)?
1820 }
1821 PatternElement::AssignmentPattern(ap) => {
1822 self.emit_tdz_for_assignment_target(&ap.left)?
1823 }
1824 }
1825 }
1826 }
1827 }
1828 BindingPattern::ObjectPattern(obj) => {
1829 for prop in &obj.properties {
1830 match prop {
1831 ObjectPatternProperty::Property { value, .. } => {
1832 self.emit_tdz_for_assignment_target(value)?;
1833 }
1834 ObjectPatternProperty::RestElement(r) => {
1835 self.emit_tdz_for_assignment_target(&r.argument)?;
1836 }
1837 }
1838 }
1839 }
1840 BindingPattern::AssignmentPattern(ap) => {
1841 self.emit_tdz_for_assignment_target(&ap.left)?;
1842 }
1843 }
1844 Ok(())
1845 }
1846
1847 fn emit_tdz_for_assignment_target(&mut self, target: &AssignmentTarget) -> Result<(), String> {
1848 match target {
1849 AssignmentTarget::Identifier(name) => {
1850 if let Some(slot) = self.lookup_var_slot(name) {
1851 self.emit(Opcode::LoadTdz);
1852 self.emit_u16(slot);
1853 }
1854 }
1855 AssignmentTarget::ArrayPattern(arr) => {
1856 for elem in &arr.elements {
1857 if let Some(pat) = elem {
1858 match pat {
1859 PatternElement::Pattern(p) => self.emit_tdz_for_assignment_target(p)?,
1860 PatternElement::RestElement(r) => {
1861 self.emit_tdz_for_assignment_target(&r.argument)?
1862 }
1863 PatternElement::AssignmentPattern(ap) => {
1864 self.emit_tdz_for_assignment_target(&ap.left)?
1865 }
1866 }
1867 }
1868 }
1869 }
1870 AssignmentTarget::ObjectPattern(obj) => {
1871 for prop in &obj.properties {
1872 match prop {
1873 ObjectPatternProperty::Property { value, .. } => {
1874 self.emit_tdz_for_assignment_target(value)?;
1875 }
1876 ObjectPatternProperty::RestElement(r) => {
1877 self.emit_tdz_for_assignment_target(&r.argument)?;
1878 }
1879 }
1880 }
1881 }
1882 AssignmentTarget::AssignmentPattern(ap) => {
1883 self.emit_tdz_for_assignment_target(&ap.left)?;
1884 }
1885 _ => {}
1886 }
1887 Ok(())
1888 }
1889
1890 fn gen_statement(&mut self, stmt: &ASTNode, ctx: &mut JSContext) -> Result<(), String> {
1891 match stmt {
1892 ASTNode::ExpressionStatement(es) => {
1893 let r = self.gen_expression(&es.expression, ctx)?;
1894
1895 self.free_register(r);
1896 }
1897 ASTNode::VariableDeclaration(decl) => {
1898 self.gen_variable_declaration(decl, ctx)?;
1899 }
1900 ASTNode::FunctionDeclaration(func) => {
1901 let slot = self
1902 .lookup_var_slot(&func.name)
1903 .unwrap_or_else(|| self.declare_var(&func.name, VariableKind::Var));
1904 let reg = self.gen_function_expression(
1905 &func.params,
1906 &func.body,
1907 Some(&func.name),
1908 ctx,
1909 func.generator,
1910 func.is_async,
1911 false,
1912 )?;
1913 if slot != reg {
1914 self.emit(Opcode::Move);
1915 self.emit_u16(slot);
1916 self.emit_u16(reg);
1917 }
1918
1919 if self.is_global_scope() && !(self.is_strict && self.is_eval) {
1920 let atom = ctx.intern(&func.name);
1921 let idx = self.add_constant(JSValue::new_string(atom));
1922 if self.is_eval {
1923 self.emit(Opcode::SetGlobalVar);
1924 } else {
1925 self.emit(Opcode::SetGlobal);
1926 }
1927 self.emit_u32(idx);
1928 self.emit_u16(slot);
1929 }
1930 }
1931 ASTNode::ClassDeclaration(class) => {
1932 self.gen_class_declaration(class, ctx)?;
1933 }
1934 ASTNode::BlockStatement(block) => {
1935 self.push_scope();
1936 self.pre_scan_let_const(&block.body)?;
1937 self.emit_tdz_for_block(&block.body)?;
1938 for (i, s) in block.body.iter().enumerate() {
1939 if let Some(&line) = block.lines.get(i) {
1940 self.emit_line(line);
1941 }
1942 self.gen_statement(s, ctx)?;
1943 }
1944 self.pop_scope();
1945 }
1946 ASTNode::IfStatement(stmt) => {
1947 self.gen_if_statement(stmt, ctx)?;
1948 }
1949 ASTNode::WhileStatement(stmt) => {
1950 self.gen_while_statement(stmt, ctx)?;
1951 }
1952 ASTNode::ForStatement(stmt) => {
1953 self.gen_for_statement(stmt, ctx)?;
1954 }
1955 ASTNode::ForInStatement(stmt) => {
1956 self.gen_for_in_statement(stmt, ctx)?;
1957 }
1958 ASTNode::ForOfStatement(stmt) => {
1959 self.gen_for_of_statement(stmt, ctx)?;
1960 }
1961 ASTNode::DoWhileStatement(stmt) => {
1962 self.gen_do_while_statement(stmt, ctx)?;
1963 }
1964 ASTNode::SwitchStatement(stmt) => {
1965 self.gen_switch_statement(stmt, ctx)?;
1966 }
1967 ASTNode::BreakStatement(stmt) => {
1968 if stmt.label.is_some() {
1969 if !self
1970 .breakable_frames
1971 .iter()
1972 .any(|f| f.label.as_deref() == stmt.label.as_deref())
1973 {
1974 return Err(format!(
1975 "SyntaxError: Label '{}' not found",
1976 stmt.label.as_deref().unwrap()
1977 ));
1978 }
1979 } else if self.breakable_frames.is_empty() {
1980 return Err("SyntaxError: Illegal break statement".to_string());
1981 }
1982 self.emit(Opcode::Jump);
1983 let patch = self.code.len();
1984 self.emit_i32(0);
1985 if let Some(label) = &stmt.label {
1986 if let Some(frame) = self
1987 .breakable_frames
1988 .iter_mut()
1989 .rev()
1990 .find(|f| f.label.as_deref() == Some(label))
1991 {
1992 frame.break_patches.push(patch);
1993 }
1994 } else if let Some(frame) = self.breakable_frames.last_mut() {
1995 frame.break_patches.push(patch);
1996 }
1997 }
1998 ASTNode::ContinueStatement(stmt) => {
1999 if stmt.label.is_some() {
2000 let found = self.breakable_frames.iter().any(|f| {
2001 f.label.as_deref() == stmt.label.as_deref() && f.continue_target.is_some()
2002 });
2003 if !found {
2004 return Err(format!(
2005 "SyntaxError: Continue target '{}' not found",
2006 stmt.label.as_deref().unwrap()
2007 ));
2008 }
2009 } else if !self
2010 .breakable_frames
2011 .iter()
2012 .any(|f| f.continue_target.is_some())
2013 {
2014 return Err("SyntaxError: Illegal continue statement".to_string());
2015 }
2016 self.emit(Opcode::Jump);
2017 let patch = self.code.len();
2018 self.emit_i32(0);
2019 if let Some(label) = &stmt.label {
2020 let found =
2021 self.breakable_frames.iter_mut().rev().find(|f| {
2022 f.label.as_deref() == Some(label) && f.continue_target.is_some()
2023 });
2024 if let Some(frame) = found {
2025 frame.continue_patches.push(patch);
2026 }
2027 } else if let Some(frame) = self
2028 .breakable_frames
2029 .iter_mut()
2030 .rev()
2031 .find(|f| f.continue_target.is_some())
2032 {
2033 frame.continue_patches.push(patch);
2034 }
2035 }
2036 ASTNode::ReturnStatement(stmt) => {
2037 if self.is_script_level {
2038 return Err("SyntaxError: Illegal return statement".to_string());
2039 }
2040 let return_reg = if let Some(expr) = &stmt.argument {
2041 self.gen_expression(expr, ctx)?
2042 } else {
2043 self.emit(Opcode::LoadUndefined);
2044 self.emit_u16(0);
2045 0
2046 };
2047 self.emit(Opcode::Return);
2048 self.emit_u16(return_reg);
2049 }
2050 ASTNode::ThrowStatement(stmt) => {
2051 self.gen_throw_statement(stmt, ctx)?;
2052 }
2053 ASTNode::TryStatement(stmt) => {
2054 self.gen_try_statement(stmt, ctx)?;
2055 }
2056 ASTNode::WithStatement(_) => {
2057 if self.is_strict {
2058 return Err(
2059 "SyntaxError: Strict mode code may not include a with statement"
2060 .to_string(),
2061 );
2062 }
2063 return Err("with statement is not supported".to_string());
2064 }
2065 ASTNode::EmptyStatement => {}
2066 ASTNode::LabelledStatement(stmt) => {
2067 let saved = self.pending_label.clone();
2068 self.pending_label = None;
2069 self.breakable_frames.push(BreakableFrame {
2070 label: Some(stmt.label.clone()),
2071 break_patches: Vec::new(),
2072 continue_patches: Vec::new(),
2073 continue_target: None,
2074 });
2075 let saved_label = self.pending_label.clone();
2076 self.pending_label = Some(stmt.label.clone());
2077 self.gen_statement(&stmt.body, ctx)?;
2078 self.pending_label = saved_label;
2079 let frame = self.breakable_frames.pop().unwrap();
2080 let end_pos = self.code.len();
2081 for patch in frame.break_patches {
2082 self.patch_jump(patch, end_pos);
2083 }
2084 self.pending_label = saved;
2085 }
2086 _ => {}
2087 }
2088 Ok(())
2089 }
2090
2091 fn gen_binding_pattern(
2092 &mut self,
2093 pattern: &BindingPattern,
2094 value_reg: u16,
2095 kind: VariableKind,
2096 ctx: &mut JSContext,
2097 ) -> Result<(), String> {
2098 match pattern {
2099 BindingPattern::Identifier(name) => {
2100 if self.is_strict && Self::is_strict_reserved_word(name) {
2101 return Err(format!(
2102 "SyntaxError: '{}' cannot be declared in strict mode code",
2103 name
2104 ));
2105 }
2106 let slot = if kind == VariableKind::Var {
2107 self.declare_var(name, kind)
2108 } else {
2109 self.lookup_var_slot(name)
2110 .unwrap_or_else(|| self.declare_var(name, kind))
2111 };
2112
2113 for scope in self.scope_stack.iter_mut().rev() {
2114 if let Some(entry) = scope.get_mut(name) {
2115 entry.initialized = true;
2116 break;
2117 }
2118 }
2119 if slot != value_reg {
2120 self.emit(Opcode::Move);
2121 self.emit_u16(slot);
2122 self.emit_u16(value_reg);
2123 }
2124
2125 if kind == VariableKind::Var
2126 && self.is_global_scope()
2127 && !(self.is_strict && self.is_eval)
2128 {
2129 let atom = ctx.intern(name);
2130 let idx = self.add_constant(JSValue::new_string(atom));
2131 self.emit(Opcode::SetGlobalVar);
2132 self.emit_u32(idx);
2133 self.emit_u16(slot);
2134 }
2135 Ok(())
2136 }
2137 BindingPattern::ArrayPattern(arr) => {
2138 self.emit(Opcode::CheckObjectCoercible);
2139 self.emit_u16(value_reg);
2140 let idx_reg = self.alloc_register();
2141 self.emit_int_const(idx_reg, 0);
2142
2143 let len = arr.elements.len();
2144 for (i, elem) in arr.elements.iter().enumerate() {
2145 if i == len - 1 {
2146 if let Some(PatternElement::RestElement(rest)) = elem {
2147 let rest_reg = self.alloc_register();
2148 self.emit(Opcode::NewArray);
2149 self.emit_u16(rest_reg);
2150 self.emit_u16(0);
2151 let loop_start = self.code.len();
2152 let len_key = self.alloc_register();
2153 let len_atom = ctx.common_atoms.length;
2154 let len_const = self.add_constant(JSValue::new_string(len_atom));
2155 self.emit(Opcode::LoadConst);
2156 self.emit_u16(len_key);
2157 self.emit_u32(len_const);
2158 let arr_len = self.alloc_register();
2159 self.emit(Opcode::GetProp);
2160 self.emit_u16(arr_len);
2161 self.emit_u16(value_reg);
2162 self.emit_u16(len_key);
2163 self.free_register(len_key);
2164 let cmp = self.alloc_register();
2165 self.emit(Opcode::Lt);
2166 self.emit_u16(cmp);
2167 self.emit_u16(idx_reg);
2168 self.emit_u16(arr_len);
2169 self.free_register(arr_len);
2170 self.emit(Opcode::JumpIfNot);
2171 self.emit_u16(cmp);
2172 let exit_patch = self.code.len();
2173 self.emit_i32(0);
2174 self.free_register(cmp);
2175 let item = self.alloc_register();
2176 self.emit(Opcode::GetField);
2177 self.emit_u16(item);
2178 self.emit_u16(value_reg);
2179 self.emit_u16(idx_reg);
2180 self.emit(Opcode::ArrayPush);
2181 self.emit_u16(rest_reg);
2182 self.emit_u16(item);
2183 self.free_register(item);
2184 self.emit(Opcode::IncLocal);
2185 self.emit_u16(idx_reg);
2186 self.emit_backward_jump(loop_start);
2187 let end_pos = self.code.len();
2188 self.patch_jump(exit_patch, end_pos);
2189 self.gen_assignment_target_binding(
2190 &rest.argument,
2191 rest_reg,
2192 kind,
2193 ctx,
2194 )?;
2195 self.free_register(rest_reg);
2196 self.free_register(idx_reg);
2197 return Ok(());
2198 }
2199 }
2200
2201 if let Some(elem) = elem {
2202 match elem {
2203 PatternElement::Pattern(target) => {
2204 let elem_reg = self.alloc_register();
2205 self.emit(Opcode::GetField);
2206 self.emit_u16(elem_reg);
2207 self.emit_u16(value_reg);
2208 self.emit_u16(idx_reg);
2209 self.gen_assignment_target_binding(target, elem_reg, kind, ctx)?;
2210 self.free_register(elem_reg);
2211 }
2212 PatternElement::RestElement(_) => {
2213 return Err("rest element position not yet supported".to_string());
2214 }
2215 PatternElement::AssignmentPattern(ap) => {
2216 let elem_reg = self.alloc_register();
2217 self.emit(Opcode::GetField);
2218 self.emit_u16(elem_reg);
2219 self.emit_u16(value_reg);
2220 self.emit_u16(idx_reg);
2221 let binding_name = match &*ap.left {
2222 AssignmentTarget::Identifier(name) => Some(name.as_str()),
2223 _ => None,
2224 };
2225 self.gen_default_value_binding(
2226 &ap.right,
2227 elem_reg,
2228 binding_name,
2229 ctx,
2230 )?;
2231 self.gen_assignment_target_binding(&ap.left, elem_reg, kind, ctx)?;
2232 self.free_register(elem_reg);
2233 }
2234 }
2235 }
2236 self.emit(Opcode::IncLocal);
2237 self.emit_u16(idx_reg);
2238 }
2239 self.free_register(idx_reg);
2240 Ok(())
2241 }
2242 BindingPattern::ObjectPattern(obj) => {
2243 self.emit(Opcode::CheckObjectCoercible);
2244 self.emit_u16(value_reg);
2245 for prop in &obj.properties {
2246 match prop {
2247 ObjectPatternProperty::Property {
2248 key,
2249 value,
2250 computed,
2251 ..
2252 } => {
2253 let key_reg = if *computed {
2254 match key {
2255 PropertyKey::Computed(expr) => {
2256 self.gen_expression(expr, ctx)?
2257 }
2258 _ => return Err("Expected computed key".to_string()),
2259 }
2260 } else {
2261 match key {
2262 PropertyKey::Identifier(k)
2263 | PropertyKey::Literal(Literal::String(k, _)) => {
2264 let idx = self.intern_and_add_const(ctx, k);
2265 let kreg = self.alloc_register();
2266 self.emit(Opcode::LoadConst);
2267 self.emit_u16(kreg);
2268 self.emit_u32(idx);
2269 kreg
2270 }
2271 PropertyKey::Literal(Literal::Number(n)) => {
2272 let kreg = self.alloc_register();
2273 if *n >= i32::MIN as f64
2274 && *n <= i32::MAX as f64
2275 && n.fract() == 0.0
2276 {
2277 self.emit_int_const(kreg, *n as i32);
2278 } else {
2279 let cidx = self.add_constant(JSValue::new_float(*n));
2280 self.emit(Opcode::LoadConst);
2281 self.emit_u16(kreg);
2282 self.emit_u32(cidx);
2283 }
2284 kreg
2285 }
2286 _ => return Err("Invalid property key".to_string()),
2287 }
2288 };
2289 let elem_reg = self.alloc_register();
2290 self.emit(Opcode::GetProp);
2291 self.emit_u16(elem_reg);
2292 self.emit_u16(value_reg);
2293 self.emit_u16(key_reg);
2294 self.free_register(key_reg);
2295 self.gen_assignment_target_binding(value, elem_reg, kind, ctx)?;
2296 self.free_register(elem_reg);
2297 }
2298 ObjectPatternProperty::RestElement(rest) => {
2299 let rest_reg = self.alloc_register();
2300 self.emit(Opcode::NewObject);
2301 self.emit_u16(rest_reg);
2302 self.emit(Opcode::ObjectSpread);
2303 self.emit_u16(rest_reg);
2304 self.emit_u16(value_reg);
2305 self.gen_assignment_target_binding(
2306 &rest.argument,
2307 rest_reg,
2308 kind,
2309 ctx,
2310 )?;
2311 self.free_register(rest_reg);
2312 }
2313 }
2314 }
2315 Ok(())
2316 }
2317 BindingPattern::AssignmentPattern(ap) => {
2318 let binding_name = match &*ap.left {
2319 AssignmentTarget::Identifier(name) => Some(name.as_str()),
2320 _ => None,
2321 };
2322 self.gen_default_value_binding(&ap.right, value_reg, binding_name, ctx)?;
2323 self.gen_assignment_target_binding(&ap.left, value_reg, kind, ctx)
2324 }
2325 }
2326 }
2327
2328 fn gen_assignment_target_binding(
2329 &mut self,
2330 target: &AssignmentTarget,
2331 value_reg: u16,
2332 kind: VariableKind,
2333 ctx: &mut JSContext,
2334 ) -> Result<(), String> {
2335 match target {
2336 AssignmentTarget::Identifier(name) => {
2337 if self.is_strict && Self::is_strict_reserved_word(name) {
2338 return Err(format!(
2339 "SyntaxError: '{}' cannot be declared in strict mode code",
2340 name
2341 ));
2342 }
2343 let slot = if kind == VariableKind::Var {
2344 self.declare_var(name, kind)
2345 } else {
2346 self.lookup_var_slot(name)
2347 .unwrap_or_else(|| self.declare_var(name, kind))
2348 };
2349 if slot != value_reg {
2350 self.emit(Opcode::Move);
2351 self.emit_u16(slot);
2352 self.emit_u16(value_reg);
2353 }
2354 if kind == VariableKind::Var
2355 && self.is_global_scope()
2356 && !(self.is_strict && self.is_eval)
2357 {
2358 let atom = ctx.intern(name);
2359 let idx = self.add_constant(JSValue::new_string(atom));
2360 self.emit(Opcode::SetGlobal);
2361 self.emit_u32(idx);
2362 self.emit_u16(slot);
2363 }
2364 Ok(())
2365 }
2366 AssignmentTarget::ArrayPattern(arr) => self.gen_binding_pattern(
2367 &BindingPattern::ArrayPattern(arr.clone()),
2368 value_reg,
2369 kind,
2370 ctx,
2371 ),
2372 AssignmentTarget::ObjectPattern(obj) => self.gen_binding_pattern(
2373 &BindingPattern::ObjectPattern(obj.clone()),
2374 value_reg,
2375 kind,
2376 ctx,
2377 ),
2378 AssignmentTarget::AssignmentPattern(ap) => {
2379 let binding_name = match &*ap.left {
2380 AssignmentTarget::Identifier(name) => Some(name.as_str()),
2381 _ => None,
2382 };
2383 self.gen_default_value_binding(&ap.right, value_reg, binding_name, ctx)?;
2384 self.gen_assignment_target_binding(&ap.left, value_reg, kind, ctx)
2385 }
2386 _ => Err("Unsupported target in binding pattern".to_string()),
2387 }
2388 }
2389
2390 fn gen_default_value_binding(
2391 &mut self,
2392 default_expr: &Expression,
2393 value_reg: u16,
2394 binding_name: Option<&str>,
2395 ctx: &mut JSContext,
2396 ) -> Result<(), String> {
2397 let cmp_reg = self.alloc_register();
2398 self.emit(Opcode::LoadUndefined);
2399 self.emit_u16(cmp_reg);
2400
2401 self.emit(Opcode::StrictNeq);
2402 self.emit_u16(cmp_reg);
2403 self.emit_u16(value_reg);
2404 self.emit_u16(cmp_reg);
2405
2406 self.emit(Opcode::JumpIf);
2407 self.emit_u16(cmp_reg);
2408 let skip_patch = self.code.len();
2409 self.emit_i32(0);
2410 self.free_register(cmp_reg);
2411
2412 let default_reg = if let Some(name) = binding_name {
2413 self.gen_expression_with_assign_name(default_expr, name, ctx)?
2414 } else {
2415 self.gen_expression(default_expr, ctx)?
2416 };
2417 if default_reg != value_reg {
2418 self.emit(Opcode::Move);
2419 self.emit_u16(value_reg);
2420 self.emit_u16(default_reg);
2421 }
2422 self.free_register(default_reg);
2423
2424 let after_pos = self.code.len();
2425 self.patch_jump(skip_patch, after_pos);
2426
2427 Ok(())
2428 }
2429
2430 fn predeclare_binding_pattern(&mut self, pattern: &BindingPattern, kind: VariableKind) {
2431 match pattern {
2432 BindingPattern::Identifier(name) => {
2433 let _ = self.declare_var(name, kind);
2434 }
2435 BindingPattern::ArrayPattern(arr) => {
2436 for elem in &arr.elements {
2437 if let Some(elem) = elem {
2438 match elem {
2439 PatternElement::Pattern(AssignmentTarget::Identifier(name)) => {
2440 let _ = self.declare_var(name, kind);
2441 }
2442 PatternElement::Pattern(AssignmentTarget::ArrayPattern(arr)) => {
2443 self.predeclare_binding_pattern(
2444 &BindingPattern::ArrayPattern(arr.clone()),
2445 kind,
2446 );
2447 }
2448 PatternElement::Pattern(AssignmentTarget::ObjectPattern(obj)) => {
2449 self.predeclare_binding_pattern(
2450 &BindingPattern::ObjectPattern(obj.clone()),
2451 kind,
2452 );
2453 }
2454 PatternElement::Pattern(AssignmentTarget::AssignmentPattern(ap)) => {
2455 self.predeclare_binding_pattern(
2456 &BindingPattern::AssignmentPattern(ap.clone()),
2457 kind,
2458 );
2459 }
2460 PatternElement::RestElement(rest) => {
2461 self.predeclare_assignment_target(&rest.argument, kind);
2462 }
2463 PatternElement::AssignmentPattern(ap) => {
2464 self.predeclare_assignment_target(&ap.left, kind);
2465 }
2466 _ => {}
2467 }
2468 }
2469 }
2470 }
2471 BindingPattern::ObjectPattern(obj) => {
2472 for prop in &obj.properties {
2473 match prop {
2474 ObjectPatternProperty::Property { value, .. } => {
2475 self.predeclare_assignment_target(value, kind);
2476 }
2477 ObjectPatternProperty::RestElement(rest) => {
2478 self.predeclare_assignment_target(&rest.argument, kind);
2479 }
2480 }
2481 }
2482 }
2483 BindingPattern::AssignmentPattern(ap) => {
2484 self.predeclare_assignment_target(&ap.left, kind);
2485 }
2486 }
2487 }
2488
2489 fn predeclare_assignment_target(&mut self, target: &AssignmentTarget, kind: VariableKind) {
2490 match target {
2491 AssignmentTarget::Identifier(name) => {
2492 let _ = self.declare_var(name, kind);
2493 }
2494 AssignmentTarget::ArrayPattern(arr) => {
2495 self.predeclare_binding_pattern(&BindingPattern::ArrayPattern(arr.clone()), kind);
2496 }
2497 AssignmentTarget::ObjectPattern(obj) => {
2498 self.predeclare_binding_pattern(&BindingPattern::ObjectPattern(obj.clone()), kind);
2499 }
2500 AssignmentTarget::AssignmentPattern(ap) => {
2501 self.predeclare_assignment_target(&ap.left, kind);
2502 }
2503 AssignmentTarget::RestElement(rest) => {
2504 self.predeclare_assignment_target(&rest.argument, kind);
2505 }
2506 _ => {}
2507 }
2508 }
2509
2510 fn lookup_var_slot_for_pattern(&self, pattern: &BindingPattern) -> Option<u16> {
2511 match pattern {
2512 BindingPattern::Identifier(name) => self.lookup_var_slot(name),
2513 _ => None,
2514 }
2515 }
2516
2517 fn gen_variable_declaration(
2518 &mut self,
2519 decl: &VariableDeclaration,
2520 ctx: &mut JSContext,
2521 ) -> Result<(), String> {
2522 for d in &decl.declarations {
2523 if decl.kind == VariableKind::Const && d.init.is_none() {
2524 return Err("SyntaxError: Missing initializer in const declaration".to_string());
2525 }
2526
2527 if let Some(init) = &d.init {
2528 let reg = self.gen_expression_with_name(init, &d.id, ctx)?;
2529 self.gen_binding_pattern(&d.id, reg, decl.kind, ctx)?;
2530 self.free_register(reg);
2531 } else {
2532 if decl.kind == VariableKind::Var {
2533 self.predeclare_binding_pattern(&d.id, VariableKind::Var);
2534 if self.is_global_scope() && !(self.is_strict && self.is_eval) {
2535 if let BindingPattern::Identifier(name) = &d.id {
2536 let slot = self.lookup_var_slot(name).unwrap();
2537 let atom = ctx.intern(name);
2538 let idx = self.add_constant(JSValue::new_string(atom));
2539 if self.is_eval {
2540 self.emit(Opcode::InitGlobalVar);
2541 self.emit_u32(idx);
2542 self.emit_u16(slot);
2543 } else {
2544 self.emit(Opcode::DefineGlobal);
2545 self.emit_u32(idx);
2546 self.emit_u16(slot);
2547 }
2548 }
2549 }
2550 } else {
2551 if let Some(slot) = self.lookup_var_slot_for_pattern(&d.id) {
2552 self.emit(Opcode::LoadUndefined);
2553 self.emit_u16(slot);
2554 }
2555
2556 if let BindingPattern::Identifier(name) = &d.id {
2557 for scope in self.scope_stack.iter_mut().rev() {
2558 if let Some(entry) = scope.get_mut(name) {
2559 entry.initialized = true;
2560 break;
2561 }
2562 }
2563 }
2564 }
2565 }
2566 }
2567 Ok(())
2568 }
2569
2570 fn gen_if_statement(&mut self, stmt: &IfStatement, ctx: &mut JSContext) -> Result<(), String> {
2571 if self.p2_fold_profile.allow_if_bool() {
2572 if let Expression::Literal(Literal::Boolean(test_bool)) = &stmt.test {
2573 if *test_bool {
2574 self.gen_statement(&stmt.consequent, ctx)?;
2575 } else if let Some(alt) = &stmt.alternate {
2576 self.gen_statement(alt, ctx)?;
2577 }
2578 return Ok(());
2579 }
2580 }
2581
2582 if self.p2_fold_profile.allow_aggressive_truthy() {
2583 if let Expression::Literal(lit) = &stmt.test {
2584 if let Some(truthy) = Self::literal_truthiness(lit) {
2585 if truthy {
2586 self.gen_statement(&stmt.consequent, ctx)?;
2587 } else if let Some(alt) = &stmt.alternate {
2588 self.gen_statement(alt, ctx)?;
2589 }
2590 return Ok(());
2591 }
2592 }
2593 }
2594
2595 let test_reg = self.gen_expression(&stmt.test, ctx)?;
2596 self.emit(Opcode::JumpIfNot);
2597 self.emit_u16(test_reg);
2598 self.free_register(test_reg);
2599 let else_jump = self.code.len();
2600 self.emit_i32(0);
2601
2602 self.gen_statement(&stmt.consequent, ctx)?;
2603
2604 let has_else = stmt.alternate.is_some();
2605 let end_jump = if has_else {
2606 self.emit(Opcode::Jump);
2607 let pos = self.code.len();
2608 self.emit_i32(0);
2609 Some(pos)
2610 } else {
2611 None
2612 };
2613
2614 let else_pos = self.code.len();
2615 self.patch_jump(else_jump, else_pos);
2616
2617 if let Some(alt) = &stmt.alternate {
2618 self.gen_statement(alt, ctx)?;
2619 }
2620
2621 if let Some(end_jump_pos) = end_jump {
2622 let end_pos = self.code.len();
2623 self.patch_jump(end_jump_pos, end_pos);
2624 }
2625
2626 Ok(())
2627 }
2628
2629 fn gen_while_statement(
2630 &mut self,
2631 stmt: &WhileStatement,
2632 ctx: &mut JSContext,
2633 ) -> Result<(), String> {
2634 let loop_start = self.code.len();
2635
2636 let break_jump = if self.opt_fused_cmp_jump {
2637 if let Expression::BinaryExpression(bin) = &stmt.test {
2638 if let Some(fused) = Self::fused_op_from_binary_cmp(&bin.op, false) {
2639 let left = self.gen_expression(&bin.left, ctx)?;
2640 let right = self.gen_expression(&bin.right, ctx)?;
2641 self.emit(fused);
2642 self.emit_u16(left);
2643 self.emit_u16(right);
2644 self.free_register(left);
2645 self.free_register(right);
2646 let p = self.code.len();
2647 self.emit_i32(0);
2648 p
2649 } else {
2650 let test_reg = self.gen_expression(&stmt.test, ctx)?;
2651 self.emit(Opcode::JumpIfNot);
2652 self.emit_u16(test_reg);
2653 self.free_register(test_reg);
2654 let p = self.code.len();
2655 self.emit_i32(0);
2656 p
2657 }
2658 } else {
2659 let test_reg = self.gen_expression(&stmt.test, ctx)?;
2660 self.emit(Opcode::JumpIfNot);
2661 self.emit_u16(test_reg);
2662 self.free_register(test_reg);
2663 let p = self.code.len();
2664 self.emit_i32(0);
2665 p
2666 }
2667 } else {
2668 let test_reg = self.gen_expression(&stmt.test, ctx)?;
2669 self.emit(Opcode::JumpIfNot);
2670 self.emit_u16(test_reg);
2671 self.free_register(test_reg);
2672 let p = self.code.len();
2673 self.emit_i32(0);
2674 p
2675 };
2676
2677 self.breakable_frames.push(BreakableFrame {
2678 label: self.pending_label.take(),
2679 break_patches: vec![break_jump],
2680 continue_patches: Vec::new(),
2681 continue_target: Some(loop_start),
2682 });
2683
2684 self.gen_statement(&stmt.body, ctx)?;
2685
2686 let frame = self.breakable_frames.pop().unwrap();
2687
2688 self.emit_backward_jump(loop_start);
2689
2690 let end_pos = self.code.len();
2691 for patch in frame.break_patches {
2692 self.patch_jump(patch, end_pos);
2693 }
2694 for patch in frame.continue_patches {
2695 self.patch_jump(patch, frame.continue_target.unwrap());
2696 }
2697
2698 Ok(())
2699 }
2700
2701 fn gen_for_statement(
2702 &mut self,
2703 stmt: &ForStatement,
2704 ctx: &mut JSContext,
2705 ) -> Result<(), String> {
2706 let needs_block_scope = if let Some(ForInit::VariableDeclaration(decl)) = &stmt.init {
2707 decl.kind != VariableKind::Var
2708 } else {
2709 false
2710 };
2711
2712 if needs_block_scope {
2713 self.push_scope();
2714 if let Some(ForInit::VariableDeclaration(decl)) = &stmt.init {
2715 for d in &decl.declarations {
2716 self.pre_scan_binding(&d.id, decl.kind);
2717 }
2718 for d in &decl.declarations {
2719 self.emit_tdz_for_binding(&d.id)?;
2720 }
2721 }
2722 }
2723
2724 if let Some(init) = &stmt.init {
2725 match init {
2726 ForInit::VariableDeclaration(decl) => {
2727 self.gen_variable_declaration(decl, ctx)?;
2728 }
2729 ForInit::Expression(expr) => {
2730 let r = self.gen_expression(expr, ctx)?;
2731 self.free_register(r);
2732 }
2733 }
2734 }
2735
2736 let loop_start = self.code.len();
2737
2738 let break_jump = if let Some(test) = &stmt.test {
2739 if self.opt_fused_cmp_jump {
2740 if let Expression::BinaryExpression(bin) = test {
2741 if let Some(fused) = Self::fused_op_from_binary_cmp(&bin.op, false) {
2742 let left = self.gen_expression(&bin.left, ctx)?;
2743 let right = self.gen_expression(&bin.right, ctx)?;
2744 self.emit(fused);
2745 self.emit_u16(left);
2746 self.emit_u16(right);
2747 self.free_register(left);
2748 self.free_register(right);
2749 let bj = self.code.len();
2750 self.emit_i32(0);
2751 bj
2752 } else {
2753 let test_reg = self.gen_expression(test, ctx)?;
2754 self.emit(Opcode::JumpIfNot);
2755 self.emit_u16(test_reg);
2756 self.free_register(test_reg);
2757 let bj = self.code.len();
2758 self.emit_i32(0);
2759 bj
2760 }
2761 } else {
2762 let test_reg = self.gen_expression(test, ctx)?;
2763 self.emit(Opcode::JumpIfNot);
2764 self.emit_u16(test_reg);
2765 self.free_register(test_reg);
2766 let bj = self.code.len();
2767 self.emit_i32(0);
2768 bj
2769 }
2770 } else {
2771 let test_reg = self.gen_expression(test, ctx)?;
2772 self.emit(Opcode::JumpIfNot);
2773 self.emit_u16(test_reg);
2774 self.free_register(test_reg);
2775 let bj = self.code.len();
2776 self.emit_i32(0);
2777 bj
2778 }
2779 } else {
2780 0
2781 };
2782
2783 let loop_start_for_continue = loop_start;
2784 self.breakable_frames.push(BreakableFrame {
2785 label: self.pending_label.take(),
2786 break_patches: if stmt.test.is_some() {
2787 vec![break_jump]
2788 } else {
2789 Vec::new()
2790 },
2791 continue_patches: Vec::new(),
2792 continue_target: Some(loop_start_for_continue),
2793 });
2794
2795 self.gen_statement(&stmt.body, ctx)?;
2796
2797 let update_pos = self.code.len();
2798 if let Some(update) = &stmt.update {
2799 let r = self.gen_expression(update, ctx)?;
2800 self.free_register(r);
2801 }
2802 if let Some(frame) = self.breakable_frames.last_mut() {
2803 frame.continue_target = Some(update_pos);
2804 }
2805
2806 let frame = self.breakable_frames.pop().unwrap();
2807
2808 self.emit_backward_jump(loop_start);
2809
2810 let end_pos = self.code.len();
2811 for patch in frame.break_patches {
2812 self.patch_jump(patch, end_pos);
2813 }
2814 for patch in frame.continue_patches {
2815 self.patch_jump(patch, frame.continue_target.unwrap());
2816 }
2817
2818 if needs_block_scope {
2819 self.pop_scope();
2820 }
2821
2822 Ok(())
2823 }
2824
2825 fn gen_for_in_statement(
2826 &mut self,
2827 stmt: &ForInStatement,
2828 ctx: &mut JSContext,
2829 ) -> Result<(), String> {
2830 let needs_block_scope = match &stmt.left {
2831 ForInOfLeft::VariableDeclaration(decl) => decl.kind != VariableKind::Var,
2832 _ => false,
2833 };
2834
2835 if needs_block_scope {
2836 self.push_scope();
2837 if let ForInOfLeft::VariableDeclaration(decl) = &stmt.left {
2838 if let Some(first) = decl.declarations.first() {
2839 self.pre_scan_binding(&first.id, decl.kind);
2840 self.emit_tdz_for_binding(&first.id)?;
2841 }
2842 }
2843 }
2844
2845 let has_complex_left = match &stmt.left {
2846 ForInOfLeft::VariableDeclaration(decl) => decl
2847 .declarations
2848 .first()
2849 .map_or(false, |d| !matches!(d.id, BindingPattern::Identifier(_))),
2850 ForInOfLeft::AssignmentTarget(at) => !matches!(at, AssignmentTarget::Identifier(_)),
2851 };
2852
2853 let var_slot = if has_complex_left {
2854 u16::MAX
2855 } else {
2856 match &stmt.left {
2857 ForInOfLeft::VariableDeclaration(decl) => {
2858 if let Some(first) = decl.declarations.first() {
2859 if let BindingPattern::Identifier(name) = &first.id {
2860 if decl.kind == VariableKind::Var {
2861 self.declare_var(name, VariableKind::Var)
2862 } else {
2863 self.lookup_var_slot(name).unwrap_or(u16::MAX)
2864 }
2865 } else {
2866 255
2867 }
2868 } else {
2869 return Err("for-in without variable".to_string());
2870 }
2871 }
2872 ForInOfLeft::AssignmentTarget(AssignmentTarget::Identifier(name)) => {
2873 if let Some(slot) = self.lookup_var_slot(name) {
2874 slot
2875 } else {
2876 return Err(format!("for-in: undefined variable {}", name));
2877 }
2878 }
2879 _ => 255,
2880 }
2881 };
2882
2883 let obj_reg = self.gen_expression(&stmt.right, ctx)?;
2884
2885 let keys_reg = self.alloc_register();
2886 self.emit(Opcode::GetPropertyNames);
2887 self.emit_u16(keys_reg);
2888 self.emit_u16(obj_reg);
2889 self.free_register(obj_reg);
2890
2891 let idx_reg = self.alloc_register();
2892 self.emit_int_const(idx_reg, 0);
2893
2894 let loop_start = self.code.len();
2895
2896 let len_key_reg = self.alloc_register();
2897 let len_atom = ctx.common_atoms.length;
2898 let len_const = self.add_constant(JSValue::new_string(len_atom));
2899 self.emit(Opcode::LoadConst);
2900 self.emit_u16(len_key_reg);
2901 self.emit_u32(len_const);
2902 let len_val_reg = self.alloc_register();
2903 self.emit(Opcode::GetProp);
2904 self.emit_u16(len_val_reg);
2905 self.emit_u16(keys_reg);
2906 self.emit_u16(len_key_reg);
2907 self.free_register(len_key_reg);
2908
2909 let cmp_reg = self.alloc_register();
2910 self.emit(Opcode::Lt);
2911 self.emit_u16(cmp_reg);
2912 self.emit_u16(idx_reg);
2913 self.emit_u16(len_val_reg);
2914 self.free_register(len_val_reg);
2915
2916 self.emit(Opcode::JumpIfNot);
2917 self.emit_u16(cmp_reg);
2918 let exit_patch = self.code.len();
2919 self.emit_i32(0);
2920 self.free_register(cmp_reg);
2921
2922 self.breakable_frames.push(BreakableFrame {
2923 label: self.pending_label.take(),
2924 break_patches: Vec::new(),
2925 continue_patches: Vec::new(),
2926 continue_target: Some(0),
2927 });
2928
2929 let key_val_reg = self.alloc_register();
2930 self.emit(Opcode::GetField);
2931 self.emit_u16(key_val_reg);
2932 self.emit_u16(keys_reg);
2933 self.emit_u16(idx_reg);
2934 if has_complex_left {
2935 match &stmt.left {
2936 ForInOfLeft::VariableDeclaration(decl) => {
2937 if let Some(first) = decl.declarations.first() {
2938 self.gen_binding_pattern(&first.id, key_val_reg, decl.kind, ctx)?;
2939 } else {
2940 return Err("for-in without variable".to_string());
2941 }
2942 }
2943 ForInOfLeft::AssignmentTarget(at) => {
2944 self.gen_assignment_target_destructure(at, key_val_reg, ctx)?;
2945 }
2946 };
2947 } else {
2948 let loop_var_name = match &stmt.left {
2949 ForInOfLeft::VariableDeclaration(decl) => decl.declarations.first().and_then(|d| {
2950 if let BindingPattern::Identifier(n) = &d.id {
2951 Some(n.clone())
2952 } else {
2953 None
2954 }
2955 }),
2956 ForInOfLeft::AssignmentTarget(AssignmentTarget::Identifier(n)) => Some(n.clone()),
2957 _ => None,
2958 };
2959 if var_slot != key_val_reg {
2960 self.emit(Opcode::Move);
2961 self.emit_u16(var_slot);
2962 self.emit_u16(key_val_reg);
2963 }
2964
2965 if let Some(ref name) = loop_var_name {
2966 if self.is_global_var(name) {
2967 let atom = ctx.intern(name);
2968 let idx = self.add_constant(JSValue::new_string(atom));
2969 let src = if var_slot != key_val_reg {
2970 var_slot
2971 } else {
2972 key_val_reg
2973 };
2974 self.emit(Opcode::SetGlobal);
2975 self.emit_u32(idx);
2976 self.emit_u16(src);
2977 }
2978 }
2979 }
2980 self.free_register(key_val_reg);
2981
2982 self.gen_statement(&stmt.body, ctx)?;
2983
2984 let continue_target = self.code.len();
2985 if let Some(frame) = self.breakable_frames.last_mut() {
2986 frame.continue_target = Some(continue_target);
2987 }
2988
2989 self.emit(Opcode::IncLocal);
2990 self.emit_u16(idx_reg);
2991
2992 self.emit_backward_jump(loop_start);
2993
2994 let frame = self.breakable_frames.pop().unwrap();
2995 let end_pos = self.code.len();
2996
2997 self.patch_jump(exit_patch, end_pos);
2998
2999 for patch in frame.break_patches {
3000 self.patch_jump(patch, end_pos);
3001 }
3002
3003 for patch in frame.continue_patches {
3004 self.patch_jump(patch, continue_target);
3005 }
3006
3007 self.free_register(keys_reg);
3008 self.free_register(idx_reg);
3009
3010 if needs_block_scope {
3011 self.pop_scope();
3012 }
3013
3014 Ok(())
3015 }
3016
3017 fn gen_for_of_statement(
3018 &mut self,
3019 stmt: &ForOfStatement,
3020 ctx: &mut JSContext,
3021 ) -> Result<(), String> {
3022 let needs_block_scope = match &stmt.left {
3023 ForInOfLeft::VariableDeclaration(decl) => decl.kind != VariableKind::Var,
3024 _ => false,
3025 };
3026
3027 if needs_block_scope {
3028 self.push_scope();
3029 if let ForInOfLeft::VariableDeclaration(decl) = &stmt.left {
3030 if let Some(first) = decl.declarations.first() {
3031 self.pre_scan_binding(&first.id, decl.kind);
3032 self.emit_tdz_for_binding(&first.id)?;
3033 }
3034 }
3035 }
3036
3037 let iterable_reg = self.gen_expression(&stmt.right, ctx)?;
3038 let iter_reg = self.alloc_register();
3039 self.emit(Opcode::GetIterator);
3040 self.emit_u16(iter_reg);
3041 self.emit_u16(iterable_reg);
3042 self.free_register(iterable_reg);
3043
3044 let loop_start = self.code.len();
3045
3046 let val_reg = self.alloc_register();
3047 let done_reg = self.alloc_register();
3048 self.emit(Opcode::IteratorNext);
3049 self.emit_u16(val_reg);
3050 self.emit_u16(done_reg);
3051 self.emit_u16(iter_reg);
3052
3053 self.emit(Opcode::JumpIf);
3054 self.emit_u16(done_reg);
3055 let exit_patch = self.code.len();
3056 self.emit_i32(0);
3057 self.free_register(done_reg);
3058
3059 self.breakable_frames.push(BreakableFrame {
3060 label: self.pending_label.take(),
3061 break_patches: Vec::new(),
3062 continue_patches: Vec::new(),
3063 continue_target: Some(0),
3064 });
3065
3066 match &stmt.left {
3067 ForInOfLeft::VariableDeclaration(decl) => {
3068 if let Some(first) = decl.declarations.first() {
3069 self.gen_binding_pattern(&first.id, val_reg, decl.kind, ctx)?;
3070 } else {
3071 return Err("for-of without variable".to_string());
3072 }
3073 }
3074 ForInOfLeft::AssignmentTarget(target) => {
3075 self.gen_assignment_target_destructure(target, val_reg, ctx)?;
3076 }
3077 }
3078 self.free_register(val_reg);
3079
3080 self.gen_statement(&stmt.body, ctx)?;
3081
3082 let continue_target = self.code.len();
3083 if let Some(frame) = self.breakable_frames.last_mut() {
3084 frame.continue_target = Some(continue_target);
3085 }
3086
3087 self.emit_backward_jump(loop_start);
3088
3089 let frame = self.breakable_frames.pop().unwrap();
3090 let end_pos = self.code.len();
3091
3092 self.patch_jump(exit_patch, end_pos);
3093
3094 for patch in frame.break_patches {
3095 self.patch_jump(patch, end_pos);
3096 }
3097 for patch in frame.continue_patches {
3098 self.patch_jump(patch, continue_target);
3099 }
3100
3101 self.free_register(iter_reg);
3102
3103 if needs_block_scope {
3104 self.pop_scope();
3105 }
3106
3107 Ok(())
3108 }
3109
3110 fn gen_do_while_statement(
3111 &mut self,
3112 stmt: &DoWhileStatement,
3113 ctx: &mut JSContext,
3114 ) -> Result<(), String> {
3115 let loop_start = self.code.len();
3116
3117 self.breakable_frames.push(BreakableFrame {
3118 label: self.pending_label.take(),
3119 break_patches: Vec::new(),
3120 continue_patches: Vec::new(),
3121 continue_target: Some(0),
3122 });
3123
3124 self.gen_statement(&stmt.body, ctx)?;
3125
3126 let continue_target = self.code.len();
3127 if let Some(frame) = self.breakable_frames.last_mut() {
3128 frame.continue_target = Some(continue_target);
3129 }
3130
3131 let test_reg = self.gen_expression(&stmt.test, ctx)?;
3132 self.emit_conditional_backward_jump(test_reg, loop_start, true);
3133 self.free_register(test_reg);
3134
3135 let frame = self.breakable_frames.pop().unwrap();
3136
3137 let end_pos = self.code.len();
3138 for patch in frame.break_patches {
3139 self.patch_jump(patch, end_pos);
3140 }
3141 for patch in frame.continue_patches {
3142 self.patch_jump(patch, frame.continue_target.unwrap());
3143 }
3144
3145 Ok(())
3146 }
3147
3148 fn gen_switch_statement(
3149 &mut self,
3150 stmt: &SwitchStatement,
3151 ctx: &mut JSContext,
3152 ) -> Result<(), String> {
3153 let disc = self.gen_expression(&stmt.discriminant, ctx)?;
3154
3155 let mut test_jumps: Vec<(usize, usize)> = Vec::new();
3156 for (case_idx, case) in stmt.cases.iter().enumerate() {
3157 if let Some(test_expr) = &case.test {
3158 let test_reg = self.gen_expression(test_expr, ctx)?;
3159 let result_reg = self.alloc_register();
3160 self.emit(Opcode::StrictEq);
3161 self.emit_u16(result_reg);
3162 self.emit_u16(disc);
3163 self.emit_u16(test_reg);
3164 self.free_register(test_reg);
3165 self.emit(Opcode::JumpIf);
3166 self.emit_u16(result_reg);
3167 test_jumps.push((self.code.len(), case_idx));
3168 self.emit_i32(0);
3169 self.free_register(result_reg);
3170 }
3171 }
3172
3173 self.emit(Opcode::Jump);
3174 let default_jump_placeholder = self.code.len();
3175 self.emit_i32(0);
3176
3177 self.breakable_frames.push(BreakableFrame {
3178 label: self.pending_label.take(),
3179 break_patches: Vec::new(),
3180 continue_patches: Vec::new(),
3181 continue_target: None,
3182 });
3183
3184 let mut body_starts = vec![0usize; stmt.cases.len()];
3185 for (case_idx, case) in stmt.cases.iter().enumerate() {
3186 body_starts[case_idx] = self.code.len();
3187 for s in &case.consequent {
3188 self.gen_statement(s, ctx)?;
3189 }
3190 }
3191
3192 let frame = self.breakable_frames.pop().unwrap();
3193 let end_pos = self.code.len();
3194
3195 for patch in frame.break_patches {
3196 self.patch_jump(patch, end_pos);
3197 }
3198
3199 let default_case_idx = stmt.cases.iter().position(|c| c.test.is_none());
3200 let default_target = default_case_idx
3201 .map(|idx| body_starts[idx])
3202 .unwrap_or(end_pos);
3203 self.patch_jump(default_jump_placeholder, default_target);
3204
3205 for (placeholder, case_idx) in test_jumps {
3206 self.patch_jump(placeholder, body_starts[case_idx]);
3207 }
3208
3209 self.free_register(disc);
3210 Ok(())
3211 }
3212
3213 fn gen_throw_statement(
3214 &mut self,
3215 stmt: &ThrowStatement,
3216 ctx: &mut JSContext,
3217 ) -> Result<(), String> {
3218 let reg = self.gen_expression(&stmt.argument, ctx)?;
3219 self.emit(Opcode::Throw);
3220 self.emit_u16(reg);
3221 self.free_register(reg);
3222 Ok(())
3223 }
3224
3225 fn gen_try_statement(
3226 &mut self,
3227 stmt: &TryStatement,
3228 ctx: &mut JSContext,
3229 ) -> Result<(), String> {
3230 self.emit(Opcode::Try);
3231 let catch_pc_pos = self.code.len();
3232 self.emit_i32(0);
3233 let finally_pc_pos = self.code.len();
3234 self.emit_i32(0);
3235
3236 self.push_scope();
3237 self.pre_scan_let_const(&stmt.block.body)?;
3238 self.emit_tdz_for_block(&stmt.block.body)?;
3239 for s in &stmt.block.body {
3240 self.gen_statement(s, ctx)?;
3241 }
3242 self.pop_scope();
3243
3244 self.emit(Opcode::Catch);
3245
3246 self.emit(Opcode::Jump);
3247 let end_jump_pos = self.code.len();
3248 self.emit_i32(0);
3249
3250 let has_catch = stmt.handler.is_some();
3251 let mut after_catch_jump_pos: Option<usize> = None;
3252
3253 if let Some(handler) = &stmt.handler {
3254 let catch_start = self.code.len();
3255
3256 let catch_bytes = (catch_start as i32).to_le_bytes();
3257 self.code[catch_pc_pos..catch_pc_pos + 4].copy_from_slice(&catch_bytes);
3258
3259 self.push_scope();
3260 if let Some(param) = &handler.param {
3261 match param {
3262 BindingPattern::Identifier(name) => {
3263 self.pre_scan_binding(param, VariableKind::Let);
3264 self.emit_tdz_for_binding(param)?;
3265 let slot = self.declare_var(name, VariableKind::Let);
3266 if slot != 0 {
3267 self.emit(Opcode::Move);
3268 self.emit_u16(slot);
3269 self.emit_u16(0);
3270 }
3271
3272 if self.is_global_var(name) {
3273 let atom = ctx.intern(name);
3274 let idx = self.add_constant(JSValue::new_string(atom));
3275 self.emit(Opcode::SetGlobal);
3276 self.emit_u32(idx);
3277 self.emit_u16(0);
3278 }
3279 }
3280 _ => self.gen_binding_pattern(param, 0, VariableKind::Let, ctx)?,
3281 }
3282 }
3283 self.pre_scan_let_const(&handler.body.body)?;
3284 self.emit_tdz_for_block(&handler.body.body)?;
3285 for s in &handler.body.body {
3286 self.gen_statement(s, ctx)?;
3287 }
3288 self.pop_scope();
3289
3290 self.emit(Opcode::Jump);
3291 after_catch_jump_pos = Some(self.code.len());
3292 self.emit_i32(0);
3293 }
3294
3295 let has_finally = stmt.finalizer.is_some();
3296 let mut finally_start = 0usize;
3297
3298 if let Some(finalizer) = &stmt.finalizer {
3299 finally_start = self.code.len();
3300
3301 let finally_bytes = (finally_start as i32).to_le_bytes();
3302 self.code[finally_pc_pos..finally_pc_pos + 4].copy_from_slice(&finally_bytes);
3303
3304 if !has_catch {
3305 self.code[catch_pc_pos..catch_pc_pos + 4].copy_from_slice(&finally_bytes);
3306 }
3307
3308 self.patch_jump(end_jump_pos, finally_start);
3309
3310 self.emit(Opcode::Finally);
3311
3312 self.push_scope();
3313 self.pre_scan_let_const(&finalizer.body)?;
3314 self.emit_tdz_for_block(&finalizer.body)?;
3315 for s in &finalizer.body {
3316 self.gen_statement(s, ctx)?;
3317 }
3318 self.pop_scope();
3319 }
3320
3321 let end_pos = self.code.len();
3322
3323 if !has_finally {
3324 self.patch_jump(end_jump_pos, end_pos);
3325 }
3326
3327 if let Some(pos) = after_catch_jump_pos {
3328 let catch_jump_target = if has_finally { finally_start } else { end_pos };
3329 self.patch_jump(pos, catch_jump_target);
3330 }
3331
3332 Ok(())
3333 }
3334
3335 fn gen_function_like_with_name(
3336 &mut self,
3337 expr: &Expression,
3338 name: &str,
3339 ctx: &mut JSContext,
3340 ) -> Result<u16, String> {
3341 match expr {
3342 Expression::FunctionExpression(func) => self.gen_function_expression(
3343 &func.params,
3344 &func.body,
3345 Some(name),
3346 ctx,
3347 func.generator,
3348 func.is_async,
3349 false,
3350 ),
3351 Expression::ArrowFunction(arrow) => {
3352 let body = match &arrow.body {
3353 ArrowBody::Expression(expr) => BlockStatement {
3354 body: vec![ASTNode::ReturnStatement(ReturnStatement {
3355 argument: Some((**expr).clone()),
3356 })],
3357 lines: vec![],
3358 },
3359 ArrowBody::Block(block) => block.clone(),
3360 };
3361 self.gen_function_expression(
3362 &arrow.params,
3363 &body,
3364 Some(name),
3365 ctx,
3366 false,
3367 arrow.is_async,
3368 true,
3369 )
3370 }
3371 Expression::ClassExpression(ce) => {
3372 let class_slot = self.alloc_register();
3373 self.gen_class_body(class_slot, name, &ce.super_class, &ce.body, ctx)?;
3374 Ok(class_slot)
3375 }
3376 _ => unreachable!(),
3377 }
3378 }
3379
3380 fn gen_expression_with_name(
3381 &mut self,
3382 expr: &Expression,
3383 binding: &BindingPattern,
3384 ctx: &mut JSContext,
3385 ) -> Result<u16, String> {
3386 let needs_name = match binding {
3387 BindingPattern::Identifier(binding_name) => match expr {
3388 Expression::FunctionExpression(func) if func.name.is_none() => {
3389 Some(binding_name.as_str())
3390 }
3391 Expression::ArrowFunction(_) => Some(binding_name.as_str()),
3392 Expression::ClassExpression(ce) if ce.name.is_none() => Some(binding_name.as_str()),
3393 _ => None,
3394 },
3395 _ => None,
3396 };
3397 if let Some(name) = needs_name {
3398 self.gen_function_like_with_name(expr, name, ctx)
3399 } else {
3400 self.gen_expression(expr, ctx)
3401 }
3402 }
3403
3404 fn gen_expression_with_assign_name(
3405 &mut self,
3406 expr: &Expression,
3407 target_name: &str,
3408 ctx: &mut JSContext,
3409 ) -> Result<u16, String> {
3410 let needs_name = match expr {
3411 Expression::FunctionExpression(func) if func.name.is_none() => Some(target_name),
3412 Expression::ArrowFunction(_) => Some(target_name),
3413 Expression::ClassExpression(ce) if ce.name.is_none() => Some(target_name),
3414 _ => None,
3415 };
3416 if let Some(name) = needs_name {
3417 self.gen_function_like_with_name(expr, name, ctx)
3418 } else {
3419 self.gen_expression(expr, ctx)
3420 }
3421 }
3422
3423 fn gen_expression(&mut self, expr: &Expression, ctx: &mut JSContext) -> Result<u16, String> {
3424 match expr {
3425 Expression::Identifier(id) => {
3426 if id.name == "arguments"
3427 && !self.is_script_level
3428 && !self.is_arrow
3429 && self.lookup_var("arguments").is_none()
3430 {
3431 self.function_uses_arguments = true;
3432 let r = self.alloc_register();
3433 self.emit(Opcode::GetArguments);
3434 self.emit_u16(r);
3435 return Ok(r);
3436 }
3437 match self.resolve_var(&id.name, ctx) {
3438 Some(VarLocation::Local(slot)) => {
3439 if self.is_global_var(&id.name) {
3440 let atom = ctx.intern(&id.name);
3441 let idx = self.add_constant(JSValue::new_string(atom));
3442 let r = self.alloc_register();
3443 self.emit(Opcode::GetGlobal);
3444 self.emit_u16(r);
3445 self.emit_u32(idx);
3446 return Ok(r);
3447 }
3448
3449 if let Some(entry) = self.lookup_var(&id.name) {
3450 if entry.kind != VariableKind::Var && !entry.initialized {
3451 self.emit(Opcode::CheckTdz);
3452 self.emit_u16(slot);
3453 }
3454 }
3455 Ok(slot)
3456 }
3457 Some(VarLocation::Upvalue(slot)) => {
3458 let r = self.alloc_register();
3459 self.emit(Opcode::GetUpvalue);
3460 self.emit_u16(r);
3461 self.emit_u16(slot);
3462
3463 if let Some(entry) = self.lookup_var(&id.name) {
3464 if entry.kind != VariableKind::Var && !entry.initialized {
3465 self.emit(Opcode::CheckTdz);
3466 self.emit_u16(r);
3467 }
3468 }
3469 Ok(r)
3470 }
3471 None => {
3472 let atom = ctx.intern(&id.name);
3473 let idx = self.add_constant(JSValue::new_string(atom));
3474 let r = self.alloc_register();
3475 self.emit(Opcode::GetGlobal);
3476 self.emit_u16(r);
3477 self.emit_u32(idx);
3478 if !self.suppress_ref_error {
3479 self.emit(Opcode::CheckRef);
3480 self.emit_u16(r);
3481 self.emit_u32(idx);
3482 }
3483 Ok(r)
3484 }
3485 }
3486 }
3487 Expression::Literal(lit) => {
3488 let r = self.alloc_register();
3489 match lit {
3490 Literal::Number(n) => {
3491 if *n >= i32::MIN as f64 && *n <= i32::MAX as f64 && n.fract() == 0.0 {
3492 let val = *n as i32;
3493 if val >= i8::MIN as i32 && val <= i8::MAX as i32 {
3494 self.emit(Opcode::LoadInt8);
3495 self.emit_u16(r);
3496 self.emit_u8(val as u8);
3497 } else {
3498 self.emit_int_const(r, val);
3499 }
3500 } else {
3501 let idx = self.add_constant(JSValue::new_float(*n));
3502 self.emit(Opcode::LoadConst);
3503 self.emit_u16(r);
3504 self.emit_u32(idx);
3505 }
3506 }
3507 Literal::LegacyOctal(n) => {
3508 if self.is_strict {
3509 return Err(
3510 "SyntaxError: Octal literals are not allowed in strict mode"
3511 .to_string(),
3512 );
3513 }
3514 if *n >= i32::MIN as i64 && *n <= i32::MAX as i64 {
3515 let val = *n as i32;
3516 if val >= i8::MIN as i32 && val <= i8::MAX as i32 {
3517 self.emit(Opcode::LoadInt8);
3518 self.emit_u16(r);
3519 self.emit_u8(val as u8);
3520 } else {
3521 self.emit_int_const(r, val);
3522 }
3523 } else {
3524 let idx = self.add_constant(JSValue::new_float(*n as f64));
3525 self.emit(Opcode::LoadConst);
3526 self.emit_u16(r);
3527 self.emit_u32(idx);
3528 }
3529 }
3530 Literal::String(s, _) => {
3531 let idx = self.intern_and_add_const(ctx, s);
3532 self.emit(Opcode::LoadConst);
3533 self.emit_u16(r);
3534 self.emit_u32(idx);
3535 }
3536 Literal::Boolean(b) => {
3537 if *b {
3538 self.emit(Opcode::LoadTrue);
3539 } else {
3540 self.emit(Opcode::LoadFalse);
3541 }
3542 self.emit_u16(r);
3543 }
3544 Literal::Null => {
3545 self.emit(Opcode::LoadNull);
3546 self.emit_u16(r);
3547 }
3548 Literal::Undefined => {
3549 self.emit(Opcode::LoadUndefined);
3550 self.emit_u16(r);
3551 }
3552 Literal::BigInt(s) => {
3553 let atom = ctx.atom_table_mut().intern(s);
3554 let idx = self.add_constant(JSValue::new_string(atom));
3555 self.emit(Opcode::LoadConst);
3556 self.emit_u16(r);
3557 self.emit_u32(idx);
3558
3559 let bigint_atom = ctx.common_atoms.bigint;
3560 let bigint_idx = self.add_constant(JSValue::new_string(bigint_atom));
3561 let func_reg = self.alloc_register();
3562 self.emit(Opcode::GetGlobal);
3563 self.emit_u16(func_reg);
3564 self.emit_u32(bigint_idx);
3565 self.emit(Opcode::Call);
3566 self.emit_u16(r);
3567 self.emit_u16(func_reg);
3568 self.emit_u16(1);
3569 self.emit_u16(r);
3570 self.free_register(func_reg);
3571 }
3572 }
3573 Ok(r)
3574 }
3575 Expression::BinaryExpression(bin) => self.gen_binary_expression(bin, ctx),
3576 Expression::LogicalExpression(logical) => self.gen_logical_expression(logical, ctx),
3577 Expression::UnaryExpression(unary) => self.gen_unary_expression(unary, ctx),
3578 Expression::UpdateExpression(update) => self.gen_update_expression(update, ctx),
3579 Expression::AssignmentExpression(assign) => self.gen_assignment_expression(assign, ctx),
3580 Expression::ConditionalExpression(cond) => self.gen_conditional_expression(cond, ctx),
3581 Expression::MemberExpression(member) => self.gen_member_expression(member, ctx),
3582 Expression::OptionalMemberExpression(opt) => {
3583 let obj_reg = self.gen_expression(&opt.object, ctx)?;
3584 let result = self.alloc_register();
3585
3586 let undef_reg = self.alloc_register();
3587 self.emit(Opcode::LoadUndefined);
3588 self.emit_u16(undef_reg);
3589 let is_undef = self.alloc_register();
3590 self.emit(Opcode::StrictEq);
3591 self.emit_u16(is_undef);
3592 self.emit_u16(obj_reg);
3593 self.emit_u16(undef_reg);
3594 self.free_register(undef_reg);
3595 self.emit(Opcode::JumpIf);
3596 self.emit_u16(is_undef);
3597 let short_jump = self.code.len();
3598 self.emit_i32(0);
3599 self.free_register(is_undef);
3600
3601 let null_reg = self.alloc_register();
3602 self.emit(Opcode::LoadNull);
3603 self.emit_u16(null_reg);
3604 let is_null = self.alloc_register();
3605 self.emit(Opcode::StrictEq);
3606 self.emit_u16(is_null);
3607 self.emit_u16(obj_reg);
3608 self.emit_u16(null_reg);
3609 self.free_register(null_reg);
3610 self.emit(Opcode::JumpIf);
3611 self.emit_u16(is_null);
3612 let short_jump_2 = self.code.len();
3613 self.emit_i32(0);
3614 self.free_register(is_null);
3615
3616 match &opt.property {
3617 MemberProperty::Identifier(name) => {
3618 let atom = ctx.intern(name);
3619 self.emit(Opcode::GetNamedProp);
3620 self.emit_u16(result);
3621 self.emit_u16(obj_reg);
3622 self.emit_u32(atom.0);
3623 }
3624 MemberProperty::Computed(expr) => {
3625 let key_reg = self.gen_expression(expr, ctx)?;
3626 self.emit(Opcode::GetField);
3627 self.emit_u16(result);
3628 self.emit_u16(obj_reg);
3629 self.emit_u16(key_reg);
3630 self.free_register(key_reg);
3631 }
3632 MemberProperty::PrivateIdentifier(name) => {
3633 let key_idx = self.intern_and_add_const(ctx, name);
3634 let key_reg = self.alloc_register();
3635 self.emit(Opcode::LoadConst);
3636 self.emit_u16(key_reg);
3637 self.emit_u32(key_idx);
3638 self.emit(Opcode::GetPrivate);
3639 self.emit_u16(result);
3640 self.emit_u16(obj_reg);
3641 self.emit_u16(key_reg);
3642 self.free_register(key_reg);
3643 }
3644 }
3645 self.emit(Opcode::Jump);
3646 let end_jump = self.code.len();
3647 self.emit_i32(0);
3648
3649 let short_pos = self.code.len();
3650 self.patch_jump(short_jump, short_pos);
3651 self.patch_jump(short_jump_2, short_pos);
3652 self.emit(Opcode::LoadUndefined);
3653 self.emit_u16(result);
3654
3655 let end_pos = self.code.len();
3656 self.patch_jump(end_jump, end_pos);
3657 self.free_register(obj_reg);
3658 Ok(result)
3659 }
3660 Expression::CallExpression(call) => self.gen_call_expression(call, ctx),
3661 Expression::NewExpression(new) => self.gen_new_expression(new, ctx),
3662 Expression::ArrayExpression(arr) => self.gen_array_expression(arr, ctx),
3663 Expression::ObjectExpression(obj) => self.gen_object_expression(obj, ctx),
3664 Expression::TemplateLiteral(tpl) => {
3665 let first_quasi = tpl
3666 .quasis
3667 .first()
3668 .map(|quasi| quasi.value.as_str())
3669 .unwrap_or("");
3670 let result = self.alloc_register();
3671 let first_idx = self.intern_and_add_const(ctx, first_quasi);
3672 self.emit(Opcode::LoadConst);
3673 self.emit_u16(result);
3674 self.emit_u32(first_idx);
3675
3676 for (i, expr) in tpl.expressions.iter().enumerate() {
3677 let expr_reg = self.gen_expression(expr, ctx)?;
3678 self.emit(Opcode::Add);
3679 self.emit_u16(result);
3680 self.emit_u16(result);
3681 self.emit_u16(expr_reg);
3682 self.free_register(expr_reg);
3683
3684 if let Some(quasi) = tpl.quasis.get(i + 1) {
3685 let quasi_idx = self.intern_and_add_const(ctx, &quasi.value);
3686 let quasi_reg = self.alloc_register();
3687 self.emit(Opcode::LoadConst);
3688 self.emit_u16(quasi_reg);
3689 self.emit_u32(quasi_idx);
3690 self.emit(Opcode::Add);
3691 self.emit_u16(result);
3692 self.emit_u16(result);
3693 self.emit_u16(quasi_reg);
3694 self.free_register(quasi_reg);
3695 }
3696 }
3697
3698 Ok(result)
3699 }
3700 Expression::FunctionExpression(func) => self.gen_function_expression(
3701 &func.params,
3702 &func.body,
3703 func.name.as_deref(),
3704 ctx,
3705 func.generator,
3706 func.is_async,
3707 false,
3708 ),
3709 Expression::ArrowFunction(arrow) => {
3710 let body = match &arrow.body {
3711 ArrowBody::Expression(expr) => BlockStatement {
3712 body: vec![ASTNode::ReturnStatement(ReturnStatement {
3713 argument: Some((**expr).clone()),
3714 })],
3715 lines: vec![],
3716 },
3717 ArrowBody::Block(block) => block.clone(),
3718 };
3719 self.gen_function_expression(
3720 &arrow.params,
3721 &body,
3722 None,
3723 ctx,
3724 false,
3725 arrow.is_async,
3726 true,
3727 )
3728 }
3729 Expression::This => Ok(0),
3730 Expression::Super => {
3731 let r = self.alloc_register();
3732 self.emit(Opcode::GetSuper);
3733 self.emit_u16(r);
3734 Ok(r)
3735 }
3736 Expression::PrivateIdentifier(name) => {
3737 let idx = self.intern_and_add_const(ctx, name);
3738 let r = self.alloc_register();
3739 self.emit(Opcode::LoadConst);
3740 self.emit_u16(r);
3741 self.emit_u32(idx);
3742 Ok(r)
3743 }
3744 Expression::YieldExpression(yield_expr) => {
3745 if yield_expr.delegate {
3746 return Err("yield* not yet supported in register VM".to_string());
3747 }
3748 let val_reg = if let Some(arg) = &yield_expr.argument {
3749 self.gen_expression(arg, ctx)?
3750 } else {
3751 let r = self.alloc_register();
3752 self.emit(Opcode::LoadUndefined);
3753 self.emit_u16(r);
3754 r
3755 };
3756 self.emit(Opcode::Yield);
3757 self.emit_u16(val_reg);
3758 Ok(val_reg)
3759 }
3760 Expression::AwaitExpression(await_expr) => {
3761 let val_reg = self.gen_expression(&await_expr.argument, ctx)?;
3762 self.emit(Opcode::Await);
3763 self.emit_u16(val_reg);
3764 Ok(val_reg)
3765 }
3766 Expression::RegExpLiteral(re) => {
3767 let pattern_idx = self.intern_and_add_const(ctx, &re.pattern);
3768 let flags_idx = self.intern_and_add_const(ctx, &re.flags);
3769 let dst = self.alloc_register();
3770 self.emit(Opcode::NewRegExp);
3771 self.emit_u16(dst);
3772 self.emit_u32(pattern_idx);
3773 self.emit_u32(flags_idx);
3774 Ok(dst)
3775 }
3776 Expression::SequenceExpression(seq) => {
3777 let mut last_r = None;
3778 for (i, e) in seq.expressions.iter().enumerate() {
3779 let r = self.gen_expression(e, ctx)?;
3780 if i == seq.expressions.len() - 1 {
3781 last_r = Some(r);
3782 } else {
3783 self.free_register(r);
3784 }
3785 }
3786
3787 Ok(last_r.unwrap_or_else(|| {
3788 let r = self.alloc_register();
3789 self.emit(Opcode::LoadUndefined);
3790 self.emit_u16(r);
3791 r
3792 }))
3793 }
3794 Expression::ClassExpression(ce) => {
3795 let class_name = ce.name.as_deref().unwrap_or("");
3796 let class_slot = self.alloc_register();
3797 self.gen_class_body(class_slot, class_name, &ce.super_class, &ce.body, ctx)?;
3798 Ok(class_slot)
3799 }
3800 _ => {
3801 let r = self.alloc_register();
3802 self.emit(Opcode::LoadUndefined);
3803 self.emit_u16(r);
3804 Ok(r)
3805 }
3806 }
3807 }
3808
3809 fn gen_binary_expression(
3810 &mut self,
3811 bin: &BinaryExpression,
3812 ctx: &mut JSContext,
3813 ) -> Result<u16, String> {
3814 let left = self.gen_expression(&bin.left, ctx)?;
3815
3816 let right_imm8 = if let Expression::Literal(Literal::Number(n)) = &*bin.right {
3817 if n.fract() == 0.0 {
3818 let v = *n as i64;
3819 if v >= i8::MIN as i64 && v <= i8::MAX as i64 {
3820 Some(v as i8)
3821 } else {
3822 None
3823 }
3824 } else {
3825 None
3826 }
3827 } else {
3828 None
3829 };
3830
3831 if let Some(imm) = right_imm8 {
3832 match bin.op {
3833 BinaryOp::Sub => {
3834 let dst = self.alloc_register();
3835 self.emit(Opcode::SubImm8);
3836 self.emit_u16(dst);
3837 self.emit_u16(left);
3838 self.emit_u8(imm as u8);
3839 self.free_register(left);
3840 return Ok(dst);
3841 }
3842 BinaryOp::Lte => {
3843 let dst = self.alloc_register();
3844 self.emit(Opcode::LteImm8);
3845 self.emit_u16(dst);
3846 self.emit_u16(left);
3847 self.emit_u8(imm as u8);
3848 self.free_register(left);
3849 return Ok(dst);
3850 }
3851 _ => {}
3852 }
3853 }
3854
3855 let right = self.gen_expression(&bin.right, ctx)?;
3856
3857 let op = match bin.op {
3858 BinaryOp::Add => Opcode::Add,
3859 BinaryOp::Sub => Opcode::Sub,
3860 BinaryOp::Mul => Opcode::Mul,
3861 BinaryOp::Div => Opcode::Div,
3862 BinaryOp::Mod => Opcode::Mod,
3863 BinaryOp::Pow => Opcode::Pow,
3864 BinaryOp::BitAnd => Opcode::BitAnd,
3865 BinaryOp::BitOr => Opcode::BitOr,
3866 BinaryOp::BitXor => Opcode::BitXor,
3867 BinaryOp::Shl => Opcode::Shl,
3868 BinaryOp::Shr => Opcode::Shr,
3869 BinaryOp::UShr => Opcode::UShr,
3870 BinaryOp::Lt => Opcode::Lt,
3871 BinaryOp::Lte => Opcode::Lte,
3872 BinaryOp::Gt => Opcode::Gt,
3873 BinaryOp::Gte => Opcode::Gte,
3874 BinaryOp::Eq => Opcode::Eq,
3875 BinaryOp::Neq => Opcode::Neq,
3876 BinaryOp::StrictEq => Opcode::StrictEq,
3877 BinaryOp::StrictNeq => Opcode::StrictNeq,
3878 BinaryOp::In => {
3879 if matches!(&*bin.left, Expression::PrivateIdentifier(_)) {
3880 Opcode::HasPrivate
3881 } else {
3882 Opcode::HasProperty
3883 }
3884 }
3885 BinaryOp::InstanceOf => Opcode::InstanceOf,
3886 };
3887
3888 let dst = self.alloc_register();
3889 self.emit(op);
3890 self.emit_u16(dst);
3891
3892 if bin.op == BinaryOp::In {
3893 self.emit_u16(right);
3894 self.emit_u16(left);
3895 } else {
3896 self.emit_u16(left);
3897 self.emit_u16(right);
3898 }
3899
3900 self.free_register(left);
3901 self.free_register(right);
3902 Ok(dst)
3903 }
3904
3905 fn gen_logical_expression(
3906 &mut self,
3907 logical: &LogicalExpression,
3908 ctx: &mut JSContext,
3909 ) -> Result<u16, String> {
3910 if self.p2_fold_profile.allow_logical_bool_left() {
3911 if let Expression::Literal(Literal::Boolean(left_bool)) = &*logical.left {
3912 match logical.op {
3913 LogicalOp::And => {
3914 if *left_bool {
3915 return self.gen_expression(&logical.right, ctx);
3916 }
3917 let dst = self.alloc_register();
3918 self.emit_bool_const(dst, false);
3919 return Ok(dst);
3920 }
3921 LogicalOp::Or => {
3922 if *left_bool {
3923 let dst = self.alloc_register();
3924 self.emit_bool_const(dst, true);
3925 return Ok(dst);
3926 }
3927 return self.gen_expression(&logical.right, ctx);
3928 }
3929 LogicalOp::NullishCoalescing => {}
3930 }
3931 }
3932 }
3933
3934 if self.p2_fold_profile.allow_aggressive_truthy() {
3935 if let Expression::Literal(lit) = &*logical.left {
3936 if let Some(left_truthy) = Self::literal_truthiness(lit) {
3937 match logical.op {
3938 LogicalOp::And => {
3939 if left_truthy {
3940 return self.gen_expression(&logical.right, ctx);
3941 }
3942 return self.gen_expression(&logical.left, ctx);
3943 }
3944 LogicalOp::Or => {
3945 if left_truthy {
3946 return self.gen_expression(&logical.left, ctx);
3947 }
3948 return self.gen_expression(&logical.right, ctx);
3949 }
3950 LogicalOp::NullishCoalescing => {}
3951 }
3952 }
3953 }
3954 }
3955
3956 if self.p2_fold_profile.allow_nullish_literal_left() {
3957 if let LogicalOp::NullishCoalescing = logical.op {
3958 if let Expression::Literal(lit) = &*logical.left {
3959 return match lit {
3960 Literal::Null | Literal::Undefined => {
3961 self.gen_expression(&logical.right, ctx)
3962 }
3963 _ => self.gen_expression(&logical.left, ctx),
3964 };
3965 }
3966 }
3967 }
3968
3969 match logical.op {
3970 LogicalOp::And => {
3971 if self.opt_branch_result_prealloc {
3972 let left = self.gen_expression(&logical.left, ctx)?;
3973 let result = if self.reserved_slots.contains(&left) {
3974 let dst = self.alloc_register();
3975 self.emit(Opcode::Move);
3976 self.emit_u16(dst);
3977 self.emit_u16(left);
3978 dst
3979 } else {
3980 left
3981 };
3982
3983 self.emit(Opcode::JumpIfNot);
3984 self.emit_u16(left);
3985 let short_jump = self.code.len();
3986 self.emit_i32(0);
3987
3988 let right = self.gen_expression(&logical.right, ctx)?;
3989 if right != result {
3990 self.emit(Opcode::Move);
3991 self.emit_u16(result);
3992 self.emit_u16(right);
3993 self.free_register(right);
3994 }
3995
3996 let end_pos = self.code.len();
3997 self.patch_jump(short_jump, end_pos);
3998 return Ok(result);
3999 }
4000
4001 let left = self.gen_expression(&logical.left, ctx)?;
4002 self.emit(Opcode::JumpIfNot);
4003 self.emit_u16(left);
4004 let short_jump = self.code.len();
4005 self.emit_i32(0);
4006
4007 let right = self.gen_expression(&logical.right, ctx)?;
4008
4009 let result = if self.reserved_slots.contains(&left) && right != left {
4010 let dst = self.alloc_register();
4011 self.emit(Opcode::Move);
4012 self.emit_u16(dst);
4013 self.emit_u16(right);
4014 self.free_register(right);
4015 dst
4016 } else if right != left {
4017 self.emit(Opcode::Move);
4018 self.emit_u16(left);
4019 self.emit_u16(right);
4020 self.free_register(right);
4021 left
4022 } else {
4023 left
4024 };
4025
4026 if result != left {
4027 self.emit(Opcode::Jump);
4028 let end_jump = self.code.len();
4029 self.emit_i32(0);
4030 let patch_pos = self.code.len();
4031 self.patch_jump(short_jump, patch_pos);
4032 self.emit(Opcode::Move);
4033 self.emit_u16(result);
4034 self.emit_u16(left);
4035 let end_pos = self.code.len();
4036 self.patch_jump(end_jump, end_pos);
4037 } else {
4038 let end_pos = self.code.len();
4039 self.patch_jump(short_jump, end_pos);
4040 }
4041 Ok(result)
4042 }
4043 LogicalOp::Or => {
4044 if self.opt_branch_result_prealloc {
4045 let left = self.gen_expression(&logical.left, ctx)?;
4046 let result = if self.reserved_slots.contains(&left) {
4047 let dst = self.alloc_register();
4048 self.emit(Opcode::Move);
4049 self.emit_u16(dst);
4050 self.emit_u16(left);
4051 dst
4052 } else {
4053 left
4054 };
4055
4056 self.emit(Opcode::JumpIf);
4057 self.emit_u16(left);
4058 let short_jump = self.code.len();
4059 self.emit_i32(0);
4060
4061 let right = self.gen_expression(&logical.right, ctx)?;
4062 if right != result {
4063 self.emit(Opcode::Move);
4064 self.emit_u16(result);
4065 self.emit_u16(right);
4066 self.free_register(right);
4067 }
4068
4069 let end_pos = self.code.len();
4070 self.patch_jump(short_jump, end_pos);
4071 return Ok(result);
4072 }
4073
4074 let left = self.gen_expression(&logical.left, ctx)?;
4075 self.emit(Opcode::JumpIf);
4076 self.emit_u16(left);
4077 let short_jump = self.code.len();
4078 self.emit_i32(0);
4079
4080 let right = self.gen_expression(&logical.right, ctx)?;
4081
4082 let result = if self.reserved_slots.contains(&left) && right != left {
4083 let dst = self.alloc_register();
4084 self.emit(Opcode::Move);
4085 self.emit_u16(dst);
4086 self.emit_u16(right);
4087 self.free_register(right);
4088 dst
4089 } else if right != left {
4090 self.emit(Opcode::Move);
4091 self.emit_u16(left);
4092 self.emit_u16(right);
4093 self.free_register(right);
4094 left
4095 } else {
4096 left
4097 };
4098
4099 if result != left {
4100 self.emit(Opcode::Jump);
4101 let end_jump = self.code.len();
4102 self.emit_i32(0);
4103 let patch_pos = self.code.len();
4104 self.patch_jump(short_jump, patch_pos);
4105 self.emit(Opcode::Move);
4106 self.emit_u16(result);
4107 self.emit_u16(left);
4108 let end_pos = self.code.len();
4109 self.patch_jump(end_jump, end_pos);
4110 } else {
4111 let end_pos = self.code.len();
4112 self.patch_jump(short_jump, end_pos);
4113 }
4114 Ok(result)
4115 }
4116 LogicalOp::NullishCoalescing => {
4117 let left = self.gen_expression(&logical.left, ctx)?;
4118 let result = if self.reserved_slots.contains(&left) {
4119 let dst = self.alloc_register();
4120 self.emit(Opcode::Move);
4121 self.emit_u16(dst);
4122 self.emit_u16(left);
4123 dst
4124 } else {
4125 left
4126 };
4127
4128 let eval_right_jump = if self.opt_jump_if_nullish {
4129 self.emit(Opcode::JumpIfNullish);
4130 self.emit_u16(left);
4131 let patch = self.code.len();
4132 self.emit_i32(0);
4133 patch
4134 } else {
4135 let check_reg = self.alloc_register();
4136 self.emit(Opcode::LoadNull);
4137 self.emit_u16(check_reg);
4138 self.emit(Opcode::Eq);
4139 self.emit_u16(check_reg);
4140 self.emit_u16(left);
4141 self.emit_u16(check_reg);
4142 self.emit(Opcode::JumpIf);
4143 self.emit_u16(check_reg);
4144 let patch = self.code.len();
4145 self.emit_i32(0);
4146 self.free_register(check_reg);
4147 patch
4148 };
4149
4150 self.emit(Opcode::Jump);
4151 let end_jump = self.code.len();
4152 self.emit_i32(0);
4153
4154 let eval_right_pos = self.code.len();
4155 self.patch_jump(eval_right_jump, eval_right_pos);
4156 let right = self.gen_expression(&logical.right, ctx)?;
4157 if right != result {
4158 self.emit(Opcode::Move);
4159 self.emit_u16(result);
4160 self.emit_u16(right);
4161 self.free_register(right);
4162 }
4163
4164 let end_pos = self.code.len();
4165 self.patch_jump(end_jump, end_pos);
4166 Ok(result)
4167 }
4168 }
4169 }
4170
4171 fn gen_unary_expression(
4172 &mut self,
4173 unary: &UnaryExpression,
4174 ctx: &mut JSContext,
4175 ) -> Result<u16, String> {
4176 let saved = self.suppress_ref_error;
4177 if matches!(unary.op, UnaryOp::TypeOf) {
4178 if matches!(&*unary.argument, Expression::Identifier(_)) {
4179 self.suppress_ref_error = true;
4180 }
4181 }
4182 if matches!(unary.op, UnaryOp::Delete) {
4183 if matches!(&*unary.argument, Expression::Identifier(_)) {
4184 self.suppress_ref_error = true;
4185 }
4186 }
4187 let arg = self.gen_expression(&unary.argument, ctx)?;
4188 self.suppress_ref_error = saved;
4189 match unary.op {
4190 UnaryOp::Minus => {
4191 let dst = self.alloc_register();
4192 self.emit(Opcode::Neg);
4193 self.emit_u16(dst);
4194 self.emit_u16(arg);
4195 self.free_register(arg);
4196 return Ok(dst);
4197 }
4198 UnaryOp::Not => {
4199 let dst = self.alloc_register();
4200 self.emit(Opcode::Not);
4201 self.emit_u16(dst);
4202 self.emit_u16(arg);
4203 self.free_register(arg);
4204 return Ok(dst);
4205 }
4206 UnaryOp::TypeOf => {
4207 let dst = self.alloc_register();
4208 self.emit(Opcode::TypeOf);
4209 self.emit_u16(dst);
4210 self.emit_u16(arg);
4211 self.free_register(arg);
4212 return Ok(dst);
4213 }
4214 UnaryOp::BitNot => {
4215 let dst = self.alloc_register();
4216 self.emit(Opcode::BitNot);
4217 self.emit_u16(dst);
4218 self.emit_u16(arg);
4219 self.free_register(arg);
4220 return Ok(dst);
4221 }
4222 UnaryOp::Plus => {}
4223 UnaryOp::Void => {
4224 let dst = self.alloc_register();
4225 self.emit(Opcode::LoadUndefined);
4226 self.emit_u16(dst);
4227 self.free_register(arg);
4228 return Ok(dst);
4229 }
4230 UnaryOp::Delete => {
4231 return self.gen_delete_expression(arg, &unary.argument, ctx);
4232 }
4233 }
4234 Ok(arg)
4235 }
4236
4237 fn gen_delete_expression(
4238 &mut self,
4239 arg_reg: u16,
4240 arg_expr: &Expression,
4241 ctx: &mut JSContext,
4242 ) -> Result<u16, String> {
4243 match arg_expr {
4244 Expression::Identifier(id) => {
4245 self.free_register(arg_reg);
4246 let dst = self.alloc_register();
4247 if id.name == "arguments" && !self.is_script_level && !self.is_arrow {
4248 self.emit(Opcode::LoadFalse);
4249 self.emit_u16(dst);
4250 } else if self.resolve_var(&id.name, ctx).is_some() {
4251 self.emit(Opcode::LoadFalse);
4252 self.emit_u16(dst);
4253 } else {
4254 let atom = ctx.intern(&id.name);
4255 let idx = self.add_constant(JSValue::new_string(atom));
4256 self.emit(Opcode::DeleteGlobal);
4257 self.emit_u16(dst);
4258 self.emit_u32(idx);
4259 }
4260 Ok(dst)
4261 }
4262 Expression::MemberExpression(member) => {
4263 if matches!(&*member.object, Expression::Super) {
4264 if let MemberProperty::Computed(expr) = &member.property {
4265 self.gen_expression(expr, ctx)?;
4266 }
4267 self.free_register(arg_reg);
4268 let msg_idx = self.add_constant(JSValue::new_string(
4269 ctx.intern("Cannot delete super reference"),
4270 ));
4271 self.emit(Opcode::ThrowReferenceError);
4272 self.emit_u32(msg_idx);
4273 let dst = self.alloc_register();
4274 self.emit(Opcode::LoadUndefined);
4275 self.emit_u16(dst);
4276 return Ok(dst);
4277 }
4278 let obj = self.gen_expression(&member.object, ctx)?;
4279 let dst = self.alloc_register();
4280 match &member.property {
4281 MemberProperty::Identifier(name) => {
4282 let key_idx = self.intern_and_add_const(ctx, name);
4283 let key = self.alloc_register();
4284 self.emit(Opcode::LoadConst);
4285 self.emit_u16(key);
4286 self.emit_u32(key_idx);
4287 self.emit(Opcode::DeleteProp);
4288 self.emit_u16(dst);
4289 self.emit_u16(obj);
4290 self.emit_u16(key);
4291 self.free_register(key);
4292 }
4293 MemberProperty::Computed(expr) => {
4294 let key = self.gen_expression(expr, ctx)?;
4295 self.emit(Opcode::DeleteProp);
4296 self.emit_u16(dst);
4297 self.emit_u16(obj);
4298 self.emit_u16(key);
4299 }
4300 MemberProperty::PrivateIdentifier(name) => {
4301 let key_idx = self.intern_and_add_const(ctx, name);
4302 let key = self.alloc_register();
4303 self.emit(Opcode::LoadConst);
4304 self.emit_u16(key);
4305 self.emit_u32(key_idx);
4306 self.emit(Opcode::DeleteProp);
4307 self.emit_u16(dst);
4308 self.emit_u16(obj);
4309 self.emit_u16(key);
4310 self.free_register(key);
4311 }
4312 }
4313 self.free_register(obj);
4314 Ok(dst)
4315 }
4316 Expression::Literal(Literal::Number(n)) if n.is_nan() || n.is_infinite() => {
4317 self.free_register(arg_reg);
4318 let name = if n.is_nan() { "NaN" } else { "Infinity" };
4319 let atom = ctx.intern(name);
4320 let idx = self.add_constant(JSValue::new_string(atom));
4321 let dst = self.alloc_register();
4322 self.emit(Opcode::DeleteGlobal);
4323 self.emit_u16(dst);
4324 self.emit_u32(idx);
4325 Ok(dst)
4326 }
4327 _ => {
4328 self.free_register(arg_reg);
4329 let dst = self.alloc_register();
4330 self.emit(Opcode::LoadTrue);
4331 self.emit_u16(dst);
4332 Ok(dst)
4333 }
4334 }
4335 }
4336
4337 fn gen_update_expression(
4338 &mut self,
4339 update: &UpdateExpression,
4340 ctx: &mut JSContext,
4341 ) -> Result<u16, String> {
4342 match &*update.argument {
4343 Expression::Identifier(id) => {
4344 if let Some(entry) = self.lookup_var(&id.name) {
4345 if entry.kind == VariableKind::Const {
4346 return Err(format!(
4347 "TypeError: Assignment to constant variable '{}'",
4348 id.name
4349 ));
4350 }
4351 }
4352 match self.resolve_var(&id.name, ctx) {
4353 Some(VarLocation::Local(slot)) => {
4354 if let Some(entry) = self.lookup_var(&id.name) {
4355 if entry.kind != VariableKind::Var && !entry.initialized {
4356 self.emit(Opcode::CheckTdz);
4357 self.emit_u16(slot);
4358 }
4359 }
4360 let op = match update.op {
4361 UpdateOp::Increment => Opcode::IncLocal,
4362 UpdateOp::Decrement => Opcode::DecLocal,
4363 };
4364
4365 if update.prefix {
4366 self.emit(op);
4367 self.emit_u16(slot);
4368 if self.is_global_var(&id.name) {
4369 let atom = ctx.intern(&id.name);
4370 let idx = self.add_constant(JSValue::new_string(atom));
4371 self.emit(Opcode::SetGlobal);
4372 self.emit_u32(idx);
4373 self.emit_u16(slot);
4374 }
4375 Ok(slot)
4376 } else {
4377 let old_val = self.alloc_register();
4378 self.emit(Opcode::Move);
4379 self.emit_u16(old_val);
4380 self.emit_u16(slot);
4381 self.emit(Opcode::Pos);
4382 self.emit_u16(old_val);
4383 self.emit_u16(old_val);
4384 self.emit(op);
4385 self.emit_u16(slot);
4386 if self.is_global_var(&id.name) {
4387 let atom = ctx.intern(&id.name);
4388 let idx = self.add_constant(JSValue::new_string(atom));
4389 self.emit(Opcode::SetGlobal);
4390 self.emit_u32(idx);
4391 self.emit_u16(slot);
4392 }
4393 Ok(old_val)
4394 }
4395 }
4396 Some(VarLocation::Upvalue(slot)) => {
4397 let temp = self.alloc_register();
4398 self.emit(Opcode::GetUpvalue);
4399 self.emit_u16(temp);
4400 self.emit_u16(slot);
4401
4402 if let Some(entry) = self.lookup_var(&id.name) {
4403 if entry.kind != VariableKind::Var && !entry.initialized {
4404 self.emit(Opcode::CheckTdz);
4405 self.emit_u16(temp);
4406 }
4407 }
4408
4409 let op = match update.op {
4410 UpdateOp::Increment => Opcode::IncLocal,
4411 UpdateOp::Decrement => Opcode::DecLocal,
4412 };
4413
4414 if update.prefix {
4415 self.emit(op);
4416 self.emit_u16(temp);
4417 self.emit(Opcode::SetUpvalue);
4418 self.emit_u16(slot);
4419 self.emit_u16(temp);
4420 Ok(temp)
4421 } else {
4422 let old_val = self.alloc_register();
4423 self.emit(Opcode::Move);
4424 self.emit_u16(old_val);
4425 self.emit_u16(temp);
4426 self.emit(op);
4427 self.emit_u16(temp);
4428 self.emit(Opcode::SetUpvalue);
4429 self.emit_u16(slot);
4430 self.emit_u16(temp);
4431 Ok(old_val)
4432 }
4433 }
4434 None => {
4435 let atom = ctx.intern(&id.name);
4436 let const_idx = self.add_constant(JSValue::new_string(atom));
4437 let r = self.alloc_register();
4438 self.emit(Opcode::GetGlobal);
4439 self.emit_u16(r);
4440 self.emit_u32(const_idx);
4441 let op = match update.op {
4442 UpdateOp::Increment => Opcode::IncLocal,
4443 UpdateOp::Decrement => Opcode::DecLocal,
4444 };
4445 if update.prefix {
4446 self.emit(op);
4447 self.emit_u16(r);
4448 self.emit(Opcode::SetGlobal);
4449 self.emit_u32(const_idx);
4450 self.emit_u16(r);
4451 Ok(r)
4452 } else {
4453 let old = self.alloc_register();
4454 self.emit(Opcode::Move);
4455 self.emit_u16(old);
4456 self.emit_u16(r);
4457 self.emit(op);
4458 self.emit_u16(r);
4459 self.emit(Opcode::SetGlobal);
4460 self.emit_u32(const_idx);
4461 self.emit_u16(r);
4462 self.free_register(r);
4463 Ok(old)
4464 }
4465 }
4466 }
4467 }
4468 Expression::MemberExpression(member) => {
4469 let obj = self.gen_expression(&member.object, ctx)?;
4470 let op = match update.op {
4471 UpdateOp::Increment => Opcode::IncLocal,
4472 UpdateOp::Decrement => Opcode::DecLocal,
4473 };
4474
4475 match &member.property {
4476 MemberProperty::Identifier(name) => {
4477 let atom = ctx.intern(name);
4478 let tmp = self.alloc_register();
4479 self.emit(Opcode::GetNamedProp);
4480 self.emit_u16(tmp);
4481 self.emit_u16(obj);
4482 self.emit_u32(atom.0);
4483
4484 if update.prefix {
4485 self.emit(op);
4486 self.emit_u16(tmp);
4487 self.emit(Opcode::SetNamedProp);
4488 self.emit_u16(obj);
4489 self.emit_u16(tmp);
4490 self.emit_u32(atom.0);
4491 self.free_register(obj);
4492 Ok(tmp)
4493 } else {
4494 let old_val = self.alloc_register();
4495 self.emit(Opcode::Move);
4496 self.emit_u16(old_val);
4497 self.emit_u16(tmp);
4498 self.emit(Opcode::Pos);
4499 self.emit_u16(old_val);
4500 self.emit_u16(old_val);
4501 self.emit(op);
4502 self.emit_u16(tmp);
4503 self.emit(Opcode::SetNamedProp);
4504 self.emit_u16(obj);
4505 self.emit_u16(tmp);
4506 self.emit_u32(atom.0);
4507 self.free_register(obj);
4508 self.free_register(tmp);
4509 Ok(old_val)
4510 }
4511 }
4512 MemberProperty::Computed(expr) => {
4513 let key = self.gen_expression(expr, ctx)?;
4514
4515 let tmp = self.alloc_register();
4516 self.emit(Opcode::GetField);
4517 self.emit_u16(tmp);
4518 self.emit_u16(obj);
4519 self.emit_u16(key);
4520
4521 if update.prefix {
4522 self.emit(op);
4523 self.emit_u16(tmp);
4524 self.emit(Opcode::SetProp);
4525 self.emit_u16(obj);
4526 self.emit_u16(key);
4527 self.emit_u16(tmp);
4528 self.free_register(obj);
4529 self.free_register(key);
4530 Ok(tmp)
4531 } else {
4532 let old_val = self.alloc_register();
4533 self.emit(Opcode::Move);
4534 self.emit_u16(old_val);
4535 self.emit_u16(tmp);
4536 self.emit(op);
4537 self.emit_u16(tmp);
4538 self.emit(Opcode::SetField);
4539 self.emit_u16(obj);
4540 self.emit_u16(key);
4541 self.emit_u16(tmp);
4542 self.free_register(obj);
4543 self.free_register(key);
4544 self.free_register(tmp);
4545 Ok(old_val)
4546 }
4547 }
4548 MemberProperty::PrivateIdentifier(_) => {
4549 self.free_register(obj);
4550 let r = self.alloc_register();
4551 self.emit(Opcode::LoadUndefined);
4552 self.emit_u16(r);
4553 Ok(r)
4554 }
4555 }
4556 }
4557 _ => {
4558 let r = self.alloc_register();
4559 self.emit(Opcode::LoadUndefined);
4560 self.emit_u16(r);
4561 Ok(r)
4562 }
4563 }
4564 }
4565
4566 fn assign_op_to_reg_opcode(op: &AssignOp) -> Option<Opcode> {
4567 match op {
4568 AssignOp::AddAssign => Some(Opcode::Add),
4569 AssignOp::SubAssign => Some(Opcode::Sub),
4570 AssignOp::MulAssign => Some(Opcode::Mul),
4571 AssignOp::DivAssign => Some(Opcode::Div),
4572 AssignOp::ModAssign => Some(Opcode::Mod),
4573 AssignOp::PowAssign => Some(Opcode::Pow),
4574 AssignOp::BitAndAssign => Some(Opcode::BitAnd),
4575 AssignOp::BitOrAssign => Some(Opcode::BitOr),
4576 AssignOp::BitXorAssign => Some(Opcode::BitXor),
4577 AssignOp::ShlAssign => Some(Opcode::Shl),
4578 AssignOp::ShrAssign => Some(Opcode::Shr),
4579 AssignOp::UShrAssign => Some(Opcode::UShr),
4580 _ => None,
4581 }
4582 }
4583
4584 fn gen_assignment_expression(
4585 &mut self,
4586 assign: &AssignmentExpression,
4587 ctx: &mut JSContext,
4588 ) -> Result<u16, String> {
4589 let is_compound = Self::assign_op_to_reg_opcode(&assign.op);
4590
4591 match &assign.left {
4592 AssignmentTarget::Identifier(name) => {
4593 if self.is_strict && Self::is_strict_reserved_word(name) {
4594 return Err(format!(
4595 "SyntaxError: Assignment to '{}' in strict mode is not allowed",
4596 name
4597 ));
4598 }
4599 if let Some(entry) = self.lookup_var(name) {
4600 if entry.kind == VariableKind::Const {
4601 return Err(format!(
4602 "TypeError: Assignment to constant variable '{}'",
4603 name
4604 ));
4605 }
4606 }
4607
4608 if matches!(
4609 assign.op,
4610 AssignOp::LogicalAndAssign
4611 | AssignOp::LogicalOrAssign
4612 | AssignOp::NullishAssign
4613 ) {
4614 let resolved = self.resolve_var(name, ctx);
4615 let global_atom = ctx.intern(name);
4616 let global_idx = self.add_constant(JSValue::new_string(global_atom));
4617
4618 let current = match resolved {
4619 Some(VarLocation::Local(slot)) => slot,
4620 Some(VarLocation::Upvalue(slot)) => {
4621 let tmp = self.alloc_register();
4622 self.emit(Opcode::GetUpvalue);
4623 self.emit_u16(tmp);
4624 self.emit_u16(slot);
4625 tmp
4626 }
4627 None => {
4628 let tmp = self.alloc_register();
4629 self.emit(Opcode::GetGlobal);
4630 self.emit_u16(tmp);
4631 self.emit_u32(global_idx);
4632 tmp
4633 }
4634 };
4635
4636 let jump_to_end = match assign.op {
4637 AssignOp::LogicalAndAssign => {
4638 self.emit(Opcode::JumpIfNot);
4639 self.emit_u16(current);
4640 let patch = self.code.len();
4641 self.emit_i32(0);
4642 patch
4643 }
4644 AssignOp::LogicalOrAssign => {
4645 self.emit(Opcode::JumpIf);
4646 self.emit_u16(current);
4647 let patch = self.code.len();
4648 self.emit_i32(0);
4649 patch
4650 }
4651 AssignOp::NullishAssign => {
4652 let null_reg = self.alloc_register();
4653 self.emit(Opcode::LoadNull);
4654 self.emit_u16(null_reg);
4655 let cmp_reg = self.alloc_register();
4656 self.emit(Opcode::StrictEq);
4657 self.emit_u16(cmp_reg);
4658 self.emit_u16(current);
4659 self.emit_u16(null_reg);
4660 self.emit(Opcode::JumpIf);
4661 self.emit_u16(cmp_reg);
4662 let jump_to_assign = self.code.len();
4663 self.emit_i32(0);
4664
4665 let undef_reg = self.alloc_register();
4666 self.emit(Opcode::LoadUndefined);
4667 self.emit_u16(undef_reg);
4668 self.emit(Opcode::StrictEq);
4669 self.emit_u16(cmp_reg);
4670 self.emit_u16(current);
4671 self.emit_u16(undef_reg);
4672 self.emit(Opcode::JumpIfNot);
4673 self.emit_u16(cmp_reg);
4674 let jump_to_end = self.code.len();
4675 self.emit_i32(0);
4676
4677 let assign_pos = self.code.len();
4678 self.patch_jump(jump_to_assign, assign_pos);
4679
4680 self.free_register(undef_reg);
4681 self.free_register(cmp_reg);
4682 self.free_register(null_reg);
4683 jump_to_end
4684 }
4685 _ => unreachable!(),
4686 };
4687
4688 let rhs = self.gen_expression(&assign.right, ctx)?;
4689 if current != rhs {
4690 self.emit(Opcode::Move);
4691 self.emit_u16(current);
4692 self.emit_u16(rhs);
4693 }
4694
4695 match resolved {
4696 Some(VarLocation::Local(slot)) => {
4697 if slot != current {
4698 self.emit(Opcode::Move);
4699 self.emit_u16(slot);
4700 self.emit_u16(current);
4701 }
4702 if self.is_global_var(name) {
4703 let idx = self.add_constant(JSValue::new_string(ctx.intern(name)));
4704 if self.is_eval {
4705 self.emit(Opcode::SetGlobalVar);
4706 } else {
4707 self.emit(Opcode::SetGlobal);
4708 }
4709 self.emit_u32(idx);
4710 self.emit_u16(slot);
4711 }
4712 }
4713 Some(VarLocation::Upvalue(slot)) => {
4714 self.emit(Opcode::SetUpvalue);
4715 self.emit_u16(slot);
4716 self.emit_u16(current);
4717 }
4718 None => {
4719 let idx = self.add_constant(JSValue::new_string(ctx.intern(name)));
4720 if self.is_eval {
4721 self.emit(Opcode::SetGlobalVar);
4722 } else {
4723 self.emit(Opcode::SetGlobal);
4724 }
4725 self.emit_u32(idx);
4726 self.emit_u16(current);
4727 }
4728 }
4729
4730 let end_pos = self.code.len();
4731 self.patch_jump(jump_to_end, end_pos);
4732 if current != rhs {
4733 self.free_register(rhs);
4734 }
4735 return Ok(current);
4736 }
4737
4738 let val = self.gen_expression_with_assign_name(&assign.right, name, ctx)?;
4739 match self.resolve_var(name, ctx) {
4740 Some(VarLocation::Local(slot)) => {
4741 if let Some(opcode) = is_compound {
4742 self.emit(opcode);
4743 self.emit_u16(slot);
4744 self.emit_u16(slot);
4745 self.emit_u16(val);
4746 } else if slot != val {
4747 self.emit(Opcode::Move);
4748 self.emit_u16(slot);
4749 self.emit_u16(val);
4750 }
4751
4752 if self.is_global_var(name) {
4753 let atom = ctx.intern(name);
4754 let idx = self.add_constant(JSValue::new_string(atom));
4755 if self.is_eval {
4756 self.emit(Opcode::SetGlobalVar);
4757 } else {
4758 self.emit(Opcode::SetGlobal);
4759 }
4760 self.emit_u32(idx);
4761 self.emit_u16(slot);
4762 }
4763 Ok(slot)
4764 }
4765 Some(VarLocation::Upvalue(slot)) => {
4766 if let Some(opcode) = is_compound {
4767 let tmp = self.alloc_register();
4768 self.emit(Opcode::GetUpvalue);
4769 self.emit_u16(tmp);
4770 self.emit_u16(slot);
4771 self.emit(opcode);
4772 self.emit_u16(tmp);
4773 self.emit_u16(tmp);
4774 self.emit_u16(val);
4775 self.emit(Opcode::SetUpvalue);
4776 self.emit_u16(slot);
4777 self.emit_u16(tmp);
4778 self.free_register(tmp);
4779 } else {
4780 self.emit(Opcode::SetUpvalue);
4781 self.emit_u16(slot);
4782 self.emit_u16(val);
4783 }
4784 Ok(val)
4785 }
4786 None => {
4787 let atom = ctx.intern(name);
4788 let idx = self.add_constant(JSValue::new_string(atom));
4789 if let Some(opcode) = is_compound {
4790 let tmp = self.alloc_register();
4791 self.emit(Opcode::GetGlobal);
4792 self.emit_u16(tmp);
4793 self.emit_u32(idx);
4794 if !self.suppress_ref_error {
4795 self.emit(Opcode::CheckRef);
4796 self.emit_u16(tmp);
4797 self.emit_u32(idx);
4798 }
4799 self.emit(opcode);
4800 self.emit_u16(tmp);
4801 self.emit_u16(tmp);
4802 self.emit_u16(val);
4803 self.emit(Opcode::SetGlobal);
4804 self.emit_u32(idx);
4805 self.emit_u16(tmp);
4806 self.free_register(tmp);
4807 Ok(val)
4808 } else {
4809 self.emit(Opcode::SetGlobal);
4810 self.emit_u32(idx);
4811 self.emit_u16(val);
4812 Ok(val)
4813 }
4814 }
4815 }
4816 }
4817 AssignmentTarget::MemberExpression(member) => {
4818 let obj = self.gen_expression(&member.object, ctx)?;
4819 let val = self.gen_expression(&assign.right, ctx)?;
4820
4821 let mut result_reg = val;
4822 match &member.property {
4823 MemberProperty::Identifier(name) => {
4824 let atom = ctx.intern(name);
4825 if let Some(opcode) = is_compound {
4826 let tmp = self.alloc_register();
4827 self.emit(Opcode::GetNamedProp);
4828 self.emit_u16(tmp);
4829 self.emit_u16(obj);
4830 self.emit_u32(atom.0);
4831 self.emit(opcode);
4832 self.emit_u16(tmp);
4833 self.emit_u16(tmp);
4834 self.emit_u16(val);
4835 self.emit(Opcode::SetNamedProp);
4836 self.emit_u16(obj);
4837 self.emit_u16(tmp);
4838 self.emit_u32(atom.0);
4839 self.free_register(val);
4840 result_reg = tmp;
4841 } else {
4842 self.emit(Opcode::SetNamedProp);
4843 self.emit_u16(obj);
4844 self.emit_u16(val);
4845 self.emit_u32(atom.0);
4846 }
4847 }
4848 MemberProperty::Computed(expr) => {
4849 let key = self.gen_expression(expr, ctx)?;
4850 if let Some(opcode) = is_compound {
4851 let tmp = self.alloc_register();
4852 self.emit(Opcode::GetField);
4853 self.emit_u16(tmp);
4854 self.emit_u16(obj);
4855 self.emit_u16(key);
4856 self.emit(opcode);
4857 self.emit_u16(tmp);
4858 self.emit_u16(tmp);
4859 self.emit_u16(val);
4860 self.emit(Opcode::SetField);
4861 self.emit_u16(obj);
4862 self.emit_u16(key);
4863 self.emit_u16(tmp);
4864 self.free_register(val);
4865 result_reg = tmp;
4866 } else {
4867 self.emit(Opcode::SetField);
4868 self.emit_u16(obj);
4869 self.emit_u16(key);
4870 self.emit_u16(val);
4871 }
4872 self.free_register(key);
4873 }
4874 MemberProperty::PrivateIdentifier(name) => {
4875 let key_idx = self.intern_and_add_const(ctx, name);
4876 let key = self.alloc_register();
4877 self.emit(Opcode::LoadConst);
4878 self.emit_u16(key);
4879 self.emit_u32(key_idx);
4880 if let Some(opcode) = is_compound {
4881 let tmp = self.alloc_register();
4882 self.emit(Opcode::GetPrivate);
4883 self.emit_u16(tmp);
4884 self.emit_u16(obj);
4885 self.emit_u16(key);
4886 self.emit(opcode);
4887 self.emit_u16(tmp);
4888 self.emit_u16(tmp);
4889 self.emit_u16(val);
4890 self.emit(Opcode::SetPrivate);
4891 self.emit_u16(obj);
4892 self.emit_u16(key);
4893 self.emit_u16(tmp);
4894 self.free_register(val);
4895 result_reg = tmp;
4896 } else {
4897 self.emit(Opcode::SetPrivate);
4898 self.emit_u16(obj);
4899 self.emit_u16(key);
4900 self.emit_u16(val);
4901 }
4902 self.free_register(key);
4903 }
4904 }
4905 self.free_register(obj);
4906 Ok(result_reg)
4907 }
4908 _ => {
4909 let val = self.gen_expression(&assign.right, ctx)?;
4910 self.gen_assignment_target_destructure(&assign.left, val, ctx)?;
4911 Ok(val)
4912 }
4913 }
4914 }
4915
4916 fn gen_assignment_target_destructure(
4917 &mut self,
4918 target: &AssignmentTarget,
4919 value_reg: u16,
4920 ctx: &mut JSContext,
4921 ) -> Result<(), String> {
4922 match target {
4923 AssignmentTarget::Identifier(name) => match self.resolve_var(name, ctx) {
4924 Some(VarLocation::Local(slot)) => {
4925 if slot != value_reg {
4926 self.emit(Opcode::Move);
4927 self.emit_u16(slot);
4928 self.emit_u16(value_reg);
4929 }
4930 if self.is_global_var(name) {
4931 let global_idx = self.intern_and_add_const(ctx, name);
4932 self.emit(Opcode::SetGlobal);
4933 self.emit_u32(global_idx);
4934 self.emit_u16(slot);
4935 }
4936 Ok(())
4937 }
4938 Some(VarLocation::Upvalue(slot)) => {
4939 self.emit(Opcode::SetUpvalue);
4940 self.emit_u16(slot);
4941 self.emit_u16(value_reg);
4942 Ok(())
4943 }
4944 None => {
4945 let global_idx = self.intern_and_add_const(ctx, name);
4946 self.emit(Opcode::SetGlobal);
4947 self.emit_u32(global_idx);
4948 self.emit_u16(value_reg);
4949 Ok(())
4950 }
4951 },
4952 AssignmentTarget::MemberExpression(member) => {
4953 let obj = self.gen_expression(&member.object, ctx)?;
4954 match &member.property {
4955 MemberProperty::Identifier(name) => {
4956 let atom = ctx.intern(name);
4957 self.emit(Opcode::SetNamedProp);
4958 self.emit_u16(obj);
4959 self.emit_u16(value_reg);
4960 self.emit_u32(atom.0);
4961 }
4962 MemberProperty::Computed(expr) => {
4963 let key = self.gen_expression(expr, ctx)?;
4964 self.emit(Opcode::SetField);
4965 self.emit_u16(obj);
4966 self.emit_u16(key);
4967 self.emit_u16(value_reg);
4968 self.free_register(key);
4969 }
4970 MemberProperty::PrivateIdentifier(name) => {
4971 let key_idx = self.intern_and_add_const(ctx, name);
4972 let key = self.alloc_register();
4973 self.emit(Opcode::LoadConst);
4974 self.emit_u16(key);
4975 self.emit_u32(key_idx);
4976 self.emit(Opcode::SetPrivate);
4977 self.emit_u16(obj);
4978 self.emit_u16(key);
4979 self.emit_u16(value_reg);
4980 self.free_register(key);
4981 }
4982 }
4983 self.free_register(obj);
4984 Ok(())
4985 }
4986 AssignmentTarget::ComputedMember(obj_expr, key_expr) => {
4987 let obj = self.gen_expression(obj_expr, ctx)?;
4988 let key = self.gen_expression(key_expr, ctx)?;
4989 self.emit(Opcode::SetField);
4990 self.emit_u16(obj);
4991 self.emit_u16(key);
4992 self.emit_u16(value_reg);
4993 self.free_register(key);
4994 self.free_register(obj);
4995 Ok(())
4996 }
4997 AssignmentTarget::ArrayPattern(arr) => {
4998 self.gen_array_destructuring_target(arr, value_reg, ctx)
4999 }
5000 AssignmentTarget::ObjectPattern(obj) => {
5001 self.gen_object_destructuring_target(obj, value_reg, ctx)
5002 }
5003 AssignmentTarget::AssignmentPattern(ap) => {
5004 self.gen_default_value_assignment(&ap.right, value_reg, ctx)?;
5005 self.gen_assignment_target_destructure(&ap.left, value_reg, ctx)
5006 }
5007 AssignmentTarget::RestElement(_) => {
5008 Err("Rest element not valid in assignment target".to_string())
5009 }
5010 AssignmentTarget::OptionalMemberExpression(_) => {
5011 Err("Optional member expression not valid in assignment target".to_string())
5012 }
5013 }
5014 }
5015
5016 fn gen_array_destructuring_target(
5017 &mut self,
5018 arr: &ArrayPattern,
5019 value_reg: u16,
5020 ctx: &mut JSContext,
5021 ) -> Result<(), String> {
5022 let idx_reg = self.alloc_register();
5023 self.emit_int_const(idx_reg, 0);
5024
5025 let len = arr.elements.len();
5026 for (i, elem) in arr.elements.iter().enumerate() {
5027 if i == len - 1 {
5028 if let Some(PatternElement::RestElement(rest)) = elem {
5029 let rest_reg = self.alloc_register();
5030 self.emit(Opcode::NewArray);
5031 self.emit_u16(rest_reg);
5032 self.emit_u16(0);
5033 let loop_start = self.code.len();
5034 let len_key = self.alloc_register();
5035 let len_atom = ctx.common_atoms.length;
5036 let len_const = self.add_constant(JSValue::new_string(len_atom));
5037 self.emit(Opcode::LoadConst);
5038 self.emit_u16(len_key);
5039 self.emit_u32(len_const);
5040 let arr_len = self.alloc_register();
5041 self.emit(Opcode::GetProp);
5042 self.emit_u16(arr_len);
5043 self.emit_u16(value_reg);
5044 self.emit_u16(len_key);
5045 self.free_register(len_key);
5046 let cmp = self.alloc_register();
5047 self.emit(Opcode::Lt);
5048 self.emit_u16(cmp);
5049 self.emit_u16(idx_reg);
5050 self.emit_u16(arr_len);
5051 self.free_register(arr_len);
5052 self.emit(Opcode::JumpIfNot);
5053 self.emit_u16(cmp);
5054 let exit_patch = self.code.len();
5055 self.emit_i32(0);
5056 self.free_register(cmp);
5057 let item = self.alloc_register();
5058 self.emit(Opcode::GetField);
5059 self.emit_u16(item);
5060 self.emit_u16(value_reg);
5061 self.emit_u16(idx_reg);
5062 self.emit(Opcode::ArrayPush);
5063 self.emit_u16(rest_reg);
5064 self.emit_u16(item);
5065 self.free_register(item);
5066 self.emit(Opcode::IncLocal);
5067 self.emit_u16(idx_reg);
5068 self.emit_backward_jump(loop_start);
5069 let end_pos = self.code.len();
5070 self.patch_jump(exit_patch, end_pos);
5071
5072 self.gen_assignment_target_destructure(&rest.argument, rest_reg, ctx)?;
5073 self.free_register(rest_reg);
5074 self.free_register(idx_reg);
5075 return Ok(());
5076 }
5077 }
5078
5079 if let Some(elem) = elem {
5080 match elem {
5081 PatternElement::Pattern(target) => {
5082 let elem_reg = self.alloc_register();
5083 self.emit(Opcode::GetField);
5084 self.emit_u16(elem_reg);
5085 self.emit_u16(value_reg);
5086 self.emit_u16(idx_reg);
5087 self.gen_assignment_target_destructure(target, elem_reg, ctx)?;
5088 self.free_register(elem_reg);
5089 }
5090 PatternElement::RestElement(_) => {
5091 return Err("rest element position not yet supported".to_string());
5092 }
5093 PatternElement::AssignmentPattern(ap) => {
5094 let elem_reg = self.alloc_register();
5095 self.emit(Opcode::GetField);
5096 self.emit_u16(elem_reg);
5097 self.emit_u16(value_reg);
5098 self.emit_u16(idx_reg);
5099 self.gen_default_value_assignment(&ap.right, elem_reg, ctx)?;
5100 self.gen_assignment_target_destructure(&ap.left, elem_reg, ctx)?;
5101 self.free_register(elem_reg);
5102 }
5103 }
5104 }
5105 self.emit(Opcode::IncLocal);
5106 self.emit_u16(idx_reg);
5107 }
5108 self.free_register(idx_reg);
5109 Ok(())
5110 }
5111
5112 fn gen_object_destructuring_target(
5113 &mut self,
5114 obj: &ObjectPattern,
5115 value_reg: u16,
5116 ctx: &mut JSContext,
5117 ) -> Result<(), String> {
5118 for prop in &obj.properties {
5119 match prop {
5120 ObjectPatternProperty::Property {
5121 key,
5122 value,
5123 computed,
5124 ..
5125 } => {
5126 let key_reg = if *computed {
5127 match key {
5128 PropertyKey::Computed(expr) => self.gen_expression(expr, ctx)?,
5129 _ => return Err("Expected computed key".to_string()),
5130 }
5131 } else {
5132 match key {
5133 PropertyKey::Identifier(k)
5134 | PropertyKey::Literal(Literal::String(k, _)) => {
5135 let idx = self.intern_and_add_const(ctx, k);
5136 let kreg = self.alloc_register();
5137 self.emit(Opcode::LoadConst);
5138 self.emit_u16(kreg);
5139 self.emit_u32(idx);
5140 kreg
5141 }
5142 PropertyKey::Literal(Literal::Number(n)) => {
5143 let kreg = self.alloc_register();
5144 if *n >= i32::MIN as f64
5145 && *n <= i32::MAX as f64
5146 && n.fract() == 0.0
5147 {
5148 self.emit_int_const(kreg, *n as i32);
5149 } else {
5150 let cidx = self.add_constant(JSValue::new_float(*n));
5151 self.emit(Opcode::LoadConst);
5152 self.emit_u16(kreg);
5153 self.emit_u32(cidx);
5154 }
5155 kreg
5156 }
5157 _ => return Err("Invalid property key".to_string()),
5158 }
5159 };
5160 let elem_reg = self.alloc_register();
5161 self.emit(Opcode::GetProp);
5162 self.emit_u16(elem_reg);
5163 self.emit_u16(value_reg);
5164 self.emit_u16(key_reg);
5165 self.free_register(key_reg);
5166 self.gen_assignment_target_destructure(value, elem_reg, ctx)?;
5167 self.free_register(elem_reg);
5168 }
5169 ObjectPatternProperty::RestElement(rest) => {
5170 let rest_reg = self.alloc_register();
5171 self.emit(Opcode::NewObject);
5172 self.emit_u16(rest_reg);
5173 self.emit(Opcode::ObjectSpread);
5174 self.emit_u16(rest_reg);
5175 self.emit_u16(value_reg);
5176 self.gen_assignment_target_destructure(&rest.argument, rest_reg, ctx)?;
5177 self.free_register(rest_reg);
5178 }
5179 }
5180 }
5181 Ok(())
5182 }
5183
5184 fn gen_default_value_assignment(
5185 &mut self,
5186 default_expr: &Expression,
5187 value_reg: u16,
5188 ctx: &mut JSContext,
5189 ) -> Result<(), String> {
5190 let cmp_reg = self.alloc_register();
5191 self.emit(Opcode::LoadUndefined);
5192 self.emit_u16(cmp_reg);
5193
5194 self.emit(Opcode::StrictNeq);
5195 self.emit_u16(cmp_reg);
5196 self.emit_u16(value_reg);
5197 self.emit_u16(cmp_reg);
5198
5199 self.emit(Opcode::JumpIf);
5200 self.emit_u16(cmp_reg);
5201 let skip_patch = self.code.len();
5202 self.emit_i32(0);
5203 self.free_register(cmp_reg);
5204
5205 let default_reg = self.gen_expression(default_expr, ctx)?;
5206 if default_reg != value_reg {
5207 self.emit(Opcode::Move);
5208 self.emit_u16(value_reg);
5209 self.emit_u16(default_reg);
5210 }
5211 self.free_register(default_reg);
5212
5213 let after_pos = self.code.len();
5214 self.patch_jump(skip_patch, after_pos);
5215
5216 Ok(())
5217 }
5218
5219 fn gen_conditional_expression(
5220 &mut self,
5221 cond: &ConditionalExpression,
5222 ctx: &mut JSContext,
5223 ) -> Result<u16, String> {
5224 if self.p2_fold_profile.allow_ternary_bool() {
5225 if let Expression::Literal(Literal::Boolean(test_bool)) = &*cond.test {
5226 if *test_bool {
5227 return self.gen_expression(&cond.consequent, ctx);
5228 }
5229 return self.gen_expression(&cond.alternate, ctx);
5230 }
5231 }
5232
5233 if self.p2_fold_profile.allow_aggressive_truthy() {
5234 if let Expression::Literal(lit) = &*cond.test {
5235 if let Some(truthy) = Self::literal_truthiness(lit) {
5236 if truthy {
5237 return self.gen_expression(&cond.consequent, ctx);
5238 }
5239 return self.gen_expression(&cond.alternate, ctx);
5240 }
5241 }
5242 }
5243
5244 let test = self.gen_expression(&cond.test, ctx)?;
5245
5246 if self.opt_branch_result_prealloc {
5247 let result = if self.reserved_slots.contains(&test) {
5248 let dst = self.alloc_register();
5249 self.emit(Opcode::Move);
5250 self.emit_u16(dst);
5251 self.emit_u16(test);
5252 dst
5253 } else {
5254 test
5255 };
5256
5257 self.emit(Opcode::JumpIfNot);
5258 self.emit_u16(test);
5259 let else_jump = self.code.len();
5260 self.emit_i32(0);
5261
5262 let cons = self.gen_expression(&cond.consequent, ctx)?;
5263 if cons != result {
5264 self.emit(Opcode::Move);
5265 self.emit_u16(result);
5266 self.emit_u16(cons);
5267 self.free_register(cons);
5268 }
5269
5270 self.emit(Opcode::Jump);
5271 let end_jump = self.code.len();
5272 self.emit_i32(0);
5273
5274 let else_pos = self.code.len();
5275 self.patch_jump(else_jump, else_pos);
5276
5277 let alt = self.gen_expression(&cond.alternate, ctx)?;
5278 if alt != result {
5279 self.emit(Opcode::Move);
5280 self.emit_u16(result);
5281 self.emit_u16(alt);
5282 self.free_register(alt);
5283 }
5284
5285 let end_pos = self.code.len();
5286 self.patch_jump(end_jump, end_pos);
5287 return Ok(result);
5288 }
5289
5290 self.emit(Opcode::JumpIfNot);
5291 self.emit_u16(test);
5292 let else_jump = self.code.len();
5293 self.emit_i32(0);
5294
5295 let cons = self.gen_expression(&cond.consequent, ctx)?;
5296
5297 let result = if self.reserved_slots.contains(&test) && cons != test {
5298 let dst = self.alloc_register();
5299 self.emit(Opcode::Move);
5300 self.emit_u16(dst);
5301 self.emit_u16(cons);
5302 self.free_register(cons);
5303 dst
5304 } else if cons != test {
5305 self.emit(Opcode::Move);
5306 self.emit_u16(test);
5307 self.emit_u16(cons);
5308 self.free_register(cons);
5309 test
5310 } else {
5311 test
5312 };
5313
5314 self.emit(Opcode::Jump);
5315 let end_jump = self.code.len();
5316 self.emit_i32(0);
5317
5318 let else_pos = self.code.len();
5319 self.patch_jump(else_jump, else_pos);
5320
5321 let alt = self.gen_expression(&cond.alternate, ctx)?;
5322 if self.reserved_slots.contains(&test) && alt != result && alt != test {
5323 self.emit(Opcode::Move);
5324 self.emit_u16(result);
5325 self.emit_u16(alt);
5326 self.free_register(alt);
5327 } else if alt != result && alt != test {
5328 self.emit(Opcode::Move);
5329 self.emit_u16(result);
5330 self.emit_u16(alt);
5331 self.free_register(alt);
5332 }
5333
5334 let end_pos = self.code.len();
5335 self.patch_jump(end_jump, end_pos);
5336
5337 Ok(result)
5338 }
5339
5340 fn gen_member_expression(
5341 &mut self,
5342 member: &MemberExpression,
5343 ctx: &mut JSContext,
5344 ) -> Result<u16, String> {
5345 let obj = self.gen_expression(&member.object, ctx)?;
5346
5347 if matches!(&*member.object, Expression::Super) && !self.in_static_method {
5348 let proto_key = self.alloc_register();
5349 let proto_key_idx = self.add_constant(JSValue::new_string(ctx.common_atoms.prototype));
5350 self.emit(Opcode::LoadConst);
5351 self.emit_u16(proto_key);
5352 self.emit_u32(proto_key_idx);
5353 self.emit(Opcode::GetProp);
5354 self.emit_u16(obj);
5355 self.emit_u16(obj);
5356 self.emit_u16(proto_key);
5357 self.free_register(proto_key);
5358 }
5359 let dst = self.alloc_register();
5360 match &member.property {
5361 MemberProperty::Identifier(name) => {
5362 let atom = ctx.intern(name);
5363 self.emit(Opcode::GetNamedProp);
5364 self.emit_u16(dst);
5365 self.emit_u16(obj);
5366 self.emit_u32(atom.0);
5367 }
5368 MemberProperty::Computed(expr) => {
5369 let key = self.gen_expression(expr, ctx)?;
5370 self.emit(Opcode::GetField);
5371 self.emit_u16(dst);
5372 self.emit_u16(obj);
5373 self.emit_u16(key);
5374 }
5375 MemberProperty::PrivateIdentifier(name) => {
5376 let key_idx = self.intern_and_add_const(ctx, name);
5377 let key = self.alloc_register();
5378 self.emit(Opcode::LoadConst);
5379 self.emit_u16(key);
5380 self.emit_u32(key_idx);
5381 self.emit(Opcode::GetPrivate);
5382 self.emit_u16(dst);
5383 self.emit_u16(obj);
5384 self.emit_u16(key);
5385 self.free_register(key);
5386 }
5387 }
5388 self.free_register(obj);
5389 Ok(dst)
5390 }
5391
5392 fn gen_call_expression(
5393 &mut self,
5394 call: &CallExpression,
5395 ctx: &mut JSContext,
5396 ) -> Result<u16, String> {
5397 if self.opt_tiny_inline {
5398 if let Some(reg) = self.try_emit_tiny_inline_call(call, ctx)? {
5399 return Ok(reg);
5400 }
5401 }
5402
5403 if self.opt_fused_getprop_call {
5404 if let Expression::MemberExpression(member) = &*call.callee {
5405 if !call
5406 .arguments
5407 .iter()
5408 .any(|arg| matches!(arg, Argument::Spread(_)))
5409 {
5410 if let MemberProperty::Identifier(name) = &member.property {
5411 let is_namespace = if let Expression::Identifier(ident) = &*member.object {
5412 matches!(
5413 ident.name.as_str(),
5414 "Math"
5415 | "Object"
5416 | "JSON"
5417 | "Number"
5418 | "Boolean"
5419 | "String"
5420 | "Array"
5421 | "RegExp"
5422 | "Error"
5423 | "Function"
5424 | "Promise"
5425 | "Symbol"
5426 | "Map"
5427 | "Set"
5428 | "WeakMap"
5429 | "WeakSet"
5430 | "BigInt"
5431 | "Date"
5432 | "Reflect"
5433 | "Proxy"
5434 | "console"
5435 )
5436 } else {
5437 false
5438 };
5439
5440 if !is_namespace {
5441 let obj_reg = self.gen_expression(&member.object, ctx)?;
5442 let atom = ctx.intern(name);
5443
5444 let mut arg_regs = Vec::new();
5445 for arg in &call.arguments {
5446 if let Argument::Expression(expr) = arg {
5447 arg_regs.push(self.gen_expression(expr, ctx)?);
5448 }
5449 }
5450
5451 let dst = self.alloc_register();
5452 self.emit(Opcode::CallNamedMethod);
5453 self.emit_u16(dst);
5454 self.emit_u16(obj_reg);
5455 self.emit_u32(atom.0);
5456 self.emit_u16(arg_regs.len() as u16);
5457 for r in &arg_regs {
5458 self.emit_u16(*r);
5459 }
5460
5461 self.free_register(obj_reg);
5462 for r in arg_regs {
5463 self.free_register(r);
5464 }
5465 return Ok(dst);
5466 }
5467 }
5468 }
5469 }
5470 }
5471
5472 let is_self_recursive_call = matches!(&*call.callee, Expression::Identifier(id)
5473 if self.current_function_name.as_ref().map_or(false, |n| n == &id.name)
5474 && self.lookup_var_slot(&id.name).is_none()
5475 && !self.upvalue_slots.contains_key(&id.name));
5476
5477 let is_namespace_call = if let Expression::MemberExpression(member) = &*call.callee {
5478 if let Expression::Identifier(ident) = &*member.object {
5479 matches!(
5480 ident.name.as_str(),
5481 "Math"
5482 | "Object"
5483 | "JSON"
5484 | "Number"
5485 | "Boolean"
5486 | "String"
5487 | "Array"
5488 | "RegExp"
5489 | "Error"
5490 | "Function"
5491 | "Promise"
5492 | "Symbol"
5493 | "Map"
5494 | "Set"
5495 | "WeakMap"
5496 | "WeakSet"
5497 | "BigInt"
5498 | "Date"
5499 | "Reflect"
5500 | "Proxy"
5501 | "console"
5502 )
5503 } else {
5504 false
5505 }
5506 } else {
5507 false
5508 };
5509
5510 let has_spread = call
5511 .arguments
5512 .iter()
5513 .any(|arg| matches!(arg, Argument::Spread(_)));
5514
5515 if is_self_recursive_call && !has_spread {
5516 let mut arg_regs = Vec::new();
5517 for arg in &call.arguments {
5518 if let Argument::Expression(expr) = arg {
5519 arg_regs.push(self.gen_expression(expr, ctx)?);
5520 }
5521 }
5522 let dst = self.alloc_register();
5523 if arg_regs.len() == 1 {
5524 self.emit(Opcode::CallCurrent1);
5525 self.emit_u16(dst);
5526 self.emit_u16(arg_regs[0]);
5527 } else {
5528 self.emit(Opcode::CallCurrent);
5529 self.emit_u16(dst);
5530 self.emit_u16(arg_regs.len() as u16);
5531 for r in &arg_regs {
5532 self.emit_u16(*r);
5533 }
5534 }
5535 for r in arg_regs {
5536 self.free_register(r);
5537 }
5538 return Ok(dst);
5539 }
5540
5541 if !has_spread {
5542 if let Expression::MemberExpression(member) = &*call.callee {
5543 if let Expression::Identifier(ident) = &*member.object {
5544 if ident.name == "Math" {
5545 if let MemberProperty::Identifier(name) = &member.property {
5546 let math_op = match name.as_str() {
5547 "sin" if call.arguments.len() == 1 => Some(Opcode::MathSin),
5548 "cos" if call.arguments.len() == 1 => Some(Opcode::MathCos),
5549 "sqrt" if call.arguments.len() == 1 => Some(Opcode::MathSqrt),
5550 "abs" if call.arguments.len() == 1 => Some(Opcode::MathAbs),
5551 "floor" if call.arguments.len() == 1 => Some(Opcode::MathFloor),
5552 "ceil" if call.arguments.len() == 1 => Some(Opcode::MathCeil),
5553 "round" if call.arguments.len() == 1 => Some(Opcode::MathRound),
5554 "pow" if call.arguments.len() == 2 => Some(Opcode::MathPow),
5555 "min" if call.arguments.len() == 2 => Some(Opcode::MathMin),
5556 "max" if call.arguments.len() == 2 => Some(Opcode::MathMax),
5557 _ => None,
5558 };
5559 if let Some(op) = math_op {
5560 let mut arg_regs = Vec::new();
5561 for arg in &call.arguments {
5562 if let Argument::Expression(expr) = arg {
5563 arg_regs.push(self.gen_expression(expr, ctx)?);
5564 }
5565 }
5566 let dst = self.alloc_register();
5567 self.emit(op);
5568 self.emit_u16(dst);
5569 for r in &arg_regs {
5570 self.emit_u16(*r);
5571 }
5572 for r in arg_regs {
5573 self.free_register(r);
5574 }
5575 return Ok(dst);
5576 }
5577 }
5578 }
5579 }
5580 }
5581 }
5582
5583 let (func, obj, namespace_obj_reg) = match &*call.callee {
5584 Expression::MemberExpression(member) => {
5585 let obj_reg = self.gen_expression(&member.object, ctx)?;
5586 let func_reg = match &member.property {
5587 MemberProperty::Identifier(name) => {
5588 let atom = ctx.intern(name);
5589 let f = self.alloc_register();
5590 self.emit(Opcode::GetNamedProp);
5591 self.emit_u16(f);
5592 self.emit_u16(obj_reg);
5593 self.emit_u32(atom.0);
5594 f
5595 }
5596 MemberProperty::PrivateIdentifier(name) => {
5597 let key_idx = self.intern_and_add_const(ctx, name);
5598 let key = self.alloc_register();
5599 self.emit(Opcode::LoadConst);
5600 self.emit_u16(key);
5601 self.emit_u32(key_idx);
5602 let f = self.alloc_register();
5603 self.emit(Opcode::GetPrivate);
5604 self.emit_u16(f);
5605 self.emit_u16(obj_reg);
5606 self.emit_u16(key);
5607 self.free_register(key);
5608 f
5609 }
5610 MemberProperty::Computed(expr) => {
5611 let key = self.gen_expression(expr, ctx)?;
5612 let f = self.alloc_register();
5613 self.emit(Opcode::GetField);
5614 self.emit_u16(f);
5615 self.emit_u16(obj_reg);
5616 self.emit_u16(key);
5617 self.free_register(key);
5618 f
5619 }
5620 };
5621 if is_namespace_call {
5622 (func_reg, None, Some(obj_reg))
5623 } else {
5624 (func_reg, Some(obj_reg), None)
5625 }
5626 }
5627 other => {
5628 let func_reg = self.gen_expression(other, ctx)?;
5629 (func_reg, None, None)
5630 }
5631 };
5632
5633 let dst = self.alloc_register();
5634 if has_spread {
5635 let arr_reg = self.alloc_register();
5636 self.emit(Opcode::NewArray);
5637 self.emit_u16(arr_reg);
5638 self.emit_u16(0);
5639 for arg in &call.arguments {
5640 match arg {
5641 Argument::Expression(expr) => {
5642 let val = self.gen_expression(expr, ctx)?;
5643 self.emit(Opcode::ArrayPush);
5644 self.emit_u16(arr_reg);
5645 self.emit_u16(val);
5646 self.free_register(val);
5647 }
5648 Argument::Spread(expr) => {
5649 let val = self.gen_expression(expr, ctx)?;
5650 self.emit(Opcode::ArrayExtend);
5651 self.emit_u16(arr_reg);
5652 self.emit_u16(val);
5653 self.free_register(val);
5654 }
5655 }
5656 }
5657 if let Some(obj_reg) = obj {
5658 self.emit(Opcode::CallMethodSpread);
5659 self.emit_u16(dst);
5660 self.emit_u16(obj_reg);
5661 self.emit_u16(func);
5662 self.emit_u16(arr_reg);
5663 } else {
5664 self.emit(Opcode::CallSpread);
5665 self.emit_u16(dst);
5666 self.emit_u16(func);
5667 self.emit_u16(arr_reg);
5668 }
5669 if let Some(obj_reg) = obj {
5670 self.free_register(obj_reg);
5671 }
5672 if let Some(reg) = namespace_obj_reg {
5673 self.free_register(reg);
5674 }
5675 self.free_register(func);
5676 self.free_register(arr_reg);
5677 } else {
5678 let mut arg_regs = Vec::new();
5679 for arg in &call.arguments {
5680 match arg {
5681 Argument::Expression(expr) => {
5682 arg_regs.push(self.gen_expression(expr, ctx)?);
5683 }
5684 _ => {}
5685 }
5686 }
5687 if let Some(obj_reg) = obj {
5688 self.emit(Opcode::CallMethod);
5689 self.emit_u16(dst);
5690 self.emit_u16(obj_reg);
5691 self.emit_u16(func);
5692 self.emit_u16(arg_regs.len() as u16);
5693 for r in &arg_regs {
5694 self.emit_u16(*r);
5695 }
5696 } else {
5697 match arg_regs.len() {
5698 0 => {
5699 self.emit(Opcode::Call0);
5700 self.emit_u16(dst);
5701 self.emit_u16(func);
5702 }
5703 1 => {
5704 self.emit(Opcode::Call1);
5705 self.emit_u16(dst);
5706 self.emit_u16(func);
5707 self.emit_u16(arg_regs[0]);
5708 }
5709 2 => {
5710 if self.opt_call23 {
5711 self.emit(Opcode::Call2);
5712 self.emit_u16(dst);
5713 self.emit_u16(func);
5714 self.emit_u16(arg_regs[0]);
5715 self.emit_u16(arg_regs[1]);
5716 } else {
5717 self.emit(Opcode::Call);
5718 self.emit_u16(dst);
5719 self.emit_u16(func);
5720 self.emit_u16(2);
5721 self.emit_u16(arg_regs[0]);
5722 self.emit_u16(arg_regs[1]);
5723 }
5724 }
5725 3 => {
5726 if self.opt_call23 {
5727 self.emit(Opcode::Call3);
5728 self.emit_u16(dst);
5729 self.emit_u16(func);
5730 self.emit_u16(arg_regs[0]);
5731 self.emit_u16(arg_regs[1]);
5732 self.emit_u16(arg_regs[2]);
5733 } else {
5734 self.emit(Opcode::Call);
5735 self.emit_u16(dst);
5736 self.emit_u16(func);
5737 self.emit_u16(3);
5738 self.emit_u16(arg_regs[0]);
5739 self.emit_u16(arg_regs[1]);
5740 self.emit_u16(arg_regs[2]);
5741 }
5742 }
5743 _ => {
5744 self.emit(Opcode::Call);
5745 self.emit_u16(dst);
5746 self.emit_u16(func);
5747 self.emit_u16(arg_regs.len() as u16);
5748 for r in &arg_regs {
5749 self.emit_u16(*r);
5750 }
5751 }
5752 }
5753 }
5754 if let Some(obj_reg) = obj {
5755 self.free_register(obj_reg);
5756 }
5757 if let Some(reg) = namespace_obj_reg {
5758 self.free_register(reg);
5759 }
5760 self.free_register(func);
5761 for r in arg_regs {
5762 self.free_register(r);
5763 }
5764 }
5765 Ok(dst)
5766 }
5767
5768 fn gen_new_expression(
5769 &mut self,
5770 new: &NewExpression,
5771 ctx: &mut JSContext,
5772 ) -> Result<u16, String> {
5773 let func = self.gen_expression(&new.callee, ctx)?;
5774 let has_spread = new
5775 .arguments
5776 .iter()
5777 .any(|arg| matches!(arg, Argument::Spread(_)));
5778 let dst = self.alloc_register();
5779 if has_spread {
5780 let arr_reg = self.alloc_register();
5781 self.emit(Opcode::NewArray);
5782 self.emit_u16(arr_reg);
5783 self.emit_u16(0);
5784 for arg in &new.arguments {
5785 match arg {
5786 Argument::Expression(expr) => {
5787 let val = self.gen_expression(expr, ctx)?;
5788 self.emit(Opcode::ArrayPush);
5789 self.emit_u16(arr_reg);
5790 self.emit_u16(val);
5791 self.free_register(val);
5792 }
5793 Argument::Spread(expr) => {
5794 let val = self.gen_expression(expr, ctx)?;
5795 self.emit(Opcode::ArrayExtend);
5796 self.emit_u16(arr_reg);
5797 self.emit_u16(val);
5798 self.free_register(val);
5799 }
5800 }
5801 }
5802 self.emit(Opcode::CallNewSpread);
5803 self.emit_u16(dst);
5804 self.emit_u16(func);
5805 self.emit_u16(arr_reg);
5806 self.free_register(func);
5807 self.free_register(arr_reg);
5808 } else {
5809 let mut arg_regs = Vec::new();
5810 for arg in &new.arguments {
5811 match arg {
5812 Argument::Expression(expr) => {
5813 arg_regs.push(self.gen_expression(expr, ctx)?);
5814 }
5815 _ => {}
5816 }
5817 }
5818 self.emit(Opcode::CallNew);
5819 self.emit_u16(dst);
5820 self.emit_u16(func);
5821 self.emit_u16(arg_regs.len() as u16);
5822 for r in &arg_regs {
5823 self.emit_u16(*r);
5824 }
5825 self.free_register(func);
5826 for r in arg_regs {
5827 self.free_register(r);
5828 }
5829 }
5830 Ok(dst)
5831 }
5832
5833 fn gen_array_expression(
5834 &mut self,
5835 arr: &ArrayExpression,
5836 ctx: &mut JSContext,
5837 ) -> Result<u16, String> {
5838 let dst = self.alloc_register();
5839 let count = arr.elements.len() as u16;
5840 self.emit(Opcode::NewArray);
5841 self.emit_u16(dst);
5842 self.emit_u16(count);
5843
5844 let has_holes = arr.elements.iter().any(|e| e.is_none());
5845 let has_spread = arr
5846 .elements
5847 .iter()
5848 .any(|e| matches!(e, Some(ArrayElement::Spread(_))));
5849
5850 if !has_holes && !has_spread {
5851 for elem in &arr.elements {
5852 if let Some(ArrayElement::Expression(expr)) = elem {
5853 let val = self.gen_expression(expr, ctx)?;
5854 self.emit(Opcode::ArrayPush);
5855 self.emit_u16(dst);
5856 self.emit_u16(val);
5857 self.free_register(val);
5858 }
5859 }
5860 return Ok(dst);
5861 }
5862
5863 let idx_reg = self.alloc_register();
5864 self.emit_int_const(idx_reg, 0);
5865
5866 for elem in &arr.elements {
5867 match elem {
5868 Some(ArrayElement::Expression(expr)) => {
5869 let val = self.gen_expression(expr, ctx)?;
5870 self.emit(Opcode::SetField);
5871 self.emit_u16(dst);
5872 self.emit_u16(idx_reg);
5873 self.emit_u16(val);
5874 self.free_register(val);
5875 self.emit(Opcode::IncLocal);
5876 self.emit_u16(idx_reg);
5877 }
5878 None => {
5879 self.emit(Opcode::IncLocal);
5880 self.emit_u16(idx_reg);
5881 }
5882 Some(ArrayElement::Spread(expr)) => {
5883 let src = self.gen_expression(expr, ctx)?;
5884 self.emit(Opcode::ArrayExtend);
5885 self.emit_u16(dst);
5886 self.emit_u16(src);
5887 self.free_register(src);
5888
5889 let len_key = self.alloc_register();
5890 let len_atom = ctx.common_atoms.length;
5891 let len_const = self.add_constant(JSValue::new_string(len_atom));
5892 self.emit(Opcode::LoadConst);
5893 self.emit_u16(len_key);
5894 self.emit_u32(len_const);
5895 self.emit(Opcode::GetProp);
5896 self.emit_u16(idx_reg);
5897 self.emit_u16(dst);
5898 self.emit_u16(len_key);
5899 self.free_register(len_key);
5900 }
5901 }
5902 }
5903 let len_atom = ctx.common_atoms.length;
5904 let len_const = self.add_constant(JSValue::new_string(len_atom));
5905 let len_key = self.alloc_register();
5906 self.emit(Opcode::LoadConst);
5907 self.emit_u16(len_key);
5908 self.emit_u32(len_const);
5909 self.emit(Opcode::SetProp);
5910 self.emit_u16(dst);
5911 self.emit_u16(len_key);
5912 self.emit_u16(idx_reg);
5913 self.free_register(len_key);
5914 self.free_register(idx_reg);
5915 Ok(dst)
5916 }
5917
5918 fn gen_object_expression(
5919 &mut self,
5920 obj: &ObjectExpression,
5921 ctx: &mut JSContext,
5922 ) -> Result<u16, String> {
5923 let dst = self.alloc_register();
5924 self.emit(Opcode::NewObject);
5925 self.emit_u16(dst);
5926
5927 for prop in &obj.properties {
5928 match prop {
5929 Property::Property {
5930 key,
5931 value,
5932 getter,
5933 setter,
5934 ..
5935 } => {
5936 if *getter || *setter {
5937 let func_reg = self.gen_expression(value, ctx)?;
5938 let key_reg = match key {
5939 PropertyKey::Identifier(name)
5940 | PropertyKey::Literal(Literal::String(name, _)) => {
5941 let atom = ctx.intern(name);
5942 let idx = self.add_constant(JSValue::new_string(atom));
5943 let k = self.alloc_register();
5944 self.emit(Opcode::LoadConst);
5945 self.emit_u16(k);
5946 self.emit_u32(idx);
5947 k
5948 }
5949 PropertyKey::Computed(expr) => self.gen_expression(expr, ctx)?,
5950 _ => {
5951 self.free_register(func_reg);
5952 continue;
5953 }
5954 };
5955 let getter_reg = if *getter { func_reg } else { u16::MAX };
5956 let setter_reg = if *setter { func_reg } else { u16::MAX };
5957 self.emit(Opcode::DefineAccessor);
5958 self.emit_u16(dst);
5959 self.emit_u16(key_reg);
5960 self.emit_u16(getter_reg);
5961 self.emit_u16(setter_reg);
5962 self.free_register(key_reg);
5963 self.free_register(func_reg);
5964 continue;
5965 }
5966 match key {
5967 PropertyKey::Identifier(name)
5968 | PropertyKey::Literal(Literal::String(name, _)) => {
5969 let val = self.gen_expression(value, ctx)?;
5970 let atom = ctx.intern(name);
5971 self.emit(Opcode::SetNamedProp);
5972 self.emit_u16(dst);
5973 self.emit_u16(val);
5974 self.emit_u32(atom.0);
5975 self.free_register(val);
5976 }
5977 _ => {
5978 let key_reg = match key {
5979 PropertyKey::Literal(Literal::Number(n)) => {
5980 let k = self.alloc_register();
5981 let nv = *n;
5982 if nv >= i32::MIN as f64
5983 && nv <= i32::MAX as f64
5984 && nv.fract() == 0.0
5985 {
5986 self.emit_int_const(k, nv as i32);
5987 } else {
5988 let idx = self.add_constant(JSValue::new_float(nv));
5989 self.emit(Opcode::LoadConst);
5990 self.emit_u16(k);
5991 self.emit_u32(idx);
5992 }
5993 k
5994 }
5995 PropertyKey::Computed(expr) => self.gen_expression(expr, ctx)?,
5996 _ => self.alloc_register(),
5997 };
5998 let val = self.gen_expression(value, ctx)?;
5999 self.emit(Opcode::SetField);
6000 self.emit_u16(dst);
6001 self.emit_u16(key_reg);
6002 self.emit_u16(val);
6003 self.free_register(key_reg);
6004 self.free_register(val);
6005 }
6006 }
6007 }
6008 Property::SpreadElement(expr) => {
6009 let src = self.gen_expression(expr, ctx)?;
6010 self.emit(Opcode::ObjectSpread);
6011 self.emit_u16(dst);
6012 self.emit_u16(src);
6013 self.free_register(src);
6014 }
6015 }
6016 }
6017 Ok(dst)
6018 }
6019
6020 fn gen_function_expression(
6021 &mut self,
6022 params: &[Parameter],
6023 body: &BlockStatement,
6024 name: Option<&str>,
6025 ctx: &mut JSContext,
6026 is_generator: bool,
6027 is_async: bool,
6028 is_arrow: bool,
6029 ) -> Result<u16, String> {
6030 let mut nested_codegen = self.nested_codegen();
6031 nested_codegen.is_arrow = is_arrow;
6032 let mut parent_vars: FxHashMap<String, ParentVar> = FxHashMap::default();
6033 for (scope_idx, scope) in self.scope_stack.iter().enumerate() {
6034 if self
6035 .scope_is_global
6036 .get(scope_idx)
6037 .copied()
6038 .unwrap_or(false)
6039 {
6040 continue;
6041 }
6042 for (name, entry) in scope {
6043 parent_vars.insert(
6044 name.clone(),
6045 ParentVar {
6046 local_idx: entry.slot,
6047 is_inherited: false,
6048 },
6049 );
6050 }
6051 }
6052 for (name, &slot) in &self.upvalue_slots {
6053 if !parent_vars.contains_key(name) {
6054 parent_vars.insert(
6055 name.clone(),
6056 ParentVar {
6057 local_idx: slot,
6058 is_inherited: true,
6059 },
6060 );
6061 }
6062 }
6063 let (bytecode, upvalues) = nested_codegen
6064 .compile_function_with_parent(params, body, ctx, parent_vars, name, false)
6065 .map_err(|e| format!("nested function compile error: {}", e))?;
6066
6067 let func_name_atom = name.map(|n| ctx.atom_table_mut().intern(n).0).unwrap_or(0);
6068 let dst = if let Some(name) = name {
6069 self.lookup_var_slot(name)
6070 .unwrap_or_else(|| self.alloc_register())
6071 } else {
6072 self.alloc_register()
6073 };
6074 self.emit_embedded_function(
6075 &bytecode,
6076 &upvalues,
6077 func_name_atom,
6078 dst,
6079 is_generator,
6080 is_async,
6081 );
6082 Ok(dst)
6083 }
6084
6085 fn gen_class_declaration(
6086 &mut self,
6087 class: &ClassDeclaration,
6088 ctx: &mut JSContext,
6089 ) -> Result<(), String> {
6090 let class_name = &class.name;
6091 let class_slot = self
6092 .lookup_var_slot(class_name)
6093 .unwrap_or_else(|| self.declare_var(class_name, VariableKind::Let));
6094 let class_name_atom =
6095 self.gen_class_body(class_slot, class_name, &class.super_class, &class.body, ctx)?;
6096 if self.is_global_scope() && !self.is_eval {
6097 let class_idx = self.add_constant(JSValue::new_string(class_name_atom));
6098 self.emit(Opcode::SetGlobal);
6099 self.emit_u32(class_idx);
6100 self.emit_u16(class_slot);
6101 }
6102 Ok(())
6103 }
6104
6105 fn gen_class_body(
6106 &mut self,
6107 class_slot: u16,
6108 class_name: &str,
6109 super_class: &Option<Box<Expression>>,
6110 body: &ClassBody,
6111 ctx: &mut JSContext,
6112 ) -> Result<crate::runtime::atom::Atom, String> {
6113 let has_super = super_class.is_some();
6114
6115 let ctor_method = body.elements.iter().find(|e| {
6116 if let ClassElement::Method(m) = e {
6117 m.kind == MethodKind::Constructor && !m.is_static
6118 } else {
6119 false
6120 }
6121 });
6122
6123 let instance_fields: Vec<_> = body
6124 .elements
6125 .iter()
6126 .filter_map(|e| {
6127 if let ClassElement::Property(p) = e {
6128 if !p.is_static {
6129 return Some(p.clone());
6130 }
6131 }
6132 None
6133 })
6134 .collect();
6135
6136 let field_init_stmts: Vec<ASTNode> = instance_fields
6137 .iter()
6138 .filter_map(|f| {
6139 let value = f
6140 .value
6141 .clone()
6142 .unwrap_or(Expression::Literal(Literal::Undefined));
6143 let (property, computed) = match &f.name {
6144 PropertyKey::Identifier(n) if f.is_private => {
6145 (MemberProperty::PrivateIdentifier(n.clone()), false)
6146 }
6147 PropertyKey::Identifier(n) => (MemberProperty::Identifier(n.clone()), false),
6148 PropertyKey::Literal(Literal::String(s, _)) => {
6149 (MemberProperty::Identifier(s.clone()), false)
6150 }
6151 PropertyKey::PrivateIdentifier(n) => {
6152 (MemberProperty::PrivateIdentifier(n.clone()), false)
6153 }
6154 PropertyKey::Computed(expr) => (MemberProperty::Computed(expr.clone()), true),
6155 _ => return None,
6156 };
6157 Some(ASTNode::ExpressionStatement(ExpressionStatement {
6158 expression: Expression::AssignmentExpression(AssignmentExpression {
6159 op: AssignOp::Assign,
6160 left: AssignmentTarget::MemberExpression(MemberExpression {
6161 object: Box::new(Expression::This),
6162 property,
6163 computed,
6164 }),
6165 right: Box::new(value),
6166 }),
6167 }))
6168 })
6169 .collect();
6170
6171 let mut class_parent_vars = FxHashMap::default();
6172
6173 for (scope_idx, scope) in self.scope_stack.iter().enumerate() {
6174 if self
6175 .scope_is_global
6176 .get(scope_idx)
6177 .copied()
6178 .unwrap_or(false)
6179 {
6180 continue;
6181 }
6182 for (var_name, entry) in scope {
6183 class_parent_vars.insert(
6184 var_name.clone(),
6185 ParentVar {
6186 local_idx: entry.slot,
6187 is_inherited: false,
6188 },
6189 );
6190 }
6191 }
6192
6193 for (var_name, &slot) in &self.upvalue_slots {
6194 if !class_parent_vars.contains_key(var_name) {
6195 class_parent_vars.insert(
6196 var_name.clone(),
6197 ParentVar {
6198 local_idx: slot,
6199 is_inherited: true,
6200 },
6201 );
6202 }
6203 }
6204
6205 if !class_name.is_empty() {
6206 class_parent_vars.insert(
6207 class_name.to_string(),
6208 ParentVar {
6209 local_idx: class_slot,
6210 is_inherited: false,
6211 },
6212 );
6213 }
6214
6215 let (ctor_bytecode, ctor_upvalues) = if let Some(ClassElement::Method(m)) = ctor_method {
6216 let mut body = m.body.clone();
6217 let insert_pos = 0;
6218 for (i, stmt) in field_init_stmts.into_iter().enumerate() {
6219 body.body.insert(insert_pos + i, stmt);
6220 body.lines.insert(insert_pos + i, 0);
6221 }
6222 let mut nested = self.nested_codegen();
6223 nested.compile_function_with_parent(
6224 &m.params,
6225 &body,
6226 ctx,
6227 class_parent_vars.clone(),
6228 None,
6229 false,
6230 )?
6231 } else {
6232 let mut default_body = Vec::new();
6233 let mut default_lines = Vec::new();
6234 if has_super {
6235 let super_call = ASTNode::ExpressionStatement(ExpressionStatement {
6236 expression: Expression::CallExpression(CallExpression {
6237 callee: Box::new(Expression::Super),
6238 arguments: vec![Argument::Spread(Expression::Identifier(Identifier {
6239 name: "arguments".to_string(),
6240 }))],
6241 }),
6242 });
6243 default_body.push(super_call);
6244 default_lines.push(0);
6245 }
6246 default_body.extend(field_init_stmts);
6247 default_lines.extend(vec![0; instance_fields.len()]);
6248 let mut nested = self.nested_codegen();
6249 nested.compile_function_with_parent(
6250 &[],
6251 &BlockStatement {
6252 body: default_body,
6253 lines: default_lines,
6254 },
6255 ctx,
6256 class_parent_vars.clone(),
6257 None,
6258 false,
6259 )?
6260 };
6261
6262 let mut instance_methods: Vec<(
6263 PropertyKey,
6264 Bytecode,
6265 Vec<(u32, u16)>,
6266 bool,
6267 bool,
6268 MethodKind,
6269 bool,
6270 )> = Vec::new();
6271 for element in &body.elements {
6272 if let ClassElement::Method(m) = element {
6273 if m.is_static || m.kind == MethodKind::Constructor {
6274 continue;
6275 }
6276 let can_handle = match &m.name {
6277 PropertyKey::Identifier(_)
6278 | PropertyKey::Literal(Literal::String(_, _))
6279 | PropertyKey::Literal(Literal::Number(_))
6280 | PropertyKey::PrivateIdentifier(_)
6281 | PropertyKey::Computed(_) => true,
6282 _ => false,
6283 };
6284 if !can_handle {
6285 continue;
6286 }
6287 let mut nested = self.nested_codegen();
6288 let (method_bc, method_upvalues) = nested.compile_function_with_parent(
6289 &m.params,
6290 &m.body,
6291 ctx,
6292 class_parent_vars.clone(),
6293 None,
6294 false,
6295 )?;
6296 instance_methods.push((
6297 m.name.clone(),
6298 method_bc,
6299 method_upvalues,
6300 m.generator,
6301 m.is_async,
6302 m.kind.clone(),
6303 m.is_private,
6304 ));
6305 }
6306 }
6307
6308 let mut static_methods: Vec<(
6309 PropertyKey,
6310 Bytecode,
6311 Vec<(u32, u16)>,
6312 bool,
6313 bool,
6314 MethodKind,
6315 bool,
6316 )> = Vec::new();
6317 for element in &body.elements {
6318 if let ClassElement::Method(m) = element {
6319 if !m.is_static {
6320 continue;
6321 }
6322 let can_handle = match &m.name {
6323 PropertyKey::Identifier(_)
6324 | PropertyKey::Literal(Literal::String(_, _))
6325 | PropertyKey::Literal(Literal::Number(_))
6326 | PropertyKey::PrivateIdentifier(_)
6327 | PropertyKey::Computed(_) => true,
6328 _ => false,
6329 };
6330 if !can_handle {
6331 continue;
6332 }
6333 let mut nested = self.nested_codegen();
6334 nested.in_static_method = true;
6335 let (method_bc, method_upvalues) = nested.compile_function_with_parent(
6336 &m.params,
6337 &m.body,
6338 ctx,
6339 class_parent_vars.clone(),
6340 None,
6341 false,
6342 )?;
6343 static_methods.push((
6344 m.name.clone(),
6345 method_bc,
6346 method_upvalues,
6347 m.generator,
6348 m.is_async,
6349 m.kind.clone(),
6350 m.is_private,
6351 ));
6352 }
6353 }
6354
6355 let mut static_blocks: Vec<(Bytecode, Vec<(u32, u16)>)> = Vec::new();
6356 for element in &body.elements {
6357 if let ClassElement::StaticBlock(block) = element {
6358 let mut nested = self.nested_codegen();
6359 let (block_bc, block_upvalues) = nested.compile_function_with_parent(
6360 &[],
6361 &BlockStatement {
6362 body: block.body.clone(),
6363 lines: block.lines.clone(),
6364 },
6365 ctx,
6366 class_parent_vars.clone(),
6367 None,
6368 false,
6369 )?;
6370 static_blocks.push((block_bc, block_upvalues));
6371 }
6372 }
6373
6374 let ctor_reg = self.alloc_register();
6375 let class_name_atom = ctx.atom_table_mut().intern(class_name);
6376 let ctor_name_atom = class_name_atom.0;
6377 self.emit_embedded_function(
6378 &ctor_bytecode,
6379 &ctor_upvalues,
6380 ctor_name_atom,
6381 ctor_reg,
6382 false,
6383 false,
6384 );
6385
6386 if let Some(super_expr) = super_class {
6387 let super_key_idx = self.add_constant(JSValue::new_string(ctx.common_atoms.__super__));
6388 let super_key = self.alloc_register();
6389 self.emit(Opcode::LoadConst);
6390 self.emit_u16(super_key);
6391 self.emit_u32(super_key_idx);
6392 let super_val = self.gen_expression(super_expr, ctx)?;
6393 self.emit(Opcode::SetProp);
6394 self.emit_u16(ctor_reg);
6395 self.emit_u16(super_key);
6396 self.emit_u16(super_val);
6397 self.free_register(super_key);
6398 self.free_register(super_val);
6399 }
6400
6401 if class_slot != ctor_reg {
6402 self.emit(Opcode::Move);
6403 self.emit_u16(class_slot);
6404 self.emit_u16(ctor_reg);
6405 }
6406
6407 let proto_reg = self.alloc_register();
6408 self.emit(Opcode::NewObject);
6409 self.emit_u16(proto_reg);
6410
6411 if let Some(super_expr) = super_class {
6412 let proto_key_idx = self.add_constant(JSValue::new_string(ctx.common_atoms.prototype));
6413 let proto_key = self.alloc_register();
6414 self.emit(Opcode::LoadConst);
6415 self.emit_u16(proto_key);
6416 self.emit_u32(proto_key_idx);
6417 let super_val = self.gen_expression(super_expr, ctx)?;
6418 let super_proto = self.alloc_register();
6419 self.emit(Opcode::GetProp);
6420 self.emit_u16(super_proto);
6421 self.emit_u16(super_val);
6422 self.emit_u16(proto_key);
6423 self.free_register(super_val);
6424 self.emit(Opcode::SetProto);
6425 self.emit_u16(proto_reg);
6426 self.emit_u16(super_proto);
6427 self.free_register(super_proto);
6428 self.free_register(proto_key);
6429 }
6430
6431 for (method_key, method_bc, method_upvalues, is_gen, is_async, kind, is_private) in
6432 instance_methods
6433 {
6434 let key = self.alloc_register();
6435 let method_name_atom = match &method_key {
6436 PropertyKey::Identifier(n) | PropertyKey::Literal(Literal::String(n, _)) => {
6437 let method_atom = ctx.atom_table_mut().intern(n.as_str());
6438 let method_idx = self.add_constant(JSValue::new_string(method_atom));
6439 self.emit(Opcode::LoadConst);
6440 self.emit_u16(key);
6441 self.emit_u32(method_idx);
6442 method_atom.0
6443 }
6444 PropertyKey::Literal(Literal::Number(n)) => {
6445 let s = if n.fract() == 0.0 && *n >= i32::MIN as f64 && *n <= i32::MAX as f64 {
6446 (*n as i32).to_string()
6447 } else {
6448 f64_to_js_string(*n)
6449 };
6450 let method_atom = ctx.atom_table_mut().intern(&s);
6451 let method_idx = self.add_constant(JSValue::new_string(method_atom));
6452 self.emit(Opcode::LoadConst);
6453 self.emit_u16(key);
6454 self.emit_u32(method_idx);
6455 method_atom.0
6456 }
6457 PropertyKey::PrivateIdentifier(n) => {
6458 let method_atom = ctx.atom_table_mut().intern(n.as_str());
6459 let method_idx = self.add_constant(JSValue::new_string(method_atom));
6460 self.emit(Opcode::LoadConst);
6461 self.emit_u16(key);
6462 self.emit_u32(method_idx);
6463 method_atom.0
6464 }
6465 PropertyKey::Computed(expr) => {
6466 let k = self.gen_expression(expr, ctx)?;
6467 self.emit(Opcode::Move);
6468 self.emit_u16(key);
6469 self.emit_u16(k);
6470 self.free_register(k);
6471 0
6472 }
6473 _ => {
6474 self.free_register(key);
6475 continue;
6476 }
6477 };
6478 let method_reg = self.alloc_register();
6479 self.emit_embedded_function(
6480 &method_bc,
6481 &method_upvalues,
6482 method_name_atom,
6483 method_reg,
6484 is_gen,
6485 is_async,
6486 );
6487 match kind {
6488 MethodKind::Get if is_private => {
6489 self.emit(Opcode::DefinePrivateAccessor);
6490 self.emit_u16(proto_reg);
6491 self.emit_u16(key);
6492 self.emit_u16(method_reg);
6493 self.emit_u16(u16::MAX);
6494 }
6495 MethodKind::Set if is_private => {
6496 self.emit(Opcode::DefinePrivateAccessor);
6497 self.emit_u16(proto_reg);
6498 self.emit_u16(key);
6499 self.emit_u16(u16::MAX);
6500 self.emit_u16(method_reg);
6501 }
6502 MethodKind::Get => {
6503 self.emit(Opcode::DefineAccessor);
6504 self.emit_u16(proto_reg);
6505 self.emit_u16(key);
6506 self.emit_u16(method_reg);
6507 self.emit_u16(u16::MAX);
6508 }
6509 MethodKind::Set => {
6510 self.emit(Opcode::DefineAccessor);
6511 self.emit_u16(proto_reg);
6512 self.emit_u16(key);
6513 self.emit_u16(u16::MAX);
6514 self.emit_u16(method_reg);
6515 }
6516 _ => {
6517 if is_private {
6518 self.emit(Opcode::SetPrivate);
6519 self.emit_u16(proto_reg);
6520 self.emit_u16(key);
6521 self.emit_u16(method_reg);
6522 } else {
6523 self.emit(Opcode::SetMethodProp);
6524 self.emit_u16(proto_reg);
6525 self.emit_u16(key);
6526 self.emit_u16(method_reg);
6527 }
6528 }
6529 }
6530 self.free_register(key);
6531 self.free_register(method_reg);
6532 }
6533
6534 for (method_key, method_bc, method_upvalues, is_gen, is_async, kind, is_private) in
6535 static_methods
6536 {
6537 let key = self.alloc_register();
6538 let method_name_atom = match &method_key {
6539 PropertyKey::Identifier(n) | PropertyKey::Literal(Literal::String(n, _)) => {
6540 let method_atom = ctx.atom_table_mut().intern(n.as_str());
6541 let method_idx = self.add_constant(JSValue::new_string(method_atom));
6542 self.emit(Opcode::LoadConst);
6543 self.emit_u16(key);
6544 self.emit_u32(method_idx);
6545 method_atom.0
6546 }
6547 PropertyKey::Literal(Literal::Number(n)) => {
6548 let s = if n.fract() == 0.0 && *n >= i32::MIN as f64 && *n <= i32::MAX as f64 {
6549 (*n as i32).to_string()
6550 } else {
6551 f64_to_js_string(*n)
6552 };
6553 let method_atom = ctx.atom_table_mut().intern(&s);
6554 let method_idx = self.add_constant(JSValue::new_string(method_atom));
6555 self.emit(Opcode::LoadConst);
6556 self.emit_u16(key);
6557 self.emit_u32(method_idx);
6558 method_atom.0
6559 }
6560 PropertyKey::PrivateIdentifier(n) => {
6561 let method_atom = ctx.atom_table_mut().intern(n.as_str());
6562 let method_idx = self.add_constant(JSValue::new_string(method_atom));
6563 self.emit(Opcode::LoadConst);
6564 self.emit_u16(key);
6565 self.emit_u32(method_idx);
6566 method_atom.0
6567 }
6568 PropertyKey::Computed(expr) => {
6569 let k = self.gen_expression(expr, ctx)?;
6570 self.emit(Opcode::Move);
6571 self.emit_u16(key);
6572 self.emit_u16(k);
6573 self.free_register(k);
6574 0
6575 }
6576 _ => {
6577 self.free_register(key);
6578 continue;
6579 }
6580 };
6581 let method_reg = self.alloc_register();
6582 self.emit_embedded_function(
6583 &method_bc,
6584 &method_upvalues,
6585 method_name_atom,
6586 method_reg,
6587 is_gen,
6588 is_async,
6589 );
6590 match kind {
6591 MethodKind::Get if is_private => {
6592 self.emit(Opcode::DefinePrivateAccessor);
6593 self.emit_u16(class_slot);
6594 self.emit_u16(key);
6595 self.emit_u16(method_reg);
6596 self.emit_u16(u16::MAX);
6597 }
6598 MethodKind::Set if is_private => {
6599 self.emit(Opcode::DefinePrivateAccessor);
6600 self.emit_u16(class_slot);
6601 self.emit_u16(key);
6602 self.emit_u16(u16::MAX);
6603 self.emit_u16(method_reg);
6604 }
6605 MethodKind::Get => {
6606 self.emit(Opcode::DefineAccessor);
6607 self.emit_u16(class_slot);
6608 self.emit_u16(key);
6609 self.emit_u16(method_reg);
6610 self.emit_u16(u16::MAX);
6611 }
6612 MethodKind::Set => {
6613 self.emit(Opcode::DefineAccessor);
6614 self.emit_u16(class_slot);
6615 self.emit_u16(key);
6616 self.emit_u16(u16::MAX);
6617 self.emit_u16(method_reg);
6618 }
6619 _ => {
6620 if is_private {
6621 self.emit(Opcode::SetPrivate);
6622 self.emit_u16(class_slot);
6623 self.emit_u16(key);
6624 self.emit_u16(method_reg);
6625 } else {
6626 self.emit(Opcode::SetMethodProp);
6627 self.emit_u16(class_slot);
6628 self.emit_u16(key);
6629 self.emit_u16(method_reg);
6630 }
6631 }
6632 }
6633 self.free_register(key);
6634 self.free_register(method_reg);
6635 }
6636
6637 let proto_key_idx = self.add_constant(JSValue::new_string(ctx.common_atoms.prototype));
6638 let proto_key = self.alloc_register();
6639 self.emit(Opcode::LoadConst);
6640 self.emit_u16(proto_key);
6641 self.emit_u32(proto_key_idx);
6642 self.emit(Opcode::SetProp);
6643 self.emit_u16(class_slot);
6644 self.emit_u16(proto_key);
6645 self.emit_u16(proto_reg);
6646 self.free_register(proto_key);
6647 self.free_register(proto_reg);
6648
6649 for element in &body.elements {
6650 if let ClassElement::Property(p) = element {
6651 if !p.is_static {
6652 continue;
6653 }
6654 let val_reg = if let Some(value) = &p.value {
6655 self.gen_expression(value, ctx)?
6656 } else {
6657 let r = self.alloc_register();
6658 self.emit(Opcode::LoadUndefined);
6659 self.emit_u16(r);
6660 r
6661 };
6662 if let PropertyKey::Computed(key_expr) = &p.name {
6663 let key = self.gen_expression(key_expr, ctx)?;
6664 self.emit(Opcode::SetField);
6665 self.emit_u16(class_slot);
6666 self.emit_u16(key);
6667 self.emit_u16(val_reg);
6668 self.free_register(key);
6669 self.free_register(val_reg);
6670 } else {
6671 let field_name = match &p.name {
6672 PropertyKey::Identifier(n) => n.clone(),
6673 PropertyKey::Literal(Literal::String(s, _)) => s.clone(),
6674 PropertyKey::PrivateIdentifier(n) => n.clone(),
6675 _ => {
6676 self.free_register(val_reg);
6677 continue;
6678 }
6679 };
6680 let field_idx = self.intern_and_add_const(ctx, &field_name);
6681 let key = self.alloc_register();
6682 self.emit(Opcode::LoadConst);
6683 self.emit_u16(key);
6684 self.emit_u32(field_idx);
6685 if p.is_private {
6686 self.emit(Opcode::SetPrivate);
6687 } else {
6688 self.emit(Opcode::SetProp);
6689 }
6690 self.emit_u16(class_slot);
6691 self.emit_u16(key);
6692 self.emit_u16(val_reg);
6693 self.free_register(key);
6694 self.free_register(val_reg);
6695 }
6696 }
6697 }
6698
6699 for (block_bc, block_upvalues) in static_blocks {
6700 let block_reg = self.alloc_register();
6701 self.emit_embedded_function(&block_bc, &block_upvalues, 0, block_reg, false, false);
6702 self.emit(Opcode::Call);
6703 self.emit_u16(block_reg);
6704 self.emit_u16(block_reg);
6705 self.emit_u16(0);
6706 self.free_register(block_reg);
6707 }
6708
6709 Ok(class_name_atom)
6710 }
6711}
6712
6713pub(crate) fn detect_simple_constructor_props(
6714 bytecode: &crate::compiler::opcode::Bytecode,
6715) -> bool {
6716 let code = &bytecode.code;
6717 if code.is_empty() {
6718 return false;
6719 }
6720 let mut pc = 0usize;
6721 let mut found_return = false;
6722 let mut has_simple_prop = false;
6723 let pc_limit = bytecode.param_count;
6724 while pc < code.len() {
6725 let op = crate::compiler::opcode::Opcode::from_u8_unchecked(code[pc]);
6726 let size = crate::compiler::opcode::Opcode::instruction_size(op);
6727 if size == 0 || pc + size > code.len() {
6728 break;
6729 }
6730 match op {
6731 crate::compiler::opcode::Opcode::SetNamedProp => {
6732 let obj = u16::from_le_bytes([code[pc + 1], code[pc + 2]]);
6733 let val = u16::from_le_bytes([code[pc + 3], code[pc + 4]]);
6734 if val > pc_limit {
6735 return false;
6736 }
6737 if obj == 0 && val >= 1 {
6738 has_simple_prop = true;
6739 } else {
6740 return false;
6741 }
6742 }
6743 crate::compiler::opcode::Opcode::LoadUndefined => {
6744 let dst = u16::from_le_bytes([code[pc + 1], code[pc + 2]]);
6745 if dst != 0 {
6746 return false;
6747 }
6748 }
6749 crate::compiler::opcode::Opcode::Return => {
6750 let src = u16::from_le_bytes([code[pc + 1], code[pc + 2]]);
6751 if src == 0 {
6752 found_return = true;
6753 }
6754 break;
6755 }
6756 _ => {
6757 if op != crate::compiler::opcode::Opcode::Nop {
6758 return false;
6759 }
6760 }
6761 }
6762 pc += size;
6763 }
6764 found_return && has_simple_prop
6765}
6766
6767fn f64_to_js_string(n: f64) -> String {
6768 if n.is_nan() {
6769 return "NaN".to_string();
6770 }
6771 if n.is_infinite() {
6772 if n.is_sign_negative() {
6773 return "-Infinity".to_string();
6774 }
6775 return "Infinity".to_string();
6776 }
6777 let abs = n.abs();
6778 if abs == 0.0 {
6779 if n.is_sign_negative() {
6780 return "0".to_string();
6781 }
6782 return "0".to_string();
6783 }
6784 if abs < 1e-6 || abs >= 1e21 {
6785 let s = format!("{:e}", n);
6786 s
6787 } else {
6788 n.to_string()
6789 }
6790}
6791
6792#[cfg(test)]
6793mod tests {
6794 use super::*;
6795 use crate::runtime::vm::VM;
6796 use crate::runtime::{JSContext, JSRuntime};
6797
6798 fn run_expr(code: &str) -> i64 {
6799 let mut rt = JSRuntime::new();
6800 let mut ctx = JSContext::new(&mut rt);
6801
6802 let ast = crate::compiler::parser::Parser::new(code).parse().unwrap();
6803 let expr = match ast.body.first() {
6804 Some(ASTNode::ExpressionStatement(es)) => es.expression.clone(),
6805 _ => panic!("expected expression"),
6806 };
6807
6808 let mut codegen = CodeGenerator::with_opt_level(OptLevel::default());
6809 let reg = codegen.compile_expression(&expr, &mut ctx).unwrap();
6810
6811 let mut bytecode = Bytecode::new();
6812 let (code, constants, locals_count, _) = codegen.take_bytecode();
6813 bytecode.code = code;
6814 bytecode.constants = constants;
6815 bytecode.locals_count = locals_count.max((reg + 1) as u32);
6816
6817 if reg != 0 {
6818 bytecode.code.push(Opcode::Move as u8);
6819 bytecode.code.extend_from_slice(&0u16.to_le_bytes());
6820 bytecode.code.extend_from_slice(®.to_le_bytes());
6821 }
6822 bytecode.code.push(Opcode::End as u8);
6823
6824 let mut vm = VM::new();
6825 let result = match vm.execute(&mut ctx, &bytecode).unwrap() {
6826 crate::runtime::vm::ExecutionOutcome::Complete(v) => v,
6827 crate::runtime::vm::ExecutionOutcome::Yield(v) => v,
6828 };
6829 result.get_int()
6830 }
6831
6832 fn run_func(code: &str) -> i64 {
6833 let mut rt = JSRuntime::new();
6834 let mut ctx = JSContext::new(&mut rt);
6835
6836 let ast = crate::compiler::parser::Parser::new(code).parse().unwrap();
6837 let func = match ast.body.first() {
6838 Some(ASTNode::FunctionDeclaration(fd)) => fd.clone(),
6839 _ => panic!("expected function declaration"),
6840 };
6841
6842 let mut codegen = CodeGenerator::with_opt_level(OptLevel::default());
6843 let (bytecode, _upvalues) = codegen
6844 .compile_function(&func.params, &func.body, &mut ctx)
6845 .unwrap();
6846
6847 let mut vm = VM::new();
6848 let result = match vm.execute(&mut ctx, &bytecode).unwrap() {
6849 crate::runtime::vm::ExecutionOutcome::Complete(v) => v,
6850 crate::runtime::vm::ExecutionOutcome::Yield(v) => v,
6851 };
6852 result.get_int()
6853 }
6854
6855 fn compile_func_bytecode(code: &str, opt_level: OptLevel) -> Bytecode {
6856 let mut rt = JSRuntime::new();
6857 let mut ctx = JSContext::new(&mut rt);
6858 let ast = crate::compiler::parser::Parser::new(code).parse().unwrap();
6859 let func = match ast.body.first() {
6860 Some(ASTNode::FunctionDeclaration(fd)) => fd.clone(),
6861 _ => panic!("expected function declaration"),
6862 };
6863 let mut codegen = CodeGenerator::with_opt_level(opt_level);
6864 let (bc, _) = codegen
6865 .compile_function(&func.params, &func.body, &mut ctx)
6866 .unwrap();
6867 bc
6868 }
6869
6870 fn compile_script_bytecode_len(code: &str, opt_level: OptLevel) -> usize {
6871 let mut rt = JSRuntime::new();
6872 let mut ctx = JSContext::new(&mut rt);
6873 let ast = crate::compiler::parser::Parser::new(code).parse().unwrap();
6874 let block = crate::compiler::ast::BlockStatement {
6875 body: ast.body,
6876 lines: ast.lines,
6877 };
6878 let mut codegen = CodeGenerator::with_opt_level(opt_level);
6879 let (bc, _) = codegen.compile_script(&block, &mut ctx).unwrap();
6880 bc.code.len()
6881 }
6882
6883 #[test]
6884 fn test_literal_int() {
6885 assert_eq!(run_expr("42"), 42);
6886 }
6887
6888 #[test]
6889 fn test_add_sub() {
6890 assert_eq!(run_expr("1 + 2"), 3);
6891 assert_eq!(run_expr("10 - 3"), 7);
6892 }
6893
6894 #[test]
6895 fn test_mul_div() {
6896 assert_eq!(run_expr("3 * 4"), 12);
6897 assert_eq!(run_expr("20 / 4"), 5);
6898 }
6899
6900 #[test]
6901 fn test_complex_arithmetic() {
6902 assert_eq!(run_expr("1 + 2 * 3"), 7);
6903 assert_eq!(run_expr("(1 + 2) * 3"), 9);
6904 }
6905
6906 #[test]
6907 fn test_variable_and_assignment() {
6908 assert_eq!(
6909 run_func("function f() { var x = 10; x = x + 5; return x; }"),
6910 15
6911 );
6912 }
6913
6914 #[test]
6915 fn test_if_statement() {
6916 assert_eq!(
6917 run_func("function f() { var x = 3; if (x < 5) { return 1; } return 0; }"),
6918 1
6919 );
6920 assert_eq!(
6921 run_func("function f() { var x = 10; if (x < 5) { return 1; } return 0; }"),
6922 0
6923 );
6924 }
6925
6926 #[test]
6927 fn test_if_else() {
6928 assert_eq!(
6929 run_func("function f() { var x = 2; if (x > 5) { return 10; } else { return 20; } }"),
6930 20
6931 );
6932 }
6933
6934 #[test]
6935 fn test_while_loop() {
6936 assert_eq!(
6937 run_func(
6938 "function f() { var sum = 0; var i = 0; while (i < 10) { sum = sum + i; i = i + 1; } return sum; }"
6939 ),
6940 45
6941 );
6942 }
6943
6944 #[test]
6945 fn test_for_loop() {
6946 assert_eq!(
6947 run_func(
6948 "function f() { var sum = 0; for (var i = 0; i <= 10; i = i + 1) { sum = sum + i; } return sum; }"
6949 ),
6950 55
6951 );
6952 }
6953
6954 #[test]
6955 fn test_ternary() {
6956 assert_eq!(run_expr("1 < 2 ? 10 : 20"), 10);
6957 assert_eq!(run_expr("1 > 2 ? 10 : 20"), 20);
6958 }
6959
6960 #[test]
6961 fn test_unary_neg() {
6962 assert_eq!(run_expr("-5"), -5);
6963 assert_eq!(run_expr("-(-3)"), 3);
6964 }
6965
6966 #[test]
6967 fn test_logical_and() {
6968 assert_eq!(run_expr("1 && 1 ? 1 : 0"), 1);
6969 }
6970
6971 #[test]
6972 fn test_p2_profile_raw_disables_all_literal_folds() {
6973 let code = "function f() { if (true) { return 1; } return 2; }";
6974 let bc = compile_func_bytecode(code, OptLevel::O0);
6975 assert!(bc.code.contains(&(Opcode::JumpIfNot as u8)));
6976 }
6977
6978 #[test]
6979 fn test_p2_profile_ternary_keeps_if_unfolded() {
6980 let code = "function f() { if (true) { return 1; } return 2; }";
6981 let bc = compile_func_bytecode(code, OptLevel::O1);
6982 assert!(bc.code.contains(&(Opcode::JumpIfNot as u8)));
6983 }
6984
6985 #[test]
6986 fn test_p2_profile_stable_folds_if_true() {
6987 let code = "function f() { if (true) { return 1; } return 2; }";
6988 let bc = compile_func_bytecode(code, OptLevel::O2);
6989 assert!(!bc.code.contains(&(Opcode::JumpIfNot as u8)));
6990 }
6991
6992 #[test]
6993 fn test_p2_profile_aggressive_folds_string_truthy_conditional() {
6994 let mut rt = JSRuntime::new();
6995 let mut ctx = JSContext::new(&mut rt);
6996 let ast = crate::compiler::parser::Parser::new("'x' ? 11 : 22")
6997 .parse()
6998 .unwrap();
6999 let expr = match ast.body.first() {
7000 Some(ASTNode::ExpressionStatement(es)) => es.expression.clone(),
7001 _ => panic!("expected expression"),
7002 };
7003 let mut codegen = CodeGenerator::with_opt_level(OptLevel::O3);
7004 let reg = codegen.compile_expression(&expr, &mut ctx).unwrap();
7005
7006 let mut bytecode = Bytecode::new();
7007 let (code, constants, locals_count, _) = codegen.take_bytecode();
7008 bytecode.code = code;
7009 bytecode.constants = constants;
7010 bytecode.locals_count = locals_count.max((reg + 1) as u32);
7011 if reg != 0 {
7012 bytecode.code.push(Opcode::Move as u8);
7013 bytecode.code.extend_from_slice(&0u16.to_le_bytes());
7014 bytecode.code.extend_from_slice(®.to_le_bytes());
7015 }
7016 bytecode.code.push(Opcode::End as u8);
7017
7018 let mut vm = VM::new();
7019 let result = match vm.execute(&mut ctx, &bytecode).unwrap() {
7020 crate::runtime::vm::ExecutionOutcome::Complete(v) => v,
7021 crate::runtime::vm::ExecutionOutcome::Yield(v) => v,
7022 };
7023 assert_eq!(result.get_int(), 11);
7024 }
7025
7026 #[test]
7027 fn test_prefix_increment() {
7028 assert_eq!(run_func("function f() { var x = 5; return ++x; }"), 6);
7029 assert_eq!(
7030 run_func("function f() { var x = 5; var y = ++x; return x + y; }"),
7031 12
7032 );
7033 }
7034
7035 #[test]
7036 fn test_postfix_increment() {
7037 assert_eq!(run_func("function f() { var x = 5; return x++; }"), 5);
7038 assert_eq!(
7039 run_func("function f() { var x = 5; var y = x++; return x + y; }"),
7040 11
7041 );
7042 }
7043
7044 #[test]
7045 fn test_prefix_decrement() {
7046 assert_eq!(run_func("function f() { var x = 5; return --x; }"), 4);
7047 assert_eq!(
7048 run_func("function f() { var x = 5; var y = --x; return x + y; }"),
7049 8
7050 );
7051 }
7052
7053 #[test]
7054 fn test_postfix_decrement() {
7055 assert_eq!(run_func("function f() { var x = 5; return x--; }"), 5);
7056 assert_eq!(
7057 run_func("function f() { var x = 5; var y = x--; return x + y; }"),
7058 9
7059 );
7060 }
7061
7062 #[test]
7063 fn test_do_while_loop() {
7064 assert_eq!(
7065 run_func(
7066 "function f() { var sum = 0; var i = 0; do { sum = sum + i; i = i + 1; } while (i < 10); return sum; }"
7067 ),
7068 45
7069 );
7070 }
7071
7072 #[test]
7073 fn test_break_in_while() {
7074 assert_eq!(
7075 run_func(
7076 "function f() { var sum = 0; var i = 0; while (i < 100) { sum = sum + i; i = i + 1; if (i > 5) break; } return sum; }"
7077 ),
7078 15
7079 );
7080 }
7081
7082 #[test]
7083 fn test_break_in_for() {
7084 assert_eq!(
7085 run_func(
7086 "function f() { var sum = 0; for (var i = 0; i < 100; i = i + 1) { sum = sum + i; if (i == 5) break; } return sum; }"
7087 ),
7088 15
7089 );
7090 }
7091
7092 #[test]
7093 fn test_break_in_do_while() {
7094 assert_eq!(
7095 run_func(
7096 "function f() { var sum = 0; var i = 0; do { sum = sum + i; i = i + 1; if (i > 5) break; } while (i < 100); return sum; }"
7097 ),
7098 15
7099 );
7100 }
7101
7102 #[test]
7103 fn test_continue_in_while() {
7104 assert_eq!(
7105 run_func(
7106 "function f() { var sum = 0; var i = 0; while (i < 5) { i = i + 1; if (i == 3) continue; sum = sum + i; } return sum; }"
7107 ),
7108 12
7109 );
7110 }
7111
7112 #[test]
7113 fn test_continue_in_for() {
7114 assert_eq!(
7115 run_func(
7116 "function f() { var sum = 0; for (var i = 0; i < 5; i = i + 1) { if (i == 3) continue; sum = sum + i; } return sum; }"
7117 ),
7118 7
7119 );
7120 }
7121
7122 #[test]
7123 fn test_tiny_inline_getter_reduces_bytecode() {
7124 let code = r#"
7125 function car(p) { return p.car; }
7126 function main(x) { return car(x); }
7127 "#;
7128
7129 let inline_len = compile_script_bytecode_len(code, OptLevel::O2);
7130 let noninline_len = compile_script_bytecode_len(code, OptLevel::O0);
7131
7132 assert!(
7133 inline_len < noninline_len,
7134 "tiny getter inline should reduce bytecode: inline={}, noninline={}",
7135 inline_len,
7136 noninline_len
7137 );
7138 }
7139
7140 #[test]
7141 fn test_tiny_inline_nested_getter_reduces_bytecode() {
7142 let code = r#"
7143 function cadr(p) { return p.cdr.car; }
7144 function main(x) { return cadr(x); }
7145 "#;
7146
7147 let inline_len = compile_script_bytecode_len(code, OptLevel::O2);
7148 let noninline_len = compile_script_bytecode_len(code, OptLevel::O0);
7149
7150 assert!(
7151 inline_len < noninline_len,
7152 "tiny nested getter inline should reduce bytecode: inline={}, noninline={}",
7153 inline_len,
7154 noninline_len
7155 );
7156 }
7157
7158 #[test]
7159 fn test_continue_in_do_while() {
7160 assert_eq!(
7161 run_func(
7162 "function f() { var sum = 0; var i = 0; do { i = i + 1; if (i == 3) continue; sum = sum + i; } while (i < 5); return sum; }"
7163 ),
7164 12
7165 );
7166 }
7167
7168 #[test]
7169 fn test_bitwise_codegen() {
7170 assert_eq!(run_expr("12 & 10"), 8);
7171 assert_eq!(run_expr("12 | 10"), 14);
7172 assert_eq!(run_expr("12 ^ 10"), 6);
7173 assert_eq!(run_expr("~8"), -9);
7174 assert_eq!(run_expr("1 << 4"), 16);
7175 assert_eq!(run_expr("16 >> 2"), 4);
7176 assert_eq!(run_expr("-1 >>> 1"), 2147483647);
7177 }
7178
7179 #[test]
7180 fn test_mod_pow_codegen() {
7181 assert_eq!(run_expr("17 % 5"), 2);
7182 assert_eq!(run_expr("2 ** 10"), 1024);
7183 }
7184
7185 #[test]
7186 fn test_switch_basic() {
7187 assert_eq!(
7188 run_func(
7189 "function f() { var x = 2; switch (x) { case 1: return 10; case 2: return 20; case 3: return 30; default: return 99; } }"
7190 ),
7191 20
7192 );
7193 }
7194
7195 #[test]
7196 fn test_switch_default() {
7197 assert_eq!(
7198 run_func(
7199 "function f() { var x = 5; switch (x) { case 1: return 10; case 2: return 20; default: return 99; } }"
7200 ),
7201 99
7202 );
7203 }
7204
7205 #[test]
7206 fn test_switch_fallthrough() {
7207 assert_eq!(
7208 run_func(
7209 "function f() { var x = 1; var r = 0; switch (x) { case 1: r = r + 1; case 2: r = r + 10; default: r = r + 100; } return r; }"
7210 ),
7211 111
7212 );
7213 }
7214
7215 #[test]
7216 fn test_switch_break() {
7217 assert_eq!(
7218 run_func(
7219 "function f() { var x = 1; var r = 0; switch (x) { case 1: r = r + 1; break; case 2: r = r + 10; break; default: r = r + 100; } return r; }"
7220 ),
7221 1
7222 );
7223 }
7224
7225 #[test]
7226 fn test_switch_break_in_nested_loop() {
7227 assert_eq!(
7228 run_func(
7229 "function f() { var r = 0; for (var i = 0; i < 3; i = i + 1) { switch (i) { case 0: r = r + 1; break; case 1: r = r + 10; break; default: r = r + 100; } } return r; }"
7230 ),
7231 111
7232 );
7233 }
7234
7235 #[test]
7236 fn test_closure_basic() {
7237 assert_eq!(
7238 run_func(
7239 "function f() { var count = 0; function inc() { count = count + 1; return count; } return inc() + inc(); }"
7240 ),
7241 3
7242 );
7243 }
7244
7245 #[test]
7246 fn test_closure_counter() {
7247 assert_eq!(
7248 run_func(
7249 "function f() { function makeCounter() { var count = 0; return function() { count = count + 1; return count; }; } var c = makeCounter(); return c() + c(); }"
7250 ),
7251 3
7252 );
7253 }
7254
7255 #[test]
7256 fn test_closure_with_param() {
7257 assert_eq!(
7258 run_func(
7259 "function f() { function makeAdder(x) { return function(y) { return x + y; }; } var add5 = makeAdder(5); return add5(3); }"
7260 ),
7261 8
7262 );
7263 }
7264
7265 #[test]
7266 fn test_nested_closure() {
7267 assert_eq!(
7268 run_func(
7269 "function f() { var a = 1; function g() { var b = 2; return function() { return a + b; }; } return g()(); }"
7270 ),
7271 3
7272 );
7273 }
7274
7275 #[test]
7276 fn test_arrow_function() {
7277 assert_eq!(
7278 run_func(
7279 "function f() { var double = function(x) { return x * 2; }; return double(5); }"
7280 ),
7281 10
7282 );
7283 }
7284
7285 #[test]
7286 fn test_delete_prop() {
7287 assert_eq!(
7288 run_func("function f() { var o = {x: 1, y: 2}; delete o.x; return o.y; }"),
7289 2
7290 );
7291 }
7292
7293 #[test]
7294 fn test_delete_returns_true() {
7295 assert_eq!(
7296 run_func("function f() { var o = {x: 1}; return delete o.x; }"),
7297 1
7298 );
7299 }
7300
7301 #[test]
7302 fn test_has_property_in() {
7303 assert_eq!(
7304 run_func("function f() { var o = {x: 1}; return 'x' in o; }"),
7305 1
7306 );
7307 }
7308
7309 #[test]
7310 fn test_has_property_not_in() {
7311 assert_eq!(
7312 run_func("function f() { var o = {x: 1}; return 'y' in o; }"),
7313 0
7314 );
7315 }
7316
7317 #[test]
7318 fn test_rest_params() {
7319 assert_eq!(
7320 run_func(
7321 "function outer() { function f(a, b, ...rest) { return rest.length; } return f(1, 2, 3, 4); }"
7322 ),
7323 2
7324 );
7325 }
7326
7327 #[test]
7328 fn test_rest_params_values() {
7329 assert_eq!(
7330 run_func(
7331 "function outer() { function f(a, ...rest) { return rest[0] + rest[1]; } return f(1, 10, 20); }"
7332 ),
7333 30
7334 );
7335 }
7336
7337 #[test]
7338 fn test_rest_params_empty() {
7339 assert_eq!(
7340 run_func(
7341 "function outer() { function f(a, b, ...rest) { return rest.length; } return f(1, 2); }"
7342 ),
7343 0
7344 );
7345 }
7346
7347 #[test]
7348 fn test_object_spread() {
7349 assert_eq!(
7350 run_func(
7351 "function f() { var o = {a: 1, b: 2}; var n = {...o, c: 3}; return n.a + n.b + n.c; }"
7352 ),
7353 6
7354 );
7355 }
7356
7357 #[test]
7358 fn test_array_spread() {
7359 assert_eq!(
7360 run_func(
7361 "function f() { var a = [1, 2]; var b = [...a, 3, 4]; return b[0] + b[1] + b[2] + b[3]; }"
7362 ),
7363 10
7364 );
7365 }
7366
7367 #[test]
7368 fn test_array_spread_mixed() {
7369 assert_eq!(
7370 run_func("function f() { var a = [10, 20]; var b = [5, ...a]; return b.length; }"),
7371 3
7372 );
7373 }
7374
7375 #[test]
7376 fn test_for_of_basic() {
7377 assert_eq!(
7378 run_func(
7379 "function f() { var s = 0; for (var x of [1, 2, 3]) { s = s + x; } return s; }"
7380 ),
7381 6
7382 );
7383 }
7384
7385 #[test]
7386 fn test_for_of_destructuring_array() {
7387 assert_eq!(
7388 run_func(
7389 "function f() { var s = 0; for (var [a, b] of [[1,2],[3,4]]) { s = s + a + b; } return s; }"
7390 ),
7391 10
7392 );
7393 }
7394
7395 #[test]
7396 fn test_for_of_destructuring_object() {
7397 assert_eq!(
7398 run_func(
7399 "function f() { var s = 0; for (var {x} of [{x:1},{x:2},{x:3}]) { s = s + x; } return s; }"
7400 ),
7401 6
7402 );
7403 }
7404
7405 #[test]
7406 fn test_binding_destructuring_nested() {
7407 assert_eq!(
7408 run_func("function f() { var [[x], {y}] = [[1], {y: 2}]; return x + y; }"),
7409 3
7410 );
7411 }
7412
7413 #[test]
7414 fn test_binding_destructuring_defaults() {
7415 assert_eq!(
7416 run_func("function f() { var [a = 10, b = 20] = [1]; return a + b; }"),
7417 21
7418 );
7419 }
7420
7421 #[test]
7422 fn test_for_in_destructuring_array() {
7423 assert_eq!(
7424 run_func(
7425 "function f() { var o = {a:1,b:2}; var s = 0; for (var [k] in o) { s = s + 1; } return s; }"
7426 ),
7427 2
7428 );
7429 }
7430
7431 #[test]
7432 fn test_class_basic() {
7433 assert_eq!(
7434 run_func(
7435 "function f() { class A { constructor() { this.x = 42; } getX() { return this.x; } } var a = new A(); return a.getX(); }"
7436 ),
7437 42
7438 );
7439 }
7440
7441 #[test]
7442 fn test_class_with_static_method() {
7443 assert_eq!(
7444 run_func("function f() { class A { static foo() { return 10; } } return A.foo(); }"),
7445 10
7446 );
7447 }
7448
7449 #[test]
7450 fn test_class_with_instance_field() {
7451 assert_eq!(
7452 run_func(
7453 "function f() { class A { x = 5; y = 7; sum() { return this.x + this.y; } } var a = new A(); return a.sum(); }"
7454 ),
7455 12
7456 );
7457 }
7458
7459 #[test]
7460 fn test_class_inheritance() {
7461 assert_eq!(
7462 run_func(
7463 "function f() { class A { constructor() { this.x = 10; } foo() { return this.x; } } class B extends A { constructor() { super(); this.y = 20; } bar() { return this.y; } } var b = new B(); return b.foo() + b.bar(); }"
7464 ),
7465 30
7466 );
7467 }
7468
7469 #[test]
7470 fn test_class_default_constructor_with_super() {
7471 assert_eq!(
7472 run_func(
7473 "function f() { class A { constructor() { this.x = 100; } } class B extends A {} var b = new B(); return b.x; }"
7474 ),
7475 100
7476 );
7477 }
7478
7479 #[test]
7480 fn test_class_static_field() {
7481 assert_eq!(
7482 run_func("function f() { class A { static count = 3; } return A.count; }"),
7483 3
7484 );
7485 }
7486
7487 #[test]
7488 fn test_for_in_basic() {
7489 assert_eq!(
7490 run_func(
7491 "function f() { var o = {a: 1, b: 2, c: 3}; var sum = 0; for (var k in o) { sum = sum + 1; } return sum; }"
7492 ),
7493 3
7494 );
7495 }
7496
7497 #[test]
7498 fn test_for_in_sum_values() {
7499 assert_eq!(
7500 run_func(
7501 "function f() { var o = {a: 10, b: 20}; var sum = 0; for (var k in o) { sum = sum + o[k]; } return sum; }"
7502 ),
7503 30
7504 );
7505 }
7506
7507 #[test]
7508 fn test_computed_prop_access() {
7509 assert_eq!(
7510 run_func("function f() { var o = {a: 10}; return o['a']; }"),
7511 10
7512 );
7513 }
7514
7515 #[test]
7516 fn test_try_catch_throw() {
7517 assert_eq!(
7518 run_func("function f() { try { throw 42; return 0; } catch(e) { return e + 1; } }"),
7519 43
7520 );
7521 }
7522
7523 #[test]
7524 fn test_try_no_throw() {
7525 assert_eq!(
7526 run_func("function f() { try { return 7; } catch(e) { return 99; } }"),
7527 7
7528 );
7529 }
7530
7531 #[test]
7532 fn test_try_catch_finally() {
7533 assert_eq!(
7534 run_func(
7535 "function f() { var x = 0; try { throw 5; } catch(e) { x = e + 10; } finally { x = x + 1; } return x; }"
7536 ),
7537 16
7538 );
7539 }
7540
7541 #[test]
7542 fn test_try_catch_nested() {
7543 assert_eq!(
7544 run_func(
7545 "function f() { try { try { throw 3; } catch(e) { throw e + 2; } } catch(e) { return e * 2; } }"
7546 ),
7547 10
7548 );
7549 }
7550
7551 #[test]
7552 fn test_private_instance_field_get() {
7553 assert_eq!(
7554 run_func(
7555 "function f() { class A { #x = 42; getX() { return this.#x; } } return new A().getX(); }"
7556 ),
7557 42
7558 );
7559 }
7560
7561 #[test]
7562 fn test_private_instance_field_set() {
7563 assert_eq!(
7564 run_func(
7565 "function f() { class A { #x = 10; setX(v) { this.#x = v; } getX() { return this.#x; } } var a = new A(); a.setX(99); return a.getX(); }"
7566 ),
7567 99
7568 );
7569 }
7570
7571 #[test]
7572 fn test_private_instance_field_in() {
7573 assert_eq!(
7574 run_func(
7575 "function f() { class A { #x = 1; hasX() { return #x in this; } } return new A().hasX() ? 1 : 0; }"
7576 ),
7577 1
7578 );
7579 }
7580
7581 #[test]
7582 fn test_private_static_field() {
7583 assert_eq!(
7584 run_func(
7585 "function f() { class A { static #x = 7; static getX() { return A.#x; } } return A.getX(); }"
7586 ),
7587 7
7588 );
7589 }
7590
7591 #[test]
7592 fn test_generator_basic() {
7593 let mut rt = JSRuntime::new();
7594 let mut ctx = JSContext::new(&mut rt);
7595 let code = "(function*() { yield 1; yield 2; })";
7596 let ast = crate::compiler::parser::Parser::new(code).parse().unwrap();
7597 let expr = match ast.body.first() {
7598 Some(ASTNode::ExpressionStatement(es)) => es.expression.clone(),
7599 _ => panic!("expected expression"),
7600 };
7601 let mut codegen = CodeGenerator::new();
7602 let func_reg = codegen.compile_expression(&expr, &mut ctx).unwrap();
7603
7604 let call_dst = codegen.alloc_register();
7605 codegen.emit(Opcode::Call);
7606 codegen.emit_u16(call_dst);
7607 codegen.emit_u16(func_reg);
7608 codegen.emit_u16(0);
7609
7610 let mut bytecode = Bytecode::new();
7611 let (code, constants, locals_count, _) = codegen.take_bytecode();
7612 bytecode.code = code;
7613 bytecode.constants = constants;
7614 bytecode.locals_count = locals_count.max((call_dst + 1) as u32);
7615 bytecode.code.push(Opcode::Move as u8);
7616 bytecode.code.extend_from_slice(&0u16.to_le_bytes());
7617 bytecode.code.extend_from_slice(&call_dst.to_le_bytes());
7618 bytecode.code.push(Opcode::End as u8);
7619
7620 let mut vm = VM::new();
7621 let result = match vm.execute(&mut ctx, &bytecode).unwrap() {
7622 crate::runtime::vm::ExecutionOutcome::Complete(v) => v,
7623 crate::runtime::vm::ExecutionOutcome::Yield(v) => v,
7624 };
7625 assert!(result.is_object(), "expected generator object");
7626 }
7627
7628 #[test]
7629 fn test_async_generator_basic() {
7630 let mut rt = JSRuntime::new();
7631 let mut ctx = JSContext::new(&mut rt);
7632 let code = "(async function*() { yield 1; yield 2; })";
7633 let ast = crate::compiler::parser::Parser::new(code).parse().unwrap();
7634 let expr = match ast.body.first() {
7635 Some(ASTNode::ExpressionStatement(es)) => es.expression.clone(),
7636 _ => panic!("expected expression"),
7637 };
7638 let mut codegen = CodeGenerator::new();
7639 let func_reg = codegen.compile_expression(&expr, &mut ctx).unwrap();
7640
7641 let call_dst = codegen.alloc_register();
7642 codegen.emit(Opcode::Call);
7643 codegen.emit_u16(call_dst);
7644 codegen.emit_u16(func_reg);
7645 codegen.emit_u16(0);
7646
7647 let mut bytecode = Bytecode::new();
7648 let (code, constants, locals_count, _) = codegen.take_bytecode();
7649 bytecode.code = code;
7650 bytecode.constants = constants;
7651 bytecode.locals_count = locals_count.max((call_dst + 1) as u32);
7652 bytecode.code.push(Opcode::Move as u8);
7653 bytecode.code.extend_from_slice(&0u16.to_le_bytes());
7654 bytecode.code.extend_from_slice(&call_dst.to_le_bytes());
7655 bytecode.code.push(Opcode::End as u8);
7656
7657 let mut vm = VM::new();
7658 let result = match vm.execute(&mut ctx, &bytecode).unwrap() {
7659 crate::runtime::vm::ExecutionOutcome::Complete(v) => v,
7660 crate::runtime::vm::ExecutionOutcome::Yield(v) => v,
7661 };
7662 assert!(result.is_object(), "expected async generator object");
7663 let obj = result.as_object();
7664 assert!(obj.is_generator(), "expected generator flag to be set");
7665 }
7666
7667 #[test]
7668 fn test_promise_resolve_in_register_vm() {
7669 let mut rt = JSRuntime::new();
7670 let mut ctx = JSContext::new(&mut rt);
7671 let code = "Promise.resolve(42)";
7672 let ast = crate::compiler::parser::Parser::new(code).parse().unwrap();
7673 let expr = match ast.body.first() {
7674 Some(ASTNode::ExpressionStatement(es)) => es.expression.clone(),
7675 _ => panic!("expected expression"),
7676 };
7677 let mut codegen = CodeGenerator::new();
7678 let dst = codegen.compile_expression(&expr, &mut ctx).unwrap();
7679
7680 let mut bytecode = Bytecode::new();
7681 let (code, constants, locals_count, _) = codegen.take_bytecode();
7682 bytecode.code = code;
7683 bytecode.constants = constants;
7684 bytecode.locals_count = locals_count.max((dst + 1) as u32);
7685 bytecode.code.push(Opcode::Move as u8);
7686 bytecode.code.extend_from_slice(&0u16.to_le_bytes());
7687 bytecode.code.extend_from_slice(&dst.to_le_bytes());
7688 bytecode.code.push(Opcode::End as u8);
7689
7690 let mut vm = VM::new();
7691 let result = match vm.execute(&mut ctx, &bytecode).unwrap() {
7692 crate::runtime::vm::ExecutionOutcome::Complete(v) => v,
7693 crate::runtime::vm::ExecutionOutcome::Yield(v) => v,
7694 };
7695 assert!(result.is_object(), "expected promise object");
7696 assert!(
7697 crate::builtins::promise::is_promise(&result),
7698 "expected a Promise"
7699 );
7700 let promise_obj = result.as_object();
7701 let state = promise_obj
7702 .get(ctx.common_atoms.__promise_state__)
7703 .unwrap_or(JSValue::new_int(0))
7704 .get_int();
7705 assert_eq!(state, 1, "expected resolved promise");
7706 let value = promise_obj
7707 .get(ctx.common_atoms.__promise_result__)
7708 .unwrap_or(JSValue::undefined());
7709 assert_eq!(value.get_int(), 42, "expected promise result to be 42");
7710 }
7711
7712 #[test]
7713 fn test_async_function_await_resolved_promise() {
7714 let mut rt = JSRuntime::new();
7715 let mut ctx = JSContext::new(&mut rt);
7716 let code = "(async function() { return await Promise.resolve(42); })";
7717 let ast = crate::compiler::parser::Parser::new(code).parse().unwrap();
7718 let expr = match ast.body.first() {
7719 Some(ASTNode::ExpressionStatement(es)) => es.expression.clone(),
7720 _ => panic!("expected expression"),
7721 };
7722 let mut codegen = CodeGenerator::new();
7723 let func_reg = codegen.compile_expression(&expr, &mut ctx).unwrap();
7724
7725 let call_dst = codegen.alloc_register();
7726 codegen.emit(Opcode::Call);
7727 codegen.emit_u16(call_dst);
7728 codegen.emit_u16(func_reg);
7729 codegen.emit_u16(0);
7730
7731 let mut bytecode = Bytecode::new();
7732 let (code, constants, locals_count, _) = codegen.take_bytecode();
7733 bytecode.code = code;
7734 bytecode.constants = constants;
7735 bytecode.locals_count = locals_count.max((call_dst + 1) as u32);
7736 bytecode.code.push(Opcode::Move as u8);
7737 bytecode.code.extend_from_slice(&0u16.to_le_bytes());
7738 bytecode.code.extend_from_slice(&call_dst.to_le_bytes());
7739 bytecode.code.push(Opcode::End as u8);
7740
7741 let mut vm = VM::new();
7742 let result = match vm.execute(&mut ctx, &bytecode).unwrap() {
7743 crate::runtime::vm::ExecutionOutcome::Complete(v) => v,
7744 crate::runtime::vm::ExecutionOutcome::Yield(v) => v,
7745 };
7746 assert!(
7747 result.is_object(),
7748 "expected promise object from async function call"
7749 );
7750
7751 assert!(
7752 crate::builtins::promise::is_promise(&result),
7753 "expected a Promise"
7754 );
7755 let promise_obj = result.as_object();
7756 let state = promise_obj
7757 .get(ctx.common_atoms.__promise_state__)
7758 .unwrap_or(JSValue::new_int(0))
7759 .get_int();
7760 assert_eq!(state, 1, "expected resolved promise");
7761 let value = promise_obj
7762 .get(ctx.common_atoms.__promise_result__)
7763 .unwrap_or(JSValue::undefined());
7764 assert_eq!(value.get_int(), 42, "expected promise result to be 42");
7765 }
7766
7767 #[test]
7768 fn test_deep_nesting_no_overflow() {
7769 let code = "1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1";
7770 assert_eq!(run_expr(code), 49);
7771 }
7772
7773 #[test]
7774 fn test_very_deep_nesting_no_overflow() {
7775 let ones: Vec<&str> = std::iter::repeat("1").take(100).collect();
7776 let code = ones.join(" + ");
7777 assert_eq!(run_expr(&code), 100);
7778 }
7779
7780 #[test]
7781 fn test_extreme_deep_nesting_no_overflow() {
7782 let ones: Vec<&str> = std::iter::repeat("1").take(300).collect();
7783 let code = ones.join(" + ");
7784 assert_eq!(run_expr(&code), 300);
7785 }
7786}