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 functions: Vec<(String, FuncDef)>,
114 tries: Vec<TryDef>,
115 loops: Vec<LoopCtx>,
116 tmp: usize,
117 pending_label: Option<String>,
120 debug: bool,
122 chunk_loop_base: usize,
127 chunk_signals: bool,
130 scope_depth: usize,
133 in_async_generator: bool,
136 iter_depth: usize,
140 strict: bool,
146 call_sites: Vec<(usize, String)>,
150 yield_sites: Vec<(usize, usize)>,
154 slots: crate::slots::Plan,
158}
159
160pub fn compile(stmts: &[Stmt], debug: bool) -> Result<Program, String> {
162 let mut c = Compiler {
163 debug,
164 slots: if debug {
167 Default::default()
168 } else {
169 crate::slots::plan(&[], stmts, true)
170 },
171 strict: has_use_strict(stmts),
172 ..Default::default()
173 };
174 let mut b = ChunkBuilder::new();
175 c.hoist_funcs(&mut b, stmts)?;
177 c.compile_stmts(&mut b, stmts)?;
178 Ok(Program {
179 main: c.finish_chunk(b),
180 functions: c.functions,
181 tries: c.tries,
182 })
183}
184
185pub fn compile_completion(stmts: &[Stmt], debug: bool) -> Result<Program, String> {
189 let mut c = Compiler {
190 debug,
191 strict: has_use_strict(stmts),
192 ..Default::default()
193 };
194 let mut b = ChunkBuilder::new();
195 c.hoist_funcs(&mut b, stmts)?;
196 if let Some((last, rest)) = stmts.split_last() {
197 c.compile_stmts(&mut b, rest)?;
198 if let StmtKind::Expr(e) = &last.kind {
199 c.compile_expr(&mut b, e)?;
201 } else {
202 c.compile_stmt(&mut b, last)?;
203 }
204 }
205 Ok(Program {
206 main: c.finish_chunk(b),
207 functions: c.functions,
208 tries: c.tries,
209 })
210}
211
212fn has_use_strict(stmts: &[Stmt]) -> bool {
218 for s in stmts {
219 match &s.kind {
220 StmtKind::Expr(e) => match e {
221 Expr::Str(v) if v == "use strict" => return true,
222 Expr::Str(_) => continue,
223 _ => return false,
224 },
225 _ => return false,
226 }
227 }
228 false
229}
230
231fn callee_text(e: &Expr) -> Option<String> {
240 Some(match e {
241 Expr::Ident(n) => n.clone(),
242 Expr::This => "this".into(),
243 Expr::Number(n) => crate::host::fmt_number(*n),
244 Expr::Str(s) => format!("\"{s}\""),
245 Expr::True => "true".into(),
246 Expr::False => "false".into(),
247 Expr::Null => "null".into(),
248 Expr::Undefined => "undefined".into(),
249 Expr::Array(items) if items.is_empty() => "[]".into(),
250 Expr::Object(props) if props.is_empty() => "{}".into(),
251 Expr::Member {
252 object,
253 property,
254 optional,
255 } => {
256 let dot = if *optional { "?." } else { "." };
257 format!("{}{dot}{property}", callee_text(object)?)
258 }
259 Expr::Index {
260 object,
261 index,
262 optional,
263 } => {
264 let obj = callee_text(object)?;
265 if let Expr::Str(k) = &**index {
268 if is_identifier(k) {
269 let dot = if *optional { "?." } else { "." };
270 return Some(format!("{obj}{dot}{k}"));
271 }
272 }
273 let idx = callee_text(index)?;
274 let open = if *optional { "?.[" } else { "[" };
275 format!("{obj}{open}{idx}]")
276 }
277 Expr::Call { func, .. } => format!("{}(...)", callee_text(func)?),
280 Expr::Sequence(items) => {
281 let parts: Option<Vec<String>> = items.iter().map(callee_text).collect();
282 format!("({})", parts?.join(" , "))
283 }
284 _ => return None,
285 })
286}
287
288fn is_identifier(s: &str) -> bool {
291 let mut chars = s.chars();
292 match chars.next() {
293 Some(c) if c.is_ascii_alphabetic() || c == '_' || c == '$' => {}
294 _ => return false,
295 }
296 chars.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '$')
297}
298
299fn argc(n: usize) -> Result<u8, String> {
300 u8::try_from(n).map_err(|_| "too many arguments (>255) for one call".to_string())
301}
302
303fn yields_bool(e: &Expr) -> bool {
318 match e {
319 Expr::True | Expr::False => true,
320 Expr::Unary(UnOp::Not, _) | Expr::Unary(UnOp::Delete, _) => true,
321 Expr::Binary(op, _, _) => matches!(
322 op,
323 BinOp::Lt
324 | BinOp::Le
325 | BinOp::Gt
326 | BinOp::Ge
327 | BinOp::EqEq
328 | BinOp::NeEq
329 | BinOp::EqEqEq
330 | BinOp::NeEqEq
331 | BinOp::In
332 | BinOp::InstanceOf
333 ),
334 _ => false,
335 }
336}
337
338impl Compiler {
339 fn name_const(&self, b: &mut ChunkBuilder, s: &str) {
341 let k = b.add_constant(Value::str(s));
342 b.emit(Op::LoadConst(k), 0);
343 }
344 fn strlit(&self, b: &mut ChunkBuilder, s: &str) {
345 let k = b.add_constant(Value::str(s));
346 b.emit(Op::LoadConst(k), 0);
347 b.emit(Op::CallBuiltin(ops::MKSTR, 1), 0);
348 }
349 fn tmp_name(&mut self, tag: &str) -> String {
350 let n = format!(".{tag}{}", self.tmp);
351 self.tmp += 1;
352 n
353 }
354
355 fn emit_mkfunc(&self, b: &mut ChunkBuilder, def_id: usize) {
358 b.emit(Op::LoadInt(def_id as i64), 0);
359 b.emit(Op::CallBuiltin(ops::MKFUNC, 1), 0);
360 }
361
362 fn hoist_funcs(&mut self, b: &mut ChunkBuilder, stmts: &[Stmt]) -> Result<(), String> {
363 for s in stmts {
364 if let StmtKind::FuncDecl {
365 name,
366 params,
367 body,
368 is_generator,
369 is_async,
370 } = &s.kind
371 {
372 let def_id = self.build_function(name, params, body, *is_generator, *is_async)?;
373 self.emit_mkfunc(b, def_id);
374 self.declare_as(b, &Expr::Ident(name.clone()), BindMode::Var);
376 }
377 }
378 Ok(())
379 }
380
381 fn compile_stmts(&mut self, b: &mut ChunkBuilder, stmts: &[Stmt]) -> Result<(), String> {
382 for s in stmts {
383 self.compile_stmt(b, s)?;
384 }
385 Ok(())
386 }
387
388 fn compile_stmt(&mut self, b: &mut ChunkBuilder, s: &Stmt) -> Result<(), String> {
389 if self.debug && s.line != 0 {
390 b.emit(Op::LoadInt(s.line as i64), s.line);
391 b.emit(Op::CallBuiltin(ops::DBG_LINE, 1), s.line);
392 b.emit(Op::Pop, s.line);
393 }
394 let line = s.line;
395 match &s.kind {
396 StmtKind::Expr(e) => {
397 self.compile_expr(b, e)?;
398 b.emit(Op::Pop, line);
399 }
400 StmtKind::Empty => {}
401 StmtKind::FuncDecl { .. } => {} StmtKind::ClassDecl(node) => {
403 self.compile_class(b, node)?;
404 if let Some(name) = &node.name {
406 self.declare(b, &Expr::Ident(name.clone()));
407 } else {
408 b.emit(Op::Pop, line);
409 }
410 }
411 StmtKind::Decl { kind, decls } => {
412 let mode = bind_mode(*kind);
413 for d in decls {
414 match &d.init {
415 Some(v) => {
416 self.compile_expr(b, v)?;
417 if let Expr::Ident(name) = &d.target {
420 self.infer_name(b, v, name);
421 }
422 }
423 None => {
424 b.emit(Op::LoadUndef, line);
425 }
426 }
427 self.compile_bind(b, &d.target, mode)?;
428 }
429 }
430 StmtKind::Block(body) => {
431 let scoped = crate::capture::block_needs_scope(body);
436 if scoped {
437 self.emit_push_scope(b);
438 }
439 self.hoist_funcs(b, body)?;
440 self.compile_stmts(b, body)?;
441 if scoped {
442 self.emit_pop_scope(b);
443 }
444 }
445 StmtKind::If { test, cons, alt } => self.compile_if(b, test, cons, alt)?,
446 StmtKind::While { test, body } => self.compile_while(b, test, body)?,
447 StmtKind::DoWhile { body, test } => self.compile_do_while(b, body, test)?,
448 StmtKind::For {
449 init,
450 test,
451 update,
452 body,
453 } => self.compile_for(b, init, test, update, body)?,
454 StmtKind::ForOf {
455 decl_kind,
456 target,
457 iter,
458 body,
459 is_await,
460 } => {
461 let mode = decl_kind.map(bind_mode).unwrap_or(BindMode::Assign);
462 if *is_await {
463 self.compile_for_await(b, mode, target, iter, body)?
464 } else {
465 self.compile_for_of(b, mode, target, iter, body)?
466 }
467 }
468 StmtKind::ForIn {
469 decl_kind,
470 target,
471 object,
472 body,
473 } => {
474 let mode = decl_kind.map(bind_mode).unwrap_or(BindMode::Assign);
475 self.compile_for_in(b, mode, target, object, body)?
476 }
477 StmtKind::Switch { disc, cases } => self.compile_switch(b, disc, cases)?,
478 StmtKind::Return(e) => {
479 match e {
480 Some(e) => self.compile_expr(b, e)?,
481 None => {
482 b.emit(Op::LoadUndef, line);
483 }
484 }
485 self.emit_close_iters_under_value(b);
491 b.emit(Op::CallBuiltin(ops::SIG_RETURN, 1), line);
492 }
493 StmtKind::Labeled { label, body } => self.compile_labeled(b, label, body)?,
494 StmtKind::Break(label) => {
495 let idx = match label {
496 Some(name) => self
498 .loops
499 .iter()
500 .rposition(|c| c.label.as_deref() == Some(name.as_str()))
501 .ok_or_else(|| format!("SyntaxError: Undefined label '{name}'"))?,
502 None => self
503 .loops
504 .len()
505 .checked_sub(1)
506 .ok_or("SyntaxError: 'break' outside loop")?,
507 };
508 if idx >= self.chunk_loop_base {
509 self.emit_unwind_scopes(b, self.loops[idx].break_depth);
510 self.emit_close_iters(b, self.loops[idx].iter_depth);
511 let j = b.emit(Op::Jump(0), line);
512 self.loops[idx].breaks.push(j);
513 } else {
514 self.emit_signal_jump(b, ops::SIG_BREAK, label.as_deref(), line);
515 }
516 }
517 StmtKind::Continue(label) => {
518 let idx = match label {
519 Some(name) => self
522 .loops
523 .iter()
524 .rposition(|c| {
525 c.catches_continue && c.label.as_deref() == Some(name.as_str())
526 })
527 .ok_or_else(|| {
528 format!("SyntaxError: Undefined label '{name}' for continue")
529 })?,
530 None => self
531 .loops
532 .iter()
533 .rposition(|c| c.catches_continue)
534 .ok_or("SyntaxError: 'continue' outside loop")?,
535 };
536 if idx >= self.chunk_loop_base {
537 self.emit_unwind_scopes(b, self.loops[idx].continue_depth);
538 self.emit_close_iters(b, self.loops[idx].iter_depth);
539 let j = b.emit(Op::Jump(0), line);
540 self.loops[idx].continues.push(j);
541 } else {
542 self.emit_signal_jump(b, ops::SIG_CONTINUE, label.as_deref(), line);
543 }
544 }
545 StmtKind::Throw(e) => {
546 self.compile_expr(b, e)?;
547 b.emit(Op::CallBuiltin(ops::THROW, 1), line);
548 }
549 StmtKind::Try {
550 block,
551 handler,
552 finalizer,
553 } => self.compile_try(b, block, handler, finalizer)?,
554 }
555 Ok(())
556 }
557
558 fn compile_bind(
562 &mut self,
563 b: &mut ChunkBuilder,
564 target: &Expr,
565 declare: BindMode,
566 ) -> Result<(), String> {
567 match target {
568 Expr::Ident(_) => {
569 if declare == BindMode::Assign {
570 self.store_simple(b, target)?;
571 } else {
572 self.declare_as(b, target, declare);
573 }
574 }
575 Expr::Member { .. } | Expr::Index { .. } => {
576 self.store_simple(b, target)?;
577 }
578 Expr::Array(items) => self.destructure_array(b, items, declare)?,
579 Expr::Object(props) => self.destructure_object(b, props, declare)?,
580 Expr::Assign { target, value } => {
581 b.emit(Op::Dup, 0);
583 b.emit(Op::LoadUndef, 0);
584 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
585 let jf = b.emit(Op::JumpIfFalse(0), 0);
586 b.emit(Op::Pop, 0); self.compile_expr(b, value)?;
588 if let Expr::Ident(n) = &**target {
592 self.infer_name(b, value, n);
593 }
594 let end = b.current_pos();
595 b.patch_jump(jf, end);
596 self.compile_bind(b, target, declare)?;
597 }
598 _ => return Err("SyntaxError: invalid assignment target".into()),
599 }
600 Ok(())
601 }
602
603 fn declare(&self, b: &mut ChunkBuilder, target: &Expr) {
605 self.declare_as(b, target, BindMode::Lexical);
606 }
607
608 fn declare_as(&self, b: &mut ChunkBuilder, target: &Expr, mode: BindMode) {
611 if let Expr::Ident(n) = target {
612 if let Some(slot) = self.slot_of(n) {
615 b.emit(Op::SetSlot(slot), 0);
616 return;
617 }
618 let op = match mode {
619 BindMode::Var => ops::DECLARE_VAR,
620 BindMode::Const => ops::DECLARE_CONST,
621 _ => ops::DECLARE,
622 };
623 self.name_const(b, n);
624 b.emit(Op::Swap, 0);
625 b.emit(Op::CallBuiltin(op, 2), 0);
626 b.emit(Op::Pop, 0);
627 }
628 }
629
630 fn throw_const_assignment(&mut self, b: &mut ChunkBuilder) {
637 let e = Expr::New {
638 callee: Box::new(Expr::Ident("TypeError".into())),
639 args: vec![Expr::Str("Assignment to constant variable.".into())],
640 };
641 if self.compile_expr(b, &e).is_ok() {
644 b.emit(Op::CallBuiltin(ops::THROW, 1), 0);
645 }
646 }
647
648 fn store_simple(&mut self, b: &mut ChunkBuilder, target: &Expr) -> Result<(), String> {
650 match target {
651 Expr::Ident(n) => {
652 if self.slots.consts.contains(n) {
660 b.emit(Op::Pop, 0); self.throw_const_assignment(b);
662 return Ok(());
663 }
664 if let Some(slot) = self.slot_of(n) {
665 b.emit(Op::SetSlot(slot), 0);
666 return Ok(());
667 }
668 self.name_const(b, n);
669 b.emit(Op::Swap, 0);
670 let op = if self.strict {
673 ops::SETLOCAL_STRICT
674 } else {
675 ops::SETLOCAL
676 };
677 b.emit(Op::CallBuiltin(op, 2), 0);
678 b.emit(Op::Pop, 0);
679 }
680 Expr::Member {
681 object, property, ..
682 } => {
683 self.compile_expr(b, object)?; self.name_const(b, property); b.emit(Op::Rot, 0); b.emit(Op::CallBuiltin(ops::SETATTR, 3), 0);
687 b.emit(Op::Pop, 0);
688 }
689 Expr::Index { object, index, .. } => {
690 self.compile_expr(b, object)?; self.compile_expr(b, index)?; b.emit(Op::Rot, 0); b.emit(Op::CallBuiltin(ops::SETITEM, 3), 0);
694 b.emit(Op::Pop, 0);
695 }
696 _ => return Err("SyntaxError: invalid assignment target".into()),
697 }
698 Ok(())
699 }
700
701 fn destructure_array(
702 &mut self,
703 b: &mut ChunkBuilder,
704 items: &[Expr],
705 declare: BindMode,
706 ) -> Result<(), String> {
707 let star_idx = items
708 .iter()
709 .position(|e| matches!(e, Expr::Spread(_)))
710 .map(|i| i as i64)
711 .unwrap_or(-1);
712 b.emit(Op::LoadInt(items.len() as i64), 0);
713 b.emit(Op::LoadInt(star_idx), 0);
714 b.emit(Op::CallBuiltin(ops::UNPACK, 3), 0); for it in items {
716 match it {
717 Expr::Hole | Expr::Undefined => {
720 b.emit(Op::Pop, 0);
721 }
722 Expr::Spread(inner) => self.compile_bind(b, inner, declare)?,
723 _ => self.compile_bind(b, it, declare)?,
724 }
725 }
726 Ok(())
727 }
728
729 fn destructure_object(
730 &mut self,
731 b: &mut ChunkBuilder,
732 props: &[Prop],
733 declare: BindMode,
734 ) -> Result<(), String> {
735 let obj_tmp = self.tmp_name("destr");
737 self.name_const(b, &obj_tmp);
738 b.emit(Op::Swap, 0);
739 b.emit(Op::CallBuiltin(ops::DECLARE, 2), 0);
740 b.emit(Op::Pop, 0);
741 let mut named: Vec<String> = Vec::new();
743 for p in props {
744 match p {
745 Prop::KeyValue { key, value, .. } => {
746 if let Expr::Str(s) = key {
747 named.push(s.clone());
748 }
749 self.load_local(b, &obj_tmp);
751 self.compile_expr(b, key)?;
752 b.emit(Op::CallBuiltin(ops::GETITEM, 2), 0); self.compile_bind(b, value, declare)?;
754 }
755 Prop::Spread(target) => {
756 self.load_local(b, &obj_tmp);
757 for k in &named {
758 self.strlit(b, k);
759 }
760 b.emit(Op::CallBuiltin(ops::MKARR, argc(named.len())?), 0);
761 b.emit(Op::CallBuiltin(ops::OBJ_REST, 2), 0); self.compile_bind(b, target, declare)?;
763 }
764 Prop::Accessor { .. } => {}
766 }
767 }
768 Ok(())
769 }
770
771 fn load_local(&self, b: &mut ChunkBuilder, name: &str) {
772 if let Some(slot) = self.slot_of(name) {
773 b.emit(Op::GetSlot(slot), 0);
774 return;
775 }
776 self.name_const(b, name);
777 b.emit(Op::CallBuiltin(ops::GETLOCAL, 1), 0);
778 }
779
780 fn slot_of(&self, name: &str) -> Option<u16> {
782 self.slots.table.get(name).copied()
783 }
784
785 fn numeric_slot_of(&self, name: &str) -> Option<u16> {
788 self.slots
789 .numeric
790 .contains(name)
791 .then(|| self.slot_of(name))
792 .flatten()
793 }
794
795 fn compile_condition(&mut self, b: &mut ChunkBuilder, e: &Expr) -> Result<(), String> {
797 self.compile_expr(b, e)?;
798 if !yields_bool(e) {
799 b.emit(Op::CallBuiltin(ops::TRUTHY, 1), 0);
800 }
801 Ok(())
802 }
803
804 fn compile_if(
805 &mut self,
806 b: &mut ChunkBuilder,
807 test: &Expr,
808 cons: &Stmt,
809 alt: &Option<Box<Stmt>>,
810 ) -> Result<(), String> {
811 self.compile_condition(b, test)?;
812 let jfalse = b.emit(Op::JumpIfFalse(0), 0);
813 self.compile_stmt(b, cons)?;
814 if let Some(alt) = alt {
815 let jend = b.emit(Op::Jump(0), 0);
816 let else_start = b.current_pos();
817 b.patch_jump(jfalse, else_start);
818 self.compile_stmt(b, alt)?;
819 let end = b.current_pos();
820 b.patch_jump(jend, end);
821 } else {
822 let end = b.current_pos();
823 b.patch_jump(jfalse, end);
824 }
825 Ok(())
826 }
827
828 fn compile_labeled(
832 &mut self,
833 b: &mut ChunkBuilder,
834 label: &str,
835 body: &Stmt,
836 ) -> Result<(), String> {
837 if matches!(
838 body.kind,
839 StmtKind::While { .. }
840 | StmtKind::DoWhile { .. }
841 | StmtKind::For { .. }
842 | StmtKind::ForOf { .. }
843 | StmtKind::ForIn { .. }
844 ) {
845 self.pending_label = Some(label.to_string());
846 self.compile_stmt(b, body)?;
847 self.pending_label = None;
849 } else {
850 self.loops.push(LoopCtx {
851 breaks: Vec::new(),
852 continues: Vec::new(),
853 break_depth: self.scope_depth,
854 continue_depth: self.scope_depth,
855 iter_depth: self.iter_depth,
856 catches_continue: false,
857 label: Some(label.to_string()),
858 });
859 self.compile_stmt(b, body)?;
860 let ctx = self.loops.pop().unwrap();
861 let end = b.current_pos();
862 for br in ctx.breaks {
863 b.patch_jump(br, end);
864 }
865 self.redispatch_after_loop(b);
866 }
867 Ok(())
868 }
869
870 fn redispatch_after_loop(&mut self, b: &mut ChunkBuilder) {
874 if self.chunk_signals {
875 self.emit_signal_dispatch(b);
876 }
877 }
878
879 fn compile_while(
898 &mut self,
899 b: &mut ChunkBuilder,
900 test: &Expr,
901 body: &Stmt,
902 ) -> Result<(), String> {
903 self.compile_condition(b, test)?;
904 let jfalse = b.emit(Op::JumpIfFalse(0), 0);
905 let top = b.current_pos();
906 self.loops.push(LoopCtx {
907 breaks: Vec::new(),
908 continues: Vec::new(),
909 break_depth: self.scope_depth,
910 continue_depth: self.scope_depth,
911 iter_depth: self.iter_depth,
912 catches_continue: true,
913 label: self.pending_label.take(),
914 });
915 self.compile_stmt(b, body)?;
916 let cont_target = b.current_pos();
918 self.compile_condition(b, test)?;
919 b.emit(Op::JumpIfTrue(top), 0);
920 let ctx = self.loops.pop().unwrap();
921 for c in ctx.continues {
922 b.patch_jump(c, cont_target);
923 }
924 let end = b.current_pos();
925 b.patch_jump(jfalse, end);
926 for br in ctx.breaks {
927 b.patch_jump(br, end);
928 }
929 self.redispatch_after_loop(b);
930 Ok(())
931 }
932
933 fn compile_do_while(
934 &mut self,
935 b: &mut ChunkBuilder,
936 body: &Stmt,
937 test: &Expr,
938 ) -> Result<(), String> {
939 let start = b.current_pos();
940 self.loops.push(LoopCtx {
941 breaks: Vec::new(),
942 continues: Vec::new(),
943 break_depth: self.scope_depth,
944 continue_depth: self.scope_depth,
945 iter_depth: self.iter_depth,
946 catches_continue: true,
947 label: self.pending_label.take(),
948 });
949 self.compile_stmt(b, body)?;
950 let cont_target = b.current_pos();
951 self.compile_condition(b, test)?;
952 b.emit(Op::JumpIfTrue(start), 0);
953 let ctx = self.loops.pop().unwrap();
954 for c in ctx.continues {
955 b.patch_jump(c, cont_target);
956 }
957 let end = b.current_pos();
958 for br in ctx.breaks {
959 b.patch_jump(br, end);
960 }
961 self.redispatch_after_loop(b);
962 Ok(())
963 }
964
965 fn compile_for(
966 &mut self,
967 b: &mut ChunkBuilder,
968 init: &Option<Box<Stmt>>,
969 test: &Option<Expr>,
970 update: &Option<Expr>,
971 body: &Stmt,
972 ) -> Result<(), String> {
973 let lexical_head = matches!(
977 init.as_deref(),
978 Some(Stmt {
979 kind: StmtKind::Decl {
980 kind: DeclKind::Let | DeclKind::Const,
981 ..
982 },
983 ..
984 })
985 );
986 let per_iteration = lexical_head;
993 let copy_per_iteration = lexical_head
994 && (crate::capture::stmt_captures(body)
995 || init.as_deref().is_some_and(crate::capture::stmt_captures)
996 || test.as_ref().is_some_and(crate::capture::expr_captures)
997 || update.as_ref().is_some_and(crate::capture::expr_captures));
998 if per_iteration {
999 self.emit_push_scope(b);
1000 }
1001 if let Some(init) = init {
1002 self.compile_stmt(b, init)?;
1003 }
1004 if copy_per_iteration {
1005 self.emit_copy_scope(b);
1006 }
1007 let jfalse = match test {
1010 Some(t) => {
1011 self.compile_condition(b, t)?;
1012 Some(b.emit(Op::JumpIfFalse(0), 0))
1013 }
1014 None => None,
1015 };
1016 let top = b.current_pos();
1017 self.loops.push(LoopCtx {
1018 breaks: Vec::new(),
1019 continues: Vec::new(),
1020 break_depth: self.scope_depth,
1021 continue_depth: self.scope_depth,
1022 iter_depth: self.iter_depth,
1023 catches_continue: true,
1024 label: self.pending_label.take(),
1025 });
1026 self.compile_stmt(b, body)?;
1027 let cont_target = b.current_pos();
1028 if copy_per_iteration {
1029 self.emit_copy_scope(b);
1032 }
1033 if let Some(u) = update {
1034 self.compile_expr(b, u)?;
1035 b.emit(Op::Pop, 0);
1036 }
1037 match test {
1038 Some(t) => {
1039 self.compile_condition(b, t)?;
1040 b.emit(Op::JumpIfTrue(top), 0);
1041 }
1042 None => {
1051 b.emit(Op::LoadTrue, 0);
1052 b.emit(Op::JumpIfTrue(top), 0);
1053 }
1054 }
1055 let ctx = self.loops.pop().unwrap();
1056 for c in ctx.continues {
1057 b.patch_jump(c, cont_target);
1058 }
1059 let end = b.current_pos();
1060 if let Some(jf) = jfalse {
1061 b.patch_jump(jf, end);
1062 }
1063 for br in ctx.breaks {
1064 b.patch_jump(br, end);
1065 }
1066 if per_iteration {
1067 self.emit_pop_scope(b);
1068 }
1069 self.redispatch_after_loop(b);
1070 Ok(())
1071 }
1072
1073 fn compile_for_of(
1074 &mut self,
1075 b: &mut ChunkBuilder,
1076 declare: BindMode,
1077 target: &Expr,
1078 iter: &Expr,
1079 body: &Stmt,
1080 ) -> Result<(), String> {
1081 self.compile_expr(b, iter)?;
1082 b.emit(Op::CallBuiltin(ops::GETITER, 1), 0); self.iter_depth += 1;
1084 let r = self.loop_over(b, declare, target, body);
1085 self.iter_depth -= 1;
1086 r
1087 }
1088
1089 fn compile_for_in(
1090 &mut self,
1091 b: &mut ChunkBuilder,
1092 declare: BindMode,
1093 target: &Expr,
1094 object: &Expr,
1095 body: &Stmt,
1096 ) -> Result<(), String> {
1097 self.compile_expr(b, object)?;
1098 b.emit(Op::CallBuiltin(ops::FORIN_KEYS, 1), 0); b.emit(Op::CallBuiltin(ops::GETITER, 1), 0); self.iter_depth += 1;
1101 let r = self.loop_over(b, declare, target, body);
1102 self.iter_depth -= 1;
1103 r
1104 }
1105
1106 fn compile_for_await(
1110 &mut self,
1111 b: &mut ChunkBuilder,
1112 declare: BindMode,
1113 target: &Expr,
1114 iter: &Expr,
1115 body: &Stmt,
1116 ) -> Result<(), String> {
1117 let iter_tmp = self.tmp_name("aiter");
1118 self.compile_expr(b, iter)?;
1119 b.emit(Op::CallBuiltin(ops::GET_ASYNC_ITER, 1), 0); self.name_const(b, &iter_tmp);
1121 b.emit(Op::Swap, 0);
1122 b.emit(Op::CallBuiltin(ops::DECLARE, 2), 0);
1123 b.emit(Op::Pop, 0);
1124 let start = b.current_pos();
1125 self.load_local(b, &iter_tmp);
1127 b.emit(Op::CallBuiltin(ops::ASYNC_STEP, 1), 0); b.emit(Op::CallBuiltin(ops::AWAIT, 1), 0); let step_tmp = self.tmp_name("astep");
1130 self.name_const(b, &step_tmp);
1131 b.emit(Op::Swap, 0);
1132 b.emit(Op::CallBuiltin(ops::DECLARE, 2), 0);
1133 b.emit(Op::Pop, 0);
1134 self.load_local(b, &step_tmp);
1136 self.name_const(b, "done");
1137 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0);
1138 b.emit(Op::CallBuiltin(ops::TRUTHY, 1), 0);
1139 let jdone = b.emit(Op::JumpIfTrue(0), 0);
1140 self.load_local(b, &step_tmp);
1142 self.name_const(b, "value");
1143 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0); let per_iteration = matches!(declare, BindMode::Lexical | BindMode::Const);
1145 if per_iteration {
1146 self.emit_push_scope(b);
1147 }
1148 self.compile_bind(b, target, declare)?;
1149 self.loops.push(LoopCtx {
1150 breaks: Vec::new(),
1151 continues: Vec::new(),
1152 break_depth: self.scope_depth,
1153 continue_depth: self.scope_depth,
1154 iter_depth: self.iter_depth,
1155 catches_continue: true,
1156 label: self.pending_label.take(),
1157 });
1158 self.compile_stmt(b, body)?;
1159 let cont_target = b.current_pos();
1160 if per_iteration {
1161 self.emit_pop_scope(b);
1162 }
1163 b.emit(Op::Jump(start), 0);
1164 let ctx = self.loops.pop().unwrap();
1165 for c in ctx.continues {
1166 b.patch_jump(c, cont_target);
1167 }
1168 let break_target = b.current_pos();
1171 if per_iteration {
1172 b.emit(Op::CallBuiltin(ops::POP_SCOPE, 0), 0);
1173 b.emit(Op::Pop, 0);
1174 }
1175 self.load_local(b, &iter_tmp);
1178 b.emit(Op::CallBuiltin(ops::ITER_CLOSE, 1), 0);
1179 b.emit(Op::Pop, 0);
1180 let end = b.current_pos();
1181 b.patch_jump(jdone, end);
1182 for br in ctx.breaks {
1183 b.patch_jump(br, break_target);
1184 }
1185 self.redispatch_after_loop(b);
1186 Ok(())
1187 }
1188
1189 fn loop_over(
1191 &mut self,
1192 b: &mut ChunkBuilder,
1193 declare: BindMode,
1194 target: &Expr,
1195 body: &Stmt,
1196 ) -> Result<(), String> {
1197 let per_iteration = matches!(declare, BindMode::Lexical | BindMode::Const);
1200 let start = b.current_pos();
1201 b.emit(Op::CallBuiltin(ops::FORITER, 0), 0); let jdone = b.emit(Op::JumpIfFalse(0), 0); if per_iteration {
1204 self.emit_push_scope(b);
1205 }
1206 self.compile_bind(b, target, declare)?; self.loops.push(LoopCtx {
1208 breaks: Vec::new(),
1209 continues: Vec::new(),
1210 break_depth: self.scope_depth,
1211 continue_depth: self.scope_depth,
1212 iter_depth: self.iter_depth,
1213 catches_continue: true,
1214 label: self.pending_label.take(),
1215 });
1216 self.compile_stmt(b, body)?;
1217 let cont_target = b.current_pos();
1218 if per_iteration {
1219 self.emit_pop_scope(b);
1220 }
1221 b.emit(Op::Jump(start), 0);
1222 let ctx = self.loops.pop().unwrap();
1223 for c in ctx.continues {
1224 b.patch_jump(c, cont_target);
1225 }
1226 let done = b.current_pos();
1227 b.patch_jump(jdone, done);
1228 b.emit(Op::Pop, 0); let jafter = b.emit(Op::Jump(0), 0);
1230 let break_target = b.current_pos();
1231 if per_iteration {
1234 b.emit(Op::CallBuiltin(ops::POP_SCOPE, 0), 0);
1235 b.emit(Op::Pop, 0);
1236 }
1237 b.emit(Op::CallBuiltin(ops::ITER_CLOSE, 1), 0);
1238 b.emit(Op::Pop, 0); let end = b.current_pos();
1240 b.patch_jump(jafter, end);
1241 for br in ctx.breaks {
1242 b.patch_jump(br, break_target);
1243 }
1244 self.iter_depth -= 1;
1247 self.redispatch_after_loop(b);
1248 self.iter_depth += 1;
1249 Ok(())
1250 }
1251
1252 fn compile_switch(
1253 &mut self,
1254 b: &mut ChunkBuilder,
1255 disc: &Expr,
1256 cases: &[SwitchCase],
1257 ) -> Result<(), String> {
1258 let disc_tmp = self.tmp_name("switch");
1259 self.compile_expr(b, disc)?;
1260 self.name_const(b, &disc_tmp);
1261 b.emit(Op::Swap, 0);
1262 b.emit(Op::CallBuiltin(ops::DECLARE, 2), 0);
1263 b.emit(Op::Pop, 0);
1264 self.emit_push_scope(b);
1268 let mut body_jumps: Vec<Option<usize>> = Vec::new();
1270 let mut default_idx: Option<usize> = None;
1271 for (i, case) in cases.iter().enumerate() {
1272 match &case.test {
1273 Some(t) => {
1274 self.load_local(b, &disc_tmp);
1275 self.compile_expr(b, t)?;
1276 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
1277 let j = b.emit(Op::JumpIfTrue(0), 0);
1278 body_jumps.push(Some(j));
1279 }
1280 None => {
1281 default_idx = Some(i);
1282 body_jumps.push(None);
1283 }
1284 }
1285 }
1286 let no_match_jump = b.emit(Op::Jump(0), 0);
1288 self.loops.push(LoopCtx {
1289 breaks: Vec::new(),
1290 continues: Vec::new(),
1291 break_depth: self.scope_depth,
1292 continue_depth: self.scope_depth,
1293 iter_depth: self.iter_depth,
1294 catches_continue: false,
1295 label: None,
1296 });
1297 let mut body_starts: Vec<usize> = Vec::new();
1298 for case in cases {
1299 body_starts.push(b.current_pos());
1300 self.compile_stmts(b, &case.body)?;
1301 }
1302 let end = b.current_pos();
1303 for (i, j) in body_jumps.iter().enumerate() {
1305 if let Some(j) = j {
1306 b.patch_jump(*j, body_starts[i]);
1307 }
1308 }
1309 match default_idx {
1310 Some(i) => b.patch_jump(no_match_jump, body_starts[i]),
1311 None => b.patch_jump(no_match_jump, end),
1312 }
1313 let ctx = self.loops.pop().unwrap();
1314 for br in ctx.breaks {
1315 b.patch_jump(br, end);
1316 }
1317 self.emit_pop_scope(b);
1318 self.redispatch_after_loop(b);
1319 Ok(())
1320 }
1321
1322 fn compile_try(
1323 &mut self,
1324 b: &mut ChunkBuilder,
1325 block: &[Stmt],
1326 handler: &Option<(Option<Expr>, Vec<Stmt>)>,
1327 finalizer: &Option<Vec<Stmt>>,
1328 ) -> Result<(), String> {
1329 let block_chunk = self.compile_block_chunk(block)?;
1330 let handler_def = match handler {
1331 Some((param, body)) => {
1332 let param_name = match param {
1333 Some(Expr::Ident(n)) => Some(n.clone()),
1334 _ => None,
1335 };
1336 let hbody = self.compile_block_chunk(body)?;
1337 Some((param_name, hbody))
1338 }
1339 None => None,
1340 };
1341 let final_chunk = match finalizer {
1342 Some(f) => Some(self.compile_block_chunk(f)?),
1343 None => None,
1344 };
1345 let id = self.tries.len();
1346 self.tries.push(TryDef {
1347 block: block_chunk,
1348 handler: handler_def,
1349 finalizer: final_chunk,
1350 });
1351 b.emit(Op::LoadInt(id as i64), 0);
1352 b.emit(Op::CallBuiltin(ops::TRY, 1), 0);
1353 b.emit(Op::Pop, 0);
1354 self.emit_signal_dispatch(b);
1357 Ok(())
1358 }
1359
1360 fn compile_block_chunk(&mut self, stmts: &[Stmt]) -> Result<Chunk, String> {
1364 let mut cb = ChunkBuilder::new();
1365 let saved_slot_table = std::mem::take(&mut self.slots);
1370 let base = std::mem::replace(&mut self.chunk_loop_base, self.loops.len());
1371 let signals = std::mem::take(&mut self.chunk_signals);
1372 let depth = std::mem::take(&mut self.scope_depth);
1373 let iters = std::mem::take(&mut self.iter_depth);
1374 let sites = std::mem::take(&mut self.call_sites);
1375 let yields = std::mem::take(&mut self.yield_sites);
1376 let r = (|| {
1377 self.hoist_funcs(&mut cb, stmts)?;
1378 self.compile_stmts(&mut cb, stmts)
1379 })();
1380 self.chunk_loop_base = base;
1381 self.scope_depth = depth;
1382 self.iter_depth = iters;
1383 self.slots = saved_slot_table;
1384 self.chunk_signals |= signals;
1387 r?;
1388 let chunk = self.finish_chunk(cb);
1389 self.call_sites = sites;
1390 self.yield_sites = yields;
1391 Ok(chunk)
1392 }
1393
1394 fn build_function(
1396 &mut self,
1397 name: &str,
1398 params: &[Param],
1399 body: &[Stmt],
1400 is_generator: bool,
1401 is_async: bool,
1402 ) -> Result<usize, String> {
1403 let (param_slots, prologue) = self.lower_params(params)?;
1404 let mut fb = ChunkBuilder::new();
1405 let mut planned: Vec<Stmt> = prologue.clone();
1409 planned.extend_from_slice(body);
1410 let saved_slot_table = std::mem::replace(
1411 &mut self.slots,
1412 if self.debug || is_generator || is_async {
1413 Default::default()
1414 } else {
1415 crate::slots::plan(params, &planned, false)
1416 },
1417 );
1418 for name in crate::slots::param_names(params) {
1422 if let Some(slot) = self.slot_of(&name) {
1423 self.name_const(&mut fb, &name);
1424 fb.emit(Op::CallBuiltin(ops::GETLOCAL, 1), 0);
1425 fb.emit(Op::SetSlot(slot), 0);
1426 }
1427 }
1428 let saved_loops = std::mem::take(&mut self.loops);
1431 let saved_base = std::mem::replace(&mut self.chunk_loop_base, 0);
1432 let saved_signals = std::mem::take(&mut self.chunk_signals);
1433 let saved_depth = std::mem::take(&mut self.scope_depth);
1434 let saved_iters = std::mem::take(&mut self.iter_depth);
1435 let saved_agen = std::mem::replace(&mut self.in_async_generator, is_generator && is_async);
1436 let saved_strict = self.strict;
1439 self.strict = self.strict || has_use_strict(body);
1440 let saved_sites = std::mem::take(&mut self.call_sites);
1443 let saved_yields = std::mem::take(&mut self.yield_sites);
1444 let r = (|| {
1445 self.hoist_funcs(&mut fb, &prologue)?;
1447 self.hoist_funcs(&mut fb, body)?;
1448 self.compile_stmts(&mut fb, &prologue)?;
1449 self.compile_stmts(&mut fb, body)
1450 })();
1451 self.loops = saved_loops;
1452 self.chunk_loop_base = saved_base;
1453 self.chunk_signals = saved_signals;
1454 self.scope_depth = saved_depth;
1455 self.iter_depth = saved_iters;
1456 self.in_async_generator = saved_agen;
1457 self.strict = saved_strict;
1458 self.slots = saved_slot_table;
1459 r?;
1460 let def = FuncDef {
1461 name: name.to_string(),
1462 params: param_slots,
1463 chunk: self.finish_chunk(fb),
1464 is_arrow: false,
1465 is_generator,
1466 is_async,
1467 is_method: false,
1468 self_name: false,
1469 };
1470 self.call_sites = saved_sites;
1471 self.yield_sites = saved_yields;
1472 self.functions.push((name.to_string(), def));
1473 Ok(self.functions.len() - 1)
1474 }
1475
1476 fn build_arrow(
1477 &mut self,
1478 params: &[Param],
1479 body: &FnBody,
1480 is_async: bool,
1481 ) -> Result<usize, String> {
1482 let stmts = match body {
1483 FnBody::Block(b) => b.clone(),
1484 FnBody::Expr(e) => vec![Stmt::from(StmtKind::Return(Some((**e).clone())))],
1485 };
1486 let id = self.build_function("", params, &stmts, false, is_async)?;
1487 self.functions[id].1.is_arrow = true;
1489 Ok(id)
1490 }
1491
1492 fn compile_class(&mut self, b: &mut ChunkBuilder, node: &ClassNode) -> Result<(), String> {
1497 let saved_strict = std::mem::replace(&mut self.strict, true);
1499 let r = self.compile_class_body(b, node);
1500 self.strict = saved_strict;
1501 r
1502 }
1503
1504 fn private_key(m: &ClassMember) -> Option<String> {
1507 match &m.key {
1508 Expr::Str(s) if !m.computed && s.starts_with('#') => Some(s.clone()),
1509 Expr::Ident(s) if !m.computed && s.starts_with('#') => Some(s.clone()),
1510 _ => None,
1511 }
1512 }
1513
1514 fn compile_class_body(&mut self, b: &mut ChunkBuilder, node: &ClassNode) -> Result<(), String> {
1515 let cname = node.name.clone().unwrap_or_default();
1516 self.name_const(b, &cname);
1518 match &node.parent {
1519 Some(p) => self.compile_expr(b, p)?,
1520 None => {
1521 b.emit(Op::LoadUndef, 0);
1522 }
1523 }
1524 let ctor = node
1525 .members
1526 .iter()
1527 .find(|m| m.kind == MemberKind::Constructor);
1528 match ctor {
1529 Some(m) => {
1530 let def_id = self.build_function(&cname, &m.params, &m.body, false, false)?;
1531 self.emit_mkfunc(b, def_id);
1532 }
1533 None => {
1534 b.emit(Op::LoadUndef, 0);
1535 }
1536 }
1537 b.emit(Op::CallBuiltin(ops::MKCLASS, 3), 0); let body_scope = node.name.is_some();
1551 if let Some(name) = &node.name {
1552 self.emit_push_scope(b);
1553 b.emit(Op::Dup, 0); self.declare_as(b, &Expr::Ident(name.clone()), BindMode::Lexical); }
1556
1557 let deferred = |k: &MemberKind| matches!(k, MemberKind::Field | MemberKind::StaticBlock);
1566 let ordered = node
1567 .members
1568 .iter()
1569 .filter(|m| !deferred(&m.kind))
1570 .chain(node.members.iter().filter(|m| deferred(&m.kind)));
1571 let mut static_block_n = 0usize;
1572 for m in ordered {
1573 match m.kind {
1574 MemberKind::Constructor => {}
1575 MemberKind::Field if m.is_static && Self::private_key(m).is_some() => {
1581 let key = Self::private_key(m).expect("guarded above");
1582 self.name_const(b, &key); b.emit(Op::LoadInt(member::STATIC_FIELD), 0);
1584 b.emit(Op::LoadTrue, 0); match &m.field_init {
1586 Some(e) => self.emit_keyed_value(b, &m.key, e, false, member::METHOD)?,
1587 None => {
1588 b.emit(Op::LoadUndef, 0);
1589 }
1590 }
1591 b.emit(Op::CallBuiltin(ops::DEF_MEMBER, 5), 0); }
1593 MemberKind::Field if m.is_static => {
1594 b.emit(Op::Dup, 0); self.emit_member_key(b, m)?; match &m.field_init {
1600 Some(e) => {
1603 self.emit_keyed_value(b, &m.key, e, m.computed, member::METHOD)?
1604 }
1605 None => {
1606 b.emit(Op::LoadUndef, 0);
1607 }
1608 }
1609 b.emit(Op::CallBuiltin(ops::SETATTR, 3), 0);
1611 b.emit(Op::Pop, 0); }
1613 MemberKind::Field => {
1614 self.emit_member_key(b, m)?;
1616 let init = m.field_init.clone().unwrap_or(Expr::Undefined);
1617 let name_anon = Self::is_anon_fn_def(&init);
1623 let stmts = vec![Stmt::from(StmtKind::Return(Some(init)))];
1624 let def_id = self.build_function("", &[], &stmts, false, false)?;
1625 self.emit_mkfunc(b, def_id);
1626 b.emit(
1627 if name_anon {
1628 Op::LoadTrue
1629 } else {
1630 Op::LoadFalse
1631 },
1632 0,
1633 );
1634 b.emit(Op::CallBuiltin(ops::DEF_FIELD, 4), 0);
1635 }
1636 MemberKind::StaticBlock => {
1637 static_block_n += 1;
1646 let slot = format!("@@staticBlock:{static_block_n}");
1647 self.name_const(b, &slot);
1649 b.emit(Op::LoadInt(member::METHOD), 0);
1650 b.emit(Op::LoadTrue, 0);
1651 let def_id = self.build_function("", &[], &m.body, false, false)?;
1652 self.functions[def_id].1.is_method = true;
1653 self.emit_mkfunc(b, def_id);
1654 b.emit(Op::CallBuiltin(ops::DEF_MEMBER, 5), 0);
1655 b.emit(Op::Dup, 0);
1657 self.name_const(b, &slot);
1658 b.emit(Op::CallBuiltin(ops::CALL_METHOD, 2), 0);
1659 b.emit(Op::Pop, 0);
1660 b.emit(Op::Dup, 0);
1662 self.name_const(b, &slot);
1663 b.emit(Op::CallBuiltin(ops::DELPROP_NAME, 2), 0);
1664 b.emit(Op::Pop, 0);
1665 }
1666 MemberKind::Method | MemberKind::Get | MemberKind::Set => {
1667 self.emit_member_key(b, m)?;
1669 let kind = match m.kind {
1670 MemberKind::Get => member::GET,
1671 MemberKind::Set => member::SET,
1672 _ => member::METHOD,
1673 };
1674 b.emit(Op::LoadInt(kind), 0);
1675 b.emit(
1676 if m.is_static {
1677 Op::LoadTrue
1678 } else {
1679 Op::LoadFalse
1680 },
1681 0,
1682 );
1683 let mname = match &m.key {
1687 Expr::Str(s) if !m.computed => match m.kind {
1688 MemberKind::Get => format!("get {s}"),
1689 MemberKind::Set => format!("set {s}"),
1690 _ => s.clone(),
1691 },
1692 _ => String::new(),
1693 };
1694 let def_id = self.build_function(
1695 &mname,
1696 &m.params,
1697 &m.body,
1698 m.is_generator,
1699 m.is_async,
1700 )?;
1701 self.functions[def_id].1.is_method = true;
1704 self.emit_mkfunc(b, def_id);
1705 b.emit(Op::CallBuiltin(ops::DEF_MEMBER, 5), 0);
1706 }
1707 }
1708 }
1709 if body_scope {
1710 self.emit_pop_scope(b);
1711 }
1712 Ok(())
1713 }
1714
1715 fn is_anon_fn_def(init: &Expr) -> bool {
1723 match init {
1724 Expr::Function { name: None, .. } => true,
1725 Expr::Class(node) => node.name.is_none(),
1726 _ => false,
1727 }
1728 }
1729
1730 fn infer_name(&mut self, b: &mut ChunkBuilder, init: &Expr, name: &str) {
1733 if !Self::is_anon_fn_def(init) {
1734 return;
1735 }
1736 b.emit(Op::Dup, 0);
1738 self.name_const(b, "name");
1739 self.strlit(b, name);
1740 b.emit(Op::CallBuiltin(ops::SETATTR, 3), 0);
1741 b.emit(Op::Pop, 0);
1742 }
1743
1744 fn emit_keyed_value(
1755 &mut self,
1756 b: &mut ChunkBuilder,
1757 key: &Expr,
1758 value: &Expr,
1759 computed: bool,
1760 kind: i64,
1761 ) -> Result<(), String> {
1762 match (Self::is_anon_fn_def(value), computed, key) {
1763 (true, false, Expr::Str(s)) => {
1764 self.compile_expr(b, value)?;
1765 let name = match kind {
1766 member::GET => format!("get {s}"),
1767 member::SET => format!("set {s}"),
1768 _ => s.clone(),
1769 };
1770 self.infer_name(b, value, &name);
1771 }
1772 (true, true, _) => {
1774 b.emit(Op::Dup, 0);
1775 b.emit(Op::LoadInt(kind), 0);
1776 self.compile_expr(b, value)?;
1777 b.emit(Op::CallBuiltin(ops::NAMED_EVAL, 3), 0);
1778 }
1779 _ => self.compile_expr(b, value)?,
1780 }
1781 Ok(())
1782 }
1783
1784 fn emit_member_key(&mut self, b: &mut ChunkBuilder, m: &ClassMember) -> Result<(), String> {
1787 if m.computed {
1788 self.compile_expr(b, &m.key)?;
1789 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0);
1790 } else if let Expr::Str(s) = &m.key {
1791 self.name_const(b, s);
1792 } else {
1793 self.compile_expr(b, &m.key)?;
1794 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0);
1795 }
1796 Ok(())
1797 }
1798
1799 fn compile_yield(
1801 &mut self,
1802 b: &mut ChunkBuilder,
1803 arg: &Option<Box<Expr>>,
1804 delegate: bool,
1805 ) -> Result<(), String> {
1806 if delegate && self.in_async_generator {
1807 match arg {
1811 Some(e) => self.compile_expr(b, e)?,
1812 None => {
1813 b.emit(Op::LoadUndef, 0);
1814 }
1815 }
1816 b.emit(Op::CallBuiltin(ops::GET_ASYNC_ITER, 1), 0); let start = b.current_pos();
1818 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");
1823 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0);
1824 b.emit(Op::CallBuiltin(ops::TRUTHY, 1), 0);
1825 let jdone = b.emit(Op::JumpIfTrue(0), 0); self.name_const(b, "value");
1827 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));
1830 b.emit(Op::Pop, 0); b.emit(Op::Jump(start), 0);
1832 let done = b.current_pos();
1833 b.patch_jump(jdone, done);
1834 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 {
1839 let sent_tmp = self.tmp_name("delegated");
1844 match arg {
1845 Some(e) => self.compile_expr(b, e)?,
1846 None => {
1847 b.emit(Op::LoadUndef, 0);
1848 }
1849 }
1850 b.emit(Op::CallBuiltin(ops::GETITER, 1), 0); self.iter_depth += 1;
1856 self.name_const(b, &sent_tmp);
1857 b.emit(Op::LoadUndef, 0);
1858 b.emit(Op::CallBuiltin(ops::DECLARE, 2), 0);
1859 b.emit(Op::Pop, 0);
1860 let start = b.current_pos();
1861 b.emit(Op::Dup, 0); self.name_const(b, "next");
1863 self.load_local(b, &sent_tmp);
1864 b.emit(Op::CallBuiltin(ops::CALL_METHOD, 3), 0); b.emit(Op::Dup, 0);
1866 self.name_const(b, "done");
1867 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0);
1868 b.emit(Op::CallBuiltin(ops::TRUTHY, 1), 0);
1869 let jdone = b.emit(Op::JumpIfTrue(0), 0); self.name_const(b, "value");
1871 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));
1874 self.name_const(b, &sent_tmp);
1875 b.emit(Op::Swap, 0);
1876 b.emit(Op::CallBuiltin(ops::SETLOCAL, 2), 0);
1877 b.emit(Op::Pop, 0);
1878 b.emit(Op::Jump(start), 0);
1879 let done = b.current_pos();
1880 b.patch_jump(jdone, done);
1881 self.name_const(b, "value"); b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0); b.emit(Op::Swap, 0);
1884 b.emit(Op::Pop, 0); self.iter_depth -= 1;
1886 } else {
1887 match arg {
1888 Some(e) => self.compile_expr(b, e)?,
1889 None => {
1890 b.emit(Op::LoadUndef, 0);
1891 }
1892 }
1893 let at = b.emit(Op::CallBuiltin(ops::YIELD, 1), 0);
1895 self.yield_sites.push((at, self.iter_depth));
1896 }
1897 Ok(())
1898 }
1899
1900 fn lower_params(&mut self, params: &[Param]) -> Result<(Vec<ParamSlot>, Vec<Stmt>), String> {
1903 let mut slots = Vec::new();
1904 let mut prologue: Vec<Stmt> = Vec::new();
1905 for (i, p) in params.iter().enumerate() {
1906 if p.rest {
1907 let name = match &p.pattern {
1908 Expr::Ident(n) => n.clone(),
1909 _ => return Err("SyntaxError: rest parameter must be an identifier".into()),
1910 };
1911 slots.push(ParamSlot {
1912 name,
1913 rest: true,
1914 has_default: false,
1915 });
1916 continue;
1917 }
1918 match &p.pattern {
1919 Expr::Ident(name) => {
1920 slots.push(ParamSlot {
1921 name: name.clone(),
1922 rest: false,
1923 has_default: p.default.is_some(),
1924 });
1925 if let Some(d) = &p.default {
1926 prologue.push(default_stmt(name, d));
1927 }
1928 }
1929 pattern => {
1930 let synth = format!(".param{i}");
1931 slots.push(ParamSlot {
1932 name: synth.clone(),
1933 rest: false,
1934 has_default: p.default.is_some(),
1935 });
1936 if let Some(d) = &p.default {
1937 prologue.push(default_stmt(&synth, d));
1938 }
1939 prologue.push(Stmt::from(StmtKind::Decl {
1940 kind: DeclKind::Let,
1941 decls: vec![Declarator {
1942 target: pattern.clone(),
1943 init: Some(Expr::Ident(synth)),
1944 }],
1945 }));
1946 }
1947 }
1948 }
1949 Ok((slots, prologue))
1950 }
1951
1952 fn compile_expr(&mut self, b: &mut ChunkBuilder, e: &Expr) -> Result<(), String> {
1954 match e {
1955 Expr::Undefined => {
1956 b.emit(Op::LoadUndef, 0);
1957 }
1958 Expr::Hole => {
1962 b.emit(Op::LoadUndef, 0);
1963 }
1964 Expr::Null => {
1965 b.emit(Op::CallBuiltin(ops::LOAD_NULL, 0), 0);
1966 }
1967 Expr::True => {
1968 b.emit(Op::LoadTrue, 0);
1969 }
1970 Expr::False => {
1971 b.emit(Op::LoadFalse, 0);
1972 }
1973 Expr::Number(n) => {
1974 b.emit(Op::LoadFloat(*n), 0);
1975 }
1976 Expr::BigInt(digits) => {
1977 let k = b.add_constant(Value::str(digits));
1980 b.emit(Op::LoadConst(k), 0);
1981 b.emit(Op::CallBuiltin(ops::MKBIGINT, 1), 0);
1982 }
1983 Expr::Regex(pat, flags) => {
1984 let kp = b.add_constant(Value::str(pat));
1985 b.emit(Op::LoadConst(kp), 0);
1986 let kf = b.add_constant(Value::str(flags));
1987 b.emit(Op::LoadConst(kf), 0);
1988 b.emit(Op::CallBuiltin(ops::MKREGEX, 2), 0);
1989 }
1990 Expr::Str(s) => self.strlit(b, s),
1991 Expr::Template { quasis, exprs } => self.compile_template(b, quasis, exprs)?,
1992 Expr::TaggedTemplate {
1993 tag,
1994 quasis,
1995 raws,
1996 exprs,
1997 } => self.compile_tagged_template(b, tag, quasis, raws, exprs)?,
1998 Expr::Ident(n) => self.load_local(b, n),
1999 Expr::This => {
2000 b.emit(Op::CallBuiltin(ops::THIS, 0), 0);
2001 }
2002 Expr::Array(items) => self.compile_array(b, items)?,
2003 Expr::Object(props) => self.compile_object(b, props)?,
2004 Expr::Spread(inner) => self.compile_expr(b, inner)?,
2005 Expr::Logical(op, l, r) => self.compile_logical(b, *op, l, r)?,
2006 Expr::Unary(op, e) => self.compile_unary(b, *op, e)?,
2007 Expr::Binary(op, l, r) => self.compile_binary(b, *op, l, r)?,
2008 Expr::Conditional { test, cons, alt } => {
2009 self.compile_condition(b, test)?;
2010 let jf = b.emit(Op::JumpIfFalse(0), 0);
2011 self.compile_expr(b, cons)?;
2012 let je = b.emit(Op::Jump(0), 0);
2013 let els = b.current_pos();
2014 b.patch_jump(jf, els);
2015 self.compile_expr(b, alt)?;
2016 let end = b.current_pos();
2017 b.patch_jump(je, end);
2018 }
2019 Expr::Assign { target, value } => {
2020 self.compile_expr(b, value)?;
2021 if let Expr::Ident(n) = &**target {
2025 self.infer_name(b, value, n);
2026 }
2027 b.emit(Op::Dup, 0); self.compile_bind(b, target, BindMode::Assign)?;
2029 }
2030 Expr::Update { op, prefix, target } => self.compile_update(b, *op, *prefix, target)?,
2031 Expr::Call {
2032 func,
2033 args,
2034 optional,
2035 } => self.compile_call(b, func, args, *optional)?,
2036 Expr::New { callee, args } => self.compile_new(b, callee, args)?,
2037 Expr::Member {
2038 object,
2039 property,
2040 optional,
2041 } => self.compile_member(b, object, property, *optional)?,
2042 Expr::Index {
2043 object,
2044 index,
2045 optional,
2046 } => self.compile_index(b, object, index, *optional)?,
2047 Expr::Function {
2048 params,
2049 body,
2050 is_arrow,
2051 name,
2052 is_generator,
2053 is_async,
2054 is_method,
2055 } => {
2056 let def_id = if *is_arrow {
2057 self.build_arrow(params, body, *is_async)?
2058 } else {
2059 let n = name.clone().unwrap_or_default();
2060 let stmts = match body {
2061 FnBody::Block(b) => b.clone(),
2062 FnBody::Expr(e) => vec![Stmt::from(StmtKind::Return(Some((**e).clone())))],
2063 };
2064 let id = self.build_function(&n, params, &stmts, *is_generator, *is_async)?;
2065 if name.is_some() {
2069 self.functions[id].1.self_name = true;
2070 }
2071 self.functions[id].1.is_method = *is_method;
2072 id
2073 };
2074 self.emit_mkfunc(b, def_id);
2075 }
2076 Expr::Class(node) => self.compile_class(b, node)?,
2077 Expr::Super => {
2078 b.emit(Op::LoadUndef, 0);
2081 }
2082 Expr::NewTarget => {
2083 b.emit(Op::CallBuiltin(ops::NEW_TARGET, 0), 0);
2084 }
2085 Expr::Yield { arg, delegate } => self.compile_yield(b, arg, *delegate)?,
2086 Expr::Await(inner) => {
2087 self.compile_expr(b, inner)?;
2088 b.emit(Op::CallBuiltin(ops::AWAIT, 1), 0);
2089 }
2090 Expr::Sequence(items) => {
2091 for (i, it) in items.iter().enumerate() {
2092 self.compile_expr(b, it)?;
2093 if i + 1 < items.len() {
2094 b.emit(Op::Pop, 0);
2095 }
2096 }
2097 }
2098 }
2099 Ok(())
2100 }
2101
2102 fn compile_template(
2103 &mut self,
2104 b: &mut ChunkBuilder,
2105 quasis: &[String],
2106 exprs: &[Expr],
2107 ) -> Result<(), String> {
2108 let mut n = 0;
2109 for (i, q) in quasis.iter().enumerate() {
2110 let k = b.add_constant(Value::str(q));
2111 b.emit(Op::LoadConst(k), 0);
2112 n += 1;
2113 if i < exprs.len() {
2114 self.compile_expr(b, &exprs[i])?;
2115 b.emit(Op::CallBuiltin(ops::TOSTR, 1), 0);
2116 n += 1;
2117 }
2118 }
2119 b.emit(Op::CallBuiltin(ops::MKSTR, argc(n)?), 0);
2120 Ok(())
2121 }
2122
2123 fn compile_tagged_template(
2127 &mut self,
2128 b: &mut ChunkBuilder,
2129 tag: &Expr,
2130 quasis: &[String],
2131 raws: &[String],
2132 exprs: &[Expr],
2133 ) -> Result<(), String> {
2134 self.compile_expr(b, tag)?;
2135 let n = quasis.len();
2136 let m = exprs.len();
2137 b.emit(Op::LoadInt(n as i64), 0);
2138 b.emit(Op::LoadInt(m as i64), 0);
2139 for q in quasis {
2140 self.strlit(b, q); }
2142 for r in raws {
2143 self.strlit(b, r); }
2145 for e in exprs {
2146 self.compile_expr(b, e)?; }
2148 b.emit(Op::CallBuiltin(ops::TAG_TMPL, argc(3 + 2 * n + m)?), 0);
2149 Ok(())
2150 }
2151
2152 fn compile_array(&mut self, b: &mut ChunkBuilder, items: &[Expr]) -> Result<(), String> {
2153 if items.iter().any(|e| matches!(e, Expr::Spread(_))) {
2154 for it in items {
2159 match it {
2160 Expr::Spread(inner) => {
2161 b.emit(Op::LoadInt(1), 0);
2162 self.compile_expr(b, inner)?;
2163 }
2164 Expr::Hole => {
2165 b.emit(Op::LoadInt(2), 0);
2166 b.emit(Op::LoadUndef, 0);
2167 }
2168 _ => {
2169 b.emit(Op::LoadInt(0), 0);
2170 self.compile_expr(b, it)?;
2171 }
2172 }
2173 }
2174 b.emit(Op::CallBuiltin(ops::BUILD_ARGS, argc(items.len() * 2)?), 0);
2175 } else if items.len() <= u8::MAX as usize {
2176 for it in items {
2177 self.compile_expr(b, it)?;
2178 }
2179 b.emit(Op::CallBuiltin(ops::MKARR, argc(items.len())?), 0);
2180 self.mark_literal_holes(b, items);
2181 } else {
2182 b.emit(Op::CallBuiltin(ops::MKARR, 0), 0); for (i, it) in items.iter().enumerate() {
2188 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); }
2194 self.mark_literal_holes(b, items);
2197 }
2198 Ok(())
2199 }
2200
2201 fn mark_literal_holes(&mut self, b: &mut ChunkBuilder, items: &[Expr]) {
2205 for (i, it) in items.iter().enumerate() {
2206 if !matches!(it, Expr::Hole) {
2207 continue;
2208 }
2209 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); }
2214 }
2215
2216 fn compile_object(&mut self, b: &mut ChunkBuilder, props: &[Prop]) -> Result<(), String> {
2217 let mut seen_accessor: Vec<String> = Vec::new();
2223 let data: Vec<&Prop> = props
2224 .iter()
2225 .filter(|p| match p {
2226 Prop::Accessor { key, computed, .. } => {
2227 let literal = match (key, computed) {
2230 (Expr::Str(s), false) => Some(s.clone()),
2231 _ => None,
2232 };
2233 match literal {
2234 Some(k) if seen_accessor.contains(&k) => false,
2235 Some(k) => {
2236 seen_accessor.push(k);
2237 true
2238 }
2239 None => true,
2240 }
2241 }
2242 _ => true,
2243 })
2244 .collect();
2245 let has_spread = data.iter().any(|p| matches!(p, Prop::Spread(_)));
2246 if data.len() * 3 > u8::MAX as usize && !has_spread {
2252 b.emit(Op::CallBuiltin(ops::MKOBJ, 0), 0); for p in &data {
2254 if let Prop::KeyValue {
2255 key,
2256 value,
2257 computed,
2258 } = p
2259 {
2260 b.emit(Op::Dup, 0); self.compile_expr(b, key)?;
2262 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0); self.emit_keyed_value(b, key, value, *computed, member::METHOD)?;
2264 b.emit(Op::CallBuiltin(ops::SETITEM, 3), 0); b.emit(Op::Pop, 0); }
2267 }
2268 return self.compile_object_accessors(b, props);
2271 }
2272 for p in &data {
2273 match p {
2274 Prop::KeyValue {
2275 key,
2276 value,
2277 computed,
2278 } => {
2279 b.emit(Op::LoadInt(0), 0);
2280 self.compile_expr(b, key)?;
2283 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0);
2284 self.emit_keyed_value(b, key, value, *computed, member::METHOD)?;
2285 }
2286 Prop::Spread(src) => {
2287 b.emit(Op::LoadInt(1), 0);
2288 self.compile_expr(b, src)?;
2289 b.emit(Op::LoadUndef, 0);
2290 }
2291 Prop::Accessor { key, computed, .. } => {
2294 let _ = computed; b.emit(Op::LoadInt(2), 0);
2296 self.compile_expr(b, key)?;
2297 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0);
2298 b.emit(Op::LoadUndef, 0);
2299 }
2300 }
2301 }
2302 b.emit(Op::CallBuiltin(ops::MKOBJ, argc(data.len() * 3)?), 0); self.compile_object_accessors(b, props)
2304 }
2305
2306 fn compile_object_accessors(
2309 &mut self,
2310 b: &mut ChunkBuilder,
2311 props: &[Prop],
2312 ) -> Result<(), String> {
2313 for p in props {
2314 if let Prop::Accessor {
2315 key,
2316 computed,
2317 is_getter,
2318 func,
2319 } = p
2320 {
2321 if *computed {
2322 self.compile_expr(b, key)?;
2323 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0);
2324 } else if let Expr::Str(s) = key {
2325 self.name_const(b, s);
2326 } else {
2327 self.compile_expr(b, key)?;
2328 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0);
2329 }
2330 let kind = if *is_getter { member::GET } else { member::SET };
2331 b.emit(Op::LoadInt(kind), 0);
2332 if *computed {
2337 self.compile_expr(b, func)?;
2338 } else if let Expr::Str(s) = key {
2339 self.compile_expr(b, func)?;
2340 let prefix = if *is_getter { "get" } else { "set" };
2341 self.infer_name(b, func, &format!("{prefix} {s}"));
2342 } else {
2343 self.compile_expr(b, func)?;
2344 }
2345 b.emit(Op::CallBuiltin(ops::DEF_ACCESSOR, 4), 0);
2346 }
2347 }
2348 Ok(())
2349 }
2350
2351 fn compile_logical(
2352 &mut self,
2353 b: &mut ChunkBuilder,
2354 op: LogicalOp,
2355 l: &Expr,
2356 r: &Expr,
2357 ) -> Result<(), String> {
2358 self.compile_expr(b, l)?;
2359 b.emit(Op::Dup, 0);
2360 let test_op = match op {
2361 LogicalOp::And | LogicalOp::Or => ops::TRUTHY,
2362 LogicalOp::Nullish => ops::NULLISH,
2363 };
2364 b.emit(Op::CallBuiltin(test_op, 1), 0);
2365 let jump = match op {
2366 LogicalOp::And => b.emit(Op::JumpIfFalse(0), 0), LogicalOp::Or => b.emit(Op::JumpIfTrue(0), 0), LogicalOp::Nullish => b.emit(Op::JumpIfFalse(0), 0), };
2370 b.emit(Op::Pop, 0); self.compile_expr(b, r)?;
2372 let end = b.current_pos();
2373 b.patch_jump(jump, end);
2374 Ok(())
2375 }
2376
2377 fn compile_unary(&mut self, b: &mut ChunkBuilder, op: UnOp, e: &Expr) -> Result<(), String> {
2378 match op {
2379 UnOp::Neg => {
2380 self.compile_expr(b, e)?;
2381 b.emit(Op::Negate, 0);
2382 }
2383 UnOp::Not => {
2384 self.compile_condition(b, e)?;
2385 b.emit(Op::LogNot, 0);
2386 }
2387 UnOp::Pos => {
2388 b.emit(Op::LoadInt(unop::POS), 0);
2389 self.compile_expr(b, e)?;
2390 b.emit(Op::CallBuiltin(ops::UNARY, 2), 0);
2391 }
2392 UnOp::BitNot => {
2393 b.emit(Op::LoadInt(unop::BITNOT), 0);
2394 self.compile_expr(b, e)?;
2395 b.emit(Op::CallBuiltin(ops::UNARY, 2), 0);
2396 }
2397 UnOp::TypeOf => {
2398 if let Expr::Ident(n) = e {
2402 if let Some(slot) = self.slot_of(n) {
2406 b.emit(Op::GetSlot(slot), 0);
2407 b.emit(Op::CallBuiltin(ops::TYPEOF, 1), 0);
2408 return Ok(());
2409 }
2410 self.name_const(b, n);
2411 b.emit(Op::CallBuiltin(ops::TYPEOF_NAME, 1), 0);
2412 } else {
2413 self.compile_expr(b, e)?;
2414 b.emit(Op::CallBuiltin(ops::TYPEOF, 1), 0);
2415 }
2416 }
2417 UnOp::Void => {
2418 self.compile_expr(b, e)?;
2419 b.emit(Op::Pop, 0);
2420 b.emit(Op::LoadUndef, 0);
2421 }
2422 UnOp::Delete => match e {
2423 Expr::Member {
2424 object, property, ..
2425 } => {
2426 self.compile_expr(b, object)?;
2427 self.name_const(b, property);
2428 b.emit(Op::CallBuiltin(ops::DELPROP_NAME, 2), 0);
2429 }
2430 Expr::Index { object, index, .. } => {
2431 self.compile_expr(b, object)?;
2432 self.compile_expr(b, index)?;
2433 b.emit(Op::CallBuiltin(ops::DELITEM, 2), 0);
2434 }
2435 _ => {
2436 b.emit(Op::LoadTrue, 0);
2437 }
2438 },
2439 }
2440 Ok(())
2441 }
2442
2443 fn compile_binary(
2444 &mut self,
2445 b: &mut ChunkBuilder,
2446 op: BinOp,
2447 l: &Expr,
2448 r: &Expr,
2449 ) -> Result<(), String> {
2450 macro_rules! native {
2453 ($opc:expr) => {{
2454 self.compile_expr(b, l)?;
2455 self.compile_expr(b, r)?;
2456 b.emit($opc, 0);
2457 return Ok(());
2458 }};
2459 }
2460 match op {
2461 BinOp::Add => native!(Op::Add),
2462 BinOp::Sub => native!(Op::Sub),
2463 BinOp::Mul => native!(Op::Mul),
2464 BinOp::Div => {
2465 self.compile_expr(b, l)?;
2469 self.compile_expr(b, r)?;
2470 b.emit(Op::CallBuiltin(ops::DIV, 2), 0);
2471 return Ok(());
2472 }
2473 BinOp::Mod => native!(Op::Mod),
2474 BinOp::Pow => {
2478 self.compile_expr(b, l)?;
2479 self.compile_expr(b, r)?;
2480 b.emit(Op::CallBuiltin(ops::POW, 2), 0);
2481 return Ok(());
2482 }
2483 BinOp::Lt => native!(Op::NumLt),
2484 BinOp::Le => native!(Op::NumLe),
2485 BinOp::Gt => native!(Op::NumGt),
2486 BinOp::Ge => native!(Op::NumGe),
2487 BinOp::EqEqEq => {
2488 self.compile_expr(b, l)?;
2489 self.compile_expr(b, r)?;
2490 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
2491 }
2492 BinOp::NeEqEq => {
2493 self.compile_expr(b, l)?;
2494 self.compile_expr(b, r)?;
2495 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
2496 b.emit(Op::LogNot, 0);
2497 }
2498 BinOp::EqEq => {
2499 self.compile_expr(b, l)?;
2500 self.compile_expr(b, r)?;
2501 b.emit(Op::CallBuiltin(ops::LOOSE_EQ, 2), 0);
2502 }
2503 BinOp::NeEq => {
2504 self.compile_expr(b, l)?;
2505 self.compile_expr(b, r)?;
2506 b.emit(Op::CallBuiltin(ops::LOOSE_EQ, 2), 0);
2507 b.emit(Op::LogNot, 0);
2508 }
2509 BinOp::In => {
2510 match l {
2514 Expr::Ident(n) if n.starts_with('#') => self.name_const(b, n),
2515 _ => self.compile_expr(b, l)?,
2516 }
2517 self.compile_expr(b, r)?;
2518 b.emit(Op::CallBuiltin(ops::CONTAINS, 2), 0);
2519 }
2520 BinOp::InstanceOf => {
2521 self.compile_expr(b, l)?;
2522 self.compile_expr(b, r)?;
2523 b.emit(Op::CallBuiltin(ops::INSTANCEOF, 2), 0);
2524 }
2525 BinOp::BitAnd => self.emit_bitwise(b, bop::BITAND, l, r)?,
2526 BinOp::BitOr => self.emit_bitwise(b, bop::BITOR, l, r)?,
2527 BinOp::BitXor => self.emit_bitwise(b, bop::BITXOR, l, r)?,
2528 BinOp::Shl => self.emit_bitwise(b, bop::SHL, l, r)?,
2529 BinOp::Shr => self.emit_bitwise(b, bop::SHR, l, r)?,
2530 BinOp::UShr => self.emit_bitwise(b, bop::USHR, l, r)?,
2531 }
2532 Ok(())
2533 }
2534
2535 fn emit_bitwise(
2536 &mut self,
2537 b: &mut ChunkBuilder,
2538 tag: i64,
2539 l: &Expr,
2540 r: &Expr,
2541 ) -> Result<(), String> {
2542 b.emit(Op::LoadInt(tag), 0);
2543 self.compile_expr(b, l)?;
2544 self.compile_expr(b, r)?;
2545 b.emit(Op::CallBuiltin(ops::BINOP, 3), 0);
2546 Ok(())
2547 }
2548
2549 fn compile_update(
2550 &mut self,
2551 b: &mut ChunkBuilder,
2552 op: UpdateOp,
2553 prefix: bool,
2554 target: &Expr,
2555 ) -> Result<(), String> {
2556 let tag = if matches!(op, UpdateOp::Inc) { 1 } else { -1 };
2561 if let Expr::Ident(n) = target {
2567 if self.slots.consts.contains(n) {
2573 self.throw_const_assignment(b);
2574 return Ok(());
2575 }
2576 if let Some(slot) = self.numeric_slot_of(n) {
2577 b.emit(Op::GetSlot(slot), 0); if !prefix {
2579 b.emit(Op::Dup, 0); }
2581 b.emit(Op::LoadFloat(tag as f64), 0);
2582 b.emit(Op::Add, 0); if prefix {
2584 b.emit(Op::Dup, 0); }
2586 b.emit(Op::SetSlot(slot), 0); return Ok(());
2588 }
2589 }
2590 b.emit(Op::LoadInt(tag), 0);
2591 self.compile_expr(b, target)?; b.emit(Op::CallBuiltin(ops::NUM_STEP, 2), 0); if prefix {
2594 b.emit(Op::Swap, 0); b.emit(Op::Pop, 0); b.emit(Op::Dup, 0); self.compile_bind(b, target, BindMode::Assign)?; } else {
2600 self.compile_bind(b, target, BindMode::Assign)?; }
2603 Ok(())
2604 }
2605
2606 fn compile_member(
2607 &mut self,
2608 b: &mut ChunkBuilder,
2609 object: &Expr,
2610 property: &str,
2611 optional: bool,
2612 ) -> Result<(), String> {
2613 if matches!(object, Expr::Super) {
2615 self.name_const(b, property);
2616 b.emit(Op::CallBuiltin(ops::SUPER_GET, 1), 0);
2617 return Ok(());
2618 }
2619 self.compile_expr(b, object)?;
2620 if optional {
2621 let jshort = self.emit_optional_guard(b);
2622 self.name_const(b, property);
2623 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0);
2624 let end = b.current_pos();
2625 b.patch_jump(jshort, end);
2626 } else {
2627 self.name_const(b, property);
2628 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0);
2629 }
2630 Ok(())
2631 }
2632
2633 fn compile_index(
2634 &mut self,
2635 b: &mut ChunkBuilder,
2636 object: &Expr,
2637 index: &Expr,
2638 optional: bool,
2639 ) -> Result<(), String> {
2640 self.compile_expr(b, object)?;
2641 if optional {
2642 let jshort = self.emit_optional_guard(b);
2643 self.compile_expr(b, index)?;
2644 b.emit(Op::CallBuiltin(ops::GETITEM, 2), 0);
2645 let end = b.current_pos();
2646 b.patch_jump(jshort, end);
2647 } else {
2648 self.compile_expr(b, index)?;
2649 b.emit(Op::CallBuiltin(ops::GETITEM, 2), 0);
2650 }
2651 Ok(())
2652 }
2653
2654 fn emit_push_scope(&mut self, b: &mut ChunkBuilder) {
2660 b.emit(Op::CallBuiltin(ops::PUSH_SCOPE, 0), 0);
2661 b.emit(Op::Pop, 0);
2662 self.scope_depth += 1;
2663 }
2664
2665 fn emit_pop_scope(&mut self, b: &mut ChunkBuilder) {
2666 b.emit(Op::CallBuiltin(ops::POP_SCOPE, 0), 0);
2667 b.emit(Op::Pop, 0);
2668 self.scope_depth -= 1;
2669 }
2670
2671 fn emit_copy_scope(&self, b: &mut ChunkBuilder) {
2674 b.emit(Op::CallBuiltin(ops::COPY_SCOPE, 0), 0);
2675 b.emit(Op::Pop, 0);
2676 }
2677
2678 fn finish_chunk(&mut self, b: ChunkBuilder) -> Chunk {
2690 let sites = std::mem::take(&mut self.call_sites);
2691 let yields = std::mem::take(&mut self.yield_sites);
2692 let chunk = b.build();
2693 crate::host::register_call_sites(chunk.op_hash, sites);
2694 crate::host::register_yield_sites(chunk.op_hash, yields);
2695 chunk
2696 }
2697
2698 fn note_call_site(&mut self, at: usize, callee: &Expr) {
2702 if let Some(text) = callee_text(callee) {
2703 self.call_sites.push((at, text));
2704 }
2705 }
2706
2707 fn emit_close_iters_under_value(&self, b: &mut ChunkBuilder) {
2708 for _ in 0..self.iter_depth {
2709 b.emit(Op::Swap, 0); b.emit(Op::CallBuiltin(ops::ITER_CLOSE, 1), 0); b.emit(Op::Pop, 0); }
2713 }
2714
2715 fn emit_close_iters(&self, b: &mut ChunkBuilder, target: usize) {
2716 for _ in target..self.iter_depth {
2717 b.emit(Op::CallBuiltin(ops::ITER_CLOSE, 1), 0);
2718 b.emit(Op::Pop, 0);
2719 }
2720 }
2721
2722 fn emit_unwind_scopes(&self, b: &mut ChunkBuilder, target: usize) {
2725 for _ in target..self.scope_depth {
2726 b.emit(Op::CallBuiltin(ops::POP_SCOPE, 0), 0);
2727 b.emit(Op::Pop, 0);
2728 }
2729 }
2730
2731 fn emit_signal_jump(&mut self, b: &mut ChunkBuilder, op: u16, label: Option<&str>, line: u32) {
2733 self.name_const(b, label.unwrap_or(""));
2734 b.emit(Op::CallBuiltin(op, 1), line);
2735 b.emit(Op::Pop, line);
2736 self.chunk_signals = true;
2737 }
2738
2739 fn emit_signal_dispatch(&mut self, b: &mut ChunkBuilder) {
2745 let brk = self
2752 .loops
2753 .len()
2754 .checked_sub(1)
2755 .filter(|i| *i >= self.chunk_loop_base);
2756 let cont = self
2757 .loops
2758 .iter()
2759 .rposition(|c| c.catches_continue)
2760 .filter(|i| *i >= self.chunk_loop_base);
2761 let tag_of = |i: Option<usize>, loops: &[LoopCtx]| match i {
2762 Some(i) => loops[i]
2763 .label
2764 .clone()
2765 .unwrap_or_else(|| unwind::PLAIN_LOOP.to_string()),
2766 None => unwind::NO_LOOP.to_string(),
2767 };
2768 let brk_tag = tag_of(brk, &self.loops);
2769 let cont_tag = tag_of(cont, &self.loops);
2770 self.name_const(b, &brk_tag);
2771 self.name_const(b, &cont_tag);
2772 b.emit(Op::CallBuiltin(ops::SIG_UNWIND, 2), 0); let Some(idx) = brk else {
2774 b.emit(Op::Pop, 0);
2777 return;
2778 };
2779 b.emit(Op::Dup, 0);
2780 b.emit(Op::LoadInt(unwind::BREAK), 0);
2781 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
2782 let jb = b.emit(Op::JumpIfTrue(0), 0);
2783 let jc = cont.map(|_| {
2784 b.emit(Op::Dup, 0);
2785 b.emit(Op::LoadInt(unwind::CONTINUE), 0);
2786 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
2787 b.emit(Op::JumpIfTrue(0), 0)
2788 });
2789 b.emit(Op::Pop, 0); let jafter = b.emit(Op::Jump(0), 0);
2791 let (brk_scope, brk_iter) = (self.loops[idx].break_depth, self.loops[idx].iter_depth);
2797 let brk_land = b.current_pos();
2798 b.emit(Op::Pop, 0);
2799 self.emit_unwind_scopes(b, brk_scope);
2800 self.emit_close_iters(b, brk_iter);
2801 let brk_jump = b.emit(Op::Jump(0), 0);
2802 let cont_jump = jc.map(|_| {
2803 let (cs, ci) = cont
2804 .map(|i| (self.loops[i].continue_depth, self.loops[i].iter_depth))
2805 .unwrap_or((self.scope_depth, self.iter_depth));
2806 let cont_land = b.current_pos();
2807 b.emit(Op::Pop, 0);
2808 self.emit_unwind_scopes(b, cs);
2809 self.emit_close_iters(b, ci);
2810 (cont_land, b.emit(Op::Jump(0), 0))
2811 });
2812 let after = b.current_pos();
2813 b.patch_jump(jb, brk_land);
2814 if let (Some(jc), Some((cont_land, _))) = (jc, cont_jump) {
2815 b.patch_jump(jc, cont_land);
2816 }
2817 b.patch_jump(jafter, after);
2818 self.loops[idx].breaks.push(brk_jump);
2819 if let (Some(cont_idx), Some((_, cj))) = (cont, cont_jump) {
2820 self.loops[cont_idx].continues.push(cj);
2821 }
2822 }
2823
2824 fn emit_optional_guard(&mut self, b: &mut ChunkBuilder) -> usize {
2825 b.emit(Op::Dup, 0);
2826 b.emit(Op::CallBuiltin(ops::NULLISH, 1), 0);
2827 let jnull = b.emit(Op::JumpIfFalse(0), 0); b.emit(Op::Pop, 0);
2830 b.emit(Op::LoadUndef, 0);
2831 let jend = b.emit(Op::Jump(0), 0);
2832 let cont = b.current_pos();
2833 b.patch_jump(jnull, cont);
2834 jend
2835 }
2836
2837 fn compile_optional_call(
2842 &mut self,
2843 b: &mut ChunkBuilder,
2844 func: &Expr,
2845 args: &[Expr],
2846 ) -> Result<(), String> {
2847 let has_spread = args.iter().any(|a| matches!(a, Expr::Spread(_)));
2848 if let Expr::Member {
2849 object,
2850 property,
2851 optional: obj_optional,
2852 } = func
2853 {
2854 self.compile_expr(b, object)?; let jobj = if *obj_optional {
2856 Some(self.emit_optional_guard(b))
2857 } else {
2858 None
2859 };
2860 b.emit(Op::Dup, 0); self.name_const(b, property); b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0); b.emit(Op::Dup, 0);
2865 b.emit(Op::CallBuiltin(ops::NULLISH, 1), 0);
2866 let jlive = b.emit(Op::JumpIfFalse(0), 0);
2867 b.emit(Op::Pop, 0);
2868 b.emit(Op::Pop, 0);
2869 b.emit(Op::LoadUndef, 0);
2870 let jend = b.emit(Op::Jump(0), 0);
2871 let live = b.current_pos();
2872 b.patch_jump(jlive, live);
2873 let via = if has_spread { "apply" } else { "call" };
2875 self.name_const(b, via); b.emit(Op::Rot, 0); let extra = if has_spread {
2878 self.compile_spread_args(b, args)?; 1
2880 } else {
2881 for a in args {
2882 self.compile_expr(b, a)?;
2883 }
2884 args.len()
2885 };
2886 b.emit(Op::CallBuiltin(ops::CALL_METHOD, argc(3 + extra)?), 0);
2887 let end = b.current_pos();
2888 b.patch_jump(jend, end);
2889 if let Some(j) = jobj {
2890 b.patch_jump(j, end);
2891 }
2892 return Ok(());
2893 }
2894 self.compile_expr(b, func)?;
2897 let jend = self.emit_optional_guard(b);
2898 if has_spread {
2899 self.compile_spread_args(b, args)?;
2900 b.emit(Op::CallBuiltin(ops::APPLY, 2), 0);
2901 } else {
2902 for a in args {
2903 self.compile_expr(b, a)?;
2904 }
2905 b.emit(Op::CallBuiltin(ops::CALL_VALUE, argc(1 + args.len())?), 0);
2906 }
2907 let end = b.current_pos();
2908 b.patch_jump(jend, end);
2909 Ok(())
2910 }
2911
2912 fn compile_call(
2913 &mut self,
2914 b: &mut ChunkBuilder,
2915 func: &Expr,
2916 args: &[Expr],
2917 optional: bool,
2918 ) -> Result<(), String> {
2919 if optional {
2920 return self.compile_optional_call(b, func, args);
2921 }
2922 let has_spread = args.iter().any(|a| matches!(a, Expr::Spread(_)));
2923 match func {
2924 Expr::Super => {
2927 for a in args {
2928 self.compile_expr(b, a)?;
2929 }
2930 b.emit(Op::CallBuiltin(ops::SUPER_CALL, argc(args.len())?), 0);
2931 return Ok(());
2932 }
2933 Expr::Member {
2936 object, property, ..
2937 } if matches!(**object, Expr::Super) => {
2938 self.name_const(b, property);
2939 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 {
2945 self.compile_expr(b, a)?;
2946 }
2947 b.emit(Op::CallBuiltin(ops::CALL_METHOD, argc(3 + args.len())?), 0);
2948 return Ok(());
2949 }
2950 Expr::Member {
2951 object,
2952 property,
2953 optional,
2954 } => {
2955 self.compile_expr(b, object)?;
2956 let jshort = if *optional {
2959 Some(self.emit_optional_guard(b))
2960 } else {
2961 None
2962 };
2963 self.name_const(b, property);
2964 if has_spread {
2965 self.compile_spread_args(b, args)?; b.emit(Op::CallBuiltin(ops::APPLY_METHOD, 3), 0);
2967 } else {
2968 for a in args {
2969 self.compile_expr(b, a)?;
2970 }
2971 let at = b.emit(Op::CallBuiltin(ops::CALL_METHOD, argc(2 + args.len())?), 0);
2972 self.note_call_site(at, func);
2973 }
2974 if let Some(j) = jshort {
2975 let end = b.current_pos();
2976 b.patch_jump(j, end);
2977 }
2978 }
2979 Expr::Index {
2980 object,
2981 index,
2982 optional,
2983 } => {
2984 self.compile_expr(b, object)?; let jshort = if *optional {
2988 Some(self.emit_optional_guard(b))
2989 } else {
2990 None
2991 };
2992 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 {
3000 self.compile_spread_args(b, args)?;
3001 b.emit(Op::CallBuiltin(ops::APPLY, 2), 0);
3002 } else {
3003 for a in args {
3004 self.compile_expr(b, a)?;
3005 }
3006 let at = b.emit(Op::CallBuiltin(ops::CALL_VALUE, argc(1 + args.len())?), 0);
3007 self.note_call_site(at, func);
3008 }
3009 if let Some(j) = jshort {
3010 let end = b.current_pos();
3011 b.patch_jump(j, end);
3012 }
3013 }
3014 Expr::Ident(n) if self.slot_of(n).is_none() => {
3018 self.name_const(b, n);
3019 if has_spread {
3020 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);
3026 } else {
3027 for a in args {
3028 self.compile_expr(b, a)?;
3029 }
3030 let at = b.emit(Op::CallBuiltin(ops::CALL, argc(1 + args.len())?), 0);
3031 self.note_call_site(at, func);
3032 }
3033 }
3034 _ => {
3035 self.compile_expr(b, func)?;
3036 if has_spread {
3037 self.compile_spread_args(b, args)?;
3038 b.emit(Op::CallBuiltin(ops::APPLY, 2), 0);
3039 } else {
3040 for a in args {
3041 self.compile_expr(b, a)?;
3042 }
3043 let at = b.emit(Op::CallBuiltin(ops::CALL_VALUE, argc(1 + args.len())?), 0);
3044 self.note_call_site(at, func);
3045 }
3046 }
3047 }
3048 Ok(())
3049 }
3050
3051 fn compile_spread_args(&mut self, b: &mut ChunkBuilder, args: &[Expr]) -> Result<(), String> {
3053 for a in args {
3054 match a {
3055 Expr::Spread(inner) => {
3056 b.emit(Op::LoadInt(1), 0);
3057 self.compile_expr(b, inner)?;
3058 }
3059 _ => {
3060 b.emit(Op::LoadInt(0), 0);
3061 self.compile_expr(b, a)?;
3062 }
3063 }
3064 }
3065 b.emit(Op::CallBuiltin(ops::BUILD_ARGS, argc(args.len() * 2)?), 0);
3066 Ok(())
3067 }
3068
3069 fn compile_new(
3070 &mut self,
3071 b: &mut ChunkBuilder,
3072 callee: &Expr,
3073 args: &[Expr],
3074 ) -> Result<(), String> {
3075 self.compile_expr(b, callee)?;
3076 for a in args {
3077 self.compile_expr(b, a)?;
3078 }
3079 let at = b.emit(Op::CallBuiltin(ops::NEW, argc(1 + args.len())?), 0);
3080 self.note_call_site(at, callee);
3081 Ok(())
3082 }
3083}
3084
3085fn default_stmt(name: &str, default: &Expr) -> Stmt {
3088 Stmt::from(StmtKind::If {
3089 test: Expr::Binary(
3090 BinOp::EqEqEq,
3091 Box::new(Expr::Ident(name.to_string())),
3092 Box::new(Expr::Undefined),
3093 ),
3094 cons: Box::new(Stmt::from(StmtKind::Expr(Expr::Assign {
3095 target: Box::new(Expr::Ident(name.to_string())),
3096 value: Box::new(default.clone()),
3097 }))),
3098 alt: None,
3099 })
3100}