1use crate::ast::*;
16use crate::host::{binop as bop, member, ops, unop, unwind, FuncDef, ParamSlot, TryDef};
17use fusevm::{Chunk, ChunkBuilder, Op, Value};
18
19#[derive(Default)]
22pub struct Program {
23 pub main: Chunk,
24 pub functions: Vec<(String, FuncDef)>,
25 pub tries: Vec<TryDef>,
26}
27
28pub fn rebase_program(prog: &mut Program, func_off: usize, try_off: usize) {
32 if func_off == 0 && try_off == 0 {
33 return;
34 }
35 rebase_chunk(&mut prog.main, func_off, try_off);
36 for (_, f) in &mut prog.functions {
37 rebase_chunk(&mut f.chunk, func_off, try_off);
38 }
39 for t in &mut prog.tries {
40 rebase_chunk(&mut t.block, func_off, try_off);
41 if let Some((_, hb)) = &mut t.handler {
42 rebase_chunk(hb, func_off, try_off);
43 }
44 if let Some(f) = &mut t.finalizer {
45 rebase_chunk(f, func_off, try_off);
46 }
47 }
48}
49
50fn rebase_chunk(chunk: &mut Chunk, func_off: usize, try_off: usize) {
51 for i in 1..chunk.ops.len() {
52 let off = match chunk.ops[i] {
53 Op::CallBuiltin(id, _) if id == ops::MKFUNC => func_off,
54 Op::CallBuiltin(id, 1) if id == ops::TRY => try_off,
55 _ => continue,
56 };
57 if off == 0 {
58 continue;
59 }
60 if let Op::LoadInt(v) = &mut chunk.ops[i - 1] {
61 *v += off as i64;
62 }
63 }
64 for sub in &mut chunk.sub_chunks {
65 rebase_chunk(sub, func_off, try_off);
66 }
67}
68
69fn bind_mode(kind: DeclKind) -> BindMode {
71 match kind {
72 DeclKind::Var => BindMode::Var,
73 DeclKind::Let => BindMode::Lexical,
74 DeclKind::Const => BindMode::Const,
75 }
76}
77
78#[derive(Clone, Copy, PartialEq, Eq)]
80enum BindMode {
81 Assign,
84 Lexical,
86 Const,
89 Var,
91}
92
93struct LoopCtx {
95 breaks: Vec<usize>,
96 continues: Vec<usize>,
97 break_depth: usize,
100 continue_depth: usize,
102 iter_depth: usize,
104 catches_continue: bool,
106 label: Option<String>,
109}
110
111#[derive(Default)]
112pub struct Compiler {
113 opt_chain: Vec<Vec<usize>>,
122 functions: Vec<(String, FuncDef)>,
123 tries: Vec<TryDef>,
124 loops: Vec<LoopCtx>,
125 tmp: usize,
126 pending_label: Option<String>,
129 debug: bool,
131 chunk_loop_base: usize,
136 chunk_signals: bool,
139 scope_depth: usize,
142 in_async_generator: bool,
145 iter_depth: usize,
149 strict: bool,
155 call_sites: Vec<(usize, String)>,
159 yield_sites: Vec<(usize, usize)>,
163 slots: crate::slots::Plan,
167}
168
169pub fn compile(stmts: &[Stmt], debug: bool) -> Result<Program, String> {
171 let mut c = Compiler {
172 opt_chain: Vec::new(),
173 debug,
174 slots: if debug {
177 Default::default()
178 } else {
179 crate::slots::plan(&[], stmts, true)
180 },
181 strict: has_use_strict(stmts),
182 ..Default::default()
183 };
184 let mut b = ChunkBuilder::new();
185 c.hoist_vars(&mut b, stmts)?;
187 c.hoist_funcs(&mut b, stmts)?;
188 c.compile_stmts(&mut b, stmts)?;
189 Ok(Program {
190 main: c.finish_chunk(b),
191 functions: c.functions,
192 tries: c.tries,
193 })
194}
195
196pub fn compile_completion(stmts: &[Stmt], debug: bool) -> Result<Program, String> {
200 let mut c = Compiler {
201 opt_chain: Vec::new(),
202 debug,
203 strict: has_use_strict(stmts),
204 ..Default::default()
205 };
206 let mut b = ChunkBuilder::new();
207 c.hoist_vars(&mut b, stmts)?;
208 c.hoist_funcs(&mut b, stmts)?;
209 if let Some((last, rest)) = stmts.split_last() {
210 c.compile_stmts(&mut b, rest)?;
211 if let StmtKind::Expr(e) = &last.kind {
212 c.compile_expr(&mut b, e)?;
214 } else {
215 c.compile_stmt(&mut b, last)?;
216 }
217 }
218 Ok(Program {
219 main: c.finish_chunk(b),
220 functions: c.functions,
221 tries: c.tries,
222 })
223}
224
225fn has_use_strict(stmts: &[Stmt]) -> bool {
231 for s in stmts {
232 match &s.kind {
233 StmtKind::Expr(e) => match e {
234 Expr::Str(v) if v == "use strict" => return true,
235 Expr::Str(_) => continue,
236 _ => return false,
237 },
238 _ => return false,
239 }
240 }
241 false
242}
243
244fn callee_text(e: &Expr) -> Option<String> {
253 Some(match e {
254 Expr::Ident(n) => n.clone(),
255 Expr::This => "this".into(),
256 Expr::Number(n) => crate::host::fmt_number(*n),
257 Expr::Str(s) => format!("\"{s}\""),
258 Expr::True => "true".into(),
259 Expr::False => "false".into(),
260 Expr::Null => "null".into(),
261 Expr::Undefined => "undefined".into(),
262 Expr::Array(items) if items.is_empty() => "[]".into(),
263 Expr::Object(props) if props.is_empty() => "{}".into(),
264 Expr::Member {
265 object,
266 property,
267 optional,
268 } => {
269 let dot = if *optional { "?." } else { "." };
270 format!("{}{dot}{property}", callee_text(object)?)
271 }
272 Expr::Index {
273 object,
274 index,
275 optional,
276 } => {
277 let obj = callee_text(object)?;
278 if let Expr::Str(k) = &**index {
281 if is_identifier(k) {
282 let dot = if *optional { "?." } else { "." };
283 return Some(format!("{obj}{dot}{k}"));
284 }
285 }
286 let idx = callee_text(index)?;
287 let open = if *optional { "?.[" } else { "[" };
288 format!("{obj}{open}{idx}]")
289 }
290 Expr::Call { func, .. } => format!("{}(...)", callee_text(func)?),
293 Expr::Sequence(items) => {
294 let parts: Option<Vec<String>> = items.iter().map(callee_text).collect();
295 format!("({})", parts?.join(" , "))
296 }
297 _ => return None,
298 })
299}
300
301fn is_identifier(s: &str) -> bool {
304 let mut chars = s.chars();
305 match chars.next() {
306 Some(c) if c.is_ascii_alphabetic() || c == '_' || c == '$' => {}
307 _ => return false,
308 }
309 chars.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '$')
310}
311
312fn argc(n: usize) -> Result<u8, String> {
313 u8::try_from(n).map_err(|_| "too many arguments (>255) for one call".to_string())
314}
315
316fn yields_bool(e: &Expr) -> bool {
331 match e {
332 Expr::True | Expr::False => true,
333 Expr::Unary(UnOp::Not, _) | Expr::Unary(UnOp::Delete, _) => true,
334 Expr::Binary(op, _, _) => matches!(
335 op,
336 BinOp::Lt
337 | BinOp::Le
338 | BinOp::Gt
339 | BinOp::Ge
340 | BinOp::EqEq
341 | BinOp::NeEq
342 | BinOp::EqEqEq
343 | BinOp::NeEqEq
344 | BinOp::In
345 | BinOp::InstanceOf
346 ),
347 _ => false,
348 }
349}
350
351impl Compiler {
352 fn name_const(&self, b: &mut ChunkBuilder, s: &str) {
354 let k = b.add_constant(Value::str(s));
355 b.emit(Op::LoadConst(k), 0);
356 }
357 fn strlit(&self, b: &mut ChunkBuilder, s: &str) {
358 let k = b.add_constant(Value::str(s));
359 b.emit(Op::LoadConst(k), 0);
360 b.emit(Op::CallBuiltin(ops::MKSTR, 1), 0);
361 }
362 fn tmp_name(&mut self, tag: &str) -> String {
363 let n = format!(".{tag}{}", self.tmp);
364 self.tmp += 1;
365 n
366 }
367
368 fn emit_mkfunc(&self, b: &mut ChunkBuilder, def_id: usize) {
371 b.emit(Op::LoadInt(def_id as i64), 0);
372 b.emit(Op::CallBuiltin(ops::MKFUNC, 1), 0);
373 }
374
375 fn hoist_vars(&mut self, b: &mut ChunkBuilder, stmts: &[Stmt]) -> Result<(), String> {
388 let mut names = Vec::new();
389 for s in stmts {
390 collect_var_names(s, &mut names);
391 }
392 for n in names {
393 if self.slot_of(&n).is_some() {
396 continue;
397 }
398 self.name_const(b, &n);
399 b.emit(Op::CallBuiltin(ops::HOIST_VAR, 1), 0);
400 b.emit(Op::Pop, 0);
401 }
402 Ok(())
403 }
404
405 fn hoist_funcs(&mut self, b: &mut ChunkBuilder, stmts: &[Stmt]) -> Result<(), String> {
406 for s in stmts {
407 if let StmtKind::FuncDecl {
408 name,
409 params,
410 body,
411 is_generator,
412 is_async,
413 } = &s.kind
414 {
415 let def_id = self.build_function(name, params, body, *is_generator, *is_async)?;
416 self.emit_mkfunc(b, def_id);
417 self.declare_as(b, &Expr::Ident(name.clone()), BindMode::Var);
419 }
420 }
421 Ok(())
422 }
423
424 fn compile_stmts(&mut self, b: &mut ChunkBuilder, stmts: &[Stmt]) -> Result<(), String> {
425 for s in stmts {
426 self.compile_stmt(b, s)?;
427 }
428 Ok(())
429 }
430
431 fn compile_stmt(&mut self, b: &mut ChunkBuilder, s: &Stmt) -> Result<(), String> {
432 if self.debug && s.line != 0 {
433 b.emit(Op::LoadInt(s.line as i64), s.line);
434 b.emit(Op::CallBuiltin(ops::DBG_LINE, 1), s.line);
435 b.emit(Op::Pop, s.line);
436 }
437 let line = s.line;
438 match &s.kind {
439 StmtKind::Expr(e) => {
440 self.compile_expr(b, e)?;
441 b.emit(Op::Pop, line);
442 }
443 StmtKind::Empty => {}
444 StmtKind::FuncDecl { .. } => {} StmtKind::ClassDecl(node) => {
446 self.compile_class(b, node)?;
447 if let Some(name) = &node.name {
449 self.declare(b, &Expr::Ident(name.clone()));
450 } else {
451 b.emit(Op::Pop, line);
452 }
453 }
454 StmtKind::Decl { kind, decls } => {
455 let mode = bind_mode(*kind);
456 for d in decls {
457 if d.init.is_none() && *kind == DeclKind::Var {
461 continue;
462 }
463 match &d.init {
464 Some(v) => {
465 self.compile_expr(b, v)?;
466 if let Expr::Ident(name) = &d.target {
469 self.infer_name(b, v, name);
470 }
471 }
472 None => {
473 b.emit(Op::LoadUndef, line);
474 }
475 }
476 self.compile_bind(b, &d.target, mode)?;
477 }
478 }
479 StmtKind::Block(body) => {
480 let scoped = crate::capture::block_needs_scope(body);
485 if scoped {
486 self.emit_push_scope(b);
487 }
488 self.hoist_funcs(b, body)?;
489 self.compile_stmts(b, body)?;
490 if scoped {
491 self.emit_pop_scope(b);
492 }
493 }
494 StmtKind::If { test, cons, alt } => self.compile_if(b, test, cons, alt)?,
495 StmtKind::While { test, body } => self.compile_while(b, test, body)?,
496 StmtKind::DoWhile { body, test } => self.compile_do_while(b, body, test)?,
497 StmtKind::For {
498 init,
499 test,
500 update,
501 body,
502 } => self.compile_for(b, init, test, update, body)?,
503 StmtKind::ForOf {
504 decl_kind,
505 target,
506 iter,
507 body,
508 is_await,
509 } => {
510 let mode = decl_kind.map(bind_mode).unwrap_or(BindMode::Assign);
511 if *is_await {
512 self.compile_for_await(b, mode, target, iter, body)?
513 } else {
514 self.compile_for_of(b, mode, target, iter, body)?
515 }
516 }
517 StmtKind::ForIn {
518 decl_kind,
519 target,
520 object,
521 body,
522 } => {
523 let mode = decl_kind.map(bind_mode).unwrap_or(BindMode::Assign);
524 self.compile_for_in(b, mode, target, object, body)?
525 }
526 StmtKind::Switch { disc, cases } => self.compile_switch(b, disc, cases)?,
527 StmtKind::Return(e) => {
528 match e {
529 Some(e) => self.compile_expr(b, e)?,
530 None => {
531 b.emit(Op::LoadUndef, line);
532 }
533 }
534 self.emit_close_iters_under_value(b);
540 b.emit(Op::CallBuiltin(ops::SIG_RETURN, 1), line);
541 }
542 StmtKind::Labeled { label, body } => self.compile_labeled(b, label, body)?,
543 StmtKind::Break(label) => {
544 let idx = match label {
545 Some(name) => self
547 .loops
548 .iter()
549 .rposition(|c| c.label.as_deref() == Some(name.as_str()))
550 .ok_or_else(|| format!("SyntaxError: Undefined label '{name}'"))?,
551 None => self
552 .loops
553 .len()
554 .checked_sub(1)
555 .ok_or("SyntaxError: 'break' outside loop")?,
556 };
557 if idx >= self.chunk_loop_base {
558 self.emit_unwind_scopes(b, self.loops[idx].break_depth);
559 self.emit_close_iters(b, self.loops[idx].iter_depth);
560 let j = b.emit(Op::Jump(0), line);
561 self.loops[idx].breaks.push(j);
562 } else {
563 self.emit_signal_jump(b, ops::SIG_BREAK, label.as_deref(), line);
564 }
565 }
566 StmtKind::Continue(label) => {
567 let idx = match label {
568 Some(name) => self
571 .loops
572 .iter()
573 .rposition(|c| {
574 c.catches_continue && c.label.as_deref() == Some(name.as_str())
575 })
576 .ok_or_else(|| {
577 format!("SyntaxError: Undefined label '{name}' for continue")
578 })?,
579 None => self
580 .loops
581 .iter()
582 .rposition(|c| c.catches_continue)
583 .ok_or("SyntaxError: 'continue' outside loop")?,
584 };
585 if idx >= self.chunk_loop_base {
586 self.emit_unwind_scopes(b, self.loops[idx].continue_depth);
587 self.emit_close_iters(b, self.loops[idx].iter_depth);
588 let j = b.emit(Op::Jump(0), line);
589 self.loops[idx].continues.push(j);
590 } else {
591 self.emit_signal_jump(b, ops::SIG_CONTINUE, label.as_deref(), line);
592 }
593 }
594 StmtKind::Throw(e) => {
595 self.compile_expr(b, e)?;
596 b.emit(Op::CallBuiltin(ops::THROW, 1), line);
597 }
598 StmtKind::Try {
599 block,
600 handler,
601 finalizer,
602 } => self.compile_try(b, block, handler, finalizer)?,
603 }
604 Ok(())
605 }
606
607 fn compile_bind(
611 &mut self,
612 b: &mut ChunkBuilder,
613 target: &Expr,
614 declare: BindMode,
615 ) -> Result<(), String> {
616 match target {
617 Expr::Ident(_) => {
618 if declare == BindMode::Assign {
619 self.store_simple(b, target)?;
620 } else {
621 self.declare_as(b, target, declare);
622 }
623 }
624 Expr::Member { .. } | Expr::Index { .. } => {
625 self.store_simple(b, target)?;
626 }
627 Expr::Array(items) => self.destructure_array(b, items, declare)?,
628 Expr::Object(props) => self.destructure_object(b, props, declare)?,
629 Expr::Assign { target, value } => {
630 b.emit(Op::Dup, 0);
632 b.emit(Op::LoadUndef, 0);
633 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
634 let jf = b.emit(Op::JumpIfFalse(0), 0);
635 b.emit(Op::Pop, 0); self.compile_expr(b, value)?;
637 if let Expr::Ident(n) = &**target {
641 self.infer_name(b, value, n);
642 }
643 let end = b.current_pos();
644 b.patch_jump(jf, end);
645 self.compile_bind(b, target, declare)?;
646 }
647 _ => return Err("SyntaxError: invalid assignment target".into()),
648 }
649 Ok(())
650 }
651
652 fn declare(&self, b: &mut ChunkBuilder, target: &Expr) {
654 self.declare_as(b, target, BindMode::Lexical);
655 }
656
657 fn declare_as(&self, b: &mut ChunkBuilder, target: &Expr, mode: BindMode) {
660 if let Expr::Ident(n) = target {
661 if let Some(slot) = self.slot_of(n) {
664 b.emit(Op::SetSlot(slot), 0);
665 return;
666 }
667 let op = match mode {
668 BindMode::Var => ops::DECLARE_VAR,
669 BindMode::Const => ops::DECLARE_CONST,
670 _ => ops::DECLARE,
671 };
672 self.name_const(b, n);
673 b.emit(Op::Swap, 0);
674 b.emit(Op::CallBuiltin(op, 2), 0);
675 b.emit(Op::Pop, 0);
676 }
677 }
678
679 fn throw_const_assignment(&mut self, b: &mut ChunkBuilder) {
686 let e = Expr::New {
687 callee: Box::new(Expr::Ident("TypeError".into())),
688 args: vec![Expr::Str("Assignment to constant variable.".into())],
689 };
690 if self.compile_expr(b, &e).is_ok() {
693 b.emit(Op::CallBuiltin(ops::THROW, 1), 0);
694 }
695 }
696
697 fn store_simple(&mut self, b: &mut ChunkBuilder, target: &Expr) -> Result<(), String> {
699 match target {
700 Expr::Ident(n) => {
701 if self.slots.consts.contains(n) {
709 b.emit(Op::Pop, 0); self.throw_const_assignment(b);
711 return Ok(());
712 }
713 if let Some(slot) = self.slot_of(n) {
714 b.emit(Op::SetSlot(slot), 0);
715 return Ok(());
716 }
717 self.name_const(b, n);
718 b.emit(Op::Swap, 0);
719 let op = if self.strict {
722 ops::SETLOCAL_STRICT
723 } else {
724 ops::SETLOCAL
725 };
726 b.emit(Op::CallBuiltin(op, 2), 0);
727 b.emit(Op::Pop, 0);
728 }
729 Expr::Member {
730 object, property, ..
731 } => {
732 self.compile_expr(b, object)?; self.name_const(b, property); b.emit(Op::Rot, 0); b.emit(Op::CallBuiltin(ops::SETATTR, 3), 0);
736 b.emit(Op::Pop, 0);
737 }
738 Expr::Index { object, index, .. } => {
739 self.compile_expr(b, object)?; self.compile_expr(b, index)?; b.emit(Op::Rot, 0); b.emit(Op::CallBuiltin(ops::SETITEM, 3), 0);
743 b.emit(Op::Pop, 0);
744 }
745 _ => return Err("SyntaxError: invalid assignment target".into()),
746 }
747 Ok(())
748 }
749
750 fn destructure_array(
751 &mut self,
752 b: &mut ChunkBuilder,
753 items: &[Expr],
754 declare: BindMode,
755 ) -> Result<(), String> {
756 let star_idx = items
757 .iter()
758 .position(|e| matches!(e, Expr::Spread(_)))
759 .map(|i| i as i64)
760 .unwrap_or(-1);
761 b.emit(Op::LoadInt(items.len() as i64), 0);
762 b.emit(Op::LoadInt(star_idx), 0);
763 b.emit(Op::CallBuiltin(ops::UNPACK, 3), 0); for it in items {
765 match it {
766 Expr::Hole | Expr::Undefined => {
769 b.emit(Op::Pop, 0);
770 }
771 Expr::Spread(inner) => self.compile_bind(b, inner, declare)?,
772 _ => self.compile_bind(b, it, declare)?,
773 }
774 }
775 Ok(())
776 }
777
778 fn destructure_object(
779 &mut self,
780 b: &mut ChunkBuilder,
781 props: &[Prop],
782 declare: BindMode,
783 ) -> Result<(), String> {
784 let obj_tmp = self.tmp_name("destr");
786 self.name_const(b, &obj_tmp);
787 b.emit(Op::Swap, 0);
788 b.emit(Op::CallBuiltin(ops::DECLARE, 2), 0);
789 b.emit(Op::Pop, 0);
790 let mut named: Vec<String> = Vec::new();
792 for p in props {
793 match p {
794 Prop::KeyValue { key, value, .. } => {
795 if let Expr::Str(s) = key {
796 named.push(s.clone());
797 }
798 self.load_local(b, &obj_tmp);
800 self.compile_expr(b, key)?;
801 b.emit(Op::CallBuiltin(ops::GETITEM, 2), 0); self.compile_bind(b, value, declare)?;
803 }
804 Prop::Spread(target) => {
805 self.load_local(b, &obj_tmp);
806 for k in &named {
807 self.strlit(b, k);
808 }
809 b.emit(Op::CallBuiltin(ops::MKARR, argc(named.len())?), 0);
810 b.emit(Op::CallBuiltin(ops::OBJ_REST, 2), 0); self.compile_bind(b, target, declare)?;
812 }
813 Prop::Accessor { .. } => {}
815 }
816 }
817 Ok(())
818 }
819
820 fn load_local(&self, b: &mut ChunkBuilder, name: &str) {
821 if let Some(slot) = self.slot_of(name) {
822 b.emit(Op::GetSlot(slot), 0);
823 return;
824 }
825 self.name_const(b, name);
826 b.emit(Op::CallBuiltin(ops::GETLOCAL, 1), 0);
827 }
828
829 fn slot_of(&self, name: &str) -> Option<u16> {
831 self.slots.table.get(name).copied()
832 }
833
834 fn numeric_slot_of(&self, name: &str) -> Option<u16> {
837 self.slots
838 .numeric
839 .contains(name)
840 .then(|| self.slot_of(name))
841 .flatten()
842 }
843
844 fn compile_condition(&mut self, b: &mut ChunkBuilder, e: &Expr) -> Result<(), String> {
846 self.compile_expr(b, e)?;
847 if !yields_bool(e) {
848 b.emit(Op::CallBuiltin(ops::TRUTHY, 1), 0);
849 }
850 Ok(())
851 }
852
853 fn compile_if(
854 &mut self,
855 b: &mut ChunkBuilder,
856 test: &Expr,
857 cons: &Stmt,
858 alt: &Option<Box<Stmt>>,
859 ) -> Result<(), String> {
860 self.compile_condition(b, test)?;
861 let jfalse = b.emit(Op::JumpIfFalse(0), 0);
862 self.compile_stmt(b, cons)?;
863 if let Some(alt) = alt {
864 let jend = b.emit(Op::Jump(0), 0);
865 let else_start = b.current_pos();
866 b.patch_jump(jfalse, else_start);
867 self.compile_stmt(b, alt)?;
868 let end = b.current_pos();
869 b.patch_jump(jend, end);
870 } else {
871 let end = b.current_pos();
872 b.patch_jump(jfalse, end);
873 }
874 Ok(())
875 }
876
877 fn compile_labeled(
881 &mut self,
882 b: &mut ChunkBuilder,
883 label: &str,
884 body: &Stmt,
885 ) -> Result<(), String> {
886 if matches!(
887 body.kind,
888 StmtKind::While { .. }
889 | StmtKind::DoWhile { .. }
890 | StmtKind::For { .. }
891 | StmtKind::ForOf { .. }
892 | StmtKind::ForIn { .. }
893 ) {
894 self.pending_label = Some(label.to_string());
895 self.compile_stmt(b, body)?;
896 self.pending_label = None;
898 } else {
899 self.loops.push(LoopCtx {
900 breaks: Vec::new(),
901 continues: Vec::new(),
902 break_depth: self.scope_depth,
903 continue_depth: self.scope_depth,
904 iter_depth: self.iter_depth,
905 catches_continue: false,
906 label: Some(label.to_string()),
907 });
908 self.compile_stmt(b, body)?;
909 let ctx = self.loops.pop().unwrap();
910 let end = b.current_pos();
911 for br in ctx.breaks {
912 b.patch_jump(br, end);
913 }
914 self.redispatch_after_loop(b);
915 }
916 Ok(())
917 }
918
919 fn redispatch_after_loop(&mut self, b: &mut ChunkBuilder) {
923 if self.chunk_signals {
924 self.emit_signal_dispatch(b);
925 }
926 }
927
928 fn compile_while(
947 &mut self,
948 b: &mut ChunkBuilder,
949 test: &Expr,
950 body: &Stmt,
951 ) -> Result<(), String> {
952 self.compile_condition(b, test)?;
953 let jfalse = b.emit(Op::JumpIfFalse(0), 0);
954 let top = b.current_pos();
955 self.loops.push(LoopCtx {
956 breaks: Vec::new(),
957 continues: Vec::new(),
958 break_depth: self.scope_depth,
959 continue_depth: self.scope_depth,
960 iter_depth: self.iter_depth,
961 catches_continue: true,
962 label: self.pending_label.take(),
963 });
964 self.compile_stmt(b, body)?;
965 let cont_target = b.current_pos();
967 self.compile_condition(b, test)?;
968 b.emit(Op::JumpIfTrue(top), 0);
969 let ctx = self.loops.pop().unwrap();
970 for c in ctx.continues {
971 b.patch_jump(c, cont_target);
972 }
973 let end = b.current_pos();
974 b.patch_jump(jfalse, end);
975 for br in ctx.breaks {
976 b.patch_jump(br, end);
977 }
978 self.redispatch_after_loop(b);
979 Ok(())
980 }
981
982 fn compile_do_while(
983 &mut self,
984 b: &mut ChunkBuilder,
985 body: &Stmt,
986 test: &Expr,
987 ) -> Result<(), String> {
988 let start = b.current_pos();
989 self.loops.push(LoopCtx {
990 breaks: Vec::new(),
991 continues: Vec::new(),
992 break_depth: self.scope_depth,
993 continue_depth: self.scope_depth,
994 iter_depth: self.iter_depth,
995 catches_continue: true,
996 label: self.pending_label.take(),
997 });
998 self.compile_stmt(b, body)?;
999 let cont_target = b.current_pos();
1000 self.compile_condition(b, test)?;
1001 b.emit(Op::JumpIfTrue(start), 0);
1002 let ctx = self.loops.pop().unwrap();
1003 for c in ctx.continues {
1004 b.patch_jump(c, cont_target);
1005 }
1006 let end = b.current_pos();
1007 for br in ctx.breaks {
1008 b.patch_jump(br, end);
1009 }
1010 self.redispatch_after_loop(b);
1011 Ok(())
1012 }
1013
1014 fn compile_for(
1015 &mut self,
1016 b: &mut ChunkBuilder,
1017 init: &Option<Box<Stmt>>,
1018 test: &Option<Expr>,
1019 update: &Option<Expr>,
1020 body: &Stmt,
1021 ) -> Result<(), String> {
1022 let lexical_head = matches!(
1026 init.as_deref(),
1027 Some(Stmt {
1028 kind: StmtKind::Decl {
1029 kind: DeclKind::Let | DeclKind::Const,
1030 ..
1031 },
1032 ..
1033 })
1034 );
1035 let per_iteration = lexical_head;
1042 let copy_per_iteration = lexical_head
1043 && (crate::capture::stmt_captures(body)
1044 || init.as_deref().is_some_and(crate::capture::stmt_captures)
1045 || test.as_ref().is_some_and(crate::capture::expr_captures)
1046 || update.as_ref().is_some_and(crate::capture::expr_captures));
1047 if per_iteration {
1048 self.emit_push_scope(b);
1049 }
1050 if let Some(init) = init {
1051 self.compile_stmt(b, init)?;
1052 }
1053 if copy_per_iteration {
1054 self.emit_copy_scope(b);
1055 }
1056 let jfalse = match test {
1059 Some(t) => {
1060 self.compile_condition(b, t)?;
1061 Some(b.emit(Op::JumpIfFalse(0), 0))
1062 }
1063 None => None,
1064 };
1065 let top = b.current_pos();
1066 self.loops.push(LoopCtx {
1067 breaks: Vec::new(),
1068 continues: Vec::new(),
1069 break_depth: self.scope_depth,
1070 continue_depth: self.scope_depth,
1071 iter_depth: self.iter_depth,
1072 catches_continue: true,
1073 label: self.pending_label.take(),
1074 });
1075 self.compile_stmt(b, body)?;
1076 let cont_target = b.current_pos();
1077 if copy_per_iteration {
1078 self.emit_copy_scope(b);
1081 }
1082 if let Some(u) = update {
1083 self.compile_expr(b, u)?;
1084 b.emit(Op::Pop, 0);
1085 }
1086 match test {
1087 Some(t) => {
1088 self.compile_condition(b, t)?;
1089 b.emit(Op::JumpIfTrue(top), 0);
1090 }
1091 None => {
1100 b.emit(Op::LoadTrue, 0);
1101 b.emit(Op::JumpIfTrue(top), 0);
1102 }
1103 }
1104 let ctx = self.loops.pop().unwrap();
1105 for c in ctx.continues {
1106 b.patch_jump(c, cont_target);
1107 }
1108 let end = b.current_pos();
1109 if let Some(jf) = jfalse {
1110 b.patch_jump(jf, end);
1111 }
1112 for br in ctx.breaks {
1113 b.patch_jump(br, end);
1114 }
1115 if per_iteration {
1116 self.emit_pop_scope(b);
1117 }
1118 self.redispatch_after_loop(b);
1119 Ok(())
1120 }
1121
1122 fn compile_for_of(
1123 &mut self,
1124 b: &mut ChunkBuilder,
1125 declare: BindMode,
1126 target: &Expr,
1127 iter: &Expr,
1128 body: &Stmt,
1129 ) -> Result<(), String> {
1130 self.compile_expr(b, iter)?;
1131 b.emit(Op::CallBuiltin(ops::GETITER, 1), 0); self.iter_depth += 1;
1133 let r = self.loop_over(b, declare, target, body);
1134 self.iter_depth -= 1;
1135 r
1136 }
1137
1138 fn compile_for_in(
1139 &mut self,
1140 b: &mut ChunkBuilder,
1141 declare: BindMode,
1142 target: &Expr,
1143 object: &Expr,
1144 body: &Stmt,
1145 ) -> Result<(), String> {
1146 self.compile_expr(b, object)?;
1147 b.emit(Op::CallBuiltin(ops::FORIN_KEYS, 1), 0); b.emit(Op::CallBuiltin(ops::GETITER, 1), 0); self.iter_depth += 1;
1150 let r = self.loop_over(b, declare, target, body);
1151 self.iter_depth -= 1;
1152 r
1153 }
1154
1155 fn compile_for_await(
1159 &mut self,
1160 b: &mut ChunkBuilder,
1161 declare: BindMode,
1162 target: &Expr,
1163 iter: &Expr,
1164 body: &Stmt,
1165 ) -> Result<(), String> {
1166 let iter_tmp = self.tmp_name("aiter");
1167 self.compile_expr(b, iter)?;
1168 b.emit(Op::CallBuiltin(ops::GET_ASYNC_ITER, 1), 0); self.name_const(b, &iter_tmp);
1170 b.emit(Op::Swap, 0);
1171 b.emit(Op::CallBuiltin(ops::DECLARE, 2), 0);
1172 b.emit(Op::Pop, 0);
1173 let start = b.current_pos();
1174 self.load_local(b, &iter_tmp);
1176 b.emit(Op::CallBuiltin(ops::ASYNC_STEP, 1), 0); b.emit(Op::CallBuiltin(ops::AWAIT, 1), 0); let step_tmp = self.tmp_name("astep");
1179 self.name_const(b, &step_tmp);
1180 b.emit(Op::Swap, 0);
1181 b.emit(Op::CallBuiltin(ops::DECLARE, 2), 0);
1182 b.emit(Op::Pop, 0);
1183 self.load_local(b, &step_tmp);
1185 self.name_const(b, "done");
1186 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0);
1187 b.emit(Op::CallBuiltin(ops::TRUTHY, 1), 0);
1188 let jdone = b.emit(Op::JumpIfTrue(0), 0);
1189 self.load_local(b, &step_tmp);
1191 self.name_const(b, "value");
1192 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0); let per_iteration = matches!(declare, BindMode::Lexical | BindMode::Const);
1194 if per_iteration {
1195 self.emit_push_scope(b);
1196 }
1197 self.compile_bind(b, target, declare)?;
1198 self.loops.push(LoopCtx {
1199 breaks: Vec::new(),
1200 continues: Vec::new(),
1201 break_depth: self.scope_depth,
1202 continue_depth: self.scope_depth,
1203 iter_depth: self.iter_depth,
1204 catches_continue: true,
1205 label: self.pending_label.take(),
1206 });
1207 self.compile_stmt(b, body)?;
1208 let cont_target = b.current_pos();
1209 if per_iteration {
1210 self.emit_pop_scope(b);
1211 }
1212 b.emit(Op::Jump(start), 0);
1213 let ctx = self.loops.pop().unwrap();
1214 for c in ctx.continues {
1215 b.patch_jump(c, cont_target);
1216 }
1217 let break_target = b.current_pos();
1220 if per_iteration {
1221 b.emit(Op::CallBuiltin(ops::POP_SCOPE, 0), 0);
1222 b.emit(Op::Pop, 0);
1223 }
1224 self.load_local(b, &iter_tmp);
1227 b.emit(Op::CallBuiltin(ops::ITER_CLOSE, 1), 0);
1228 b.emit(Op::Pop, 0);
1229 let end = b.current_pos();
1230 b.patch_jump(jdone, end);
1231 for br in ctx.breaks {
1232 b.patch_jump(br, break_target);
1233 }
1234 self.redispatch_after_loop(b);
1235 Ok(())
1236 }
1237
1238 fn loop_over(
1240 &mut self,
1241 b: &mut ChunkBuilder,
1242 declare: BindMode,
1243 target: &Expr,
1244 body: &Stmt,
1245 ) -> Result<(), String> {
1246 let per_iteration = matches!(declare, BindMode::Lexical | BindMode::Const);
1249 let start = b.current_pos();
1250 b.emit(Op::CallBuiltin(ops::FORITER, 0), 0); let jdone = b.emit(Op::JumpIfFalse(0), 0); if per_iteration {
1253 self.emit_push_scope(b);
1254 }
1255 self.compile_bind(b, target, declare)?; self.loops.push(LoopCtx {
1257 breaks: Vec::new(),
1258 continues: Vec::new(),
1259 break_depth: self.scope_depth,
1260 continue_depth: self.scope_depth,
1261 iter_depth: self.iter_depth,
1262 catches_continue: true,
1263 label: self.pending_label.take(),
1264 });
1265 self.compile_stmt(b, body)?;
1266 let cont_target = b.current_pos();
1267 if per_iteration {
1268 self.emit_pop_scope(b);
1269 }
1270 b.emit(Op::Jump(start), 0);
1271 let ctx = self.loops.pop().unwrap();
1272 for c in ctx.continues {
1273 b.patch_jump(c, cont_target);
1274 }
1275 let done = b.current_pos();
1276 b.patch_jump(jdone, done);
1277 b.emit(Op::Pop, 0); let jafter = b.emit(Op::Jump(0), 0);
1279 let break_target = b.current_pos();
1280 if per_iteration {
1283 b.emit(Op::CallBuiltin(ops::POP_SCOPE, 0), 0);
1284 b.emit(Op::Pop, 0);
1285 }
1286 b.emit(Op::CallBuiltin(ops::ITER_CLOSE, 1), 0);
1287 b.emit(Op::Pop, 0); let end = b.current_pos();
1289 b.patch_jump(jafter, end);
1290 for br in ctx.breaks {
1291 b.patch_jump(br, break_target);
1292 }
1293 self.iter_depth -= 1;
1296 self.redispatch_after_loop(b);
1297 self.iter_depth += 1;
1298 Ok(())
1299 }
1300
1301 fn compile_switch(
1302 &mut self,
1303 b: &mut ChunkBuilder,
1304 disc: &Expr,
1305 cases: &[SwitchCase],
1306 ) -> Result<(), String> {
1307 let disc_tmp = self.tmp_name("switch");
1308 self.compile_expr(b, disc)?;
1309 self.name_const(b, &disc_tmp);
1310 b.emit(Op::Swap, 0);
1311 b.emit(Op::CallBuiltin(ops::DECLARE, 2), 0);
1312 b.emit(Op::Pop, 0);
1313 self.emit_push_scope(b);
1317 let mut body_jumps: Vec<Option<usize>> = Vec::new();
1319 let mut default_idx: Option<usize> = None;
1320 for (i, case) in cases.iter().enumerate() {
1321 match &case.test {
1322 Some(t) => {
1323 self.load_local(b, &disc_tmp);
1324 self.compile_expr(b, t)?;
1325 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
1326 let j = b.emit(Op::JumpIfTrue(0), 0);
1327 body_jumps.push(Some(j));
1328 }
1329 None => {
1330 default_idx = Some(i);
1331 body_jumps.push(None);
1332 }
1333 }
1334 }
1335 let no_match_jump = b.emit(Op::Jump(0), 0);
1337 self.loops.push(LoopCtx {
1338 breaks: Vec::new(),
1339 continues: Vec::new(),
1340 break_depth: self.scope_depth,
1341 continue_depth: self.scope_depth,
1342 iter_depth: self.iter_depth,
1343 catches_continue: false,
1344 label: None,
1345 });
1346 let mut body_starts: Vec<usize> = Vec::new();
1347 for case in cases {
1348 body_starts.push(b.current_pos());
1349 self.compile_stmts(b, &case.body)?;
1350 }
1351 let end = b.current_pos();
1352 for (i, j) in body_jumps.iter().enumerate() {
1354 if let Some(j) = j {
1355 b.patch_jump(*j, body_starts[i]);
1356 }
1357 }
1358 match default_idx {
1359 Some(i) => b.patch_jump(no_match_jump, body_starts[i]),
1360 None => b.patch_jump(no_match_jump, end),
1361 }
1362 let ctx = self.loops.pop().unwrap();
1363 for br in ctx.breaks {
1364 b.patch_jump(br, end);
1365 }
1366 self.emit_pop_scope(b);
1367 self.redispatch_after_loop(b);
1368 Ok(())
1369 }
1370
1371 fn compile_try(
1372 &mut self,
1373 b: &mut ChunkBuilder,
1374 block: &[Stmt],
1375 handler: &Option<(Option<Expr>, Vec<Stmt>)>,
1376 finalizer: &Option<Vec<Stmt>>,
1377 ) -> Result<(), String> {
1378 let block_chunk = self.compile_block_chunk(block)?;
1379 let handler_def = match handler {
1380 Some((param, body)) => {
1381 let param_name = match param {
1382 Some(Expr::Ident(n)) => Some(n.clone()),
1383 _ => None,
1384 };
1385 let hbody = self.compile_block_chunk(body)?;
1386 Some((param_name, hbody))
1387 }
1388 None => None,
1389 };
1390 let final_chunk = match finalizer {
1391 Some(f) => Some(self.compile_block_chunk(f)?),
1392 None => None,
1393 };
1394 let id = self.tries.len();
1395 self.tries.push(TryDef {
1396 block: block_chunk,
1397 handler: handler_def,
1398 finalizer: final_chunk,
1399 });
1400 b.emit(Op::LoadInt(id as i64), 0);
1401 b.emit(Op::CallBuiltin(ops::TRY, 1), 0);
1402 b.emit(Op::Pop, 0);
1403 self.emit_signal_dispatch(b);
1406 Ok(())
1407 }
1408
1409 fn compile_block_chunk(&mut self, stmts: &[Stmt]) -> Result<Chunk, String> {
1413 let mut cb = ChunkBuilder::new();
1414 let saved_slot_table = std::mem::take(&mut self.slots);
1419 let base = std::mem::replace(&mut self.chunk_loop_base, self.loops.len());
1420 let signals = std::mem::take(&mut self.chunk_signals);
1421 let depth = std::mem::take(&mut self.scope_depth);
1422 let iters = std::mem::take(&mut self.iter_depth);
1423 let sites = std::mem::take(&mut self.call_sites);
1424 let yields = std::mem::take(&mut self.yield_sites);
1425 let r = (|| {
1426 self.hoist_funcs(&mut cb, stmts)?;
1427 self.compile_stmts(&mut cb, stmts)
1428 })();
1429 self.chunk_loop_base = base;
1430 self.scope_depth = depth;
1431 self.iter_depth = iters;
1432 self.slots = saved_slot_table;
1433 self.chunk_signals |= signals;
1436 r?;
1437 let chunk = self.finish_chunk(cb);
1438 self.call_sites = sites;
1439 self.yield_sites = yields;
1440 Ok(chunk)
1441 }
1442
1443 fn build_function(
1445 &mut self,
1446 name: &str,
1447 params: &[Param],
1448 body: &[Stmt],
1449 is_generator: bool,
1450 is_async: bool,
1451 ) -> Result<usize, String> {
1452 let (param_slots, prologue) = self.lower_params(params)?;
1453 let mut fb = ChunkBuilder::new();
1454 let mut planned: Vec<Stmt> = prologue.clone();
1458 planned.extend_from_slice(body);
1459 let saved_slot_table = std::mem::replace(
1460 &mut self.slots,
1461 if self.debug || is_generator || is_async {
1462 Default::default()
1463 } else {
1464 crate::slots::plan(params, &planned, false)
1465 },
1466 );
1467 for name in crate::slots::param_names(params) {
1471 if let Some(slot) = self.slot_of(&name) {
1472 self.name_const(&mut fb, &name);
1473 fb.emit(Op::CallBuiltin(ops::GETLOCAL, 1), 0);
1474 fb.emit(Op::SetSlot(slot), 0);
1475 }
1476 }
1477 let saved_loops = std::mem::take(&mut self.loops);
1480 let saved_base = std::mem::replace(&mut self.chunk_loop_base, 0);
1481 let saved_signals = std::mem::take(&mut self.chunk_signals);
1482 let saved_depth = std::mem::take(&mut self.scope_depth);
1483 let saved_iters = std::mem::take(&mut self.iter_depth);
1484 let saved_agen = std::mem::replace(&mut self.in_async_generator, is_generator && is_async);
1485 let saved_strict = self.strict;
1488 self.strict = self.strict || has_use_strict(body);
1489 let saved_sites = std::mem::take(&mut self.call_sites);
1492 let saved_yields = std::mem::take(&mut self.yield_sites);
1493 let r = (|| {
1494 self.hoist_vars(&mut fb, body)?;
1499 self.hoist_funcs(&mut fb, &prologue)?;
1500 self.hoist_funcs(&mut fb, body)?;
1501 self.compile_stmts(&mut fb, &prologue)?;
1502 self.compile_stmts(&mut fb, body)
1503 })();
1504 self.loops = saved_loops;
1505 self.chunk_loop_base = saved_base;
1506 self.chunk_signals = saved_signals;
1507 self.scope_depth = saved_depth;
1508 self.iter_depth = saved_iters;
1509 self.in_async_generator = saved_agen;
1510 self.strict = saved_strict;
1511 self.slots = saved_slot_table;
1512 r?;
1513 let def = FuncDef {
1514 name: name.to_string(),
1515 params: param_slots,
1516 chunk: self.finish_chunk(fb),
1517 is_arrow: false,
1518 is_generator,
1519 is_async,
1520 is_method: false,
1521 self_name: false,
1522 };
1523 self.call_sites = saved_sites;
1524 self.yield_sites = saved_yields;
1525 self.functions.push((name.to_string(), def));
1526 Ok(self.functions.len() - 1)
1527 }
1528
1529 fn build_arrow(
1530 &mut self,
1531 params: &[Param],
1532 body: &FnBody,
1533 is_async: bool,
1534 ) -> Result<usize, String> {
1535 let stmts = match body {
1536 FnBody::Block(b) => b.clone(),
1537 FnBody::Expr(e) => vec![Stmt::from(StmtKind::Return(Some((**e).clone())))],
1538 };
1539 let id = self.build_function("", params, &stmts, false, is_async)?;
1540 self.functions[id].1.is_arrow = true;
1542 Ok(id)
1543 }
1544
1545 fn compile_class(&mut self, b: &mut ChunkBuilder, node: &ClassNode) -> Result<(), String> {
1550 let saved_strict = std::mem::replace(&mut self.strict, true);
1552 let r = self.compile_class_body(b, node);
1553 self.strict = saved_strict;
1554 r
1555 }
1556
1557 fn private_key(m: &ClassMember) -> Option<String> {
1560 match &m.key {
1561 Expr::Str(s) if !m.computed && s.starts_with('#') => Some(s.clone()),
1562 Expr::Ident(s) if !m.computed && s.starts_with('#') => Some(s.clone()),
1563 _ => None,
1564 }
1565 }
1566
1567 fn compile_class_body(&mut self, b: &mut ChunkBuilder, node: &ClassNode) -> Result<(), String> {
1568 let cname = node.name.clone().unwrap_or_default();
1569 self.name_const(b, &cname);
1571 match &node.parent {
1572 Some(p) => self.compile_expr(b, p)?,
1573 None => {
1574 b.emit(Op::LoadUndef, 0);
1575 }
1576 }
1577 let ctor = node
1578 .members
1579 .iter()
1580 .find(|m| m.kind == MemberKind::Constructor);
1581 match ctor {
1582 Some(m) => {
1583 let def_id = self.build_function(&cname, &m.params, &m.body, false, false)?;
1584 self.emit_mkfunc(b, def_id);
1585 }
1586 None => {
1587 b.emit(Op::LoadUndef, 0);
1588 }
1589 }
1590 b.emit(Op::CallBuiltin(ops::MKCLASS, 3), 0); let body_scope = node.name.is_some();
1604 if let Some(name) = &node.name {
1605 self.emit_push_scope(b);
1606 b.emit(Op::Dup, 0); self.declare_as(b, &Expr::Ident(name.clone()), BindMode::Lexical); }
1609
1610 let deferred = |k: &MemberKind| matches!(k, MemberKind::Field | MemberKind::StaticBlock);
1619 let ordered = node
1620 .members
1621 .iter()
1622 .filter(|m| !deferred(&m.kind))
1623 .chain(node.members.iter().filter(|m| deferred(&m.kind)));
1624 let mut static_block_n = 0usize;
1625 for m in ordered {
1626 match m.kind {
1627 MemberKind::Constructor => {}
1628 MemberKind::Field if m.is_static && Self::private_key(m).is_some() => {
1634 let key = Self::private_key(m).expect("guarded above");
1635 self.name_const(b, &key); b.emit(Op::LoadInt(member::STATIC_FIELD), 0);
1637 b.emit(Op::LoadTrue, 0); match &m.field_init {
1639 Some(e) => self.emit_keyed_value(b, &m.key, e, false, member::METHOD)?,
1640 None => {
1641 b.emit(Op::LoadUndef, 0);
1642 }
1643 }
1644 b.emit(Op::CallBuiltin(ops::DEF_MEMBER, 5), 0); }
1646 MemberKind::Field if m.is_static => {
1647 b.emit(Op::Dup, 0); self.emit_member_key(b, m)?; match &m.field_init {
1653 Some(e) => {
1656 self.emit_keyed_value(b, &m.key, e, m.computed, member::METHOD)?
1657 }
1658 None => {
1659 b.emit(Op::LoadUndef, 0);
1660 }
1661 }
1662 b.emit(Op::CallBuiltin(ops::SETATTR, 3), 0);
1664 b.emit(Op::Pop, 0); }
1666 MemberKind::Field => {
1667 self.emit_member_key(b, m)?;
1669 let init = m.field_init.clone().unwrap_or(Expr::Undefined);
1670 let name_anon = Self::is_anon_fn_def(&init);
1676 let stmts = vec![Stmt::from(StmtKind::Return(Some(init)))];
1677 let def_id = self.build_function("", &[], &stmts, false, false)?;
1678 self.emit_mkfunc(b, def_id);
1679 b.emit(
1680 if name_anon {
1681 Op::LoadTrue
1682 } else {
1683 Op::LoadFalse
1684 },
1685 0,
1686 );
1687 b.emit(Op::CallBuiltin(ops::DEF_FIELD, 4), 0);
1688 }
1689 MemberKind::StaticBlock => {
1690 static_block_n += 1;
1699 let slot = format!("@@staticBlock:{static_block_n}");
1700 self.name_const(b, &slot);
1702 b.emit(Op::LoadInt(member::METHOD), 0);
1703 b.emit(Op::LoadTrue, 0);
1704 let def_id = self.build_function("", &[], &m.body, false, false)?;
1705 self.functions[def_id].1.is_method = true;
1706 self.emit_mkfunc(b, def_id);
1707 b.emit(Op::CallBuiltin(ops::DEF_MEMBER, 5), 0);
1708 b.emit(Op::Dup, 0);
1710 self.name_const(b, &slot);
1711 b.emit(Op::CallBuiltin(ops::CALL_METHOD, 2), 0);
1712 b.emit(Op::Pop, 0);
1713 b.emit(Op::Dup, 0);
1715 self.name_const(b, &slot);
1716 b.emit(Op::CallBuiltin(ops::DELPROP_NAME, 2), 0);
1717 b.emit(Op::Pop, 0);
1718 }
1719 MemberKind::Method | MemberKind::Get | MemberKind::Set => {
1720 self.emit_member_key(b, m)?;
1722 let kind = match m.kind {
1723 MemberKind::Get => member::GET,
1724 MemberKind::Set => member::SET,
1725 _ => member::METHOD,
1726 };
1727 b.emit(Op::LoadInt(kind), 0);
1728 b.emit(
1729 if m.is_static {
1730 Op::LoadTrue
1731 } else {
1732 Op::LoadFalse
1733 },
1734 0,
1735 );
1736 let mname = match &m.key {
1740 Expr::Str(s) if !m.computed => match m.kind {
1741 MemberKind::Get => format!("get {s}"),
1742 MemberKind::Set => format!("set {s}"),
1743 _ => s.clone(),
1744 },
1745 _ => String::new(),
1746 };
1747 let def_id = self.build_function(
1748 &mname,
1749 &m.params,
1750 &m.body,
1751 m.is_generator,
1752 m.is_async,
1753 )?;
1754 self.functions[def_id].1.is_method = true;
1757 self.emit_mkfunc(b, def_id);
1758 b.emit(Op::CallBuiltin(ops::DEF_MEMBER, 5), 0);
1759 }
1760 }
1761 }
1762 if body_scope {
1763 self.emit_pop_scope(b);
1764 }
1765 Ok(())
1766 }
1767
1768 fn is_anon_fn_def(init: &Expr) -> bool {
1776 match init {
1777 Expr::Function { name: None, .. } => true,
1778 Expr::Class(node) => node.name.is_none(),
1779 _ => false,
1780 }
1781 }
1782
1783 fn infer_name(&mut self, b: &mut ChunkBuilder, init: &Expr, name: &str) {
1786 if !Self::is_anon_fn_def(init) {
1787 return;
1788 }
1789 b.emit(Op::Dup, 0);
1791 self.name_const(b, "name");
1792 self.strlit(b, name);
1793 b.emit(Op::CallBuiltin(ops::SETATTR, 3), 0);
1794 b.emit(Op::Pop, 0);
1795 }
1796
1797 fn emit_keyed_value(
1808 &mut self,
1809 b: &mut ChunkBuilder,
1810 key: &Expr,
1811 value: &Expr,
1812 computed: bool,
1813 kind: i64,
1814 ) -> Result<(), String> {
1815 match (Self::is_anon_fn_def(value), computed, key) {
1816 (true, false, Expr::Str(s)) => {
1817 self.compile_expr(b, value)?;
1818 let name = match kind {
1819 member::GET => format!("get {s}"),
1820 member::SET => format!("set {s}"),
1821 _ => s.clone(),
1822 };
1823 self.infer_name(b, value, &name);
1824 }
1825 (true, true, _) => {
1827 b.emit(Op::Dup, 0);
1828 b.emit(Op::LoadInt(kind), 0);
1829 self.compile_expr(b, value)?;
1830 b.emit(Op::CallBuiltin(ops::NAMED_EVAL, 3), 0);
1831 }
1832 _ => self.compile_expr(b, value)?,
1833 }
1834 Ok(())
1835 }
1836
1837 fn emit_member_key(&mut self, b: &mut ChunkBuilder, m: &ClassMember) -> Result<(), String> {
1840 if m.computed {
1841 self.compile_expr(b, &m.key)?;
1842 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0);
1843 } else if let Expr::Str(s) = &m.key {
1844 self.name_const(b, s);
1845 } else {
1846 self.compile_expr(b, &m.key)?;
1847 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0);
1848 }
1849 Ok(())
1850 }
1851
1852 fn compile_yield(
1854 &mut self,
1855 b: &mut ChunkBuilder,
1856 arg: &Option<Box<Expr>>,
1857 delegate: bool,
1858 ) -> Result<(), String> {
1859 if delegate && self.in_async_generator {
1860 match arg {
1864 Some(e) => self.compile_expr(b, e)?,
1865 None => {
1866 b.emit(Op::LoadUndef, 0);
1867 }
1868 }
1869 b.emit(Op::CallBuiltin(ops::GET_ASYNC_ITER, 1), 0); let start = b.current_pos();
1871 b.emit(Op::Dup, 0); b.emit(Op::CallBuiltin(ops::ASYNC_STEP, 1), 0); b.emit(Op::CallBuiltin(ops::AWAIT, 1), 0); b.emit(Op::Dup, 0); self.name_const(b, "done");
1876 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0);
1877 b.emit(Op::CallBuiltin(ops::TRUTHY, 1), 0);
1878 let jdone = b.emit(Op::JumpIfTrue(0), 0); self.name_const(b, "value");
1880 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0); let at = b.emit(Op::CallBuiltin(ops::YIELD, 1), 0); self.yield_sites.push((at, self.iter_depth));
1883 b.emit(Op::Pop, 0); b.emit(Op::Jump(start), 0);
1885 let done = b.current_pos();
1886 b.patch_jump(jdone, done);
1887 self.name_const(b, "value"); b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0); b.emit(Op::Swap, 0); b.emit(Op::Pop, 0); } else if delegate {
1892 let sent_tmp = self.tmp_name("delegated");
1897 match arg {
1898 Some(e) => self.compile_expr(b, e)?,
1899 None => {
1900 b.emit(Op::LoadUndef, 0);
1901 }
1902 }
1903 b.emit(Op::CallBuiltin(ops::GETITER, 1), 0); self.iter_depth += 1;
1909 self.name_const(b, &sent_tmp);
1910 b.emit(Op::LoadUndef, 0);
1911 b.emit(Op::CallBuiltin(ops::DECLARE, 2), 0);
1912 b.emit(Op::Pop, 0);
1913 let start = b.current_pos();
1914 b.emit(Op::Dup, 0); self.name_const(b, "next");
1916 self.load_local(b, &sent_tmp);
1917 b.emit(Op::CallBuiltin(ops::CALL_METHOD, 3), 0); b.emit(Op::Dup, 0);
1919 self.name_const(b, "done");
1920 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0);
1921 b.emit(Op::CallBuiltin(ops::TRUTHY, 1), 0);
1922 let jdone = b.emit(Op::JumpIfTrue(0), 0); self.name_const(b, "value");
1924 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0); let at = b.emit(Op::CallBuiltin(ops::YIELD, 1), 0); self.yield_sites.push((at, self.iter_depth));
1927 self.name_const(b, &sent_tmp);
1928 b.emit(Op::Swap, 0);
1929 b.emit(Op::CallBuiltin(ops::SETLOCAL, 2), 0);
1930 b.emit(Op::Pop, 0);
1931 b.emit(Op::Jump(start), 0);
1932 let done = b.current_pos();
1933 b.patch_jump(jdone, done);
1934 self.name_const(b, "value"); b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0); b.emit(Op::Swap, 0);
1937 b.emit(Op::Pop, 0); self.iter_depth -= 1;
1939 } else {
1940 match arg {
1941 Some(e) => self.compile_expr(b, e)?,
1942 None => {
1943 b.emit(Op::LoadUndef, 0);
1944 }
1945 }
1946 let at = b.emit(Op::CallBuiltin(ops::YIELD, 1), 0);
1948 self.yield_sites.push((at, self.iter_depth));
1949 }
1950 Ok(())
1951 }
1952
1953 fn lower_params(&mut self, params: &[Param]) -> Result<(Vec<ParamSlot>, Vec<Stmt>), String> {
1956 let mut slots = Vec::new();
1957 let mut prologue: Vec<Stmt> = Vec::new();
1958 for (i, p) in params.iter().enumerate() {
1959 if p.rest {
1960 let name = match &p.pattern {
1961 Expr::Ident(n) => n.clone(),
1962 _ => return Err("SyntaxError: rest parameter must be an identifier".into()),
1963 };
1964 slots.push(ParamSlot {
1965 name,
1966 rest: true,
1967 has_default: false,
1968 });
1969 continue;
1970 }
1971 match &p.pattern {
1972 Expr::Ident(name) => {
1973 slots.push(ParamSlot {
1974 name: name.clone(),
1975 rest: false,
1976 has_default: p.default.is_some(),
1977 });
1978 if let Some(d) = &p.default {
1979 prologue.push(default_stmt(name, d));
1980 }
1981 }
1982 pattern => {
1983 let synth = format!(".param{i}");
1984 slots.push(ParamSlot {
1985 name: synth.clone(),
1986 rest: false,
1987 has_default: p.default.is_some(),
1988 });
1989 if let Some(d) = &p.default {
1990 prologue.push(default_stmt(&synth, d));
1991 }
1992 prologue.push(Stmt::from(StmtKind::Decl {
1993 kind: DeclKind::Let,
1994 decls: vec![Declarator {
1995 target: pattern.clone(),
1996 init: Some(Expr::Ident(synth)),
1997 }],
1998 }));
1999 }
2000 }
2001 }
2002 Ok((slots, prologue))
2003 }
2004
2005 fn compile_expr(&mut self, b: &mut ChunkBuilder, e: &Expr) -> Result<(), String> {
2007 match e {
2008 Expr::Undefined => {
2009 b.emit(Op::LoadUndef, 0);
2010 }
2011 Expr::Hole => {
2015 b.emit(Op::LoadUndef, 0);
2016 }
2017 Expr::Null => {
2018 b.emit(Op::CallBuiltin(ops::LOAD_NULL, 0), 0);
2019 }
2020 Expr::True => {
2021 b.emit(Op::LoadTrue, 0);
2022 }
2023 Expr::False => {
2024 b.emit(Op::LoadFalse, 0);
2025 }
2026 Expr::Number(n) => {
2027 b.emit(Op::LoadFloat(*n), 0);
2028 }
2029 Expr::BigInt(digits) => {
2030 let k = b.add_constant(Value::str(digits));
2033 b.emit(Op::LoadConst(k), 0);
2034 b.emit(Op::CallBuiltin(ops::MKBIGINT, 1), 0);
2035 }
2036 Expr::Regex(pat, flags) => {
2037 let kp = b.add_constant(Value::str(pat));
2038 b.emit(Op::LoadConst(kp), 0);
2039 let kf = b.add_constant(Value::str(flags));
2040 b.emit(Op::LoadConst(kf), 0);
2041 b.emit(Op::CallBuiltin(ops::MKREGEX, 2), 0);
2042 }
2043 Expr::Str(s) => self.strlit(b, s),
2044 Expr::Template { quasis, exprs } => self.compile_template(b, quasis, exprs)?,
2045 Expr::TaggedTemplate {
2046 tag,
2047 quasis,
2048 raws,
2049 exprs,
2050 } => self.compile_tagged_template(b, tag, quasis, raws, exprs)?,
2051 Expr::Ident(n) => self.load_local(b, n),
2052 Expr::This => {
2053 b.emit(Op::CallBuiltin(ops::THIS, 0), 0);
2054 }
2055 Expr::Array(items) => self.compile_array(b, items)?,
2056 Expr::Object(props) => self.compile_object(b, props)?,
2057 Expr::Spread(inner) => self.compile_expr(b, inner)?,
2058 Expr::Logical(op, l, r) => self.compile_logical(b, *op, l, r)?,
2059 Expr::Unary(op, e) => self.compile_unary(b, *op, e)?,
2060 Expr::Binary(op, l, r) => self.compile_binary(b, *op, l, r)?,
2061 Expr::Conditional { test, cons, alt } => {
2062 self.compile_condition(b, test)?;
2063 let jf = b.emit(Op::JumpIfFalse(0), 0);
2064 self.compile_expr(b, cons)?;
2065 let je = b.emit(Op::Jump(0), 0);
2066 let els = b.current_pos();
2067 b.patch_jump(jf, els);
2068 self.compile_expr(b, alt)?;
2069 let end = b.current_pos();
2070 b.patch_jump(je, end);
2071 }
2072 Expr::Assign { target, value } => {
2073 self.compile_expr(b, value)?;
2074 if let Expr::Ident(n) = &**target {
2078 self.infer_name(b, value, n);
2079 }
2080 b.emit(Op::Dup, 0); self.compile_bind(b, target, BindMode::Assign)?;
2082 }
2083 Expr::Update { op, prefix, target } => self.compile_update(b, *op, *prefix, target)?,
2084 Expr::Call { .. } | Expr::Member { .. } | Expr::Index { .. }
2087 if self.opt_chain.is_empty() && Self::spine_has_optional(e) =>
2088 {
2089 self.compile_chain_root(b, e)?
2090 }
2091 Expr::Call {
2092 func,
2093 args,
2094 optional,
2095 } => self.compile_call(b, func, args, *optional)?,
2096 Expr::New { callee, args } => self.compile_new(b, callee, args)?,
2097 Expr::Member {
2098 object,
2099 property,
2100 optional,
2101 } => self.compile_member(b, object, property, *optional)?,
2102 Expr::Index {
2103 object,
2104 index,
2105 optional,
2106 } => self.compile_index(b, object, index, *optional)?,
2107 Expr::Function {
2108 params,
2109 body,
2110 is_arrow,
2111 name,
2112 is_generator,
2113 is_async,
2114 is_method,
2115 } => {
2116 let def_id = if *is_arrow {
2117 self.build_arrow(params, body, *is_async)?
2118 } else {
2119 let n = name.clone().unwrap_or_default();
2120 let stmts = match body {
2121 FnBody::Block(b) => b.clone(),
2122 FnBody::Expr(e) => vec![Stmt::from(StmtKind::Return(Some((**e).clone())))],
2123 };
2124 let id = self.build_function(&n, params, &stmts, *is_generator, *is_async)?;
2125 if name.is_some() {
2129 self.functions[id].1.self_name = true;
2130 }
2131 self.functions[id].1.is_method = *is_method;
2132 id
2133 };
2134 self.emit_mkfunc(b, def_id);
2135 }
2136 Expr::Class(node) => self.compile_class(b, node)?,
2137 Expr::Super => {
2138 b.emit(Op::LoadUndef, 0);
2141 }
2142 Expr::NewTarget => {
2143 b.emit(Op::CallBuiltin(ops::NEW_TARGET, 0), 0);
2144 }
2145 Expr::Yield { arg, delegate } => self.compile_yield(b, arg, *delegate)?,
2146 Expr::Await(inner) => {
2147 self.compile_expr(b, inner)?;
2148 b.emit(Op::CallBuiltin(ops::AWAIT, 1), 0);
2149 }
2150 Expr::Sequence(items) => {
2151 for (i, it) in items.iter().enumerate() {
2152 self.compile_expr(b, it)?;
2153 if i + 1 < items.len() {
2154 b.emit(Op::Pop, 0);
2155 }
2156 }
2157 }
2158 }
2159 Ok(())
2160 }
2161
2162 fn compile_template(
2163 &mut self,
2164 b: &mut ChunkBuilder,
2165 quasis: &[String],
2166 exprs: &[Expr],
2167 ) -> Result<(), String> {
2168 let mut n = 0;
2169 for (i, q) in quasis.iter().enumerate() {
2170 let k = b.add_constant(Value::str(q));
2171 b.emit(Op::LoadConst(k), 0);
2172 n += 1;
2173 if i < exprs.len() {
2174 self.compile_expr(b, &exprs[i])?;
2175 b.emit(Op::CallBuiltin(ops::TOSTR, 1), 0);
2176 n += 1;
2177 }
2178 }
2179 b.emit(Op::CallBuiltin(ops::MKSTR, argc(n)?), 0);
2180 Ok(())
2181 }
2182
2183 fn compile_tagged_template(
2187 &mut self,
2188 b: &mut ChunkBuilder,
2189 tag: &Expr,
2190 quasis: &[String],
2191 raws: &[String],
2192 exprs: &[Expr],
2193 ) -> Result<(), String> {
2194 self.compile_expr(b, tag)?;
2195 let n = quasis.len();
2196 let m = exprs.len();
2197 b.emit(Op::LoadInt(n as i64), 0);
2198 b.emit(Op::LoadInt(m as i64), 0);
2199 for q in quasis {
2200 self.strlit(b, q); }
2202 for r in raws {
2203 self.strlit(b, r); }
2205 for e in exprs {
2206 self.compile_expr(b, e)?; }
2208 b.emit(Op::CallBuiltin(ops::TAG_TMPL, argc(3 + 2 * n + m)?), 0);
2209 Ok(())
2210 }
2211
2212 fn compile_array(&mut self, b: &mut ChunkBuilder, items: &[Expr]) -> Result<(), String> {
2213 if items.iter().any(|e| matches!(e, Expr::Spread(_))) {
2214 for it in items {
2219 match it {
2220 Expr::Spread(inner) => {
2221 b.emit(Op::LoadInt(1), 0);
2222 self.compile_expr(b, inner)?;
2223 }
2224 Expr::Hole => {
2225 b.emit(Op::LoadInt(2), 0);
2226 b.emit(Op::LoadUndef, 0);
2227 }
2228 _ => {
2229 b.emit(Op::LoadInt(0), 0);
2230 self.compile_expr(b, it)?;
2231 }
2232 }
2233 }
2234 b.emit(Op::CallBuiltin(ops::BUILD_ARGS, argc(items.len() * 2)?), 0);
2235 } else if items.len() <= u8::MAX as usize {
2236 for it in items {
2237 self.compile_expr(b, it)?;
2238 }
2239 b.emit(Op::CallBuiltin(ops::MKARR, argc(items.len())?), 0);
2240 self.mark_literal_holes(b, items);
2241 } else {
2242 b.emit(Op::CallBuiltin(ops::MKARR, 0), 0); for (i, it) in items.iter().enumerate() {
2248 b.emit(Op::Dup, 0); b.emit(Op::LoadInt(i as i64), 0); self.compile_expr(b, it)?; b.emit(Op::CallBuiltin(ops::SETITEM, 3), 0); b.emit(Op::Pop, 0); }
2254 self.mark_literal_holes(b, items);
2257 }
2258 Ok(())
2259 }
2260
2261 fn mark_literal_holes(&mut self, b: &mut ChunkBuilder, items: &[Expr]) {
2265 for (i, it) in items.iter().enumerate() {
2266 if !matches!(it, Expr::Hole) {
2267 continue;
2268 }
2269 b.emit(Op::Dup, 0); b.emit(Op::LoadInt(i as i64), 0); b.emit(Op::CallBuiltin(ops::MARK_HOLE, 2), 0); b.emit(Op::Pop, 0); }
2274 }
2275
2276 fn compile_object(&mut self, b: &mut ChunkBuilder, props: &[Prop]) -> Result<(), String> {
2277 let mut seen_accessor: Vec<String> = Vec::new();
2283 let data: Vec<&Prop> = props
2284 .iter()
2285 .filter(|p| match p {
2286 Prop::Accessor { key, computed, .. } => {
2287 let literal = match (key, computed) {
2290 (Expr::Str(s), false) => Some(s.clone()),
2291 _ => None,
2292 };
2293 match literal {
2294 Some(k) if seen_accessor.contains(&k) => false,
2295 Some(k) => {
2296 seen_accessor.push(k);
2297 true
2298 }
2299 None => true,
2300 }
2301 }
2302 _ => true,
2303 })
2304 .collect();
2305 let has_spread = data.iter().any(|p| matches!(p, Prop::Spread(_)));
2306 if data.len() * 3 > u8::MAX as usize && !has_spread {
2312 b.emit(Op::CallBuiltin(ops::MKOBJ, 0), 0); for p in &data {
2314 if let Prop::KeyValue {
2315 key,
2316 value,
2317 computed,
2318 } = p
2319 {
2320 b.emit(Op::Dup, 0); self.compile_expr(b, key)?;
2322 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0); self.emit_keyed_value(b, key, value, *computed, member::METHOD)?;
2324 b.emit(Op::CallBuiltin(ops::SETITEM, 3), 0); b.emit(Op::Pop, 0); }
2327 }
2328 return self.compile_object_accessors(b, props);
2331 }
2332 for p in &data {
2333 match p {
2334 Prop::KeyValue {
2335 key,
2336 value,
2337 computed,
2338 } => {
2339 b.emit(Op::LoadInt(0), 0);
2340 self.compile_expr(b, key)?;
2343 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0);
2344 self.emit_keyed_value(b, key, value, *computed, member::METHOD)?;
2345 }
2346 Prop::Spread(src) => {
2347 b.emit(Op::LoadInt(1), 0);
2348 self.compile_expr(b, src)?;
2349 b.emit(Op::LoadUndef, 0);
2350 }
2351 Prop::Accessor { key, computed, .. } => {
2354 let _ = computed; b.emit(Op::LoadInt(2), 0);
2356 self.compile_expr(b, key)?;
2357 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0);
2358 b.emit(Op::LoadUndef, 0);
2359 }
2360 }
2361 }
2362 b.emit(Op::CallBuiltin(ops::MKOBJ, argc(data.len() * 3)?), 0); self.compile_object_accessors(b, props)
2364 }
2365
2366 fn compile_object_accessors(
2369 &mut self,
2370 b: &mut ChunkBuilder,
2371 props: &[Prop],
2372 ) -> Result<(), String> {
2373 for p in props {
2374 if let Prop::Accessor {
2375 key,
2376 computed,
2377 is_getter,
2378 func,
2379 } = p
2380 {
2381 if *computed {
2382 self.compile_expr(b, key)?;
2383 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0);
2384 } else if let Expr::Str(s) = key {
2385 self.name_const(b, s);
2386 } else {
2387 self.compile_expr(b, key)?;
2388 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0);
2389 }
2390 let kind = if *is_getter { member::GET } else { member::SET };
2391 b.emit(Op::LoadInt(kind), 0);
2392 if *computed {
2397 self.compile_expr(b, func)?;
2398 } else if let Expr::Str(s) = key {
2399 self.compile_expr(b, func)?;
2400 let prefix = if *is_getter { "get" } else { "set" };
2401 self.infer_name(b, func, &format!("{prefix} {s}"));
2402 } else {
2403 self.compile_expr(b, func)?;
2404 }
2405 b.emit(Op::CallBuiltin(ops::DEF_ACCESSOR, 4), 0);
2406 }
2407 }
2408 Ok(())
2409 }
2410
2411 fn compile_logical(
2412 &mut self,
2413 b: &mut ChunkBuilder,
2414 op: LogicalOp,
2415 l: &Expr,
2416 r: &Expr,
2417 ) -> Result<(), String> {
2418 self.compile_expr(b, l)?;
2419 b.emit(Op::Dup, 0);
2420 let test_op = match op {
2421 LogicalOp::And | LogicalOp::Or => ops::TRUTHY,
2422 LogicalOp::Nullish => ops::NULLISH,
2423 };
2424 b.emit(Op::CallBuiltin(test_op, 1), 0);
2425 let jump = match op {
2426 LogicalOp::And => b.emit(Op::JumpIfFalse(0), 0), LogicalOp::Or => b.emit(Op::JumpIfTrue(0), 0), LogicalOp::Nullish => b.emit(Op::JumpIfFalse(0), 0), };
2430 b.emit(Op::Pop, 0); self.compile_expr(b, r)?;
2432 let end = b.current_pos();
2433 b.patch_jump(jump, end);
2434 Ok(())
2435 }
2436
2437 fn compile_unary(&mut self, b: &mut ChunkBuilder, op: UnOp, e: &Expr) -> Result<(), String> {
2438 match op {
2439 UnOp::Neg => {
2440 self.compile_expr(b, e)?;
2441 b.emit(Op::Negate, 0);
2442 }
2443 UnOp::Not => {
2444 self.compile_condition(b, e)?;
2445 b.emit(Op::LogNot, 0);
2446 }
2447 UnOp::Pos => {
2448 b.emit(Op::LoadInt(unop::POS), 0);
2449 self.compile_expr(b, e)?;
2450 b.emit(Op::CallBuiltin(ops::UNARY, 2), 0);
2451 }
2452 UnOp::BitNot => {
2453 b.emit(Op::LoadInt(unop::BITNOT), 0);
2454 self.compile_expr(b, e)?;
2455 b.emit(Op::CallBuiltin(ops::UNARY, 2), 0);
2456 }
2457 UnOp::TypeOf => {
2458 if let Expr::Ident(n) = e {
2462 if let Some(slot) = self.slot_of(n) {
2466 b.emit(Op::GetSlot(slot), 0);
2467 b.emit(Op::CallBuiltin(ops::TYPEOF, 1), 0);
2468 return Ok(());
2469 }
2470 self.name_const(b, n);
2471 b.emit(Op::CallBuiltin(ops::TYPEOF_NAME, 1), 0);
2472 } else {
2473 self.compile_expr(b, e)?;
2474 b.emit(Op::CallBuiltin(ops::TYPEOF, 1), 0);
2475 }
2476 }
2477 UnOp::Void => {
2478 self.compile_expr(b, e)?;
2479 b.emit(Op::Pop, 0);
2480 b.emit(Op::LoadUndef, 0);
2481 }
2482 UnOp::Delete => match e {
2483 Expr::Member {
2484 object, property, ..
2485 } => {
2486 self.compile_expr(b, object)?;
2487 self.name_const(b, property);
2488 b.emit(Op::CallBuiltin(ops::DELPROP_NAME, 2), 0);
2489 }
2490 Expr::Index { object, index, .. } => {
2491 self.compile_expr(b, object)?;
2492 self.compile_expr(b, index)?;
2493 b.emit(Op::CallBuiltin(ops::DELITEM, 2), 0);
2494 }
2495 _ => {
2496 b.emit(Op::LoadTrue, 0);
2497 }
2498 },
2499 }
2500 Ok(())
2501 }
2502
2503 fn compile_binary(
2504 &mut self,
2505 b: &mut ChunkBuilder,
2506 op: BinOp,
2507 l: &Expr,
2508 r: &Expr,
2509 ) -> Result<(), String> {
2510 macro_rules! native {
2513 ($opc:expr) => {{
2514 self.compile_expr(b, l)?;
2515 self.compile_expr(b, r)?;
2516 b.emit($opc, 0);
2517 return Ok(());
2518 }};
2519 }
2520 match op {
2521 BinOp::Add => native!(Op::Add),
2522 BinOp::Sub => native!(Op::Sub),
2523 BinOp::Mul => native!(Op::Mul),
2524 BinOp::Div => {
2525 self.compile_expr(b, l)?;
2529 self.compile_expr(b, r)?;
2530 b.emit(Op::CallBuiltin(ops::DIV, 2), 0);
2531 return Ok(());
2532 }
2533 BinOp::Mod => native!(Op::Mod),
2534 BinOp::Pow => {
2538 self.compile_expr(b, l)?;
2539 self.compile_expr(b, r)?;
2540 b.emit(Op::CallBuiltin(ops::POW, 2), 0);
2541 return Ok(());
2542 }
2543 BinOp::Lt => native!(Op::NumLt),
2544 BinOp::Le => native!(Op::NumLe),
2545 BinOp::Gt => native!(Op::NumGt),
2546 BinOp::Ge => native!(Op::NumGe),
2547 BinOp::EqEqEq => {
2548 self.compile_expr(b, l)?;
2549 self.compile_expr(b, r)?;
2550 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
2551 }
2552 BinOp::NeEqEq => {
2553 self.compile_expr(b, l)?;
2554 self.compile_expr(b, r)?;
2555 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
2556 b.emit(Op::LogNot, 0);
2557 }
2558 BinOp::EqEq => {
2559 self.compile_expr(b, l)?;
2560 self.compile_expr(b, r)?;
2561 b.emit(Op::CallBuiltin(ops::LOOSE_EQ, 2), 0);
2562 }
2563 BinOp::NeEq => {
2564 self.compile_expr(b, l)?;
2565 self.compile_expr(b, r)?;
2566 b.emit(Op::CallBuiltin(ops::LOOSE_EQ, 2), 0);
2567 b.emit(Op::LogNot, 0);
2568 }
2569 BinOp::In => {
2570 match l {
2574 Expr::Ident(n) if n.starts_with('#') => self.name_const(b, n),
2575 _ => self.compile_expr(b, l)?,
2576 }
2577 self.compile_expr(b, r)?;
2578 b.emit(Op::CallBuiltin(ops::CONTAINS, 2), 0);
2579 }
2580 BinOp::InstanceOf => {
2581 self.compile_expr(b, l)?;
2582 self.compile_expr(b, r)?;
2583 b.emit(Op::CallBuiltin(ops::INSTANCEOF, 2), 0);
2584 }
2585 BinOp::BitAnd => self.emit_bitwise(b, bop::BITAND, l, r)?,
2586 BinOp::BitOr => self.emit_bitwise(b, bop::BITOR, l, r)?,
2587 BinOp::BitXor => self.emit_bitwise(b, bop::BITXOR, l, r)?,
2588 BinOp::Shl => self.emit_bitwise(b, bop::SHL, l, r)?,
2589 BinOp::Shr => self.emit_bitwise(b, bop::SHR, l, r)?,
2590 BinOp::UShr => self.emit_bitwise(b, bop::USHR, l, r)?,
2591 }
2592 Ok(())
2593 }
2594
2595 fn emit_bitwise(
2596 &mut self,
2597 b: &mut ChunkBuilder,
2598 tag: i64,
2599 l: &Expr,
2600 r: &Expr,
2601 ) -> Result<(), String> {
2602 b.emit(Op::LoadInt(tag), 0);
2603 self.compile_expr(b, l)?;
2604 self.compile_expr(b, r)?;
2605 b.emit(Op::CallBuiltin(ops::BINOP, 3), 0);
2606 Ok(())
2607 }
2608
2609 fn compile_update(
2610 &mut self,
2611 b: &mut ChunkBuilder,
2612 op: UpdateOp,
2613 prefix: bool,
2614 target: &Expr,
2615 ) -> Result<(), String> {
2616 let tag = if matches!(op, UpdateOp::Inc) { 1 } else { -1 };
2621 if let Expr::Ident(n) = target {
2627 if self.slots.consts.contains(n) {
2633 self.throw_const_assignment(b);
2634 return Ok(());
2635 }
2636 if let Some(slot) = self.numeric_slot_of(n) {
2637 b.emit(Op::GetSlot(slot), 0); if !prefix {
2639 b.emit(Op::Dup, 0); }
2641 b.emit(Op::LoadFloat(tag as f64), 0);
2642 b.emit(Op::Add, 0); if prefix {
2644 b.emit(Op::Dup, 0); }
2646 b.emit(Op::SetSlot(slot), 0); return Ok(());
2648 }
2649 }
2650 b.emit(Op::LoadInt(tag), 0);
2651 self.compile_expr(b, target)?; b.emit(Op::CallBuiltin(ops::NUM_STEP, 2), 0); if prefix {
2654 b.emit(Op::Swap, 0); b.emit(Op::Pop, 0); b.emit(Op::Dup, 0); self.compile_bind(b, target, BindMode::Assign)?; } else {
2660 self.compile_bind(b, target, BindMode::Assign)?; }
2663 Ok(())
2664 }
2665
2666 fn compile_member(
2667 &mut self,
2668 b: &mut ChunkBuilder,
2669 object: &Expr,
2670 property: &str,
2671 optional: bool,
2672 ) -> Result<(), String> {
2673 if matches!(object, Expr::Super) {
2675 self.name_const(b, property);
2676 b.emit(Op::CallBuiltin(ops::SUPER_GET, 1), 0);
2677 return Ok(());
2678 }
2679 self.compile_expr(b, object)?;
2680 if optional {
2681 let jshort = self.emit_optional_guard(b);
2682 self.name_const(b, property);
2683 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0);
2684 match self.opt_chain.last_mut() {
2688 Some(frame) => frame.push(jshort),
2689 None => {
2690 let end = b.current_pos();
2691 b.patch_jump(jshort, end);
2692 }
2693 }
2694 } else {
2695 self.name_const(b, property);
2696 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0);
2697 }
2698 Ok(())
2699 }
2700
2701 fn compile_index(
2702 &mut self,
2703 b: &mut ChunkBuilder,
2704 object: &Expr,
2705 index: &Expr,
2706 optional: bool,
2707 ) -> Result<(), String> {
2708 self.compile_expr(b, object)?;
2709 if optional {
2710 let jshort = self.emit_optional_guard(b);
2711 self.compile_off_spine(b, index)?;
2712 b.emit(Op::CallBuiltin(ops::GETITEM, 2), 0);
2713 match self.opt_chain.last_mut() {
2714 Some(frame) => frame.push(jshort),
2715 None => {
2716 let end = b.current_pos();
2717 b.patch_jump(jshort, end);
2718 }
2719 }
2720 } else {
2721 self.compile_off_spine(b, index)?;
2722 b.emit(Op::CallBuiltin(ops::GETITEM, 2), 0);
2723 }
2724 Ok(())
2725 }
2726
2727 fn emit_push_scope(&mut self, b: &mut ChunkBuilder) {
2733 b.emit(Op::CallBuiltin(ops::PUSH_SCOPE, 0), 0);
2734 b.emit(Op::Pop, 0);
2735 self.scope_depth += 1;
2736 }
2737
2738 fn emit_pop_scope(&mut self, b: &mut ChunkBuilder) {
2739 b.emit(Op::CallBuiltin(ops::POP_SCOPE, 0), 0);
2740 b.emit(Op::Pop, 0);
2741 self.scope_depth -= 1;
2742 }
2743
2744 fn emit_copy_scope(&self, b: &mut ChunkBuilder) {
2747 b.emit(Op::CallBuiltin(ops::COPY_SCOPE, 0), 0);
2748 b.emit(Op::Pop, 0);
2749 }
2750
2751 fn finish_chunk(&mut self, b: ChunkBuilder) -> Chunk {
2763 let sites = std::mem::take(&mut self.call_sites);
2764 let yields = std::mem::take(&mut self.yield_sites);
2765 let chunk = b.build();
2766 crate::host::register_call_sites(chunk.op_hash, sites);
2767 crate::host::register_yield_sites(chunk.op_hash, yields);
2768 chunk
2769 }
2770
2771 fn note_call_site(&mut self, at: usize, callee: &Expr) {
2775 if let Some(text) = callee_text(callee) {
2776 self.call_sites.push((at, text));
2777 }
2778 }
2779
2780 fn emit_close_iters_under_value(&self, b: &mut ChunkBuilder) {
2781 for _ in 0..self.iter_depth {
2782 b.emit(Op::Swap, 0); b.emit(Op::CallBuiltin(ops::ITER_CLOSE, 1), 0); b.emit(Op::Pop, 0); }
2786 }
2787
2788 fn emit_close_iters(&self, b: &mut ChunkBuilder, target: usize) {
2789 for _ in target..self.iter_depth {
2790 b.emit(Op::CallBuiltin(ops::ITER_CLOSE, 1), 0);
2791 b.emit(Op::Pop, 0);
2792 }
2793 }
2794
2795 fn emit_unwind_scopes(&self, b: &mut ChunkBuilder, target: usize) {
2798 for _ in target..self.scope_depth {
2799 b.emit(Op::CallBuiltin(ops::POP_SCOPE, 0), 0);
2800 b.emit(Op::Pop, 0);
2801 }
2802 }
2803
2804 fn emit_signal_jump(&mut self, b: &mut ChunkBuilder, op: u16, label: Option<&str>, line: u32) {
2806 self.name_const(b, label.unwrap_or(""));
2807 b.emit(Op::CallBuiltin(op, 1), line);
2808 b.emit(Op::Pop, line);
2809 self.chunk_signals = true;
2810 }
2811
2812 fn emit_signal_dispatch(&mut self, b: &mut ChunkBuilder) {
2818 let brk = self
2825 .loops
2826 .len()
2827 .checked_sub(1)
2828 .filter(|i| *i >= self.chunk_loop_base);
2829 let cont = self
2830 .loops
2831 .iter()
2832 .rposition(|c| c.catches_continue)
2833 .filter(|i| *i >= self.chunk_loop_base);
2834 let tag_of = |i: Option<usize>, loops: &[LoopCtx]| match i {
2835 Some(i) => loops[i]
2836 .label
2837 .clone()
2838 .unwrap_or_else(|| unwind::PLAIN_LOOP.to_string()),
2839 None => unwind::NO_LOOP.to_string(),
2840 };
2841 let brk_tag = tag_of(brk, &self.loops);
2842 let cont_tag = tag_of(cont, &self.loops);
2843 self.name_const(b, &brk_tag);
2844 self.name_const(b, &cont_tag);
2845 b.emit(Op::CallBuiltin(ops::SIG_UNWIND, 2), 0); let Some(idx) = brk else {
2847 b.emit(Op::Pop, 0);
2850 return;
2851 };
2852 b.emit(Op::Dup, 0);
2853 b.emit(Op::LoadInt(unwind::BREAK), 0);
2854 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
2855 let jb = b.emit(Op::JumpIfTrue(0), 0);
2856 let jc = cont.map(|_| {
2857 b.emit(Op::Dup, 0);
2858 b.emit(Op::LoadInt(unwind::CONTINUE), 0);
2859 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
2860 b.emit(Op::JumpIfTrue(0), 0)
2861 });
2862 b.emit(Op::Pop, 0); let jafter = b.emit(Op::Jump(0), 0);
2864 let (brk_scope, brk_iter) = (self.loops[idx].break_depth, self.loops[idx].iter_depth);
2870 let brk_land = b.current_pos();
2871 b.emit(Op::Pop, 0);
2872 self.emit_unwind_scopes(b, brk_scope);
2873 self.emit_close_iters(b, brk_iter);
2874 let brk_jump = b.emit(Op::Jump(0), 0);
2875 let cont_jump = jc.map(|_| {
2876 let (cs, ci) = cont
2877 .map(|i| (self.loops[i].continue_depth, self.loops[i].iter_depth))
2878 .unwrap_or((self.scope_depth, self.iter_depth));
2879 let cont_land = b.current_pos();
2880 b.emit(Op::Pop, 0);
2881 self.emit_unwind_scopes(b, cs);
2882 self.emit_close_iters(b, ci);
2883 (cont_land, b.emit(Op::Jump(0), 0))
2884 });
2885 let after = b.current_pos();
2886 b.patch_jump(jb, brk_land);
2887 if let (Some(jc), Some((cont_land, _))) = (jc, cont_jump) {
2888 b.patch_jump(jc, cont_land);
2889 }
2890 b.patch_jump(jafter, after);
2891 self.loops[idx].breaks.push(brk_jump);
2892 if let (Some(cont_idx), Some((_, cj))) = (cont, cont_jump) {
2893 self.loops[cont_idx].continues.push(cj);
2894 }
2895 }
2896
2897 fn spine_has_optional(e: &Expr) -> bool {
2902 match e {
2903 Expr::Member {
2904 object, optional, ..
2905 } => *optional || Self::spine_has_optional(object),
2906 Expr::Index {
2907 object, optional, ..
2908 } => *optional || Self::spine_has_optional(object),
2909 Expr::Call { func, optional, .. } => *optional || Self::spine_has_optional(func),
2910 _ => false,
2911 }
2912 }
2913
2914 fn compile_chain_root(&mut self, b: &mut ChunkBuilder, e: &Expr) -> Result<(), String> {
2917 self.opt_chain.push(Vec::new());
2918 let r = self.compile_expr(b, e);
2919 let pending = self.opt_chain.pop().unwrap_or_default();
2920 r?;
2921 let end = b.current_pos();
2922 for j in pending {
2923 b.patch_jump(j, end);
2924 }
2925 Ok(())
2926 }
2927
2928 fn compile_off_spine(&mut self, b: &mut ChunkBuilder, e: &Expr) -> Result<(), String> {
2932 let saved = std::mem::take(&mut self.opt_chain);
2933 let r = self.compile_expr(b, e);
2934 self.opt_chain = saved;
2935 r
2936 }
2937
2938 fn emit_optional_guard(&mut self, b: &mut ChunkBuilder) -> usize {
2939 b.emit(Op::Dup, 0);
2940 b.emit(Op::CallBuiltin(ops::NULLISH, 1), 0);
2941 let jnull = b.emit(Op::JumpIfFalse(0), 0); b.emit(Op::Pop, 0);
2944 b.emit(Op::LoadUndef, 0);
2945 let jend = b.emit(Op::Jump(0), 0);
2946 let cont = b.current_pos();
2947 b.patch_jump(jnull, cont);
2948 jend
2949 }
2950
2951 fn compile_optional_call(
2956 &mut self,
2957 b: &mut ChunkBuilder,
2958 func: &Expr,
2959 args: &[Expr],
2960 ) -> Result<(), String> {
2961 let has_spread = args.iter().any(|a| matches!(a, Expr::Spread(_)));
2962 if let Expr::Member {
2963 object,
2964 property,
2965 optional: obj_optional,
2966 } = func
2967 {
2968 self.compile_expr(b, object)?; let jobj = if *obj_optional {
2970 Some(self.emit_optional_guard(b))
2971 } else {
2972 None
2973 };
2974 b.emit(Op::Dup, 0); self.name_const(b, property); b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0); b.emit(Op::Dup, 0);
2979 b.emit(Op::CallBuiltin(ops::NULLISH, 1), 0);
2980 let jlive = b.emit(Op::JumpIfFalse(0), 0);
2981 b.emit(Op::Pop, 0);
2982 b.emit(Op::Pop, 0);
2983 b.emit(Op::LoadUndef, 0);
2984 let jend = b.emit(Op::Jump(0), 0);
2985 let live = b.current_pos();
2986 b.patch_jump(jlive, live);
2987 let via = if has_spread { "apply" } else { "call" };
2989 self.name_const(b, via); b.emit(Op::Rot, 0); let extra = if has_spread {
2992 self.compile_spread_args(b, args)?; 1
2994 } else {
2995 for a in args {
2996 self.compile_expr(b, a)?;
2997 }
2998 args.len()
2999 };
3000 b.emit(Op::CallBuiltin(ops::CALL_METHOD, argc(3 + extra)?), 0);
3001 match self.opt_chain.last_mut() {
3002 Some(frame) => {
3003 frame.push(jend);
3004 if let Some(j) = jobj {
3005 frame.push(j);
3006 }
3007 }
3008 None => {
3009 let end = b.current_pos();
3010 b.patch_jump(jend, end);
3011 if let Some(j) = jobj {
3012 b.patch_jump(j, end);
3013 }
3014 }
3015 }
3016 return Ok(());
3017 }
3018 if let Expr::Index {
3024 object,
3025 index,
3026 optional: obj_optional,
3027 } = func
3028 {
3029 self.compile_expr(b, object)?; let jobj = if *obj_optional {
3031 Some(self.emit_optional_guard(b))
3032 } else {
3033 None
3034 };
3035 b.emit(Op::Dup, 0); self.compile_expr(b, index)?; b.emit(Op::CallBuiltin(ops::GETITEM, 2), 0); b.emit(Op::Dup, 0);
3039 b.emit(Op::CallBuiltin(ops::NULLISH, 1), 0);
3040 let jlive = b.emit(Op::JumpIfFalse(0), 0);
3041 b.emit(Op::Pop, 0);
3042 b.emit(Op::Pop, 0);
3043 b.emit(Op::LoadUndef, 0);
3044 let jend = b.emit(Op::Jump(0), 0);
3045 let live = b.current_pos();
3046 b.patch_jump(jlive, live);
3047 let via = if has_spread { "apply" } else { "call" };
3048 self.name_const(b, via); b.emit(Op::Rot, 0); let extra = if has_spread {
3051 self.compile_spread_args(b, args)?;
3052 1
3053 } else {
3054 for a in args {
3055 self.compile_expr(b, a)?;
3056 }
3057 args.len()
3058 };
3059 b.emit(Op::CallBuiltin(ops::CALL_METHOD, argc(3 + extra)?), 0);
3060 match self.opt_chain.last_mut() {
3061 Some(frame) => {
3062 frame.push(jend);
3063 if let Some(j) = jobj {
3064 frame.push(j);
3065 }
3066 }
3067 None => {
3068 let end = b.current_pos();
3069 b.patch_jump(jend, end);
3070 if let Some(j) = jobj {
3071 b.patch_jump(j, end);
3072 }
3073 }
3074 }
3075 return Ok(());
3076 }
3077 self.compile_expr(b, func)?;
3080 let jend = self.emit_optional_guard(b);
3081 if has_spread {
3082 self.compile_spread_args(b, args)?;
3083 b.emit(Op::CallBuiltin(ops::APPLY, 2), 0);
3084 } else {
3085 for a in args {
3086 self.compile_expr(b, a)?;
3087 }
3088 b.emit(Op::CallBuiltin(ops::CALL_VALUE, argc(1 + args.len())?), 0);
3089 }
3090 let end = b.current_pos();
3091 b.patch_jump(jend, end);
3092 Ok(())
3093 }
3094
3095 fn compile_call(
3096 &mut self,
3097 b: &mut ChunkBuilder,
3098 func: &Expr,
3099 args: &[Expr],
3100 optional: bool,
3101 ) -> Result<(), String> {
3102 if optional {
3103 return self.compile_optional_call(b, func, args);
3104 }
3105 let has_spread = args.iter().any(|a| matches!(a, Expr::Spread(_)));
3106 match func {
3107 Expr::Super => {
3110 for a in args {
3111 self.compile_expr(b, a)?;
3112 }
3113 b.emit(Op::CallBuiltin(ops::SUPER_CALL, argc(args.len())?), 0);
3114 return Ok(());
3115 }
3116 Expr::Member {
3119 object, property, ..
3120 } if matches!(**object, Expr::Super) => {
3121 self.name_const(b, property);
3122 b.emit(Op::CallBuiltin(ops::SUPER_GET, 1), 0); self.name_const(b, "call"); b.emit(Op::CallBuiltin(ops::THIS, 0), 0); for a in args {
3128 self.compile_expr(b, a)?;
3129 }
3130 b.emit(Op::CallBuiltin(ops::CALL_METHOD, argc(3 + args.len())?), 0);
3131 return Ok(());
3132 }
3133 Expr::Member {
3134 object,
3135 property,
3136 optional,
3137 } => {
3138 self.compile_expr(b, object)?;
3139 let jshort = if *optional {
3142 Some(self.emit_optional_guard(b))
3143 } else {
3144 None
3145 };
3146 self.name_const(b, property);
3147 if has_spread {
3148 self.compile_spread_args(b, args)?; b.emit(Op::CallBuiltin(ops::APPLY_METHOD, 3), 0);
3150 } else {
3151 for a in args {
3154 self.compile_off_spine(b, a)?;
3155 }
3156 let at = b.emit(Op::CallBuiltin(ops::CALL_METHOD, argc(2 + args.len())?), 0);
3157 self.note_call_site(at, func);
3158 }
3159 if let Some(j) = jshort {
3160 match self.opt_chain.last_mut() {
3161 Some(frame) => frame.push(j),
3162 None => {
3163 let end = b.current_pos();
3164 b.patch_jump(j, end);
3165 }
3166 }
3167 }
3168 }
3169 Expr::Index {
3170 object,
3171 index,
3172 optional,
3173 } => {
3174 self.compile_expr(b, object)?; let jshort = if *optional {
3178 Some(self.emit_optional_guard(b))
3179 } else {
3180 None
3181 };
3182 self.compile_expr(b, index)?; if has_spread {
3198 self.compile_spread_args(b, args)?; b.emit(Op::CallBuiltin(ops::APPLY_METHOD, 3), 0);
3200 } else {
3201 for a in args {
3202 self.compile_off_spine(b, a)?;
3203 }
3204 let at = b.emit(Op::CallBuiltin(ops::CALL_METHOD, argc(2 + args.len())?), 0);
3205 self.note_call_site(at, func);
3206 }
3207 if let Some(j) = jshort {
3208 let end = b.current_pos();
3209 b.patch_jump(j, end);
3210 }
3211 }
3212 Expr::Ident(n) if self.slot_of(n).is_none() => {
3216 self.name_const(b, n);
3217 if has_spread {
3218 self.compile_spread_args(b, args)?; b.emit(Op::Swap, 0); b.emit(Op::CallBuiltin(ops::GETLOCAL, 1), 0); b.emit(Op::Swap, 0); b.emit(Op::CallBuiltin(ops::APPLY, 2), 0);
3224 } else {
3225 for a in args {
3226 self.compile_expr(b, a)?;
3227 }
3228 let at = b.emit(Op::CallBuiltin(ops::CALL, argc(1 + args.len())?), 0);
3229 self.note_call_site(at, func);
3230 }
3231 }
3232 _ => {
3233 self.compile_expr(b, func)?;
3234 if has_spread {
3235 self.compile_spread_args(b, args)?;
3236 b.emit(Op::CallBuiltin(ops::APPLY, 2), 0);
3237 } else {
3238 for a in args {
3239 self.compile_expr(b, a)?;
3240 }
3241 let at = b.emit(Op::CallBuiltin(ops::CALL_VALUE, argc(1 + args.len())?), 0);
3242 self.note_call_site(at, func);
3243 }
3244 }
3245 }
3246 Ok(())
3247 }
3248
3249 fn compile_spread_args(&mut self, b: &mut ChunkBuilder, args: &[Expr]) -> Result<(), String> {
3251 for a in args {
3252 match a {
3253 Expr::Spread(inner) => {
3254 b.emit(Op::LoadInt(1), 0);
3255 self.compile_expr(b, inner)?;
3256 }
3257 _ => {
3258 b.emit(Op::LoadInt(0), 0);
3259 self.compile_expr(b, a)?;
3260 }
3261 }
3262 }
3263 b.emit(Op::CallBuiltin(ops::BUILD_ARGS, argc(args.len() * 2)?), 0);
3264 Ok(())
3265 }
3266
3267 fn compile_new(
3268 &mut self,
3269 b: &mut ChunkBuilder,
3270 callee: &Expr,
3271 args: &[Expr],
3272 ) -> Result<(), String> {
3273 self.compile_expr(b, callee)?;
3274 for a in args {
3275 self.compile_expr(b, a)?;
3276 }
3277 let at = b.emit(Op::CallBuiltin(ops::NEW, argc(1 + args.len())?), 0);
3278 self.note_call_site(at, callee);
3279 Ok(())
3280 }
3281}
3282
3283fn default_stmt(name: &str, default: &Expr) -> Stmt {
3286 Stmt::from(StmtKind::If {
3287 test: Expr::Binary(
3288 BinOp::EqEqEq,
3289 Box::new(Expr::Ident(name.to_string())),
3290 Box::new(Expr::Undefined),
3291 ),
3292 cons: Box::new(Stmt::from(StmtKind::Expr(Expr::Assign {
3293 target: Box::new(Expr::Ident(name.to_string())),
3294 value: Box::new(default.clone()),
3295 }))),
3296 alt: None,
3297 })
3298}
3299
3300fn collect_var_names(s: &Stmt, out: &mut Vec<String>) {
3307 match &s.kind {
3308 StmtKind::Decl {
3309 kind: DeclKind::Var,
3310 decls,
3311 } => {
3312 for d in decls {
3313 pattern_names(&d.target, out);
3314 }
3315 }
3316 StmtKind::Block(body) => body.iter().for_each(|s| collect_var_names(s, out)),
3317 StmtKind::If { cons, alt, .. } => {
3318 collect_var_names(cons, out);
3319 if let Some(a) = alt {
3320 collect_var_names(a, out);
3321 }
3322 }
3323 StmtKind::While { body, .. }
3324 | StmtKind::DoWhile { body, .. }
3325 | StmtKind::Labeled { body, .. } => collect_var_names(body, out),
3326 StmtKind::For { init, body, .. } => {
3327 if let Some(i) = init {
3328 collect_var_names(i, out);
3329 }
3330 collect_var_names(body, out);
3331 }
3332 StmtKind::ForOf {
3333 decl_kind,
3334 target,
3335 body,
3336 ..
3337 }
3338 | StmtKind::ForIn {
3339 decl_kind,
3340 target,
3341 body,
3342 ..
3343 } => {
3344 if *decl_kind == Some(DeclKind::Var) {
3345 pattern_names(target, out);
3346 }
3347 collect_var_names(body, out);
3348 }
3349 StmtKind::Switch { cases, .. } => {
3350 for c in cases {
3351 c.body.iter().for_each(|s| collect_var_names(s, out));
3352 }
3353 }
3354 StmtKind::Try {
3355 block,
3356 handler,
3357 finalizer,
3358 } => {
3359 block.iter().for_each(|s| collect_var_names(s, out));
3360 if let Some((_, body)) = handler {
3361 body.iter().for_each(|s| collect_var_names(s, out));
3364 }
3365 if let Some(f) = finalizer {
3366 f.iter().for_each(|s| collect_var_names(s, out));
3367 }
3368 }
3369 _ => {}
3370 }
3371}
3372
3373fn pattern_names(target: &Expr, out: &mut Vec<String>) {
3375 match target {
3376 Expr::Ident(n) => {
3377 if !out.iter().any(|x| x == n) {
3378 out.push(n.clone());
3379 }
3380 }
3381 Expr::Array(items) => items.iter().for_each(|i| pattern_names(i, out)),
3382 Expr::Object(props) => {
3383 for p in props {
3384 match p {
3385 Prop::KeyValue { value, .. } => pattern_names(value, out),
3386 Prop::Spread(e) => pattern_names(e, out),
3387 Prop::Accessor { .. } => {}
3388 }
3389 }
3390 }
3391 Expr::Assign { target, .. } => pattern_names(target, out),
3393 Expr::Spread(inner) => pattern_names(inner, out),
3394 _ => {}
3396 }
3397}