1use crate::ast::*;
16use crate::host::{binop as bop, member, ops, unop, 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
69struct LoopCtx {
71 breaks: Vec<usize>,
72 continues: Vec<usize>,
73 catches_continue: bool,
75 label: Option<String>,
78}
79
80#[derive(Default)]
81pub struct Compiler {
82 functions: Vec<(String, FuncDef)>,
83 tries: Vec<TryDef>,
84 loops: Vec<LoopCtx>,
85 tmp: usize,
86 pending_label: Option<String>,
89 debug: bool,
91}
92
93pub fn compile(stmts: &[Stmt], debug: bool) -> Result<Program, String> {
95 let mut c = Compiler {
96 debug,
97 ..Default::default()
98 };
99 let mut b = ChunkBuilder::new();
100 c.hoist_funcs(&mut b, stmts)?;
102 c.compile_stmts(&mut b, stmts)?;
103 Ok(Program {
104 main: b.build(),
105 functions: c.functions,
106 tries: c.tries,
107 })
108}
109
110pub fn compile_completion(stmts: &[Stmt], debug: bool) -> Result<Program, String> {
114 let mut c = Compiler {
115 debug,
116 ..Default::default()
117 };
118 let mut b = ChunkBuilder::new();
119 c.hoist_funcs(&mut b, stmts)?;
120 if let Some((last, rest)) = stmts.split_last() {
121 c.compile_stmts(&mut b, rest)?;
122 if let StmtKind::Expr(e) = &last.kind {
123 c.compile_expr(&mut b, e)?;
125 } else {
126 c.compile_stmt(&mut b, last)?;
127 }
128 }
129 Ok(Program {
130 main: b.build(),
131 functions: c.functions,
132 tries: c.tries,
133 })
134}
135
136fn argc(n: usize) -> Result<u8, String> {
137 u8::try_from(n).map_err(|_| "too many arguments (>255) for one call".to_string())
138}
139
140impl Compiler {
141 fn name_const(&self, b: &mut ChunkBuilder, s: &str) {
143 let k = b.add_constant(Value::str(s));
144 b.emit(Op::LoadConst(k), 0);
145 }
146 fn strlit(&self, b: &mut ChunkBuilder, s: &str) {
147 let k = b.add_constant(Value::str(s));
148 b.emit(Op::LoadConst(k), 0);
149 b.emit(Op::CallBuiltin(ops::MKSTR, 1), 0);
150 }
151 fn tmp_name(&mut self, tag: &str) -> String {
152 let n = format!(".{tag}{}", self.tmp);
153 self.tmp += 1;
154 n
155 }
156
157 fn emit_mkfunc(&self, b: &mut ChunkBuilder, def_id: usize) {
160 b.emit(Op::LoadInt(def_id as i64), 0);
161 b.emit(Op::CallBuiltin(ops::MKFUNC, 1), 0);
162 }
163
164 fn hoist_funcs(&mut self, b: &mut ChunkBuilder, stmts: &[Stmt]) -> Result<(), String> {
165 for s in stmts {
166 if let StmtKind::FuncDecl {
167 name,
168 params,
169 body,
170 is_generator,
171 is_async,
172 } = &s.kind
173 {
174 let def_id = self.build_function(name, params, body, *is_generator, *is_async)?;
175 self.emit_mkfunc(b, def_id);
176 self.declare(b, &Expr::Ident(name.clone()));
177 }
178 }
179 Ok(())
180 }
181
182 fn compile_stmts(&mut self, b: &mut ChunkBuilder, stmts: &[Stmt]) -> Result<(), String> {
183 for s in stmts {
184 self.compile_stmt(b, s)?;
185 }
186 Ok(())
187 }
188
189 fn compile_stmt(&mut self, b: &mut ChunkBuilder, s: &Stmt) -> Result<(), String> {
190 if self.debug && s.line != 0 {
191 b.emit(Op::LoadInt(s.line as i64), s.line);
192 b.emit(Op::CallBuiltin(ops::DBG_LINE, 1), s.line);
193 b.emit(Op::Pop, s.line);
194 }
195 let line = s.line;
196 match &s.kind {
197 StmtKind::Expr(e) => {
198 self.compile_expr(b, e)?;
199 b.emit(Op::Pop, line);
200 }
201 StmtKind::Empty => {}
202 StmtKind::FuncDecl { .. } => {} StmtKind::ClassDecl(node) => {
204 self.compile_class(b, node)?;
205 if let Some(name) = &node.name {
207 self.declare(b, &Expr::Ident(name.clone()));
208 } else {
209 b.emit(Op::Pop, line);
210 }
211 }
212 StmtKind::Decl { decls, .. } => {
213 for d in decls {
214 match &d.init {
215 Some(v) => {
216 self.compile_expr(b, v)?;
217 if let Expr::Ident(name) = &d.target {
220 self.infer_name(b, v, name);
221 }
222 }
223 None => {
224 b.emit(Op::LoadUndef, line);
225 }
226 }
227 self.compile_bind(b, &d.target, true)?;
228 }
229 }
230 StmtKind::Block(body) => {
231 self.hoist_funcs(b, body)?;
232 self.compile_stmts(b, body)?;
233 }
234 StmtKind::If { test, cons, alt } => self.compile_if(b, test, cons, alt)?,
235 StmtKind::While { test, body } => self.compile_while(b, test, body)?,
236 StmtKind::DoWhile { body, test } => self.compile_do_while(b, body, test)?,
237 StmtKind::For {
238 init,
239 test,
240 update,
241 body,
242 } => self.compile_for(b, init, test, update, body)?,
243 StmtKind::ForOf {
244 decl_kind,
245 target,
246 iter,
247 body,
248 is_await,
249 } => {
250 if *is_await {
251 self.compile_for_await(b, decl_kind.is_some(), target, iter, body)?
252 } else {
253 self.compile_for_of(b, decl_kind.is_some(), target, iter, body)?
254 }
255 }
256 StmtKind::ForIn {
257 decl_kind,
258 target,
259 object,
260 body,
261 } => self.compile_for_in(b, decl_kind.is_some(), target, object, body)?,
262 StmtKind::Switch { disc, cases } => self.compile_switch(b, disc, cases)?,
263 StmtKind::Return(e) => {
264 match e {
265 Some(e) => self.compile_expr(b, e)?,
266 None => {
267 b.emit(Op::LoadUndef, line);
268 }
269 }
270 b.emit(Op::CallBuiltin(ops::SIG_RETURN, 1), line);
271 }
272 StmtKind::Labeled { label, body } => self.compile_labeled(b, label, body)?,
273 StmtKind::Break(label) => {
274 let j = b.emit(Op::Jump(0), line);
275 let ctx = match label {
276 Some(name) => self
278 .loops
279 .iter_mut()
280 .rev()
281 .find(|c| c.label.as_deref() == Some(name.as_str()))
282 .ok_or_else(|| format!("SyntaxError: Undefined label '{name}'"))?,
283 None => self
284 .loops
285 .last_mut()
286 .ok_or("SyntaxError: 'break' outside loop")?,
287 };
288 ctx.breaks.push(j);
289 }
290 StmtKind::Continue(label) => {
291 let j = b.emit(Op::Jump(0), line);
292 let ctx = match label {
293 Some(name) => self
296 .loops
297 .iter_mut()
298 .rev()
299 .find(|c| c.catches_continue && c.label.as_deref() == Some(name.as_str()))
300 .ok_or_else(|| {
301 format!("SyntaxError: Undefined label '{name}' for continue")
302 })?,
303 None => self
304 .loops
305 .iter_mut()
306 .rev()
307 .find(|c| c.catches_continue)
308 .ok_or("SyntaxError: 'continue' outside loop")?,
309 };
310 ctx.continues.push(j);
311 }
312 StmtKind::Throw(e) => {
313 self.compile_expr(b, e)?;
314 b.emit(Op::CallBuiltin(ops::THROW, 1), line);
315 }
316 StmtKind::Try {
317 block,
318 handler,
319 finalizer,
320 } => self.compile_try(b, block, handler, finalizer)?,
321 }
322 Ok(())
323 }
324
325 fn compile_bind(
329 &mut self,
330 b: &mut ChunkBuilder,
331 target: &Expr,
332 declare: bool,
333 ) -> Result<(), String> {
334 match target {
335 Expr::Ident(_) => {
336 if declare {
337 self.declare(b, target);
338 } else {
339 self.store_simple(b, target)?;
340 }
341 }
342 Expr::Member { .. } | Expr::Index { .. } => {
343 self.store_simple(b, target)?;
344 }
345 Expr::Array(items) => self.destructure_array(b, items, declare)?,
346 Expr::Object(props) => self.destructure_object(b, props, declare)?,
347 Expr::Assign { target, value } => {
348 b.emit(Op::Dup, 0);
350 b.emit(Op::LoadUndef, 0);
351 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
352 let jf = b.emit(Op::JumpIfFalse(0), 0);
353 b.emit(Op::Pop, 0); self.compile_expr(b, value)?;
355 let end = b.current_pos();
356 b.patch_jump(jf, end);
357 self.compile_bind(b, target, declare)?;
358 }
359 _ => return Err("SyntaxError: invalid assignment target".into()),
360 }
361 Ok(())
362 }
363
364 fn declare(&self, b: &mut ChunkBuilder, target: &Expr) {
366 if let Expr::Ident(n) = target {
367 self.name_const(b, n);
368 b.emit(Op::Swap, 0);
369 b.emit(Op::CallBuiltin(ops::DECLARE, 2), 0);
370 b.emit(Op::Pop, 0);
371 }
372 }
373
374 fn store_simple(&mut self, b: &mut ChunkBuilder, target: &Expr) -> Result<(), String> {
376 match target {
377 Expr::Ident(n) => {
378 self.name_const(b, n);
379 b.emit(Op::Swap, 0);
380 b.emit(Op::CallBuiltin(ops::SETLOCAL, 2), 0);
381 b.emit(Op::Pop, 0);
382 }
383 Expr::Member {
384 object, property, ..
385 } => {
386 self.compile_expr(b, object)?; self.name_const(b, property); b.emit(Op::Rot, 0); b.emit(Op::CallBuiltin(ops::SETATTR, 3), 0);
390 b.emit(Op::Pop, 0);
391 }
392 Expr::Index { object, index, .. } => {
393 self.compile_expr(b, object)?; self.compile_expr(b, index)?; b.emit(Op::Rot, 0); b.emit(Op::CallBuiltin(ops::SETITEM, 3), 0);
397 b.emit(Op::Pop, 0);
398 }
399 _ => return Err("SyntaxError: invalid assignment target".into()),
400 }
401 Ok(())
402 }
403
404 fn destructure_array(
405 &mut self,
406 b: &mut ChunkBuilder,
407 items: &[Expr],
408 declare: bool,
409 ) -> Result<(), String> {
410 let star_idx = items
411 .iter()
412 .position(|e| matches!(e, Expr::Spread(_)))
413 .map(|i| i as i64)
414 .unwrap_or(-1);
415 b.emit(Op::LoadInt(items.len() as i64), 0);
416 b.emit(Op::LoadInt(star_idx), 0);
417 b.emit(Op::CallBuiltin(ops::UNPACK, 3), 0); for it in items {
419 match it {
420 Expr::Undefined => {
421 b.emit(Op::Pop, 0); }
423 Expr::Spread(inner) => self.compile_bind(b, inner, declare)?,
424 _ => self.compile_bind(b, it, declare)?,
425 }
426 }
427 Ok(())
428 }
429
430 fn destructure_object(
431 &mut self,
432 b: &mut ChunkBuilder,
433 props: &[Prop],
434 declare: bool,
435 ) -> Result<(), String> {
436 let obj_tmp = self.tmp_name("destr");
438 self.name_const(b, &obj_tmp);
439 b.emit(Op::Swap, 0);
440 b.emit(Op::CallBuiltin(ops::DECLARE, 2), 0);
441 b.emit(Op::Pop, 0);
442 let mut named: Vec<String> = Vec::new();
444 for p in props {
445 match p {
446 Prop::KeyValue { key, value, .. } => {
447 if let Expr::Str(s) = key {
448 named.push(s.clone());
449 }
450 self.load_local(b, &obj_tmp);
452 self.compile_expr(b, key)?;
453 b.emit(Op::CallBuiltin(ops::GETITEM, 2), 0); self.compile_bind(b, value, declare)?;
455 }
456 Prop::Spread(target) => {
457 self.load_local(b, &obj_tmp);
458 for k in &named {
459 self.strlit(b, k);
460 }
461 b.emit(Op::CallBuiltin(ops::MKARR, argc(named.len())?), 0);
462 b.emit(Op::CallBuiltin(ops::OBJ_REST, 2), 0); self.compile_bind(b, target, declare)?;
464 }
465 Prop::Accessor { .. } => {}
467 }
468 }
469 Ok(())
470 }
471
472 fn load_local(&self, b: &mut ChunkBuilder, name: &str) {
473 self.name_const(b, name);
474 b.emit(Op::CallBuiltin(ops::GETLOCAL, 1), 0);
475 }
476
477 fn compile_condition(&mut self, b: &mut ChunkBuilder, e: &Expr) -> Result<(), String> {
479 self.compile_expr(b, e)?;
480 b.emit(Op::CallBuiltin(ops::TRUTHY, 1), 0);
481 Ok(())
482 }
483
484 fn compile_if(
485 &mut self,
486 b: &mut ChunkBuilder,
487 test: &Expr,
488 cons: &Stmt,
489 alt: &Option<Box<Stmt>>,
490 ) -> Result<(), String> {
491 self.compile_condition(b, test)?;
492 let jfalse = b.emit(Op::JumpIfFalse(0), 0);
493 self.compile_stmt(b, cons)?;
494 if let Some(alt) = alt {
495 let jend = b.emit(Op::Jump(0), 0);
496 let else_start = b.current_pos();
497 b.patch_jump(jfalse, else_start);
498 self.compile_stmt(b, alt)?;
499 let end = b.current_pos();
500 b.patch_jump(jend, end);
501 } else {
502 let end = b.current_pos();
503 b.patch_jump(jfalse, end);
504 }
505 Ok(())
506 }
507
508 fn compile_labeled(
512 &mut self,
513 b: &mut ChunkBuilder,
514 label: &str,
515 body: &Stmt,
516 ) -> Result<(), String> {
517 if matches!(
518 body.kind,
519 StmtKind::While { .. }
520 | StmtKind::DoWhile { .. }
521 | StmtKind::For { .. }
522 | StmtKind::ForOf { .. }
523 | StmtKind::ForIn { .. }
524 ) {
525 self.pending_label = Some(label.to_string());
526 self.compile_stmt(b, body)?;
527 self.pending_label = None;
529 } else {
530 self.loops.push(LoopCtx {
531 breaks: Vec::new(),
532 continues: Vec::new(),
533 catches_continue: false,
534 label: Some(label.to_string()),
535 });
536 self.compile_stmt(b, body)?;
537 let ctx = self.loops.pop().unwrap();
538 let end = b.current_pos();
539 for br in ctx.breaks {
540 b.patch_jump(br, end);
541 }
542 }
543 Ok(())
544 }
545
546 fn compile_while(
547 &mut self,
548 b: &mut ChunkBuilder,
549 test: &Expr,
550 body: &Stmt,
551 ) -> Result<(), String> {
552 let start = b.current_pos();
553 self.compile_condition(b, test)?;
554 let jfalse = b.emit(Op::JumpIfFalse(0), 0);
555 self.loops.push(LoopCtx {
556 breaks: Vec::new(),
557 continues: Vec::new(),
558 catches_continue: true,
559 label: self.pending_label.take(),
560 });
561 self.compile_stmt(b, body)?;
562 b.emit(Op::Jump(start), 0);
563 let ctx = self.loops.pop().unwrap();
564 for c in ctx.continues {
565 b.patch_jump(c, start);
566 }
567 let end = b.current_pos();
568 b.patch_jump(jfalse, end);
569 for br in ctx.breaks {
570 b.patch_jump(br, end);
571 }
572 Ok(())
573 }
574
575 fn compile_do_while(
576 &mut self,
577 b: &mut ChunkBuilder,
578 body: &Stmt,
579 test: &Expr,
580 ) -> Result<(), String> {
581 let start = b.current_pos();
582 self.loops.push(LoopCtx {
583 breaks: Vec::new(),
584 continues: Vec::new(),
585 catches_continue: true,
586 label: self.pending_label.take(),
587 });
588 self.compile_stmt(b, body)?;
589 let cont_target = b.current_pos();
590 self.compile_condition(b, test)?;
591 b.emit(Op::JumpIfTrue(start), 0);
592 let ctx = self.loops.pop().unwrap();
593 for c in ctx.continues {
594 b.patch_jump(c, cont_target);
595 }
596 let end = b.current_pos();
597 for br in ctx.breaks {
598 b.patch_jump(br, end);
599 }
600 Ok(())
601 }
602
603 fn compile_for(
604 &mut self,
605 b: &mut ChunkBuilder,
606 init: &Option<Box<Stmt>>,
607 test: &Option<Expr>,
608 update: &Option<Expr>,
609 body: &Stmt,
610 ) -> Result<(), String> {
611 if let Some(init) = init {
612 self.compile_stmt(b, init)?;
613 }
614 let start = b.current_pos();
615 let jfalse = match test {
616 Some(t) => {
617 self.compile_condition(b, t)?;
618 Some(b.emit(Op::JumpIfFalse(0), 0))
619 }
620 None => None,
621 };
622 self.loops.push(LoopCtx {
623 breaks: Vec::new(),
624 continues: Vec::new(),
625 catches_continue: true,
626 label: self.pending_label.take(),
627 });
628 self.compile_stmt(b, body)?;
629 let cont_target = b.current_pos();
630 if let Some(u) = update {
631 self.compile_expr(b, u)?;
632 b.emit(Op::Pop, 0);
633 }
634 b.emit(Op::Jump(start), 0);
635 let ctx = self.loops.pop().unwrap();
636 for c in ctx.continues {
637 b.patch_jump(c, cont_target);
638 }
639 let end = b.current_pos();
640 if let Some(jf) = jfalse {
641 b.patch_jump(jf, end);
642 }
643 for br in ctx.breaks {
644 b.patch_jump(br, end);
645 }
646 Ok(())
647 }
648
649 fn compile_for_of(
650 &mut self,
651 b: &mut ChunkBuilder,
652 declare: bool,
653 target: &Expr,
654 iter: &Expr,
655 body: &Stmt,
656 ) -> Result<(), String> {
657 self.compile_expr(b, iter)?;
658 b.emit(Op::CallBuiltin(ops::GETITER, 1), 0); self.loop_over(b, declare, target, body)
660 }
661
662 fn compile_for_in(
663 &mut self,
664 b: &mut ChunkBuilder,
665 declare: bool,
666 target: &Expr,
667 object: &Expr,
668 body: &Stmt,
669 ) -> Result<(), String> {
670 self.compile_expr(b, object)?;
671 b.emit(Op::CallBuiltin(ops::FORIN_KEYS, 1), 0); b.emit(Op::CallBuiltin(ops::GETITER, 1), 0); self.loop_over(b, declare, target, body)
674 }
675
676 fn compile_for_await(
680 &mut self,
681 b: &mut ChunkBuilder,
682 declare: bool,
683 target: &Expr,
684 iter: &Expr,
685 body: &Stmt,
686 ) -> Result<(), String> {
687 let iter_tmp = self.tmp_name("aiter");
688 self.compile_expr(b, iter)?;
689 b.emit(Op::CallBuiltin(ops::GET_ASYNC_ITER, 1), 0); self.name_const(b, &iter_tmp);
691 b.emit(Op::Swap, 0);
692 b.emit(Op::CallBuiltin(ops::DECLARE, 2), 0);
693 b.emit(Op::Pop, 0);
694 let start = b.current_pos();
695 self.load_local(b, &iter_tmp);
697 b.emit(Op::CallBuiltin(ops::ASYNC_STEP, 1), 0); b.emit(Op::CallBuiltin(ops::AWAIT, 1), 0); let step_tmp = self.tmp_name("astep");
700 self.name_const(b, &step_tmp);
701 b.emit(Op::Swap, 0);
702 b.emit(Op::CallBuiltin(ops::DECLARE, 2), 0);
703 b.emit(Op::Pop, 0);
704 self.load_local(b, &step_tmp);
706 self.name_const(b, "done");
707 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0);
708 b.emit(Op::CallBuiltin(ops::TRUTHY, 1), 0);
709 let jdone = b.emit(Op::JumpIfTrue(0), 0);
710 self.load_local(b, &step_tmp);
712 self.name_const(b, "value");
713 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0); self.compile_bind(b, target, declare)?;
715 self.loops.push(LoopCtx {
716 breaks: Vec::new(),
717 continues: Vec::new(),
718 catches_continue: true,
719 label: self.pending_label.take(),
720 });
721 self.compile_stmt(b, body)?;
722 b.emit(Op::Jump(start), 0);
723 let ctx = self.loops.pop().unwrap();
724 for c in ctx.continues {
725 b.patch_jump(c, start);
726 }
727 let end = b.current_pos();
728 b.patch_jump(jdone, end);
729 for br in ctx.breaks {
730 b.patch_jump(br, end);
731 }
732 Ok(())
733 }
734
735 fn loop_over(
737 &mut self,
738 b: &mut ChunkBuilder,
739 declare: bool,
740 target: &Expr,
741 body: &Stmt,
742 ) -> Result<(), String> {
743 let start = b.current_pos();
744 b.emit(Op::CallBuiltin(ops::FORITER, 0), 0); let jdone = b.emit(Op::JumpIfFalse(0), 0); self.compile_bind(b, target, declare)?; self.loops.push(LoopCtx {
748 breaks: Vec::new(),
749 continues: Vec::new(),
750 catches_continue: true,
751 label: self.pending_label.take(),
752 });
753 self.compile_stmt(b, body)?;
754 b.emit(Op::Jump(start), 0);
755 let ctx = self.loops.pop().unwrap();
756 for c in ctx.continues {
757 b.patch_jump(c, start);
758 }
759 let done = b.current_pos();
760 b.patch_jump(jdone, done);
761 b.emit(Op::Pop, 0); let jafter = b.emit(Op::Jump(0), 0);
763 let break_target = b.current_pos();
764 b.emit(Op::CallBuiltin(ops::ITER_CLOSE, 1), 0);
767 let end = b.current_pos();
768 b.patch_jump(jafter, end);
769 for br in ctx.breaks {
770 b.patch_jump(br, break_target);
771 }
772 Ok(())
773 }
774
775 fn compile_switch(
776 &mut self,
777 b: &mut ChunkBuilder,
778 disc: &Expr,
779 cases: &[SwitchCase],
780 ) -> Result<(), String> {
781 let disc_tmp = self.tmp_name("switch");
782 self.compile_expr(b, disc)?;
783 self.name_const(b, &disc_tmp);
784 b.emit(Op::Swap, 0);
785 b.emit(Op::CallBuiltin(ops::DECLARE, 2), 0);
786 b.emit(Op::Pop, 0);
787 let mut body_jumps: Vec<Option<usize>> = Vec::new();
789 let mut default_idx: Option<usize> = None;
790 for (i, case) in cases.iter().enumerate() {
791 match &case.test {
792 Some(t) => {
793 self.load_local(b, &disc_tmp);
794 self.compile_expr(b, t)?;
795 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
796 let j = b.emit(Op::JumpIfTrue(0), 0);
797 body_jumps.push(Some(j));
798 }
799 None => {
800 default_idx = Some(i);
801 body_jumps.push(None);
802 }
803 }
804 }
805 let no_match_jump = b.emit(Op::Jump(0), 0);
807 self.loops.push(LoopCtx {
808 breaks: Vec::new(),
809 continues: Vec::new(),
810 catches_continue: false,
811 label: None,
812 });
813 let mut body_starts: Vec<usize> = Vec::new();
814 for case in cases {
815 body_starts.push(b.current_pos());
816 self.compile_stmts(b, &case.body)?;
817 }
818 let end = b.current_pos();
819 for (i, j) in body_jumps.iter().enumerate() {
821 if let Some(j) = j {
822 b.patch_jump(*j, body_starts[i]);
823 }
824 }
825 match default_idx {
826 Some(i) => b.patch_jump(no_match_jump, body_starts[i]),
827 None => b.patch_jump(no_match_jump, end),
828 }
829 let ctx = self.loops.pop().unwrap();
830 for br in ctx.breaks {
831 b.patch_jump(br, end);
832 }
833 Ok(())
834 }
835
836 fn compile_try(
837 &mut self,
838 b: &mut ChunkBuilder,
839 block: &[Stmt],
840 handler: &Option<(Option<Expr>, Vec<Stmt>)>,
841 finalizer: &Option<Vec<Stmt>>,
842 ) -> Result<(), String> {
843 let block_chunk = self.compile_block_chunk(block)?;
844 let handler_def = match handler {
845 Some((param, body)) => {
846 let param_name = match param {
847 Some(Expr::Ident(n)) => Some(n.clone()),
848 _ => None,
849 };
850 let hbody = self.compile_block_chunk(body)?;
851 Some((param_name, hbody))
852 }
853 None => None,
854 };
855 let final_chunk = match finalizer {
856 Some(f) => Some(self.compile_block_chunk(f)?),
857 None => None,
858 };
859 let id = self.tries.len();
860 self.tries.push(TryDef {
861 block: block_chunk,
862 handler: handler_def,
863 finalizer: final_chunk,
864 });
865 b.emit(Op::LoadInt(id as i64), 0);
866 b.emit(Op::CallBuiltin(ops::TRY, 1), 0);
867 b.emit(Op::Pop, 0);
868 Ok(())
869 }
870
871 fn compile_block_chunk(&mut self, stmts: &[Stmt]) -> Result<Chunk, String> {
872 let mut cb = ChunkBuilder::new();
873 self.hoist_funcs(&mut cb, stmts)?;
874 self.compile_stmts(&mut cb, stmts)?;
875 Ok(cb.build())
876 }
877
878 fn build_function(
880 &mut self,
881 name: &str,
882 params: &[Param],
883 body: &[Stmt],
884 is_generator: bool,
885 is_async: bool,
886 ) -> Result<usize, String> {
887 let (slots, prologue) = self.lower_params(params)?;
888 let mut fb = ChunkBuilder::new();
889 self.hoist_funcs(&mut fb, &prologue)?;
891 self.hoist_funcs(&mut fb, body)?;
892 self.compile_stmts(&mut fb, &prologue)?;
893 self.compile_stmts(&mut fb, body)?;
894 let def = FuncDef {
895 name: name.to_string(),
896 params: slots,
897 chunk: fb.build(),
898 is_arrow: false,
899 is_generator,
900 is_async,
901 };
902 self.functions.push((name.to_string(), def));
903 Ok(self.functions.len() - 1)
904 }
905
906 fn build_arrow(
907 &mut self,
908 params: &[Param],
909 body: &FnBody,
910 is_async: bool,
911 ) -> Result<usize, String> {
912 let stmts = match body {
913 FnBody::Block(b) => b.clone(),
914 FnBody::Expr(e) => vec![Stmt::from(StmtKind::Return(Some((**e).clone())))],
915 };
916 let id = self.build_function("", params, &stmts, false, is_async)?;
917 self.functions[id].1.is_arrow = true;
919 Ok(id)
920 }
921
922 fn compile_class(&mut self, b: &mut ChunkBuilder, node: &ClassNode) -> Result<(), String> {
927 let cname = node.name.clone().unwrap_or_default();
928 self.name_const(b, &cname);
930 match &node.parent {
931 Some(p) => self.compile_expr(b, p)?,
932 None => {
933 b.emit(Op::LoadUndef, 0);
934 }
935 }
936 let ctor = node
937 .members
938 .iter()
939 .find(|m| m.kind == MemberKind::Constructor);
940 match ctor {
941 Some(m) => {
942 let def_id = self.build_function(&cname, &m.params, &m.body, false, false)?;
943 self.emit_mkfunc(b, def_id);
944 }
945 None => {
946 b.emit(Op::LoadUndef, 0);
947 }
948 }
949 b.emit(Op::CallBuiltin(ops::MKCLASS, 3), 0); for m in &node.members {
952 match m.kind {
953 MemberKind::Constructor => {}
954 MemberKind::Field if m.is_static => {
955 b.emit(Op::Dup, 0); self.emit_member_key(b, m)?; match &m.field_init {
961 Some(e) => self.compile_expr(b, e)?,
962 None => {
963 b.emit(Op::LoadUndef, 0);
964 }
965 }
966 b.emit(Op::CallBuiltin(ops::SETATTR, 3), 0);
968 b.emit(Op::Pop, 0); }
970 MemberKind::Field => {
971 self.emit_member_key(b, m)?;
973 let init = m.field_init.clone().unwrap_or(Expr::Undefined);
974 let stmts = vec![Stmt::from(StmtKind::Return(Some(init)))];
975 let def_id = self.build_function("", &[], &stmts, false, false)?;
976 self.emit_mkfunc(b, def_id);
977 b.emit(Op::CallBuiltin(ops::DEF_FIELD, 3), 0);
978 }
979 MemberKind::Method | MemberKind::Get | MemberKind::Set => {
980 self.emit_member_key(b, m)?;
982 let kind = match m.kind {
983 MemberKind::Get => member::GET,
984 MemberKind::Set => member::SET,
985 _ => member::METHOD,
986 };
987 b.emit(Op::LoadInt(kind), 0);
988 b.emit(
989 if m.is_static {
990 Op::LoadTrue
991 } else {
992 Op::LoadFalse
993 },
994 0,
995 );
996 let mname = match &m.key {
997 Expr::Str(s) if !m.computed => s.clone(),
998 _ => String::new(),
999 };
1000 let def_id = self.build_function(
1001 &mname,
1002 &m.params,
1003 &m.body,
1004 m.is_generator,
1005 m.is_async,
1006 )?;
1007 self.emit_mkfunc(b, def_id);
1008 b.emit(Op::CallBuiltin(ops::DEF_MEMBER, 5), 0);
1009 }
1010 }
1011 }
1012 Ok(())
1013 }
1014
1015 fn infer_name(&mut self, b: &mut ChunkBuilder, init: &Expr, name: &str) {
1018 let anon = matches!(init, Expr::Function { name: None, .. } | Expr::Class(_))
1019 && !matches!(init, Expr::Class(node) if node.name.is_some());
1020 if !anon {
1021 return;
1022 }
1023 b.emit(Op::Dup, 0);
1025 self.name_const(b, "name");
1026 self.strlit(b, name);
1027 b.emit(Op::CallBuiltin(ops::SETATTR, 3), 0);
1028 b.emit(Op::Pop, 0);
1029 }
1030
1031 fn emit_member_key(&mut self, b: &mut ChunkBuilder, m: &ClassMember) -> Result<(), String> {
1034 if m.computed {
1035 self.compile_expr(b, &m.key)?;
1036 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0);
1037 } else if let Expr::Str(s) = &m.key {
1038 self.name_const(b, s);
1039 } else {
1040 self.compile_expr(b, &m.key)?;
1041 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0);
1042 }
1043 Ok(())
1044 }
1045
1046 fn compile_yield(
1048 &mut self,
1049 b: &mut ChunkBuilder,
1050 arg: &Option<Box<Expr>>,
1051 delegate: bool,
1052 ) -> Result<(), String> {
1053 if delegate {
1054 match arg {
1056 Some(e) => self.compile_expr(b, e)?,
1057 None => {
1058 b.emit(Op::LoadUndef, 0);
1059 }
1060 }
1061 b.emit(Op::CallBuiltin(ops::GETITER, 1), 0); let start = b.current_pos();
1063 b.emit(Op::CallBuiltin(ops::FORITER, 0), 0); let jdone = b.emit(Op::JumpIfFalse(0), 0);
1065 b.emit(Op::CallBuiltin(ops::YIELD, 1), 0); b.emit(Op::Pop, 0); b.emit(Op::Jump(start), 0);
1068 let done = b.current_pos();
1069 b.patch_jump(jdone, done);
1070 b.emit(Op::Pop, 0); b.emit(Op::LoadUndef, 0); } else {
1073 match arg {
1074 Some(e) => self.compile_expr(b, e)?,
1075 None => {
1076 b.emit(Op::LoadUndef, 0);
1077 }
1078 }
1079 b.emit(Op::CallBuiltin(ops::YIELD, 1), 0);
1081 }
1082 Ok(())
1083 }
1084
1085 fn lower_params(&mut self, params: &[Param]) -> Result<(Vec<ParamSlot>, Vec<Stmt>), String> {
1088 let mut slots = Vec::new();
1089 let mut prologue: Vec<Stmt> = Vec::new();
1090 for (i, p) in params.iter().enumerate() {
1091 if p.rest {
1092 let name = match &p.pattern {
1093 Expr::Ident(n) => n.clone(),
1094 _ => return Err("SyntaxError: rest parameter must be an identifier".into()),
1095 };
1096 slots.push(ParamSlot {
1097 name,
1098 rest: true,
1099 has_default: false,
1100 });
1101 continue;
1102 }
1103 match &p.pattern {
1104 Expr::Ident(name) => {
1105 slots.push(ParamSlot {
1106 name: name.clone(),
1107 rest: false,
1108 has_default: p.default.is_some(),
1109 });
1110 if let Some(d) = &p.default {
1111 prologue.push(default_stmt(name, d));
1112 }
1113 }
1114 pattern => {
1115 let synth = format!(".param{i}");
1116 slots.push(ParamSlot {
1117 name: synth.clone(),
1118 rest: false,
1119 has_default: p.default.is_some(),
1120 });
1121 if let Some(d) = &p.default {
1122 prologue.push(default_stmt(&synth, d));
1123 }
1124 prologue.push(Stmt::from(StmtKind::Decl {
1125 kind: DeclKind::Let,
1126 decls: vec![Declarator {
1127 target: pattern.clone(),
1128 init: Some(Expr::Ident(synth)),
1129 }],
1130 }));
1131 }
1132 }
1133 }
1134 Ok((slots, prologue))
1135 }
1136
1137 fn compile_expr(&mut self, b: &mut ChunkBuilder, e: &Expr) -> Result<(), String> {
1139 match e {
1140 Expr::Undefined => {
1141 b.emit(Op::LoadUndef, 0);
1142 }
1143 Expr::Null => {
1144 b.emit(Op::CallBuiltin(ops::LOAD_NULL, 0), 0);
1145 }
1146 Expr::True => {
1147 b.emit(Op::LoadTrue, 0);
1148 }
1149 Expr::False => {
1150 b.emit(Op::LoadFalse, 0);
1151 }
1152 Expr::Number(n) => {
1153 b.emit(Op::LoadFloat(*n), 0);
1154 }
1155 Expr::BigInt(digits) => {
1156 let k = b.add_constant(Value::str(digits));
1159 b.emit(Op::LoadConst(k), 0);
1160 b.emit(Op::CallBuiltin(ops::MKBIGINT, 1), 0);
1161 }
1162 Expr::Regex(pat, flags) => {
1163 let kp = b.add_constant(Value::str(pat));
1164 b.emit(Op::LoadConst(kp), 0);
1165 let kf = b.add_constant(Value::str(flags));
1166 b.emit(Op::LoadConst(kf), 0);
1167 b.emit(Op::CallBuiltin(ops::MKREGEX, 2), 0);
1168 }
1169 Expr::Str(s) => self.strlit(b, s),
1170 Expr::Template { quasis, exprs } => self.compile_template(b, quasis, exprs)?,
1171 Expr::TaggedTemplate {
1172 tag,
1173 quasis,
1174 raws,
1175 exprs,
1176 } => self.compile_tagged_template(b, tag, quasis, raws, exprs)?,
1177 Expr::Ident(n) => self.load_local(b, n),
1178 Expr::This => {
1179 b.emit(Op::CallBuiltin(ops::THIS, 0), 0);
1180 }
1181 Expr::Array(items) => self.compile_array(b, items)?,
1182 Expr::Object(props) => self.compile_object(b, props)?,
1183 Expr::Spread(inner) => self.compile_expr(b, inner)?,
1184 Expr::Logical(op, l, r) => self.compile_logical(b, *op, l, r)?,
1185 Expr::Unary(op, e) => self.compile_unary(b, *op, e)?,
1186 Expr::Binary(op, l, r) => self.compile_binary(b, *op, l, r)?,
1187 Expr::Conditional { test, cons, alt } => {
1188 self.compile_condition(b, test)?;
1189 let jf = b.emit(Op::JumpIfFalse(0), 0);
1190 self.compile_expr(b, cons)?;
1191 let je = b.emit(Op::Jump(0), 0);
1192 let els = b.current_pos();
1193 b.patch_jump(jf, els);
1194 self.compile_expr(b, alt)?;
1195 let end = b.current_pos();
1196 b.patch_jump(je, end);
1197 }
1198 Expr::Assign { target, value } => {
1199 self.compile_expr(b, value)?;
1200 b.emit(Op::Dup, 0); self.compile_bind(b, target, false)?;
1202 }
1203 Expr::Update { op, prefix, target } => self.compile_update(b, *op, *prefix, target)?,
1204 Expr::Call {
1205 func,
1206 args,
1207 optional,
1208 } => self.compile_call(b, func, args, *optional)?,
1209 Expr::New { callee, args } => self.compile_new(b, callee, args)?,
1210 Expr::Member {
1211 object,
1212 property,
1213 optional,
1214 } => self.compile_member(b, object, property, *optional)?,
1215 Expr::Index {
1216 object,
1217 index,
1218 optional,
1219 } => self.compile_index(b, object, index, *optional)?,
1220 Expr::Function {
1221 params,
1222 body,
1223 is_arrow,
1224 name,
1225 is_generator,
1226 is_async,
1227 } => {
1228 let def_id = if *is_arrow {
1229 self.build_arrow(params, body, *is_async)?
1230 } else {
1231 let n = name.clone().unwrap_or_default();
1232 let stmts = match body {
1233 FnBody::Block(b) => b.clone(),
1234 FnBody::Expr(e) => vec![Stmt::from(StmtKind::Return(Some((**e).clone())))],
1235 };
1236 self.build_function(&n, params, &stmts, *is_generator, *is_async)?
1237 };
1238 self.emit_mkfunc(b, def_id);
1239 }
1240 Expr::Class(node) => self.compile_class(b, node)?,
1241 Expr::Super => {
1242 b.emit(Op::LoadUndef, 0);
1245 }
1246 Expr::NewTarget => {
1247 b.emit(Op::CallBuiltin(ops::NEW_TARGET, 0), 0);
1248 }
1249 Expr::Yield { arg, delegate } => self.compile_yield(b, arg, *delegate)?,
1250 Expr::Await(inner) => {
1251 self.compile_expr(b, inner)?;
1252 b.emit(Op::CallBuiltin(ops::AWAIT, 1), 0);
1253 }
1254 Expr::Sequence(items) => {
1255 for (i, it) in items.iter().enumerate() {
1256 self.compile_expr(b, it)?;
1257 if i + 1 < items.len() {
1258 b.emit(Op::Pop, 0);
1259 }
1260 }
1261 }
1262 }
1263 Ok(())
1264 }
1265
1266 fn compile_template(
1267 &mut self,
1268 b: &mut ChunkBuilder,
1269 quasis: &[String],
1270 exprs: &[Expr],
1271 ) -> Result<(), String> {
1272 let mut n = 0;
1273 for (i, q) in quasis.iter().enumerate() {
1274 let k = b.add_constant(Value::str(q));
1275 b.emit(Op::LoadConst(k), 0);
1276 n += 1;
1277 if i < exprs.len() {
1278 self.compile_expr(b, &exprs[i])?;
1279 b.emit(Op::CallBuiltin(ops::TOSTR, 1), 0);
1280 n += 1;
1281 }
1282 }
1283 b.emit(Op::CallBuiltin(ops::MKSTR, argc(n)?), 0);
1284 Ok(())
1285 }
1286
1287 fn compile_tagged_template(
1291 &mut self,
1292 b: &mut ChunkBuilder,
1293 tag: &Expr,
1294 quasis: &[String],
1295 raws: &[String],
1296 exprs: &[Expr],
1297 ) -> Result<(), String> {
1298 self.compile_expr(b, tag)?;
1299 let n = quasis.len();
1300 let m = exprs.len();
1301 b.emit(Op::LoadInt(n as i64), 0);
1302 b.emit(Op::LoadInt(m as i64), 0);
1303 for q in quasis {
1304 self.strlit(b, q); }
1306 for r in raws {
1307 self.strlit(b, r); }
1309 for e in exprs {
1310 self.compile_expr(b, e)?; }
1312 b.emit(Op::CallBuiltin(ops::TAG_TMPL, argc(3 + 2 * n + m)?), 0);
1313 Ok(())
1314 }
1315
1316 fn compile_array(&mut self, b: &mut ChunkBuilder, items: &[Expr]) -> Result<(), String> {
1317 if items.iter().any(|e| matches!(e, Expr::Spread(_))) {
1318 for it in items {
1320 match it {
1321 Expr::Spread(inner) => {
1322 b.emit(Op::LoadInt(1), 0);
1323 self.compile_expr(b, inner)?;
1324 }
1325 _ => {
1326 b.emit(Op::LoadInt(0), 0);
1327 self.compile_expr(b, it)?;
1328 }
1329 }
1330 }
1331 b.emit(Op::CallBuiltin(ops::BUILD_ARGS, argc(items.len() * 2)?), 0);
1332 } else if items.len() <= u8::MAX as usize {
1333 for it in items {
1334 self.compile_expr(b, it)?;
1335 }
1336 b.emit(Op::CallBuiltin(ops::MKARR, argc(items.len())?), 0);
1337 } else {
1338 b.emit(Op::CallBuiltin(ops::MKARR, 0), 0); for (i, it) in items.iter().enumerate() {
1344 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); }
1350 }
1351 Ok(())
1352 }
1353
1354 fn compile_object(&mut self, b: &mut ChunkBuilder, props: &[Prop]) -> Result<(), String> {
1355 let data: Vec<&Prop> = props
1358 .iter()
1359 .filter(|p| !matches!(p, Prop::Accessor { .. }))
1360 .collect();
1361 let has_spread = data.iter().any(|p| matches!(p, Prop::Spread(_)));
1362 if data.len() * 3 > u8::MAX as usize && !has_spread {
1368 b.emit(Op::CallBuiltin(ops::MKOBJ, 0), 0); for p in &data {
1370 if let Prop::KeyValue { key, value, .. } = p {
1371 b.emit(Op::Dup, 0); self.compile_expr(b, key)?;
1373 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0); self.compile_expr(b, value)?; b.emit(Op::CallBuiltin(ops::SETITEM, 3), 0); b.emit(Op::Pop, 0); }
1378 }
1379 return self.compile_object_accessors(b, props);
1380 }
1381 for p in &data {
1382 match p {
1383 Prop::KeyValue { key, value, .. } => {
1384 b.emit(Op::LoadInt(0), 0);
1385 self.compile_expr(b, key)?;
1388 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0);
1389 self.compile_expr(b, value)?;
1390 }
1391 Prop::Spread(src) => {
1392 b.emit(Op::LoadInt(1), 0);
1393 self.compile_expr(b, src)?;
1394 b.emit(Op::LoadUndef, 0);
1395 }
1396 Prop::Accessor { .. } => unreachable!(),
1397 }
1398 }
1399 b.emit(Op::CallBuiltin(ops::MKOBJ, argc(data.len() * 3)?), 0); self.compile_object_accessors(b, props)
1401 }
1402
1403 fn compile_object_accessors(
1406 &mut self,
1407 b: &mut ChunkBuilder,
1408 props: &[Prop],
1409 ) -> Result<(), String> {
1410 for p in props {
1411 if let Prop::Accessor {
1412 key,
1413 computed,
1414 is_getter,
1415 func,
1416 } = p
1417 {
1418 if *computed {
1419 self.compile_expr(b, key)?;
1420 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0);
1421 } else if let Expr::Str(s) = key {
1422 self.name_const(b, s);
1423 } else {
1424 self.compile_expr(b, key)?;
1425 b.emit(Op::CallBuiltin(ops::PROPKEY, 1), 0);
1426 }
1427 b.emit(
1428 Op::LoadInt(if *is_getter { member::GET } else { member::SET }),
1429 0,
1430 );
1431 self.compile_expr(b, func)?;
1432 b.emit(Op::CallBuiltin(ops::DEF_ACCESSOR, 4), 0);
1433 }
1434 }
1435 Ok(())
1436 }
1437
1438 fn compile_logical(
1439 &mut self,
1440 b: &mut ChunkBuilder,
1441 op: LogicalOp,
1442 l: &Expr,
1443 r: &Expr,
1444 ) -> Result<(), String> {
1445 self.compile_expr(b, l)?;
1446 b.emit(Op::Dup, 0);
1447 let test_op = match op {
1448 LogicalOp::And | LogicalOp::Or => ops::TRUTHY,
1449 LogicalOp::Nullish => ops::NULLISH,
1450 };
1451 b.emit(Op::CallBuiltin(test_op, 1), 0);
1452 let jump = match op {
1453 LogicalOp::And => b.emit(Op::JumpIfFalse(0), 0), LogicalOp::Or => b.emit(Op::JumpIfTrue(0), 0), LogicalOp::Nullish => b.emit(Op::JumpIfFalse(0), 0), };
1457 b.emit(Op::Pop, 0); self.compile_expr(b, r)?;
1459 let end = b.current_pos();
1460 b.patch_jump(jump, end);
1461 Ok(())
1462 }
1463
1464 fn compile_unary(&mut self, b: &mut ChunkBuilder, op: UnOp, e: &Expr) -> Result<(), String> {
1465 match op {
1466 UnOp::Neg => {
1467 self.compile_expr(b, e)?;
1468 b.emit(Op::Negate, 0);
1469 }
1470 UnOp::Not => {
1471 self.compile_condition(b, e)?;
1472 b.emit(Op::LogNot, 0);
1473 }
1474 UnOp::Pos => {
1475 b.emit(Op::LoadInt(unop::POS), 0);
1476 self.compile_expr(b, e)?;
1477 b.emit(Op::CallBuiltin(ops::UNARY, 2), 0);
1478 }
1479 UnOp::BitNot => {
1480 b.emit(Op::LoadInt(unop::BITNOT), 0);
1481 self.compile_expr(b, e)?;
1482 b.emit(Op::CallBuiltin(ops::UNARY, 2), 0);
1483 }
1484 UnOp::TypeOf => {
1485 if let Expr::Ident(n) = e {
1489 self.name_const(b, n);
1490 b.emit(Op::CallBuiltin(ops::TYPEOF_NAME, 1), 0);
1491 } else {
1492 self.compile_expr(b, e)?;
1493 b.emit(Op::CallBuiltin(ops::TYPEOF, 1), 0);
1494 }
1495 }
1496 UnOp::Void => {
1497 self.compile_expr(b, e)?;
1498 b.emit(Op::Pop, 0);
1499 b.emit(Op::LoadUndef, 0);
1500 }
1501 UnOp::Delete => match e {
1502 Expr::Member {
1503 object, property, ..
1504 } => {
1505 self.compile_expr(b, object)?;
1506 self.name_const(b, property);
1507 b.emit(Op::CallBuiltin(ops::DELPROP_NAME, 2), 0);
1508 }
1509 Expr::Index { object, index, .. } => {
1510 self.compile_expr(b, object)?;
1511 self.compile_expr(b, index)?;
1512 b.emit(Op::CallBuiltin(ops::DELITEM, 2), 0);
1513 }
1514 _ => {
1515 b.emit(Op::LoadTrue, 0);
1516 }
1517 },
1518 }
1519 Ok(())
1520 }
1521
1522 fn compile_binary(
1523 &mut self,
1524 b: &mut ChunkBuilder,
1525 op: BinOp,
1526 l: &Expr,
1527 r: &Expr,
1528 ) -> Result<(), String> {
1529 macro_rules! native {
1532 ($opc:expr) => {{
1533 self.compile_expr(b, l)?;
1534 self.compile_expr(b, r)?;
1535 b.emit($opc, 0);
1536 return Ok(());
1537 }};
1538 }
1539 match op {
1540 BinOp::Add => native!(Op::Add),
1541 BinOp::Sub => native!(Op::Sub),
1542 BinOp::Mul => native!(Op::Mul),
1543 BinOp::Div => {
1544 self.compile_expr(b, l)?;
1548 self.compile_expr(b, r)?;
1549 b.emit(Op::CallBuiltin(ops::DIV, 2), 0);
1550 return Ok(());
1551 }
1552 BinOp::Mod => native!(Op::Mod),
1553 BinOp::Pow => native!(Op::Pow),
1554 BinOp::Lt => native!(Op::NumLt),
1555 BinOp::Le => native!(Op::NumLe),
1556 BinOp::Gt => native!(Op::NumGt),
1557 BinOp::Ge => native!(Op::NumGe),
1558 BinOp::EqEqEq => {
1559 self.compile_expr(b, l)?;
1560 self.compile_expr(b, r)?;
1561 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
1562 }
1563 BinOp::NeEqEq => {
1564 self.compile_expr(b, l)?;
1565 self.compile_expr(b, r)?;
1566 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
1567 b.emit(Op::LogNot, 0);
1568 }
1569 BinOp::EqEq => {
1570 self.compile_expr(b, l)?;
1571 self.compile_expr(b, r)?;
1572 b.emit(Op::CallBuiltin(ops::LOOSE_EQ, 2), 0);
1573 }
1574 BinOp::NeEq => {
1575 self.compile_expr(b, l)?;
1576 self.compile_expr(b, r)?;
1577 b.emit(Op::CallBuiltin(ops::LOOSE_EQ, 2), 0);
1578 b.emit(Op::LogNot, 0);
1579 }
1580 BinOp::In => {
1581 self.compile_expr(b, l)?;
1582 self.compile_expr(b, r)?;
1583 b.emit(Op::CallBuiltin(ops::CONTAINS, 2), 0);
1584 }
1585 BinOp::InstanceOf => {
1586 self.compile_expr(b, l)?;
1587 self.compile_expr(b, r)?;
1588 b.emit(Op::CallBuiltin(ops::INSTANCEOF, 2), 0);
1589 }
1590 BinOp::BitAnd => self.emit_bitwise(b, bop::BITAND, l, r)?,
1591 BinOp::BitOr => self.emit_bitwise(b, bop::BITOR, l, r)?,
1592 BinOp::BitXor => self.emit_bitwise(b, bop::BITXOR, l, r)?,
1593 BinOp::Shl => self.emit_bitwise(b, bop::SHL, l, r)?,
1594 BinOp::Shr => self.emit_bitwise(b, bop::SHR, l, r)?,
1595 BinOp::UShr => self.emit_bitwise(b, bop::USHR, l, r)?,
1596 }
1597 Ok(())
1598 }
1599
1600 fn emit_bitwise(
1601 &mut self,
1602 b: &mut ChunkBuilder,
1603 tag: i64,
1604 l: &Expr,
1605 r: &Expr,
1606 ) -> Result<(), String> {
1607 b.emit(Op::LoadInt(tag), 0);
1608 self.compile_expr(b, l)?;
1609 self.compile_expr(b, r)?;
1610 b.emit(Op::CallBuiltin(ops::BINOP, 3), 0);
1611 Ok(())
1612 }
1613
1614 fn compile_update(
1615 &mut self,
1616 b: &mut ChunkBuilder,
1617 op: UpdateOp,
1618 prefix: bool,
1619 target: &Expr,
1620 ) -> Result<(), String> {
1621 let tag = if matches!(op, UpdateOp::Inc) { 1 } else { -1 };
1626 b.emit(Op::LoadInt(tag), 0);
1627 self.compile_expr(b, target)?; b.emit(Op::CallBuiltin(ops::NUM_STEP, 2), 0); if prefix {
1630 b.emit(Op::Swap, 0); b.emit(Op::Pop, 0); b.emit(Op::Dup, 0); self.compile_bind(b, target, false)?; } else {
1636 self.compile_bind(b, target, false)?; }
1639 Ok(())
1640 }
1641
1642 fn compile_member(
1643 &mut self,
1644 b: &mut ChunkBuilder,
1645 object: &Expr,
1646 property: &str,
1647 optional: bool,
1648 ) -> Result<(), String> {
1649 if matches!(object, Expr::Super) {
1651 self.name_const(b, property);
1652 b.emit(Op::CallBuiltin(ops::SUPER_GET, 1), 0);
1653 return Ok(());
1654 }
1655 self.compile_expr(b, object)?;
1656 if optional {
1657 let jshort = self.emit_optional_guard(b);
1658 self.name_const(b, property);
1659 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0);
1660 let end = b.current_pos();
1661 b.patch_jump(jshort, end);
1662 } else {
1663 self.name_const(b, property);
1664 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0);
1665 }
1666 Ok(())
1667 }
1668
1669 fn compile_index(
1670 &mut self,
1671 b: &mut ChunkBuilder,
1672 object: &Expr,
1673 index: &Expr,
1674 optional: bool,
1675 ) -> Result<(), String> {
1676 self.compile_expr(b, object)?;
1677 if optional {
1678 let jshort = self.emit_optional_guard(b);
1679 self.compile_expr(b, index)?;
1680 b.emit(Op::CallBuiltin(ops::GETITEM, 2), 0);
1681 let end = b.current_pos();
1682 b.patch_jump(jshort, end);
1683 } else {
1684 self.compile_expr(b, index)?;
1685 b.emit(Op::CallBuiltin(ops::GETITEM, 2), 0);
1686 }
1687 Ok(())
1688 }
1689
1690 fn emit_optional_guard(&mut self, b: &mut ChunkBuilder) -> usize {
1693 b.emit(Op::Dup, 0);
1694 b.emit(Op::CallBuiltin(ops::NULLISH, 1), 0);
1695 let jnull = b.emit(Op::JumpIfFalse(0), 0); b.emit(Op::Pop, 0);
1698 b.emit(Op::LoadUndef, 0);
1699 let jend = b.emit(Op::Jump(0), 0);
1700 let cont = b.current_pos();
1701 b.patch_jump(jnull, cont);
1702 jend
1703 }
1704
1705 fn compile_call(
1706 &mut self,
1707 b: &mut ChunkBuilder,
1708 func: &Expr,
1709 args: &[Expr],
1710 _optional: bool,
1711 ) -> Result<(), String> {
1712 let has_spread = args.iter().any(|a| matches!(a, Expr::Spread(_)));
1713 match func {
1714 Expr::Super => {
1717 for a in args {
1718 self.compile_expr(b, a)?;
1719 }
1720 b.emit(Op::CallBuiltin(ops::SUPER_CALL, argc(args.len())?), 0);
1721 return Ok(());
1722 }
1723 Expr::Member {
1726 object, property, ..
1727 } if matches!(**object, Expr::Super) => {
1728 self.name_const(b, property);
1729 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 {
1735 self.compile_expr(b, a)?;
1736 }
1737 b.emit(Op::CallBuiltin(ops::CALL_METHOD, argc(3 + args.len())?), 0);
1738 return Ok(());
1739 }
1740 Expr::Member {
1741 object,
1742 property,
1743 optional,
1744 } => {
1745 self.compile_expr(b, object)?;
1746 let jshort = if *optional {
1749 Some(self.emit_optional_guard(b))
1750 } else {
1751 None
1752 };
1753 self.name_const(b, property);
1754 if has_spread {
1755 self.compile_spread_args(b, args)?; b.emit(Op::CallBuiltin(ops::APPLY_METHOD, 3), 0);
1757 } else {
1758 for a in args {
1759 self.compile_expr(b, a)?;
1760 }
1761 b.emit(Op::CallBuiltin(ops::CALL_METHOD, argc(2 + args.len())?), 0);
1762 }
1763 if let Some(j) = jshort {
1764 let end = b.current_pos();
1765 b.patch_jump(j, end);
1766 }
1767 }
1768 Expr::Index {
1769 object,
1770 index,
1771 optional,
1772 } => {
1773 self.compile_expr(b, object)?; let jshort = if *optional {
1777 Some(self.emit_optional_guard(b))
1778 } else {
1779 None
1780 };
1781 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 {
1789 self.compile_spread_args(b, args)?;
1790 b.emit(Op::CallBuiltin(ops::APPLY, 2), 0);
1791 } else {
1792 for a in args {
1793 self.compile_expr(b, a)?;
1794 }
1795 b.emit(Op::CallBuiltin(ops::CALL_VALUE, argc(1 + args.len())?), 0);
1796 }
1797 if let Some(j) = jshort {
1798 let end = b.current_pos();
1799 b.patch_jump(j, end);
1800 }
1801 }
1802 Expr::Ident(n) => {
1803 self.name_const(b, n);
1804 if has_spread {
1805 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);
1811 } else {
1812 for a in args {
1813 self.compile_expr(b, a)?;
1814 }
1815 b.emit(Op::CallBuiltin(ops::CALL, argc(1 + args.len())?), 0);
1816 }
1817 }
1818 _ => {
1819 self.compile_expr(b, func)?;
1820 if has_spread {
1821 self.compile_spread_args(b, args)?;
1822 b.emit(Op::CallBuiltin(ops::APPLY, 2), 0);
1823 } else {
1824 for a in args {
1825 self.compile_expr(b, a)?;
1826 }
1827 b.emit(Op::CallBuiltin(ops::CALL_VALUE, argc(1 + args.len())?), 0);
1828 }
1829 }
1830 }
1831 Ok(())
1832 }
1833
1834 fn compile_spread_args(&mut self, b: &mut ChunkBuilder, args: &[Expr]) -> Result<(), String> {
1836 for a in args {
1837 match a {
1838 Expr::Spread(inner) => {
1839 b.emit(Op::LoadInt(1), 0);
1840 self.compile_expr(b, inner)?;
1841 }
1842 _ => {
1843 b.emit(Op::LoadInt(0), 0);
1844 self.compile_expr(b, a)?;
1845 }
1846 }
1847 }
1848 b.emit(Op::CallBuiltin(ops::BUILD_ARGS, argc(args.len() * 2)?), 0);
1849 Ok(())
1850 }
1851
1852 fn compile_new(
1853 &mut self,
1854 b: &mut ChunkBuilder,
1855 callee: &Expr,
1856 args: &[Expr],
1857 ) -> Result<(), String> {
1858 self.compile_expr(b, callee)?;
1859 for a in args {
1860 self.compile_expr(b, a)?;
1861 }
1862 b.emit(Op::CallBuiltin(ops::NEW, argc(1 + args.len())?), 0);
1863 Ok(())
1864 }
1865}
1866
1867fn default_stmt(name: &str, default: &Expr) -> Stmt {
1870 Stmt::from(StmtKind::If {
1871 test: Expr::Binary(
1872 BinOp::EqEqEq,
1873 Box::new(Expr::Ident(name.to_string())),
1874 Box::new(Expr::Undefined),
1875 ),
1876 cons: Box::new(Stmt::from(StmtKind::Expr(Expr::Assign {
1877 target: Box::new(Expr::Ident(name.to_string())),
1878 value: Box::new(default.clone()),
1879 }))),
1880 alt: None,
1881 })
1882}