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