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_funcs(&mut b, stmts)?;
187 c.compile_stmts(&mut b, stmts)?;
188 Ok(Program {
189 main: c.finish_chunk(b),
190 functions: c.functions,
191 tries: c.tries,
192 })
193}
194
195pub fn compile_completion(stmts: &[Stmt], debug: bool) -> Result<Program, String> {
199 let mut c = Compiler {
200 opt_chain: Vec::new(),
201 debug,
202 strict: has_use_strict(stmts),
203 ..Default::default()
204 };
205 let mut b = ChunkBuilder::new();
206 c.hoist_funcs(&mut b, stmts)?;
207 if let Some((last, rest)) = stmts.split_last() {
208 c.compile_stmts(&mut b, rest)?;
209 if let StmtKind::Expr(e) = &last.kind {
210 c.compile_expr(&mut b, e)?;
212 } else {
213 c.compile_stmt(&mut b, last)?;
214 }
215 }
216 Ok(Program {
217 main: c.finish_chunk(b),
218 functions: c.functions,
219 tries: c.tries,
220 })
221}
222
223fn has_use_strict(stmts: &[Stmt]) -> bool {
229 for s in stmts {
230 match &s.kind {
231 StmtKind::Expr(e) => match e {
232 Expr::Str(v) if v == "use strict" => return true,
233 Expr::Str(_) => continue,
234 _ => return false,
235 },
236 _ => return false,
237 }
238 }
239 false
240}
241
242fn callee_text(e: &Expr) -> Option<String> {
251 Some(match e {
252 Expr::Ident(n) => n.clone(),
253 Expr::This => "this".into(),
254 Expr::Number(n) => crate::host::fmt_number(*n),
255 Expr::Str(s) => format!("\"{s}\""),
256 Expr::True => "true".into(),
257 Expr::False => "false".into(),
258 Expr::Null => "null".into(),
259 Expr::Undefined => "undefined".into(),
260 Expr::Array(items) if items.is_empty() => "[]".into(),
261 Expr::Object(props) if props.is_empty() => "{}".into(),
262 Expr::Member {
263 object,
264 property,
265 optional,
266 } => {
267 let dot = if *optional { "?." } else { "." };
268 format!("{}{dot}{property}", callee_text(object)?)
269 }
270 Expr::Index {
271 object,
272 index,
273 optional,
274 } => {
275 let obj = callee_text(object)?;
276 if let Expr::Str(k) = &**index {
279 if is_identifier(k) {
280 let dot = if *optional { "?." } else { "." };
281 return Some(format!("{obj}{dot}{k}"));
282 }
283 }
284 let idx = callee_text(index)?;
285 let open = if *optional { "?.[" } else { "[" };
286 format!("{obj}{open}{idx}]")
287 }
288 Expr::Call { func, .. } => format!("{}(...)", callee_text(func)?),
291 Expr::Sequence(items) => {
292 let parts: Option<Vec<String>> = items.iter().map(callee_text).collect();
293 format!("({})", parts?.join(" , "))
294 }
295 _ => return None,
296 })
297}
298
299fn is_identifier(s: &str) -> bool {
302 let mut chars = s.chars();
303 match chars.next() {
304 Some(c) if c.is_ascii_alphabetic() || c == '_' || c == '$' => {}
305 _ => return false,
306 }
307 chars.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '$')
308}
309
310fn argc(n: usize) -> Result<u8, String> {
311 u8::try_from(n).map_err(|_| "too many arguments (>255) for one call".to_string())
312}
313
314fn yields_bool(e: &Expr) -> bool {
329 match e {
330 Expr::True | Expr::False => true,
331 Expr::Unary(UnOp::Not, _) | Expr::Unary(UnOp::Delete, _) => true,
332 Expr::Binary(op, _, _) => matches!(
333 op,
334 BinOp::Lt
335 | BinOp::Le
336 | BinOp::Gt
337 | BinOp::Ge
338 | BinOp::EqEq
339 | BinOp::NeEq
340 | BinOp::EqEqEq
341 | BinOp::NeEqEq
342 | BinOp::In
343 | BinOp::InstanceOf
344 ),
345 _ => false,
346 }
347}
348
349impl Compiler {
350 fn name_const(&self, b: &mut ChunkBuilder, s: &str) {
352 let k = b.add_constant(Value::str(s));
353 b.emit(Op::LoadConst(k), 0);
354 }
355 fn strlit(&self, b: &mut ChunkBuilder, s: &str) {
356 let k = b.add_constant(Value::str(s));
357 b.emit(Op::LoadConst(k), 0);
358 b.emit(Op::CallBuiltin(ops::MKSTR, 1), 0);
359 }
360 fn tmp_name(&mut self, tag: &str) -> String {
361 let n = format!(".{tag}{}", self.tmp);
362 self.tmp += 1;
363 n
364 }
365
366 fn emit_mkfunc(&self, b: &mut ChunkBuilder, def_id: usize) {
369 b.emit(Op::LoadInt(def_id as i64), 0);
370 b.emit(Op::CallBuiltin(ops::MKFUNC, 1), 0);
371 }
372
373 fn hoist_funcs(&mut self, b: &mut ChunkBuilder, stmts: &[Stmt]) -> Result<(), String> {
374 for s in stmts {
375 if let StmtKind::FuncDecl {
376 name,
377 params,
378 body,
379 is_generator,
380 is_async,
381 } = &s.kind
382 {
383 let def_id = self.build_function(name, params, body, *is_generator, *is_async)?;
384 self.emit_mkfunc(b, def_id);
385 self.declare_as(b, &Expr::Ident(name.clone()), BindMode::Var);
387 }
388 }
389 Ok(())
390 }
391
392 fn compile_stmts(&mut self, b: &mut ChunkBuilder, stmts: &[Stmt]) -> Result<(), String> {
393 for s in stmts {
394 self.compile_stmt(b, s)?;
395 }
396 Ok(())
397 }
398
399 fn compile_stmt(&mut self, b: &mut ChunkBuilder, s: &Stmt) -> Result<(), String> {
400 if self.debug && s.line != 0 {
401 b.emit(Op::LoadInt(s.line as i64), s.line);
402 b.emit(Op::CallBuiltin(ops::DBG_LINE, 1), s.line);
403 b.emit(Op::Pop, s.line);
404 }
405 let line = s.line;
406 match &s.kind {
407 StmtKind::Expr(e) => {
408 self.compile_expr(b, e)?;
409 b.emit(Op::Pop, line);
410 }
411 StmtKind::Empty => {}
412 StmtKind::FuncDecl { .. } => {} StmtKind::ClassDecl(node) => {
414 self.compile_class(b, node)?;
415 if let Some(name) = &node.name {
417 self.declare(b, &Expr::Ident(name.clone()));
418 } else {
419 b.emit(Op::Pop, line);
420 }
421 }
422 StmtKind::Decl { kind, decls } => {
423 let mode = bind_mode(*kind);
424 for d in decls {
425 match &d.init {
426 Some(v) => {
427 self.compile_expr(b, v)?;
428 if let Expr::Ident(name) = &d.target {
431 self.infer_name(b, v, name);
432 }
433 }
434 None => {
435 b.emit(Op::LoadUndef, line);
436 }
437 }
438 self.compile_bind(b, &d.target, mode)?;
439 }
440 }
441 StmtKind::Block(body) => {
442 let scoped = crate::capture::block_needs_scope(body);
447 if scoped {
448 self.emit_push_scope(b);
449 }
450 self.hoist_funcs(b, body)?;
451 self.compile_stmts(b, body)?;
452 if scoped {
453 self.emit_pop_scope(b);
454 }
455 }
456 StmtKind::If { test, cons, alt } => self.compile_if(b, test, cons, alt)?,
457 StmtKind::While { test, body } => self.compile_while(b, test, body)?,
458 StmtKind::DoWhile { body, test } => self.compile_do_while(b, body, test)?,
459 StmtKind::For {
460 init,
461 test,
462 update,
463 body,
464 } => self.compile_for(b, init, test, update, body)?,
465 StmtKind::ForOf {
466 decl_kind,
467 target,
468 iter,
469 body,
470 is_await,
471 } => {
472 let mode = decl_kind.map(bind_mode).unwrap_or(BindMode::Assign);
473 if *is_await {
474 self.compile_for_await(b, mode, target, iter, body)?
475 } else {
476 self.compile_for_of(b, mode, target, iter, body)?
477 }
478 }
479 StmtKind::ForIn {
480 decl_kind,
481 target,
482 object,
483 body,
484 } => {
485 let mode = decl_kind.map(bind_mode).unwrap_or(BindMode::Assign);
486 self.compile_for_in(b, mode, target, object, body)?
487 }
488 StmtKind::Switch { disc, cases } => self.compile_switch(b, disc, cases)?,
489 StmtKind::Return(e) => {
490 match e {
491 Some(e) => self.compile_expr(b, e)?,
492 None => {
493 b.emit(Op::LoadUndef, line);
494 }
495 }
496 self.emit_close_iters_under_value(b);
502 b.emit(Op::CallBuiltin(ops::SIG_RETURN, 1), line);
503 }
504 StmtKind::Labeled { label, body } => self.compile_labeled(b, label, body)?,
505 StmtKind::Break(label) => {
506 let idx = match label {
507 Some(name) => self
509 .loops
510 .iter()
511 .rposition(|c| c.label.as_deref() == Some(name.as_str()))
512 .ok_or_else(|| format!("SyntaxError: Undefined label '{name}'"))?,
513 None => self
514 .loops
515 .len()
516 .checked_sub(1)
517 .ok_or("SyntaxError: 'break' outside loop")?,
518 };
519 if idx >= self.chunk_loop_base {
520 self.emit_unwind_scopes(b, self.loops[idx].break_depth);
521 self.emit_close_iters(b, self.loops[idx].iter_depth);
522 let j = b.emit(Op::Jump(0), line);
523 self.loops[idx].breaks.push(j);
524 } else {
525 self.emit_signal_jump(b, ops::SIG_BREAK, label.as_deref(), line);
526 }
527 }
528 StmtKind::Continue(label) => {
529 let idx = match label {
530 Some(name) => self
533 .loops
534 .iter()
535 .rposition(|c| {
536 c.catches_continue && c.label.as_deref() == Some(name.as_str())
537 })
538 .ok_or_else(|| {
539 format!("SyntaxError: Undefined label '{name}' for continue")
540 })?,
541 None => self
542 .loops
543 .iter()
544 .rposition(|c| c.catches_continue)
545 .ok_or("SyntaxError: 'continue' outside loop")?,
546 };
547 if idx >= self.chunk_loop_base {
548 self.emit_unwind_scopes(b, self.loops[idx].continue_depth);
549 self.emit_close_iters(b, self.loops[idx].iter_depth);
550 let j = b.emit(Op::Jump(0), line);
551 self.loops[idx].continues.push(j);
552 } else {
553 self.emit_signal_jump(b, ops::SIG_CONTINUE, label.as_deref(), line);
554 }
555 }
556 StmtKind::Throw(e) => {
557 self.compile_expr(b, e)?;
558 b.emit(Op::CallBuiltin(ops::THROW, 1), line);
559 }
560 StmtKind::Try {
561 block,
562 handler,
563 finalizer,
564 } => self.compile_try(b, block, handler, finalizer)?,
565 }
566 Ok(())
567 }
568
569 fn compile_bind(
573 &mut self,
574 b: &mut ChunkBuilder,
575 target: &Expr,
576 declare: BindMode,
577 ) -> Result<(), String> {
578 match target {
579 Expr::Ident(_) => {
580 if declare == BindMode::Assign {
581 self.store_simple(b, target)?;
582 } else {
583 self.declare_as(b, target, declare);
584 }
585 }
586 Expr::Member { .. } | Expr::Index { .. } => {
587 self.store_simple(b, target)?;
588 }
589 Expr::Array(items) => self.destructure_array(b, items, declare)?,
590 Expr::Object(props) => self.destructure_object(b, props, declare)?,
591 Expr::Assign { target, value } => {
592 b.emit(Op::Dup, 0);
594 b.emit(Op::LoadUndef, 0);
595 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
596 let jf = b.emit(Op::JumpIfFalse(0), 0);
597 b.emit(Op::Pop, 0); self.compile_expr(b, value)?;
599 if let Expr::Ident(n) = &**target {
603 self.infer_name(b, value, n);
604 }
605 let end = b.current_pos();
606 b.patch_jump(jf, end);
607 self.compile_bind(b, target, declare)?;
608 }
609 _ => return Err("SyntaxError: invalid assignment target".into()),
610 }
611 Ok(())
612 }
613
614 fn declare(&self, b: &mut ChunkBuilder, target: &Expr) {
616 self.declare_as(b, target, BindMode::Lexical);
617 }
618
619 fn declare_as(&self, b: &mut ChunkBuilder, target: &Expr, mode: BindMode) {
622 if let Expr::Ident(n) = target {
623 if let Some(slot) = self.slot_of(n) {
626 b.emit(Op::SetSlot(slot), 0);
627 return;
628 }
629 let op = match mode {
630 BindMode::Var => ops::DECLARE_VAR,
631 BindMode::Const => ops::DECLARE_CONST,
632 _ => ops::DECLARE,
633 };
634 self.name_const(b, n);
635 b.emit(Op::Swap, 0);
636 b.emit(Op::CallBuiltin(op, 2), 0);
637 b.emit(Op::Pop, 0);
638 }
639 }
640
641 fn throw_const_assignment(&mut self, b: &mut ChunkBuilder) {
648 let e = Expr::New {
649 callee: Box::new(Expr::Ident("TypeError".into())),
650 args: vec![Expr::Str("Assignment to constant variable.".into())],
651 };
652 if self.compile_expr(b, &e).is_ok() {
655 b.emit(Op::CallBuiltin(ops::THROW, 1), 0);
656 }
657 }
658
659 fn store_simple(&mut self, b: &mut ChunkBuilder, target: &Expr) -> Result<(), String> {
661 match target {
662 Expr::Ident(n) => {
663 if self.slots.consts.contains(n) {
671 b.emit(Op::Pop, 0); self.throw_const_assignment(b);
673 return Ok(());
674 }
675 if let Some(slot) = self.slot_of(n) {
676 b.emit(Op::SetSlot(slot), 0);
677 return Ok(());
678 }
679 self.name_const(b, n);
680 b.emit(Op::Swap, 0);
681 let op = if self.strict {
684 ops::SETLOCAL_STRICT
685 } else {
686 ops::SETLOCAL
687 };
688 b.emit(Op::CallBuiltin(op, 2), 0);
689 b.emit(Op::Pop, 0);
690 }
691 Expr::Member {
692 object, property, ..
693 } => {
694 self.compile_expr(b, object)?; self.name_const(b, property); b.emit(Op::Rot, 0); b.emit(Op::CallBuiltin(ops::SETATTR, 3), 0);
698 b.emit(Op::Pop, 0);
699 }
700 Expr::Index { object, index, .. } => {
701 self.compile_expr(b, object)?; self.compile_expr(b, index)?; b.emit(Op::Rot, 0); b.emit(Op::CallBuiltin(ops::SETITEM, 3), 0);
705 b.emit(Op::Pop, 0);
706 }
707 _ => return Err("SyntaxError: invalid assignment target".into()),
708 }
709 Ok(())
710 }
711
712 fn destructure_array(
713 &mut self,
714 b: &mut ChunkBuilder,
715 items: &[Expr],
716 declare: BindMode,
717 ) -> Result<(), String> {
718 let star_idx = items
719 .iter()
720 .position(|e| matches!(e, Expr::Spread(_)))
721 .map(|i| i as i64)
722 .unwrap_or(-1);
723 b.emit(Op::LoadInt(items.len() as i64), 0);
724 b.emit(Op::LoadInt(star_idx), 0);
725 b.emit(Op::CallBuiltin(ops::UNPACK, 3), 0); for it in items {
727 match it {
728 Expr::Hole | Expr::Undefined => {
731 b.emit(Op::Pop, 0);
732 }
733 Expr::Spread(inner) => self.compile_bind(b, inner, declare)?,
734 _ => self.compile_bind(b, it, declare)?,
735 }
736 }
737 Ok(())
738 }
739
740 fn destructure_object(
741 &mut self,
742 b: &mut ChunkBuilder,
743 props: &[Prop],
744 declare: BindMode,
745 ) -> Result<(), String> {
746 let obj_tmp = self.tmp_name("destr");
748 self.name_const(b, &obj_tmp);
749 b.emit(Op::Swap, 0);
750 b.emit(Op::CallBuiltin(ops::DECLARE, 2), 0);
751 b.emit(Op::Pop, 0);
752 let mut named: Vec<String> = Vec::new();
754 for p in props {
755 match p {
756 Prop::KeyValue { key, value, .. } => {
757 if let Expr::Str(s) = key {
758 named.push(s.clone());
759 }
760 self.load_local(b, &obj_tmp);
762 self.compile_expr(b, key)?;
763 b.emit(Op::CallBuiltin(ops::GETITEM, 2), 0); self.compile_bind(b, value, declare)?;
765 }
766 Prop::Spread(target) => {
767 self.load_local(b, &obj_tmp);
768 for k in &named {
769 self.strlit(b, k);
770 }
771 b.emit(Op::CallBuiltin(ops::MKARR, argc(named.len())?), 0);
772 b.emit(Op::CallBuiltin(ops::OBJ_REST, 2), 0); self.compile_bind(b, target, declare)?;
774 }
775 Prop::Accessor { .. } => {}
777 }
778 }
779 Ok(())
780 }
781
782 fn load_local(&self, b: &mut ChunkBuilder, name: &str) {
783 if let Some(slot) = self.slot_of(name) {
784 b.emit(Op::GetSlot(slot), 0);
785 return;
786 }
787 self.name_const(b, name);
788 b.emit(Op::CallBuiltin(ops::GETLOCAL, 1), 0);
789 }
790
791 fn slot_of(&self, name: &str) -> Option<u16> {
793 self.slots.table.get(name).copied()
794 }
795
796 fn numeric_slot_of(&self, name: &str) -> Option<u16> {
799 self.slots
800 .numeric
801 .contains(name)
802 .then(|| self.slot_of(name))
803 .flatten()
804 }
805
806 fn compile_condition(&mut self, b: &mut ChunkBuilder, e: &Expr) -> Result<(), String> {
808 self.compile_expr(b, e)?;
809 if !yields_bool(e) {
810 b.emit(Op::CallBuiltin(ops::TRUTHY, 1), 0);
811 }
812 Ok(())
813 }
814
815 fn compile_if(
816 &mut self,
817 b: &mut ChunkBuilder,
818 test: &Expr,
819 cons: &Stmt,
820 alt: &Option<Box<Stmt>>,
821 ) -> Result<(), String> {
822 self.compile_condition(b, test)?;
823 let jfalse = b.emit(Op::JumpIfFalse(0), 0);
824 self.compile_stmt(b, cons)?;
825 if let Some(alt) = alt {
826 let jend = b.emit(Op::Jump(0), 0);
827 let else_start = b.current_pos();
828 b.patch_jump(jfalse, else_start);
829 self.compile_stmt(b, alt)?;
830 let end = b.current_pos();
831 b.patch_jump(jend, end);
832 } else {
833 let end = b.current_pos();
834 b.patch_jump(jfalse, end);
835 }
836 Ok(())
837 }
838
839 fn compile_labeled(
843 &mut self,
844 b: &mut ChunkBuilder,
845 label: &str,
846 body: &Stmt,
847 ) -> Result<(), String> {
848 if matches!(
849 body.kind,
850 StmtKind::While { .. }
851 | StmtKind::DoWhile { .. }
852 | StmtKind::For { .. }
853 | StmtKind::ForOf { .. }
854 | StmtKind::ForIn { .. }
855 ) {
856 self.pending_label = Some(label.to_string());
857 self.compile_stmt(b, body)?;
858 self.pending_label = None;
860 } else {
861 self.loops.push(LoopCtx {
862 breaks: Vec::new(),
863 continues: Vec::new(),
864 break_depth: self.scope_depth,
865 continue_depth: self.scope_depth,
866 iter_depth: self.iter_depth,
867 catches_continue: false,
868 label: Some(label.to_string()),
869 });
870 self.compile_stmt(b, body)?;
871 let ctx = self.loops.pop().unwrap();
872 let end = b.current_pos();
873 for br in ctx.breaks {
874 b.patch_jump(br, end);
875 }
876 self.redispatch_after_loop(b);
877 }
878 Ok(())
879 }
880
881 fn redispatch_after_loop(&mut self, b: &mut ChunkBuilder) {
885 if self.chunk_signals {
886 self.emit_signal_dispatch(b);
887 }
888 }
889
890 fn compile_while(
909 &mut self,
910 b: &mut ChunkBuilder,
911 test: &Expr,
912 body: &Stmt,
913 ) -> Result<(), String> {
914 self.compile_condition(b, test)?;
915 let jfalse = b.emit(Op::JumpIfFalse(0), 0);
916 let top = b.current_pos();
917 self.loops.push(LoopCtx {
918 breaks: Vec::new(),
919 continues: Vec::new(),
920 break_depth: self.scope_depth,
921 continue_depth: self.scope_depth,
922 iter_depth: self.iter_depth,
923 catches_continue: true,
924 label: self.pending_label.take(),
925 });
926 self.compile_stmt(b, body)?;
927 let cont_target = b.current_pos();
929 self.compile_condition(b, test)?;
930 b.emit(Op::JumpIfTrue(top), 0);
931 let ctx = self.loops.pop().unwrap();
932 for c in ctx.continues {
933 b.patch_jump(c, cont_target);
934 }
935 let end = b.current_pos();
936 b.patch_jump(jfalse, end);
937 for br in ctx.breaks {
938 b.patch_jump(br, end);
939 }
940 self.redispatch_after_loop(b);
941 Ok(())
942 }
943
944 fn compile_do_while(
945 &mut self,
946 b: &mut ChunkBuilder,
947 body: &Stmt,
948 test: &Expr,
949 ) -> Result<(), String> {
950 let start = b.current_pos();
951 self.loops.push(LoopCtx {
952 breaks: Vec::new(),
953 continues: Vec::new(),
954 break_depth: self.scope_depth,
955 continue_depth: self.scope_depth,
956 iter_depth: self.iter_depth,
957 catches_continue: true,
958 label: self.pending_label.take(),
959 });
960 self.compile_stmt(b, body)?;
961 let cont_target = b.current_pos();
962 self.compile_condition(b, test)?;
963 b.emit(Op::JumpIfTrue(start), 0);
964 let ctx = self.loops.pop().unwrap();
965 for c in ctx.continues {
966 b.patch_jump(c, cont_target);
967 }
968 let end = b.current_pos();
969 for br in ctx.breaks {
970 b.patch_jump(br, end);
971 }
972 self.redispatch_after_loop(b);
973 Ok(())
974 }
975
976 fn compile_for(
977 &mut self,
978 b: &mut ChunkBuilder,
979 init: &Option<Box<Stmt>>,
980 test: &Option<Expr>,
981 update: &Option<Expr>,
982 body: &Stmt,
983 ) -> Result<(), String> {
984 let lexical_head = matches!(
988 init.as_deref(),
989 Some(Stmt {
990 kind: StmtKind::Decl {
991 kind: DeclKind::Let | DeclKind::Const,
992 ..
993 },
994 ..
995 })
996 );
997 let per_iteration = lexical_head;
1004 let copy_per_iteration = lexical_head
1005 && (crate::capture::stmt_captures(body)
1006 || init.as_deref().is_some_and(crate::capture::stmt_captures)
1007 || test.as_ref().is_some_and(crate::capture::expr_captures)
1008 || update.as_ref().is_some_and(crate::capture::expr_captures));
1009 if per_iteration {
1010 self.emit_push_scope(b);
1011 }
1012 if let Some(init) = init {
1013 self.compile_stmt(b, init)?;
1014 }
1015 if copy_per_iteration {
1016 self.emit_copy_scope(b);
1017 }
1018 let jfalse = match test {
1021 Some(t) => {
1022 self.compile_condition(b, t)?;
1023 Some(b.emit(Op::JumpIfFalse(0), 0))
1024 }
1025 None => None,
1026 };
1027 let top = b.current_pos();
1028 self.loops.push(LoopCtx {
1029 breaks: Vec::new(),
1030 continues: Vec::new(),
1031 break_depth: self.scope_depth,
1032 continue_depth: self.scope_depth,
1033 iter_depth: self.iter_depth,
1034 catches_continue: true,
1035 label: self.pending_label.take(),
1036 });
1037 self.compile_stmt(b, body)?;
1038 let cont_target = b.current_pos();
1039 if copy_per_iteration {
1040 self.emit_copy_scope(b);
1043 }
1044 if let Some(u) = update {
1045 self.compile_expr(b, u)?;
1046 b.emit(Op::Pop, 0);
1047 }
1048 match test {
1049 Some(t) => {
1050 self.compile_condition(b, t)?;
1051 b.emit(Op::JumpIfTrue(top), 0);
1052 }
1053 None => {
1062 b.emit(Op::LoadTrue, 0);
1063 b.emit(Op::JumpIfTrue(top), 0);
1064 }
1065 }
1066 let ctx = self.loops.pop().unwrap();
1067 for c in ctx.continues {
1068 b.patch_jump(c, cont_target);
1069 }
1070 let end = b.current_pos();
1071 if let Some(jf) = jfalse {
1072 b.patch_jump(jf, end);
1073 }
1074 for br in ctx.breaks {
1075 b.patch_jump(br, end);
1076 }
1077 if per_iteration {
1078 self.emit_pop_scope(b);
1079 }
1080 self.redispatch_after_loop(b);
1081 Ok(())
1082 }
1083
1084 fn compile_for_of(
1085 &mut self,
1086 b: &mut ChunkBuilder,
1087 declare: BindMode,
1088 target: &Expr,
1089 iter: &Expr,
1090 body: &Stmt,
1091 ) -> Result<(), String> {
1092 self.compile_expr(b, iter)?;
1093 b.emit(Op::CallBuiltin(ops::GETITER, 1), 0); self.iter_depth += 1;
1095 let r = self.loop_over(b, declare, target, body);
1096 self.iter_depth -= 1;
1097 r
1098 }
1099
1100 fn compile_for_in(
1101 &mut self,
1102 b: &mut ChunkBuilder,
1103 declare: BindMode,
1104 target: &Expr,
1105 object: &Expr,
1106 body: &Stmt,
1107 ) -> Result<(), String> {
1108 self.compile_expr(b, object)?;
1109 b.emit(Op::CallBuiltin(ops::FORIN_KEYS, 1), 0); b.emit(Op::CallBuiltin(ops::GETITER, 1), 0); self.iter_depth += 1;
1112 let r = self.loop_over(b, declare, target, body);
1113 self.iter_depth -= 1;
1114 r
1115 }
1116
1117 fn compile_for_await(
1121 &mut self,
1122 b: &mut ChunkBuilder,
1123 declare: BindMode,
1124 target: &Expr,
1125 iter: &Expr,
1126 body: &Stmt,
1127 ) -> Result<(), String> {
1128 let iter_tmp = self.tmp_name("aiter");
1129 self.compile_expr(b, iter)?;
1130 b.emit(Op::CallBuiltin(ops::GET_ASYNC_ITER, 1), 0); self.name_const(b, &iter_tmp);
1132 b.emit(Op::Swap, 0);
1133 b.emit(Op::CallBuiltin(ops::DECLARE, 2), 0);
1134 b.emit(Op::Pop, 0);
1135 let start = b.current_pos();
1136 self.load_local(b, &iter_tmp);
1138 b.emit(Op::CallBuiltin(ops::ASYNC_STEP, 1), 0); b.emit(Op::CallBuiltin(ops::AWAIT, 1), 0); let step_tmp = self.tmp_name("astep");
1141 self.name_const(b, &step_tmp);
1142 b.emit(Op::Swap, 0);
1143 b.emit(Op::CallBuiltin(ops::DECLARE, 2), 0);
1144 b.emit(Op::Pop, 0);
1145 self.load_local(b, &step_tmp);
1147 self.name_const(b, "done");
1148 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0);
1149 b.emit(Op::CallBuiltin(ops::TRUTHY, 1), 0);
1150 let jdone = b.emit(Op::JumpIfTrue(0), 0);
1151 self.load_local(b, &step_tmp);
1153 self.name_const(b, "value");
1154 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0); let per_iteration = matches!(declare, BindMode::Lexical | BindMode::Const);
1156 if per_iteration {
1157 self.emit_push_scope(b);
1158 }
1159 self.compile_bind(b, target, declare)?;
1160 self.loops.push(LoopCtx {
1161 breaks: Vec::new(),
1162 continues: Vec::new(),
1163 break_depth: self.scope_depth,
1164 continue_depth: self.scope_depth,
1165 iter_depth: self.iter_depth,
1166 catches_continue: true,
1167 label: self.pending_label.take(),
1168 });
1169 self.compile_stmt(b, body)?;
1170 let cont_target = b.current_pos();
1171 if per_iteration {
1172 self.emit_pop_scope(b);
1173 }
1174 b.emit(Op::Jump(start), 0);
1175 let ctx = self.loops.pop().unwrap();
1176 for c in ctx.continues {
1177 b.patch_jump(c, cont_target);
1178 }
1179 let break_target = b.current_pos();
1182 if per_iteration {
1183 b.emit(Op::CallBuiltin(ops::POP_SCOPE, 0), 0);
1184 b.emit(Op::Pop, 0);
1185 }
1186 self.load_local(b, &iter_tmp);
1189 b.emit(Op::CallBuiltin(ops::ITER_CLOSE, 1), 0);
1190 b.emit(Op::Pop, 0);
1191 let end = b.current_pos();
1192 b.patch_jump(jdone, end);
1193 for br in ctx.breaks {
1194 b.patch_jump(br, break_target);
1195 }
1196 self.redispatch_after_loop(b);
1197 Ok(())
1198 }
1199
1200 fn loop_over(
1202 &mut self,
1203 b: &mut ChunkBuilder,
1204 declare: BindMode,
1205 target: &Expr,
1206 body: &Stmt,
1207 ) -> Result<(), String> {
1208 let per_iteration = matches!(declare, BindMode::Lexical | BindMode::Const);
1211 let start = b.current_pos();
1212 b.emit(Op::CallBuiltin(ops::FORITER, 0), 0); let jdone = b.emit(Op::JumpIfFalse(0), 0); if per_iteration {
1215 self.emit_push_scope(b);
1216 }
1217 self.compile_bind(b, target, declare)?; self.loops.push(LoopCtx {
1219 breaks: Vec::new(),
1220 continues: Vec::new(),
1221 break_depth: self.scope_depth,
1222 continue_depth: self.scope_depth,
1223 iter_depth: self.iter_depth,
1224 catches_continue: true,
1225 label: self.pending_label.take(),
1226 });
1227 self.compile_stmt(b, body)?;
1228 let cont_target = b.current_pos();
1229 if per_iteration {
1230 self.emit_pop_scope(b);
1231 }
1232 b.emit(Op::Jump(start), 0);
1233 let ctx = self.loops.pop().unwrap();
1234 for c in ctx.continues {
1235 b.patch_jump(c, cont_target);
1236 }
1237 let done = b.current_pos();
1238 b.patch_jump(jdone, done);
1239 b.emit(Op::Pop, 0); let jafter = b.emit(Op::Jump(0), 0);
1241 let break_target = b.current_pos();
1242 if per_iteration {
1245 b.emit(Op::CallBuiltin(ops::POP_SCOPE, 0), 0);
1246 b.emit(Op::Pop, 0);
1247 }
1248 b.emit(Op::CallBuiltin(ops::ITER_CLOSE, 1), 0);
1249 b.emit(Op::Pop, 0); let end = b.current_pos();
1251 b.patch_jump(jafter, end);
1252 for br in ctx.breaks {
1253 b.patch_jump(br, break_target);
1254 }
1255 self.iter_depth -= 1;
1258 self.redispatch_after_loop(b);
1259 self.iter_depth += 1;
1260 Ok(())
1261 }
1262
1263 fn compile_switch(
1264 &mut self,
1265 b: &mut ChunkBuilder,
1266 disc: &Expr,
1267 cases: &[SwitchCase],
1268 ) -> Result<(), String> {
1269 let disc_tmp = self.tmp_name("switch");
1270 self.compile_expr(b, disc)?;
1271 self.name_const(b, &disc_tmp);
1272 b.emit(Op::Swap, 0);
1273 b.emit(Op::CallBuiltin(ops::DECLARE, 2), 0);
1274 b.emit(Op::Pop, 0);
1275 self.emit_push_scope(b);
1279 let mut body_jumps: Vec<Option<usize>> = Vec::new();
1281 let mut default_idx: Option<usize> = None;
1282 for (i, case) in cases.iter().enumerate() {
1283 match &case.test {
1284 Some(t) => {
1285 self.load_local(b, &disc_tmp);
1286 self.compile_expr(b, t)?;
1287 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
1288 let j = b.emit(Op::JumpIfTrue(0), 0);
1289 body_jumps.push(Some(j));
1290 }
1291 None => {
1292 default_idx = Some(i);
1293 body_jumps.push(None);
1294 }
1295 }
1296 }
1297 let no_match_jump = b.emit(Op::Jump(0), 0);
1299 self.loops.push(LoopCtx {
1300 breaks: Vec::new(),
1301 continues: Vec::new(),
1302 break_depth: self.scope_depth,
1303 continue_depth: self.scope_depth,
1304 iter_depth: self.iter_depth,
1305 catches_continue: false,
1306 label: None,
1307 });
1308 let mut body_starts: Vec<usize> = Vec::new();
1309 for case in cases {
1310 body_starts.push(b.current_pos());
1311 self.compile_stmts(b, &case.body)?;
1312 }
1313 let end = b.current_pos();
1314 for (i, j) in body_jumps.iter().enumerate() {
1316 if let Some(j) = j {
1317 b.patch_jump(*j, body_starts[i]);
1318 }
1319 }
1320 match default_idx {
1321 Some(i) => b.patch_jump(no_match_jump, body_starts[i]),
1322 None => b.patch_jump(no_match_jump, end),
1323 }
1324 let ctx = self.loops.pop().unwrap();
1325 for br in ctx.breaks {
1326 b.patch_jump(br, end);
1327 }
1328 self.emit_pop_scope(b);
1329 self.redispatch_after_loop(b);
1330 Ok(())
1331 }
1332
1333 fn compile_try(
1334 &mut self,
1335 b: &mut ChunkBuilder,
1336 block: &[Stmt],
1337 handler: &Option<(Option<Expr>, Vec<Stmt>)>,
1338 finalizer: &Option<Vec<Stmt>>,
1339 ) -> Result<(), String> {
1340 let block_chunk = self.compile_block_chunk(block)?;
1341 let handler_def = match handler {
1342 Some((param, body)) => {
1343 let param_name = match param {
1344 Some(Expr::Ident(n)) => Some(n.clone()),
1345 _ => None,
1346 };
1347 let hbody = self.compile_block_chunk(body)?;
1348 Some((param_name, hbody))
1349 }
1350 None => None,
1351 };
1352 let final_chunk = match finalizer {
1353 Some(f) => Some(self.compile_block_chunk(f)?),
1354 None => None,
1355 };
1356 let id = self.tries.len();
1357 self.tries.push(TryDef {
1358 block: block_chunk,
1359 handler: handler_def,
1360 finalizer: final_chunk,
1361 });
1362 b.emit(Op::LoadInt(id as i64), 0);
1363 b.emit(Op::CallBuiltin(ops::TRY, 1), 0);
1364 b.emit(Op::Pop, 0);
1365 self.emit_signal_dispatch(b);
1368 Ok(())
1369 }
1370
1371 fn compile_block_chunk(&mut self, stmts: &[Stmt]) -> Result<Chunk, String> {
1375 let mut cb = ChunkBuilder::new();
1376 let saved_slot_table = std::mem::take(&mut self.slots);
1381 let base = std::mem::replace(&mut self.chunk_loop_base, self.loops.len());
1382 let signals = std::mem::take(&mut self.chunk_signals);
1383 let depth = std::mem::take(&mut self.scope_depth);
1384 let iters = std::mem::take(&mut self.iter_depth);
1385 let sites = std::mem::take(&mut self.call_sites);
1386 let yields = std::mem::take(&mut self.yield_sites);
1387 let r = (|| {
1388 self.hoist_funcs(&mut cb, stmts)?;
1389 self.compile_stmts(&mut cb, stmts)
1390 })();
1391 self.chunk_loop_base = base;
1392 self.scope_depth = depth;
1393 self.iter_depth = iters;
1394 self.slots = saved_slot_table;
1395 self.chunk_signals |= signals;
1398 r?;
1399 let chunk = self.finish_chunk(cb);
1400 self.call_sites = sites;
1401 self.yield_sites = yields;
1402 Ok(chunk)
1403 }
1404
1405 fn build_function(
1407 &mut self,
1408 name: &str,
1409 params: &[Param],
1410 body: &[Stmt],
1411 is_generator: bool,
1412 is_async: bool,
1413 ) -> Result<usize, String> {
1414 let (param_slots, prologue) = self.lower_params(params)?;
1415 let mut fb = ChunkBuilder::new();
1416 let mut planned: Vec<Stmt> = prologue.clone();
1420 planned.extend_from_slice(body);
1421 let saved_slot_table = std::mem::replace(
1422 &mut self.slots,
1423 if self.debug || is_generator || is_async {
1424 Default::default()
1425 } else {
1426 crate::slots::plan(params, &planned, false)
1427 },
1428 );
1429 for name in crate::slots::param_names(params) {
1433 if let Some(slot) = self.slot_of(&name) {
1434 self.name_const(&mut fb, &name);
1435 fb.emit(Op::CallBuiltin(ops::GETLOCAL, 1), 0);
1436 fb.emit(Op::SetSlot(slot), 0);
1437 }
1438 }
1439 let saved_loops = std::mem::take(&mut self.loops);
1442 let saved_base = std::mem::replace(&mut self.chunk_loop_base, 0);
1443 let saved_signals = std::mem::take(&mut self.chunk_signals);
1444 let saved_depth = std::mem::take(&mut self.scope_depth);
1445 let saved_iters = std::mem::take(&mut self.iter_depth);
1446 let saved_agen = std::mem::replace(&mut self.in_async_generator, is_generator && is_async);
1447 let saved_strict = self.strict;
1450 self.strict = self.strict || has_use_strict(body);
1451 let saved_sites = std::mem::take(&mut self.call_sites);
1454 let saved_yields = std::mem::take(&mut self.yield_sites);
1455 let r = (|| {
1456 self.hoist_funcs(&mut fb, &prologue)?;
1458 self.hoist_funcs(&mut fb, body)?;
1459 self.compile_stmts(&mut fb, &prologue)?;
1460 self.compile_stmts(&mut fb, body)
1461 })();
1462 self.loops = saved_loops;
1463 self.chunk_loop_base = saved_base;
1464 self.chunk_signals = saved_signals;
1465 self.scope_depth = saved_depth;
1466 self.iter_depth = saved_iters;
1467 self.in_async_generator = saved_agen;
1468 self.strict = saved_strict;
1469 self.slots = saved_slot_table;
1470 r?;
1471 let def = FuncDef {
1472 name: name.to_string(),
1473 params: param_slots,
1474 chunk: self.finish_chunk(fb),
1475 is_arrow: false,
1476 is_generator,
1477 is_async,
1478 is_method: false,
1479 self_name: false,
1480 };
1481 self.call_sites = saved_sites;
1482 self.yield_sites = saved_yields;
1483 self.functions.push((name.to_string(), def));
1484 Ok(self.functions.len() - 1)
1485 }
1486
1487 fn build_arrow(
1488 &mut self,
1489 params: &[Param],
1490 body: &FnBody,
1491 is_async: bool,
1492 ) -> Result<usize, String> {
1493 let stmts = match body {
1494 FnBody::Block(b) => b.clone(),
1495 FnBody::Expr(e) => vec![Stmt::from(StmtKind::Return(Some((**e).clone())))],
1496 };
1497 let id = self.build_function("", params, &stmts, false, is_async)?;
1498 self.functions[id].1.is_arrow = true;
1500 Ok(id)
1501 }
1502
1503 fn compile_class(&mut self, b: &mut ChunkBuilder, node: &ClassNode) -> Result<(), String> {
1508 let saved_strict = std::mem::replace(&mut self.strict, true);
1510 let r = self.compile_class_body(b, node);
1511 self.strict = saved_strict;
1512 r
1513 }
1514
1515 fn private_key(m: &ClassMember) -> Option<String> {
1518 match &m.key {
1519 Expr::Str(s) if !m.computed && s.starts_with('#') => Some(s.clone()),
1520 Expr::Ident(s) if !m.computed && s.starts_with('#') => Some(s.clone()),
1521 _ => None,
1522 }
1523 }
1524
1525 fn compile_class_body(&mut self, b: &mut ChunkBuilder, node: &ClassNode) -> Result<(), String> {
1526 let cname = node.name.clone().unwrap_or_default();
1527 self.name_const(b, &cname);
1529 match &node.parent {
1530 Some(p) => self.compile_expr(b, p)?,
1531 None => {
1532 b.emit(Op::LoadUndef, 0);
1533 }
1534 }
1535 let ctor = node
1536 .members
1537 .iter()
1538 .find(|m| m.kind == MemberKind::Constructor);
1539 match ctor {
1540 Some(m) => {
1541 let def_id = self.build_function(&cname, &m.params, &m.body, false, false)?;
1542 self.emit_mkfunc(b, def_id);
1543 }
1544 None => {
1545 b.emit(Op::LoadUndef, 0);
1546 }
1547 }
1548 b.emit(Op::CallBuiltin(ops::MKCLASS, 3), 0); let body_scope = node.name.is_some();
1562 if let Some(name) = &node.name {
1563 self.emit_push_scope(b);
1564 b.emit(Op::Dup, 0); self.declare_as(b, &Expr::Ident(name.clone()), BindMode::Lexical); }
1567
1568 let deferred = |k: &MemberKind| matches!(k, MemberKind::Field | MemberKind::StaticBlock);
1577 let ordered = node
1578 .members
1579 .iter()
1580 .filter(|m| !deferred(&m.kind))
1581 .chain(node.members.iter().filter(|m| deferred(&m.kind)));
1582 let mut static_block_n = 0usize;
1583 for m in ordered {
1584 match m.kind {
1585 MemberKind::Constructor => {}
1586 MemberKind::Field if m.is_static && Self::private_key(m).is_some() => {
1592 let key = Self::private_key(m).expect("guarded above");
1593 self.name_const(b, &key); b.emit(Op::LoadInt(member::STATIC_FIELD), 0);
1595 b.emit(Op::LoadTrue, 0); match &m.field_init {
1597 Some(e) => self.emit_keyed_value(b, &m.key, e, false, member::METHOD)?,
1598 None => {
1599 b.emit(Op::LoadUndef, 0);
1600 }
1601 }
1602 b.emit(Op::CallBuiltin(ops::DEF_MEMBER, 5), 0); }
1604 MemberKind::Field if m.is_static => {
1605 b.emit(Op::Dup, 0); self.emit_member_key(b, m)?; match &m.field_init {
1611 Some(e) => {
1614 self.emit_keyed_value(b, &m.key, e, m.computed, member::METHOD)?
1615 }
1616 None => {
1617 b.emit(Op::LoadUndef, 0);
1618 }
1619 }
1620 b.emit(Op::CallBuiltin(ops::SETATTR, 3), 0);
1622 b.emit(Op::Pop, 0); }
1624 MemberKind::Field => {
1625 self.emit_member_key(b, m)?;
1627 let init = m.field_init.clone().unwrap_or(Expr::Undefined);
1628 let name_anon = Self::is_anon_fn_def(&init);
1634 let stmts = vec![Stmt::from(StmtKind::Return(Some(init)))];
1635 let def_id = self.build_function("", &[], &stmts, false, false)?;
1636 self.emit_mkfunc(b, def_id);
1637 b.emit(
1638 if name_anon {
1639 Op::LoadTrue
1640 } else {
1641 Op::LoadFalse
1642 },
1643 0,
1644 );
1645 b.emit(Op::CallBuiltin(ops::DEF_FIELD, 4), 0);
1646 }
1647 MemberKind::StaticBlock => {
1648 static_block_n += 1;
1657 let slot = format!("@@staticBlock:{static_block_n}");
1658 self.name_const(b, &slot);
1660 b.emit(Op::LoadInt(member::METHOD), 0);
1661 b.emit(Op::LoadTrue, 0);
1662 let def_id = self.build_function("", &[], &m.body, false, false)?;
1663 self.functions[def_id].1.is_method = true;
1664 self.emit_mkfunc(b, def_id);
1665 b.emit(Op::CallBuiltin(ops::DEF_MEMBER, 5), 0);
1666 b.emit(Op::Dup, 0);
1668 self.name_const(b, &slot);
1669 b.emit(Op::CallBuiltin(ops::CALL_METHOD, 2), 0);
1670 b.emit(Op::Pop, 0);
1671 b.emit(Op::Dup, 0);
1673 self.name_const(b, &slot);
1674 b.emit(Op::CallBuiltin(ops::DELPROP_NAME, 2), 0);
1675 b.emit(Op::Pop, 0);
1676 }
1677 MemberKind::Method | MemberKind::Get | MemberKind::Set => {
1678 self.emit_member_key(b, m)?;
1680 let kind = match m.kind {
1681 MemberKind::Get => member::GET,
1682 MemberKind::Set => member::SET,
1683 _ => member::METHOD,
1684 };
1685 b.emit(Op::LoadInt(kind), 0);
1686 b.emit(
1687 if m.is_static {
1688 Op::LoadTrue
1689 } else {
1690 Op::LoadFalse
1691 },
1692 0,
1693 );
1694 let mname = match &m.key {
1698 Expr::Str(s) if !m.computed => match m.kind {
1699 MemberKind::Get => format!("get {s}"),
1700 MemberKind::Set => format!("set {s}"),
1701 _ => s.clone(),
1702 },
1703 _ => String::new(),
1704 };
1705 let def_id = self.build_function(
1706 &mname,
1707 &m.params,
1708 &m.body,
1709 m.is_generator,
1710 m.is_async,
1711 )?;
1712 self.functions[def_id].1.is_method = true;
1715 self.emit_mkfunc(b, def_id);
1716 b.emit(Op::CallBuiltin(ops::DEF_MEMBER, 5), 0);
1717 }
1718 }
1719 }
1720 if body_scope {
1721 self.emit_pop_scope(b);
1722 }
1723 Ok(())
1724 }
1725
1726 fn is_anon_fn_def(init: &Expr) -> bool {
1734 match init {
1735 Expr::Function { name: None, .. } => true,
1736 Expr::Class(node) => node.name.is_none(),
1737 _ => false,
1738 }
1739 }
1740
1741 fn infer_name(&mut self, b: &mut ChunkBuilder, init: &Expr, name: &str) {
1744 if !Self::is_anon_fn_def(init) {
1745 return;
1746 }
1747 b.emit(Op::Dup, 0);
1749 self.name_const(b, "name");
1750 self.strlit(b, name);
1751 b.emit(Op::CallBuiltin(ops::SETATTR, 3), 0);
1752 b.emit(Op::Pop, 0);
1753 }
1754
1755 fn emit_keyed_value(
1766 &mut self,
1767 b: &mut ChunkBuilder,
1768 key: &Expr,
1769 value: &Expr,
1770 computed: bool,
1771 kind: i64,
1772 ) -> Result<(), String> {
1773 match (Self::is_anon_fn_def(value), computed, key) {
1774 (true, false, Expr::Str(s)) => {
1775 self.compile_expr(b, value)?;
1776 let name = match kind {
1777 member::GET => format!("get {s}"),
1778 member::SET => format!("set {s}"),
1779 _ => s.clone(),
1780 };
1781 self.infer_name(b, value, &name);
1782 }
1783 (true, true, _) => {
1785 b.emit(Op::Dup, 0);
1786 b.emit(Op::LoadInt(kind), 0);
1787 self.compile_expr(b, value)?;
1788 b.emit(Op::CallBuiltin(ops::NAMED_EVAL, 3), 0);
1789 }
1790 _ => self.compile_expr(b, value)?,
1791 }
1792 Ok(())
1793 }
1794
1795 fn emit_member_key(&mut self, b: &mut ChunkBuilder, m: &ClassMember) -> Result<(), String> {
1798 if m.computed {
1799 self.compile_expr(b, &m.key)?;
1800 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0);
1801 } else if let Expr::Str(s) = &m.key {
1802 self.name_const(b, s);
1803 } else {
1804 self.compile_expr(b, &m.key)?;
1805 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0);
1806 }
1807 Ok(())
1808 }
1809
1810 fn compile_yield(
1812 &mut self,
1813 b: &mut ChunkBuilder,
1814 arg: &Option<Box<Expr>>,
1815 delegate: bool,
1816 ) -> Result<(), String> {
1817 if delegate && self.in_async_generator {
1818 match arg {
1822 Some(e) => self.compile_expr(b, e)?,
1823 None => {
1824 b.emit(Op::LoadUndef, 0);
1825 }
1826 }
1827 b.emit(Op::CallBuiltin(ops::GET_ASYNC_ITER, 1), 0); let start = b.current_pos();
1829 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");
1834 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0);
1835 b.emit(Op::CallBuiltin(ops::TRUTHY, 1), 0);
1836 let jdone = b.emit(Op::JumpIfTrue(0), 0); self.name_const(b, "value");
1838 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));
1841 b.emit(Op::Pop, 0); b.emit(Op::Jump(start), 0);
1843 let done = b.current_pos();
1844 b.patch_jump(jdone, done);
1845 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 {
1850 let sent_tmp = self.tmp_name("delegated");
1855 match arg {
1856 Some(e) => self.compile_expr(b, e)?,
1857 None => {
1858 b.emit(Op::LoadUndef, 0);
1859 }
1860 }
1861 b.emit(Op::CallBuiltin(ops::GETITER, 1), 0); self.iter_depth += 1;
1867 self.name_const(b, &sent_tmp);
1868 b.emit(Op::LoadUndef, 0);
1869 b.emit(Op::CallBuiltin(ops::DECLARE, 2), 0);
1870 b.emit(Op::Pop, 0);
1871 let start = b.current_pos();
1872 b.emit(Op::Dup, 0); self.name_const(b, "next");
1874 self.load_local(b, &sent_tmp);
1875 b.emit(Op::CallBuiltin(ops::CALL_METHOD, 3), 0); b.emit(Op::Dup, 0);
1877 self.name_const(b, "done");
1878 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0);
1879 b.emit(Op::CallBuiltin(ops::TRUTHY, 1), 0);
1880 let jdone = b.emit(Op::JumpIfTrue(0), 0); self.name_const(b, "value");
1882 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));
1885 self.name_const(b, &sent_tmp);
1886 b.emit(Op::Swap, 0);
1887 b.emit(Op::CallBuiltin(ops::SETLOCAL, 2), 0);
1888 b.emit(Op::Pop, 0);
1889 b.emit(Op::Jump(start), 0);
1890 let done = b.current_pos();
1891 b.patch_jump(jdone, done);
1892 self.name_const(b, "value"); b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0); b.emit(Op::Swap, 0);
1895 b.emit(Op::Pop, 0); self.iter_depth -= 1;
1897 } else {
1898 match arg {
1899 Some(e) => self.compile_expr(b, e)?,
1900 None => {
1901 b.emit(Op::LoadUndef, 0);
1902 }
1903 }
1904 let at = b.emit(Op::CallBuiltin(ops::YIELD, 1), 0);
1906 self.yield_sites.push((at, self.iter_depth));
1907 }
1908 Ok(())
1909 }
1910
1911 fn lower_params(&mut self, params: &[Param]) -> Result<(Vec<ParamSlot>, Vec<Stmt>), String> {
1914 let mut slots = Vec::new();
1915 let mut prologue: Vec<Stmt> = Vec::new();
1916 for (i, p) in params.iter().enumerate() {
1917 if p.rest {
1918 let name = match &p.pattern {
1919 Expr::Ident(n) => n.clone(),
1920 _ => return Err("SyntaxError: rest parameter must be an identifier".into()),
1921 };
1922 slots.push(ParamSlot {
1923 name,
1924 rest: true,
1925 has_default: false,
1926 });
1927 continue;
1928 }
1929 match &p.pattern {
1930 Expr::Ident(name) => {
1931 slots.push(ParamSlot {
1932 name: name.clone(),
1933 rest: false,
1934 has_default: p.default.is_some(),
1935 });
1936 if let Some(d) = &p.default {
1937 prologue.push(default_stmt(name, d));
1938 }
1939 }
1940 pattern => {
1941 let synth = format!(".param{i}");
1942 slots.push(ParamSlot {
1943 name: synth.clone(),
1944 rest: false,
1945 has_default: p.default.is_some(),
1946 });
1947 if let Some(d) = &p.default {
1948 prologue.push(default_stmt(&synth, d));
1949 }
1950 prologue.push(Stmt::from(StmtKind::Decl {
1951 kind: DeclKind::Let,
1952 decls: vec![Declarator {
1953 target: pattern.clone(),
1954 init: Some(Expr::Ident(synth)),
1955 }],
1956 }));
1957 }
1958 }
1959 }
1960 Ok((slots, prologue))
1961 }
1962
1963 fn compile_expr(&mut self, b: &mut ChunkBuilder, e: &Expr) -> Result<(), String> {
1965 match e {
1966 Expr::Undefined => {
1967 b.emit(Op::LoadUndef, 0);
1968 }
1969 Expr::Hole => {
1973 b.emit(Op::LoadUndef, 0);
1974 }
1975 Expr::Null => {
1976 b.emit(Op::CallBuiltin(ops::LOAD_NULL, 0), 0);
1977 }
1978 Expr::True => {
1979 b.emit(Op::LoadTrue, 0);
1980 }
1981 Expr::False => {
1982 b.emit(Op::LoadFalse, 0);
1983 }
1984 Expr::Number(n) => {
1985 b.emit(Op::LoadFloat(*n), 0);
1986 }
1987 Expr::BigInt(digits) => {
1988 let k = b.add_constant(Value::str(digits));
1991 b.emit(Op::LoadConst(k), 0);
1992 b.emit(Op::CallBuiltin(ops::MKBIGINT, 1), 0);
1993 }
1994 Expr::Regex(pat, flags) => {
1995 let kp = b.add_constant(Value::str(pat));
1996 b.emit(Op::LoadConst(kp), 0);
1997 let kf = b.add_constant(Value::str(flags));
1998 b.emit(Op::LoadConst(kf), 0);
1999 b.emit(Op::CallBuiltin(ops::MKREGEX, 2), 0);
2000 }
2001 Expr::Str(s) => self.strlit(b, s),
2002 Expr::Template { quasis, exprs } => self.compile_template(b, quasis, exprs)?,
2003 Expr::TaggedTemplate {
2004 tag,
2005 quasis,
2006 raws,
2007 exprs,
2008 } => self.compile_tagged_template(b, tag, quasis, raws, exprs)?,
2009 Expr::Ident(n) => self.load_local(b, n),
2010 Expr::This => {
2011 b.emit(Op::CallBuiltin(ops::THIS, 0), 0);
2012 }
2013 Expr::Array(items) => self.compile_array(b, items)?,
2014 Expr::Object(props) => self.compile_object(b, props)?,
2015 Expr::Spread(inner) => self.compile_expr(b, inner)?,
2016 Expr::Logical(op, l, r) => self.compile_logical(b, *op, l, r)?,
2017 Expr::Unary(op, e) => self.compile_unary(b, *op, e)?,
2018 Expr::Binary(op, l, r) => self.compile_binary(b, *op, l, r)?,
2019 Expr::Conditional { test, cons, alt } => {
2020 self.compile_condition(b, test)?;
2021 let jf = b.emit(Op::JumpIfFalse(0), 0);
2022 self.compile_expr(b, cons)?;
2023 let je = b.emit(Op::Jump(0), 0);
2024 let els = b.current_pos();
2025 b.patch_jump(jf, els);
2026 self.compile_expr(b, alt)?;
2027 let end = b.current_pos();
2028 b.patch_jump(je, end);
2029 }
2030 Expr::Assign { target, value } => {
2031 self.compile_expr(b, value)?;
2032 if let Expr::Ident(n) = &**target {
2036 self.infer_name(b, value, n);
2037 }
2038 b.emit(Op::Dup, 0); self.compile_bind(b, target, BindMode::Assign)?;
2040 }
2041 Expr::Update { op, prefix, target } => self.compile_update(b, *op, *prefix, target)?,
2042 Expr::Call { .. } | Expr::Member { .. } | Expr::Index { .. }
2045 if self.opt_chain.is_empty() && Self::spine_has_optional(e) =>
2046 {
2047 self.compile_chain_root(b, e)?
2048 }
2049 Expr::Call {
2050 func,
2051 args,
2052 optional,
2053 } => self.compile_call(b, func, args, *optional)?,
2054 Expr::New { callee, args } => self.compile_new(b, callee, args)?,
2055 Expr::Member {
2056 object,
2057 property,
2058 optional,
2059 } => self.compile_member(b, object, property, *optional)?,
2060 Expr::Index {
2061 object,
2062 index,
2063 optional,
2064 } => self.compile_index(b, object, index, *optional)?,
2065 Expr::Function {
2066 params,
2067 body,
2068 is_arrow,
2069 name,
2070 is_generator,
2071 is_async,
2072 is_method,
2073 } => {
2074 let def_id = if *is_arrow {
2075 self.build_arrow(params, body, *is_async)?
2076 } else {
2077 let n = name.clone().unwrap_or_default();
2078 let stmts = match body {
2079 FnBody::Block(b) => b.clone(),
2080 FnBody::Expr(e) => vec![Stmt::from(StmtKind::Return(Some((**e).clone())))],
2081 };
2082 let id = self.build_function(&n, params, &stmts, *is_generator, *is_async)?;
2083 if name.is_some() {
2087 self.functions[id].1.self_name = true;
2088 }
2089 self.functions[id].1.is_method = *is_method;
2090 id
2091 };
2092 self.emit_mkfunc(b, def_id);
2093 }
2094 Expr::Class(node) => self.compile_class(b, node)?,
2095 Expr::Super => {
2096 b.emit(Op::LoadUndef, 0);
2099 }
2100 Expr::NewTarget => {
2101 b.emit(Op::CallBuiltin(ops::NEW_TARGET, 0), 0);
2102 }
2103 Expr::Yield { arg, delegate } => self.compile_yield(b, arg, *delegate)?,
2104 Expr::Await(inner) => {
2105 self.compile_expr(b, inner)?;
2106 b.emit(Op::CallBuiltin(ops::AWAIT, 1), 0);
2107 }
2108 Expr::Sequence(items) => {
2109 for (i, it) in items.iter().enumerate() {
2110 self.compile_expr(b, it)?;
2111 if i + 1 < items.len() {
2112 b.emit(Op::Pop, 0);
2113 }
2114 }
2115 }
2116 }
2117 Ok(())
2118 }
2119
2120 fn compile_template(
2121 &mut self,
2122 b: &mut ChunkBuilder,
2123 quasis: &[String],
2124 exprs: &[Expr],
2125 ) -> Result<(), String> {
2126 let mut n = 0;
2127 for (i, q) in quasis.iter().enumerate() {
2128 let k = b.add_constant(Value::str(q));
2129 b.emit(Op::LoadConst(k), 0);
2130 n += 1;
2131 if i < exprs.len() {
2132 self.compile_expr(b, &exprs[i])?;
2133 b.emit(Op::CallBuiltin(ops::TOSTR, 1), 0);
2134 n += 1;
2135 }
2136 }
2137 b.emit(Op::CallBuiltin(ops::MKSTR, argc(n)?), 0);
2138 Ok(())
2139 }
2140
2141 fn compile_tagged_template(
2145 &mut self,
2146 b: &mut ChunkBuilder,
2147 tag: &Expr,
2148 quasis: &[String],
2149 raws: &[String],
2150 exprs: &[Expr],
2151 ) -> Result<(), String> {
2152 self.compile_expr(b, tag)?;
2153 let n = quasis.len();
2154 let m = exprs.len();
2155 b.emit(Op::LoadInt(n as i64), 0);
2156 b.emit(Op::LoadInt(m as i64), 0);
2157 for q in quasis {
2158 self.strlit(b, q); }
2160 for r in raws {
2161 self.strlit(b, r); }
2163 for e in exprs {
2164 self.compile_expr(b, e)?; }
2166 b.emit(Op::CallBuiltin(ops::TAG_TMPL, argc(3 + 2 * n + m)?), 0);
2167 Ok(())
2168 }
2169
2170 fn compile_array(&mut self, b: &mut ChunkBuilder, items: &[Expr]) -> Result<(), String> {
2171 if items.iter().any(|e| matches!(e, Expr::Spread(_))) {
2172 for it in items {
2177 match it {
2178 Expr::Spread(inner) => {
2179 b.emit(Op::LoadInt(1), 0);
2180 self.compile_expr(b, inner)?;
2181 }
2182 Expr::Hole => {
2183 b.emit(Op::LoadInt(2), 0);
2184 b.emit(Op::LoadUndef, 0);
2185 }
2186 _ => {
2187 b.emit(Op::LoadInt(0), 0);
2188 self.compile_expr(b, it)?;
2189 }
2190 }
2191 }
2192 b.emit(Op::CallBuiltin(ops::BUILD_ARGS, argc(items.len() * 2)?), 0);
2193 } else if items.len() <= u8::MAX as usize {
2194 for it in items {
2195 self.compile_expr(b, it)?;
2196 }
2197 b.emit(Op::CallBuiltin(ops::MKARR, argc(items.len())?), 0);
2198 self.mark_literal_holes(b, items);
2199 } else {
2200 b.emit(Op::CallBuiltin(ops::MKARR, 0), 0); for (i, it) in items.iter().enumerate() {
2206 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); }
2212 self.mark_literal_holes(b, items);
2215 }
2216 Ok(())
2217 }
2218
2219 fn mark_literal_holes(&mut self, b: &mut ChunkBuilder, items: &[Expr]) {
2223 for (i, it) in items.iter().enumerate() {
2224 if !matches!(it, Expr::Hole) {
2225 continue;
2226 }
2227 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); }
2232 }
2233
2234 fn compile_object(&mut self, b: &mut ChunkBuilder, props: &[Prop]) -> Result<(), String> {
2235 let mut seen_accessor: Vec<String> = Vec::new();
2241 let data: Vec<&Prop> = props
2242 .iter()
2243 .filter(|p| match p {
2244 Prop::Accessor { key, computed, .. } => {
2245 let literal = match (key, computed) {
2248 (Expr::Str(s), false) => Some(s.clone()),
2249 _ => None,
2250 };
2251 match literal {
2252 Some(k) if seen_accessor.contains(&k) => false,
2253 Some(k) => {
2254 seen_accessor.push(k);
2255 true
2256 }
2257 None => true,
2258 }
2259 }
2260 _ => true,
2261 })
2262 .collect();
2263 let has_spread = data.iter().any(|p| matches!(p, Prop::Spread(_)));
2264 if data.len() * 3 > u8::MAX as usize && !has_spread {
2270 b.emit(Op::CallBuiltin(ops::MKOBJ, 0), 0); for p in &data {
2272 if let Prop::KeyValue {
2273 key,
2274 value,
2275 computed,
2276 } = p
2277 {
2278 b.emit(Op::Dup, 0); self.compile_expr(b, key)?;
2280 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0); self.emit_keyed_value(b, key, value, *computed, member::METHOD)?;
2282 b.emit(Op::CallBuiltin(ops::SETITEM, 3), 0); b.emit(Op::Pop, 0); }
2285 }
2286 return self.compile_object_accessors(b, props);
2289 }
2290 for p in &data {
2291 match p {
2292 Prop::KeyValue {
2293 key,
2294 value,
2295 computed,
2296 } => {
2297 b.emit(Op::LoadInt(0), 0);
2298 self.compile_expr(b, key)?;
2301 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0);
2302 self.emit_keyed_value(b, key, value, *computed, member::METHOD)?;
2303 }
2304 Prop::Spread(src) => {
2305 b.emit(Op::LoadInt(1), 0);
2306 self.compile_expr(b, src)?;
2307 b.emit(Op::LoadUndef, 0);
2308 }
2309 Prop::Accessor { key, computed, .. } => {
2312 let _ = computed; b.emit(Op::LoadInt(2), 0);
2314 self.compile_expr(b, key)?;
2315 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0);
2316 b.emit(Op::LoadUndef, 0);
2317 }
2318 }
2319 }
2320 b.emit(Op::CallBuiltin(ops::MKOBJ, argc(data.len() * 3)?), 0); self.compile_object_accessors(b, props)
2322 }
2323
2324 fn compile_object_accessors(
2327 &mut self,
2328 b: &mut ChunkBuilder,
2329 props: &[Prop],
2330 ) -> Result<(), String> {
2331 for p in props {
2332 if let Prop::Accessor {
2333 key,
2334 computed,
2335 is_getter,
2336 func,
2337 } = p
2338 {
2339 if *computed {
2340 self.compile_expr(b, key)?;
2341 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0);
2342 } else if let Expr::Str(s) = key {
2343 self.name_const(b, s);
2344 } else {
2345 self.compile_expr(b, key)?;
2346 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0);
2347 }
2348 let kind = if *is_getter { member::GET } else { member::SET };
2349 b.emit(Op::LoadInt(kind), 0);
2350 if *computed {
2355 self.compile_expr(b, func)?;
2356 } else if let Expr::Str(s) = key {
2357 self.compile_expr(b, func)?;
2358 let prefix = if *is_getter { "get" } else { "set" };
2359 self.infer_name(b, func, &format!("{prefix} {s}"));
2360 } else {
2361 self.compile_expr(b, func)?;
2362 }
2363 b.emit(Op::CallBuiltin(ops::DEF_ACCESSOR, 4), 0);
2364 }
2365 }
2366 Ok(())
2367 }
2368
2369 fn compile_logical(
2370 &mut self,
2371 b: &mut ChunkBuilder,
2372 op: LogicalOp,
2373 l: &Expr,
2374 r: &Expr,
2375 ) -> Result<(), String> {
2376 self.compile_expr(b, l)?;
2377 b.emit(Op::Dup, 0);
2378 let test_op = match op {
2379 LogicalOp::And | LogicalOp::Or => ops::TRUTHY,
2380 LogicalOp::Nullish => ops::NULLISH,
2381 };
2382 b.emit(Op::CallBuiltin(test_op, 1), 0);
2383 let jump = match op {
2384 LogicalOp::And => b.emit(Op::JumpIfFalse(0), 0), LogicalOp::Or => b.emit(Op::JumpIfTrue(0), 0), LogicalOp::Nullish => b.emit(Op::JumpIfFalse(0), 0), };
2388 b.emit(Op::Pop, 0); self.compile_expr(b, r)?;
2390 let end = b.current_pos();
2391 b.patch_jump(jump, end);
2392 Ok(())
2393 }
2394
2395 fn compile_unary(&mut self, b: &mut ChunkBuilder, op: UnOp, e: &Expr) -> Result<(), String> {
2396 match op {
2397 UnOp::Neg => {
2398 self.compile_expr(b, e)?;
2399 b.emit(Op::Negate, 0);
2400 }
2401 UnOp::Not => {
2402 self.compile_condition(b, e)?;
2403 b.emit(Op::LogNot, 0);
2404 }
2405 UnOp::Pos => {
2406 b.emit(Op::LoadInt(unop::POS), 0);
2407 self.compile_expr(b, e)?;
2408 b.emit(Op::CallBuiltin(ops::UNARY, 2), 0);
2409 }
2410 UnOp::BitNot => {
2411 b.emit(Op::LoadInt(unop::BITNOT), 0);
2412 self.compile_expr(b, e)?;
2413 b.emit(Op::CallBuiltin(ops::UNARY, 2), 0);
2414 }
2415 UnOp::TypeOf => {
2416 if let Expr::Ident(n) = e {
2420 if let Some(slot) = self.slot_of(n) {
2424 b.emit(Op::GetSlot(slot), 0);
2425 b.emit(Op::CallBuiltin(ops::TYPEOF, 1), 0);
2426 return Ok(());
2427 }
2428 self.name_const(b, n);
2429 b.emit(Op::CallBuiltin(ops::TYPEOF_NAME, 1), 0);
2430 } else {
2431 self.compile_expr(b, e)?;
2432 b.emit(Op::CallBuiltin(ops::TYPEOF, 1), 0);
2433 }
2434 }
2435 UnOp::Void => {
2436 self.compile_expr(b, e)?;
2437 b.emit(Op::Pop, 0);
2438 b.emit(Op::LoadUndef, 0);
2439 }
2440 UnOp::Delete => match e {
2441 Expr::Member {
2442 object, property, ..
2443 } => {
2444 self.compile_expr(b, object)?;
2445 self.name_const(b, property);
2446 b.emit(Op::CallBuiltin(ops::DELPROP_NAME, 2), 0);
2447 }
2448 Expr::Index { object, index, .. } => {
2449 self.compile_expr(b, object)?;
2450 self.compile_expr(b, index)?;
2451 b.emit(Op::CallBuiltin(ops::DELITEM, 2), 0);
2452 }
2453 _ => {
2454 b.emit(Op::LoadTrue, 0);
2455 }
2456 },
2457 }
2458 Ok(())
2459 }
2460
2461 fn compile_binary(
2462 &mut self,
2463 b: &mut ChunkBuilder,
2464 op: BinOp,
2465 l: &Expr,
2466 r: &Expr,
2467 ) -> Result<(), String> {
2468 macro_rules! native {
2471 ($opc:expr) => {{
2472 self.compile_expr(b, l)?;
2473 self.compile_expr(b, r)?;
2474 b.emit($opc, 0);
2475 return Ok(());
2476 }};
2477 }
2478 match op {
2479 BinOp::Add => native!(Op::Add),
2480 BinOp::Sub => native!(Op::Sub),
2481 BinOp::Mul => native!(Op::Mul),
2482 BinOp::Div => {
2483 self.compile_expr(b, l)?;
2487 self.compile_expr(b, r)?;
2488 b.emit(Op::CallBuiltin(ops::DIV, 2), 0);
2489 return Ok(());
2490 }
2491 BinOp::Mod => native!(Op::Mod),
2492 BinOp::Pow => {
2496 self.compile_expr(b, l)?;
2497 self.compile_expr(b, r)?;
2498 b.emit(Op::CallBuiltin(ops::POW, 2), 0);
2499 return Ok(());
2500 }
2501 BinOp::Lt => native!(Op::NumLt),
2502 BinOp::Le => native!(Op::NumLe),
2503 BinOp::Gt => native!(Op::NumGt),
2504 BinOp::Ge => native!(Op::NumGe),
2505 BinOp::EqEqEq => {
2506 self.compile_expr(b, l)?;
2507 self.compile_expr(b, r)?;
2508 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
2509 }
2510 BinOp::NeEqEq => {
2511 self.compile_expr(b, l)?;
2512 self.compile_expr(b, r)?;
2513 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
2514 b.emit(Op::LogNot, 0);
2515 }
2516 BinOp::EqEq => {
2517 self.compile_expr(b, l)?;
2518 self.compile_expr(b, r)?;
2519 b.emit(Op::CallBuiltin(ops::LOOSE_EQ, 2), 0);
2520 }
2521 BinOp::NeEq => {
2522 self.compile_expr(b, l)?;
2523 self.compile_expr(b, r)?;
2524 b.emit(Op::CallBuiltin(ops::LOOSE_EQ, 2), 0);
2525 b.emit(Op::LogNot, 0);
2526 }
2527 BinOp::In => {
2528 match l {
2532 Expr::Ident(n) if n.starts_with('#') => self.name_const(b, n),
2533 _ => self.compile_expr(b, l)?,
2534 }
2535 self.compile_expr(b, r)?;
2536 b.emit(Op::CallBuiltin(ops::CONTAINS, 2), 0);
2537 }
2538 BinOp::InstanceOf => {
2539 self.compile_expr(b, l)?;
2540 self.compile_expr(b, r)?;
2541 b.emit(Op::CallBuiltin(ops::INSTANCEOF, 2), 0);
2542 }
2543 BinOp::BitAnd => self.emit_bitwise(b, bop::BITAND, l, r)?,
2544 BinOp::BitOr => self.emit_bitwise(b, bop::BITOR, l, r)?,
2545 BinOp::BitXor => self.emit_bitwise(b, bop::BITXOR, l, r)?,
2546 BinOp::Shl => self.emit_bitwise(b, bop::SHL, l, r)?,
2547 BinOp::Shr => self.emit_bitwise(b, bop::SHR, l, r)?,
2548 BinOp::UShr => self.emit_bitwise(b, bop::USHR, l, r)?,
2549 }
2550 Ok(())
2551 }
2552
2553 fn emit_bitwise(
2554 &mut self,
2555 b: &mut ChunkBuilder,
2556 tag: i64,
2557 l: &Expr,
2558 r: &Expr,
2559 ) -> Result<(), String> {
2560 b.emit(Op::LoadInt(tag), 0);
2561 self.compile_expr(b, l)?;
2562 self.compile_expr(b, r)?;
2563 b.emit(Op::CallBuiltin(ops::BINOP, 3), 0);
2564 Ok(())
2565 }
2566
2567 fn compile_update(
2568 &mut self,
2569 b: &mut ChunkBuilder,
2570 op: UpdateOp,
2571 prefix: bool,
2572 target: &Expr,
2573 ) -> Result<(), String> {
2574 let tag = if matches!(op, UpdateOp::Inc) { 1 } else { -1 };
2579 if let Expr::Ident(n) = target {
2585 if self.slots.consts.contains(n) {
2591 self.throw_const_assignment(b);
2592 return Ok(());
2593 }
2594 if let Some(slot) = self.numeric_slot_of(n) {
2595 b.emit(Op::GetSlot(slot), 0); if !prefix {
2597 b.emit(Op::Dup, 0); }
2599 b.emit(Op::LoadFloat(tag as f64), 0);
2600 b.emit(Op::Add, 0); if prefix {
2602 b.emit(Op::Dup, 0); }
2604 b.emit(Op::SetSlot(slot), 0); return Ok(());
2606 }
2607 }
2608 b.emit(Op::LoadInt(tag), 0);
2609 self.compile_expr(b, target)?; b.emit(Op::CallBuiltin(ops::NUM_STEP, 2), 0); if prefix {
2612 b.emit(Op::Swap, 0); b.emit(Op::Pop, 0); b.emit(Op::Dup, 0); self.compile_bind(b, target, BindMode::Assign)?; } else {
2618 self.compile_bind(b, target, BindMode::Assign)?; }
2621 Ok(())
2622 }
2623
2624 fn compile_member(
2625 &mut self,
2626 b: &mut ChunkBuilder,
2627 object: &Expr,
2628 property: &str,
2629 optional: bool,
2630 ) -> Result<(), String> {
2631 if matches!(object, Expr::Super) {
2633 self.name_const(b, property);
2634 b.emit(Op::CallBuiltin(ops::SUPER_GET, 1), 0);
2635 return Ok(());
2636 }
2637 self.compile_expr(b, object)?;
2638 if optional {
2639 let jshort = self.emit_optional_guard(b);
2640 self.name_const(b, property);
2641 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0);
2642 match self.opt_chain.last_mut() {
2646 Some(frame) => frame.push(jshort),
2647 None => {
2648 let end = b.current_pos();
2649 b.patch_jump(jshort, end);
2650 }
2651 }
2652 } else {
2653 self.name_const(b, property);
2654 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0);
2655 }
2656 Ok(())
2657 }
2658
2659 fn compile_index(
2660 &mut self,
2661 b: &mut ChunkBuilder,
2662 object: &Expr,
2663 index: &Expr,
2664 optional: bool,
2665 ) -> Result<(), String> {
2666 self.compile_expr(b, object)?;
2667 if optional {
2668 let jshort = self.emit_optional_guard(b);
2669 self.compile_off_spine(b, index)?;
2670 b.emit(Op::CallBuiltin(ops::GETITEM, 2), 0);
2671 match self.opt_chain.last_mut() {
2672 Some(frame) => frame.push(jshort),
2673 None => {
2674 let end = b.current_pos();
2675 b.patch_jump(jshort, end);
2676 }
2677 }
2678 } else {
2679 self.compile_off_spine(b, index)?;
2680 b.emit(Op::CallBuiltin(ops::GETITEM, 2), 0);
2681 }
2682 Ok(())
2683 }
2684
2685 fn emit_push_scope(&mut self, b: &mut ChunkBuilder) {
2691 b.emit(Op::CallBuiltin(ops::PUSH_SCOPE, 0), 0);
2692 b.emit(Op::Pop, 0);
2693 self.scope_depth += 1;
2694 }
2695
2696 fn emit_pop_scope(&mut self, b: &mut ChunkBuilder) {
2697 b.emit(Op::CallBuiltin(ops::POP_SCOPE, 0), 0);
2698 b.emit(Op::Pop, 0);
2699 self.scope_depth -= 1;
2700 }
2701
2702 fn emit_copy_scope(&self, b: &mut ChunkBuilder) {
2705 b.emit(Op::CallBuiltin(ops::COPY_SCOPE, 0), 0);
2706 b.emit(Op::Pop, 0);
2707 }
2708
2709 fn finish_chunk(&mut self, b: ChunkBuilder) -> Chunk {
2721 let sites = std::mem::take(&mut self.call_sites);
2722 let yields = std::mem::take(&mut self.yield_sites);
2723 let chunk = b.build();
2724 crate::host::register_call_sites(chunk.op_hash, sites);
2725 crate::host::register_yield_sites(chunk.op_hash, yields);
2726 chunk
2727 }
2728
2729 fn note_call_site(&mut self, at: usize, callee: &Expr) {
2733 if let Some(text) = callee_text(callee) {
2734 self.call_sites.push((at, text));
2735 }
2736 }
2737
2738 fn emit_close_iters_under_value(&self, b: &mut ChunkBuilder) {
2739 for _ in 0..self.iter_depth {
2740 b.emit(Op::Swap, 0); b.emit(Op::CallBuiltin(ops::ITER_CLOSE, 1), 0); b.emit(Op::Pop, 0); }
2744 }
2745
2746 fn emit_close_iters(&self, b: &mut ChunkBuilder, target: usize) {
2747 for _ in target..self.iter_depth {
2748 b.emit(Op::CallBuiltin(ops::ITER_CLOSE, 1), 0);
2749 b.emit(Op::Pop, 0);
2750 }
2751 }
2752
2753 fn emit_unwind_scopes(&self, b: &mut ChunkBuilder, target: usize) {
2756 for _ in target..self.scope_depth {
2757 b.emit(Op::CallBuiltin(ops::POP_SCOPE, 0), 0);
2758 b.emit(Op::Pop, 0);
2759 }
2760 }
2761
2762 fn emit_signal_jump(&mut self, b: &mut ChunkBuilder, op: u16, label: Option<&str>, line: u32) {
2764 self.name_const(b, label.unwrap_or(""));
2765 b.emit(Op::CallBuiltin(op, 1), line);
2766 b.emit(Op::Pop, line);
2767 self.chunk_signals = true;
2768 }
2769
2770 fn emit_signal_dispatch(&mut self, b: &mut ChunkBuilder) {
2776 let brk = self
2783 .loops
2784 .len()
2785 .checked_sub(1)
2786 .filter(|i| *i >= self.chunk_loop_base);
2787 let cont = self
2788 .loops
2789 .iter()
2790 .rposition(|c| c.catches_continue)
2791 .filter(|i| *i >= self.chunk_loop_base);
2792 let tag_of = |i: Option<usize>, loops: &[LoopCtx]| match i {
2793 Some(i) => loops[i]
2794 .label
2795 .clone()
2796 .unwrap_or_else(|| unwind::PLAIN_LOOP.to_string()),
2797 None => unwind::NO_LOOP.to_string(),
2798 };
2799 let brk_tag = tag_of(brk, &self.loops);
2800 let cont_tag = tag_of(cont, &self.loops);
2801 self.name_const(b, &brk_tag);
2802 self.name_const(b, &cont_tag);
2803 b.emit(Op::CallBuiltin(ops::SIG_UNWIND, 2), 0); let Some(idx) = brk else {
2805 b.emit(Op::Pop, 0);
2808 return;
2809 };
2810 b.emit(Op::Dup, 0);
2811 b.emit(Op::LoadInt(unwind::BREAK), 0);
2812 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
2813 let jb = b.emit(Op::JumpIfTrue(0), 0);
2814 let jc = cont.map(|_| {
2815 b.emit(Op::Dup, 0);
2816 b.emit(Op::LoadInt(unwind::CONTINUE), 0);
2817 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
2818 b.emit(Op::JumpIfTrue(0), 0)
2819 });
2820 b.emit(Op::Pop, 0); let jafter = b.emit(Op::Jump(0), 0);
2822 let (brk_scope, brk_iter) = (self.loops[idx].break_depth, self.loops[idx].iter_depth);
2828 let brk_land = b.current_pos();
2829 b.emit(Op::Pop, 0);
2830 self.emit_unwind_scopes(b, brk_scope);
2831 self.emit_close_iters(b, brk_iter);
2832 let brk_jump = b.emit(Op::Jump(0), 0);
2833 let cont_jump = jc.map(|_| {
2834 let (cs, ci) = cont
2835 .map(|i| (self.loops[i].continue_depth, self.loops[i].iter_depth))
2836 .unwrap_or((self.scope_depth, self.iter_depth));
2837 let cont_land = b.current_pos();
2838 b.emit(Op::Pop, 0);
2839 self.emit_unwind_scopes(b, cs);
2840 self.emit_close_iters(b, ci);
2841 (cont_land, b.emit(Op::Jump(0), 0))
2842 });
2843 let after = b.current_pos();
2844 b.patch_jump(jb, brk_land);
2845 if let (Some(jc), Some((cont_land, _))) = (jc, cont_jump) {
2846 b.patch_jump(jc, cont_land);
2847 }
2848 b.patch_jump(jafter, after);
2849 self.loops[idx].breaks.push(brk_jump);
2850 if let (Some(cont_idx), Some((_, cj))) = (cont, cont_jump) {
2851 self.loops[cont_idx].continues.push(cj);
2852 }
2853 }
2854
2855 fn spine_has_optional(e: &Expr) -> bool {
2860 match e {
2861 Expr::Member {
2862 object, optional, ..
2863 } => *optional || Self::spine_has_optional(object),
2864 Expr::Index {
2865 object, optional, ..
2866 } => *optional || Self::spine_has_optional(object),
2867 Expr::Call { func, optional, .. } => *optional || Self::spine_has_optional(func),
2868 _ => false,
2869 }
2870 }
2871
2872 fn compile_chain_root(&mut self, b: &mut ChunkBuilder, e: &Expr) -> Result<(), String> {
2875 self.opt_chain.push(Vec::new());
2876 let r = self.compile_expr(b, e);
2877 let pending = self.opt_chain.pop().unwrap_or_default();
2878 r?;
2879 let end = b.current_pos();
2880 for j in pending {
2881 b.patch_jump(j, end);
2882 }
2883 Ok(())
2884 }
2885
2886 fn compile_off_spine(&mut self, b: &mut ChunkBuilder, e: &Expr) -> Result<(), String> {
2890 let saved = std::mem::take(&mut self.opt_chain);
2891 let r = self.compile_expr(b, e);
2892 self.opt_chain = saved;
2893 r
2894 }
2895
2896 fn emit_optional_guard(&mut self, b: &mut ChunkBuilder) -> usize {
2897 b.emit(Op::Dup, 0);
2898 b.emit(Op::CallBuiltin(ops::NULLISH, 1), 0);
2899 let jnull = b.emit(Op::JumpIfFalse(0), 0); b.emit(Op::Pop, 0);
2902 b.emit(Op::LoadUndef, 0);
2903 let jend = b.emit(Op::Jump(0), 0);
2904 let cont = b.current_pos();
2905 b.patch_jump(jnull, cont);
2906 jend
2907 }
2908
2909 fn compile_optional_call(
2914 &mut self,
2915 b: &mut ChunkBuilder,
2916 func: &Expr,
2917 args: &[Expr],
2918 ) -> Result<(), String> {
2919 let has_spread = args.iter().any(|a| matches!(a, Expr::Spread(_)));
2920 if let Expr::Member {
2921 object,
2922 property,
2923 optional: obj_optional,
2924 } = func
2925 {
2926 self.compile_expr(b, object)?; let jobj = if *obj_optional {
2928 Some(self.emit_optional_guard(b))
2929 } else {
2930 None
2931 };
2932 b.emit(Op::Dup, 0); self.name_const(b, property); b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0); b.emit(Op::Dup, 0);
2937 b.emit(Op::CallBuiltin(ops::NULLISH, 1), 0);
2938 let jlive = b.emit(Op::JumpIfFalse(0), 0);
2939 b.emit(Op::Pop, 0);
2940 b.emit(Op::Pop, 0);
2941 b.emit(Op::LoadUndef, 0);
2942 let jend = b.emit(Op::Jump(0), 0);
2943 let live = b.current_pos();
2944 b.patch_jump(jlive, live);
2945 let via = if has_spread { "apply" } else { "call" };
2947 self.name_const(b, via); b.emit(Op::Rot, 0); let extra = if has_spread {
2950 self.compile_spread_args(b, args)?; 1
2952 } else {
2953 for a in args {
2954 self.compile_expr(b, a)?;
2955 }
2956 args.len()
2957 };
2958 b.emit(Op::CallBuiltin(ops::CALL_METHOD, argc(3 + extra)?), 0);
2959 match self.opt_chain.last_mut() {
2960 Some(frame) => {
2961 frame.push(jend);
2962 if let Some(j) = jobj {
2963 frame.push(j);
2964 }
2965 }
2966 None => {
2967 let end = b.current_pos();
2968 b.patch_jump(jend, end);
2969 if let Some(j) = jobj {
2970 b.patch_jump(j, end);
2971 }
2972 }
2973 }
2974 return Ok(());
2975 }
2976 self.compile_expr(b, func)?;
2979 let jend = self.emit_optional_guard(b);
2980 if has_spread {
2981 self.compile_spread_args(b, args)?;
2982 b.emit(Op::CallBuiltin(ops::APPLY, 2), 0);
2983 } else {
2984 for a in args {
2985 self.compile_expr(b, a)?;
2986 }
2987 b.emit(Op::CallBuiltin(ops::CALL_VALUE, argc(1 + args.len())?), 0);
2988 }
2989 let end = b.current_pos();
2990 b.patch_jump(jend, end);
2991 Ok(())
2992 }
2993
2994 fn compile_call(
2995 &mut self,
2996 b: &mut ChunkBuilder,
2997 func: &Expr,
2998 args: &[Expr],
2999 optional: bool,
3000 ) -> Result<(), String> {
3001 if optional {
3002 return self.compile_optional_call(b, func, args);
3003 }
3004 let has_spread = args.iter().any(|a| matches!(a, Expr::Spread(_)));
3005 match func {
3006 Expr::Super => {
3009 for a in args {
3010 self.compile_expr(b, a)?;
3011 }
3012 b.emit(Op::CallBuiltin(ops::SUPER_CALL, argc(args.len())?), 0);
3013 return Ok(());
3014 }
3015 Expr::Member {
3018 object, property, ..
3019 } if matches!(**object, Expr::Super) => {
3020 self.name_const(b, property);
3021 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 {
3027 self.compile_expr(b, a)?;
3028 }
3029 b.emit(Op::CallBuiltin(ops::CALL_METHOD, argc(3 + args.len())?), 0);
3030 return Ok(());
3031 }
3032 Expr::Member {
3033 object,
3034 property,
3035 optional,
3036 } => {
3037 self.compile_expr(b, object)?;
3038 let jshort = if *optional {
3041 Some(self.emit_optional_guard(b))
3042 } else {
3043 None
3044 };
3045 self.name_const(b, property);
3046 if has_spread {
3047 self.compile_spread_args(b, args)?; b.emit(Op::CallBuiltin(ops::APPLY_METHOD, 3), 0);
3049 } else {
3050 for a in args {
3053 self.compile_off_spine(b, a)?;
3054 }
3055 let at = b.emit(Op::CallBuiltin(ops::CALL_METHOD, argc(2 + args.len())?), 0);
3056 self.note_call_site(at, func);
3057 }
3058 if let Some(j) = jshort {
3059 match self.opt_chain.last_mut() {
3060 Some(frame) => frame.push(j),
3061 None => {
3062 let end = b.current_pos();
3063 b.patch_jump(j, end);
3064 }
3065 }
3066 }
3067 }
3068 Expr::Index {
3069 object,
3070 index,
3071 optional,
3072 } => {
3073 self.compile_expr(b, object)?; let jshort = if *optional {
3077 Some(self.emit_optional_guard(b))
3078 } else {
3079 None
3080 };
3081 b.emit(Op::Dup, 0); self.compile_expr(b, index)?; b.emit(Op::CallBuiltin(ops::GETITEM, 2), 0); b.emit(Op::Swap, 0); b.emit(Op::Pop, 0); if has_spread {
3089 self.compile_spread_args(b, args)?;
3090 b.emit(Op::CallBuiltin(ops::APPLY, 2), 0);
3091 } else {
3092 for a in args {
3093 self.compile_expr(b, a)?;
3094 }
3095 let at = b.emit(Op::CallBuiltin(ops::CALL_VALUE, argc(1 + args.len())?), 0);
3096 self.note_call_site(at, func);
3097 }
3098 if let Some(j) = jshort {
3099 let end = b.current_pos();
3100 b.patch_jump(j, end);
3101 }
3102 }
3103 Expr::Ident(n) if self.slot_of(n).is_none() => {
3107 self.name_const(b, n);
3108 if has_spread {
3109 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);
3115 } else {
3116 for a in args {
3117 self.compile_expr(b, a)?;
3118 }
3119 let at = b.emit(Op::CallBuiltin(ops::CALL, argc(1 + args.len())?), 0);
3120 self.note_call_site(at, func);
3121 }
3122 }
3123 _ => {
3124 self.compile_expr(b, func)?;
3125 if has_spread {
3126 self.compile_spread_args(b, args)?;
3127 b.emit(Op::CallBuiltin(ops::APPLY, 2), 0);
3128 } else {
3129 for a in args {
3130 self.compile_expr(b, a)?;
3131 }
3132 let at = b.emit(Op::CallBuiltin(ops::CALL_VALUE, argc(1 + args.len())?), 0);
3133 self.note_call_site(at, func);
3134 }
3135 }
3136 }
3137 Ok(())
3138 }
3139
3140 fn compile_spread_args(&mut self, b: &mut ChunkBuilder, args: &[Expr]) -> Result<(), String> {
3142 for a in args {
3143 match a {
3144 Expr::Spread(inner) => {
3145 b.emit(Op::LoadInt(1), 0);
3146 self.compile_expr(b, inner)?;
3147 }
3148 _ => {
3149 b.emit(Op::LoadInt(0), 0);
3150 self.compile_expr(b, a)?;
3151 }
3152 }
3153 }
3154 b.emit(Op::CallBuiltin(ops::BUILD_ARGS, argc(args.len() * 2)?), 0);
3155 Ok(())
3156 }
3157
3158 fn compile_new(
3159 &mut self,
3160 b: &mut ChunkBuilder,
3161 callee: &Expr,
3162 args: &[Expr],
3163 ) -> Result<(), String> {
3164 self.compile_expr(b, callee)?;
3165 for a in args {
3166 self.compile_expr(b, a)?;
3167 }
3168 let at = b.emit(Op::CallBuiltin(ops::NEW, argc(1 + args.len())?), 0);
3169 self.note_call_site(at, callee);
3170 Ok(())
3171 }
3172}
3173
3174fn default_stmt(name: &str, default: &Expr) -> Stmt {
3177 Stmt::from(StmtKind::If {
3178 test: Expr::Binary(
3179 BinOp::EqEqEq,
3180 Box::new(Expr::Ident(name.to_string())),
3181 Box::new(Expr::Undefined),
3182 ),
3183 cons: Box::new(Stmt::from(StmtKind::Expr(Expr::Assign {
3184 target: Box::new(Expr::Ident(name.to_string())),
3185 value: Box::new(default.clone()),
3186 }))),
3187 alt: None,
3188 })
3189}