1use crate::ast::*;
16use crate::host::{binop as bop, 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}
76
77#[derive(Default)]
78pub struct Compiler {
79 functions: Vec<(String, FuncDef)>,
80 tries: Vec<TryDef>,
81 loops: Vec<LoopCtx>,
82 tmp: usize,
83}
84
85pub fn compile(stmts: &[Stmt]) -> Result<Program, String> {
87 let mut c = Compiler::default();
88 let mut b = ChunkBuilder::new();
89 c.hoist_funcs(&mut b, stmts)?;
91 c.compile_stmts(&mut b, stmts)?;
92 Ok(Program {
93 main: b.build(),
94 functions: c.functions,
95 tries: c.tries,
96 })
97}
98
99fn argc(n: usize) -> Result<u8, String> {
100 u8::try_from(n).map_err(|_| "too many arguments (>255) for one call".to_string())
101}
102
103impl Compiler {
104 fn name_const(&self, b: &mut ChunkBuilder, s: &str) {
106 let k = b.add_constant(Value::str(s));
107 b.emit(Op::LoadConst(k), 0);
108 }
109 fn strlit(&self, b: &mut ChunkBuilder, s: &str) {
110 let k = b.add_constant(Value::str(s));
111 b.emit(Op::LoadConst(k), 0);
112 b.emit(Op::CallBuiltin(ops::MKSTR, 1), 0);
113 }
114 fn tmp_name(&mut self, tag: &str) -> String {
115 let n = format!(".{tag}{}", self.tmp);
116 self.tmp += 1;
117 n
118 }
119
120 fn emit_mkfunc(&self, b: &mut ChunkBuilder, def_id: usize) {
123 b.emit(Op::LoadInt(def_id as i64), 0);
124 b.emit(Op::CallBuiltin(ops::MKFUNC, 1), 0);
125 }
126
127 fn hoist_funcs(&mut self, b: &mut ChunkBuilder, stmts: &[Stmt]) -> Result<(), String> {
128 for s in stmts {
129 if let StmtKind::FuncDecl { name, params, body } = &s.kind {
130 let def_id = self.build_function(name, params, body)?;
131 self.emit_mkfunc(b, def_id);
132 self.declare(b, &Expr::Ident(name.clone()));
133 }
134 }
135 Ok(())
136 }
137
138 fn compile_stmts(&mut self, b: &mut ChunkBuilder, stmts: &[Stmt]) -> Result<(), String> {
139 for s in stmts {
140 self.compile_stmt(b, s)?;
141 }
142 Ok(())
143 }
144
145 fn compile_stmt(&mut self, b: &mut ChunkBuilder, s: &Stmt) -> Result<(), String> {
146 let line = s.line;
147 match &s.kind {
148 StmtKind::Expr(e) => {
149 self.compile_expr(b, e)?;
150 b.emit(Op::Pop, line);
151 }
152 StmtKind::Empty => {}
153 StmtKind::FuncDecl { .. } => {} StmtKind::Decl { decls, .. } => {
155 for d in decls {
156 match &d.init {
157 Some(v) => self.compile_expr(b, v)?,
158 None => {
159 b.emit(Op::LoadUndef, line);
160 }
161 }
162 self.compile_bind(b, &d.target, true)?;
163 }
164 }
165 StmtKind::Block(body) => {
166 self.hoist_funcs(b, body)?;
167 self.compile_stmts(b, body)?;
168 }
169 StmtKind::If { test, cons, alt } => self.compile_if(b, test, cons, alt)?,
170 StmtKind::While { test, body } => self.compile_while(b, test, body)?,
171 StmtKind::DoWhile { body, test } => self.compile_do_while(b, body, test)?,
172 StmtKind::For {
173 init,
174 test,
175 update,
176 body,
177 } => self.compile_for(b, init, test, update, body)?,
178 StmtKind::ForOf {
179 decl_kind,
180 target,
181 iter,
182 body,
183 } => self.compile_for_of(b, decl_kind.is_some(), target, iter, body)?,
184 StmtKind::ForIn {
185 decl_kind,
186 target,
187 object,
188 body,
189 } => self.compile_for_in(b, decl_kind.is_some(), target, object, body)?,
190 StmtKind::Switch { disc, cases } => self.compile_switch(b, disc, cases)?,
191 StmtKind::Return(e) => {
192 match e {
193 Some(e) => self.compile_expr(b, e)?,
194 None => {
195 b.emit(Op::LoadUndef, line);
196 }
197 }
198 b.emit(Op::CallBuiltin(ops::SIG_RETURN, 1), line);
199 }
200 StmtKind::Break(_) => {
201 let j = b.emit(Op::Jump(0), line);
202 self.loops
203 .last_mut()
204 .ok_or("SyntaxError: 'break' outside loop")?
205 .breaks
206 .push(j);
207 }
208 StmtKind::Continue(_) => {
209 let j = b.emit(Op::Jump(0), line);
210 self.loops
211 .iter_mut()
212 .rev()
213 .find(|c| c.catches_continue)
214 .ok_or("SyntaxError: 'continue' outside loop")?
215 .continues
216 .push(j);
217 }
218 StmtKind::Throw(e) => {
219 self.compile_expr(b, e)?;
220 b.emit(Op::CallBuiltin(ops::THROW, 1), line);
221 }
222 StmtKind::Try {
223 block,
224 handler,
225 finalizer,
226 } => self.compile_try(b, block, handler, finalizer)?,
227 }
228 Ok(())
229 }
230
231 fn compile_bind(&mut self, b: &mut ChunkBuilder, target: &Expr, declare: bool) -> Result<(), String> {
235 match target {
236 Expr::Ident(_) => {
237 if declare {
238 self.declare(b, target);
239 } else {
240 self.store_simple(b, target)?;
241 }
242 }
243 Expr::Member { .. } | Expr::Index { .. } => {
244 self.store_simple(b, target)?;
245 }
246 Expr::Array(items) => self.destructure_array(b, items, declare)?,
247 Expr::Object(props) => self.destructure_object(b, props, declare)?,
248 Expr::Assign { target, value } => {
249 b.emit(Op::Dup, 0);
251 b.emit(Op::LoadUndef, 0);
252 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
253 let jf = b.emit(Op::JumpIfFalse(0), 0);
254 b.emit(Op::Pop, 0); self.compile_expr(b, value)?;
256 let end = b.current_pos();
257 b.patch_jump(jf, end);
258 self.compile_bind(b, target, declare)?;
259 }
260 _ => return Err("SyntaxError: invalid assignment target".into()),
261 }
262 Ok(())
263 }
264
265 fn declare(&self, b: &mut ChunkBuilder, target: &Expr) {
267 if let Expr::Ident(n) = target {
268 self.name_const(b, n);
269 b.emit(Op::Swap, 0);
270 b.emit(Op::CallBuiltin(ops::DECLARE, 2), 0);
271 b.emit(Op::Pop, 0);
272 }
273 }
274
275 fn store_simple(&mut self, b: &mut ChunkBuilder, target: &Expr) -> Result<(), String> {
277 match target {
278 Expr::Ident(n) => {
279 self.name_const(b, n);
280 b.emit(Op::Swap, 0);
281 b.emit(Op::CallBuiltin(ops::SETLOCAL, 2), 0);
282 b.emit(Op::Pop, 0);
283 }
284 Expr::Member { object, property, .. } => {
285 self.compile_expr(b, object)?; self.name_const(b, property); b.emit(Op::Rot, 0); b.emit(Op::CallBuiltin(ops::SETATTR, 3), 0);
289 b.emit(Op::Pop, 0);
290 }
291 Expr::Index { object, index, .. } => {
292 self.compile_expr(b, object)?; self.compile_expr(b, index)?; b.emit(Op::Rot, 0); b.emit(Op::CallBuiltin(ops::SETITEM, 3), 0);
296 b.emit(Op::Pop, 0);
297 }
298 _ => return Err("SyntaxError: invalid assignment target".into()),
299 }
300 Ok(())
301 }
302
303 fn destructure_array(&mut self, b: &mut ChunkBuilder, items: &[Expr], declare: bool) -> Result<(), String> {
304 let star_idx = items
305 .iter()
306 .position(|e| matches!(e, Expr::Spread(_)))
307 .map(|i| i as i64)
308 .unwrap_or(-1);
309 b.emit(Op::LoadInt(items.len() as i64), 0);
310 b.emit(Op::LoadInt(star_idx), 0);
311 b.emit(Op::CallBuiltin(ops::UNPACK, 3), 0); for it in items {
313 match it {
314 Expr::Undefined => {
315 b.emit(Op::Pop, 0); }
317 Expr::Spread(inner) => self.compile_bind(b, inner, declare)?,
318 _ => self.compile_bind(b, it, declare)?,
319 }
320 }
321 Ok(())
322 }
323
324 fn destructure_object(&mut self, b: &mut ChunkBuilder, props: &[Prop], declare: bool) -> Result<(), String> {
325 let obj_tmp = self.tmp_name("destr");
327 self.name_const(b, &obj_tmp);
328 b.emit(Op::Swap, 0);
329 b.emit(Op::CallBuiltin(ops::DECLARE, 2), 0);
330 b.emit(Op::Pop, 0);
331 let mut named: Vec<String> = Vec::new();
333 for p in props {
334 match p {
335 Prop::KeyValue { key, value, .. } => {
336 if let Expr::Str(s) = key {
337 named.push(s.clone());
338 }
339 self.load_local(b, &obj_tmp);
341 self.compile_expr(b, key)?;
342 b.emit(Op::CallBuiltin(ops::GETITEM, 2), 0); self.compile_bind(b, value, declare)?;
344 }
345 Prop::Spread(target) => {
346 self.load_local(b, &obj_tmp);
347 for k in &named {
348 self.strlit(b, k);
349 }
350 b.emit(Op::CallBuiltin(ops::MKARR, argc(named.len())?), 0);
351 b.emit(Op::CallBuiltin(ops::OBJ_REST, 2), 0); self.compile_bind(b, target, declare)?;
353 }
354 }
355 }
356 Ok(())
357 }
358
359 fn load_local(&self, b: &mut ChunkBuilder, name: &str) {
360 self.name_const(b, name);
361 b.emit(Op::CallBuiltin(ops::GETLOCAL, 1), 0);
362 }
363
364 fn compile_condition(&mut self, b: &mut ChunkBuilder, e: &Expr) -> Result<(), String> {
366 self.compile_expr(b, e)?;
367 b.emit(Op::CallBuiltin(ops::TRUTHY, 1), 0);
368 Ok(())
369 }
370
371 fn compile_if(&mut self, b: &mut ChunkBuilder, test: &Expr, cons: &Stmt, alt: &Option<Box<Stmt>>) -> Result<(), String> {
372 self.compile_condition(b, test)?;
373 let jfalse = b.emit(Op::JumpIfFalse(0), 0);
374 self.compile_stmt(b, cons)?;
375 if let Some(alt) = alt {
376 let jend = b.emit(Op::Jump(0), 0);
377 let else_start = b.current_pos();
378 b.patch_jump(jfalse, else_start);
379 self.compile_stmt(b, alt)?;
380 let end = b.current_pos();
381 b.patch_jump(jend, end);
382 } else {
383 let end = b.current_pos();
384 b.patch_jump(jfalse, end);
385 }
386 Ok(())
387 }
388
389 fn compile_while(&mut self, b: &mut ChunkBuilder, test: &Expr, body: &Stmt) -> Result<(), String> {
390 let start = b.current_pos();
391 self.compile_condition(b, test)?;
392 let jfalse = b.emit(Op::JumpIfFalse(0), 0);
393 self.loops.push(LoopCtx {
394 breaks: Vec::new(),
395 continues: Vec::new(),
396 catches_continue: true,
397 });
398 self.compile_stmt(b, body)?;
399 b.emit(Op::Jump(start), 0);
400 let ctx = self.loops.pop().unwrap();
401 for c in ctx.continues {
402 b.patch_jump(c, start);
403 }
404 let end = b.current_pos();
405 b.patch_jump(jfalse, end);
406 for br in ctx.breaks {
407 b.patch_jump(br, end);
408 }
409 Ok(())
410 }
411
412 fn compile_do_while(&mut self, b: &mut ChunkBuilder, body: &Stmt, test: &Expr) -> Result<(), String> {
413 let start = b.current_pos();
414 self.loops.push(LoopCtx {
415 breaks: Vec::new(),
416 continues: Vec::new(),
417 catches_continue: true,
418 });
419 self.compile_stmt(b, body)?;
420 let cont_target = b.current_pos();
421 self.compile_condition(b, test)?;
422 b.emit(Op::JumpIfTrue(start), 0);
423 let ctx = self.loops.pop().unwrap();
424 for c in ctx.continues {
425 b.patch_jump(c, cont_target);
426 }
427 let end = b.current_pos();
428 for br in ctx.breaks {
429 b.patch_jump(br, end);
430 }
431 Ok(())
432 }
433
434 fn compile_for(
435 &mut self,
436 b: &mut ChunkBuilder,
437 init: &Option<Box<Stmt>>,
438 test: &Option<Expr>,
439 update: &Option<Expr>,
440 body: &Stmt,
441 ) -> Result<(), String> {
442 if let Some(init) = init {
443 self.compile_stmt(b, init)?;
444 }
445 let start = b.current_pos();
446 let jfalse = match test {
447 Some(t) => {
448 self.compile_condition(b, t)?;
449 Some(b.emit(Op::JumpIfFalse(0), 0))
450 }
451 None => None,
452 };
453 self.loops.push(LoopCtx {
454 breaks: Vec::new(),
455 continues: Vec::new(),
456 catches_continue: true,
457 });
458 self.compile_stmt(b, body)?;
459 let cont_target = b.current_pos();
460 if let Some(u) = update {
461 self.compile_expr(b, u)?;
462 b.emit(Op::Pop, 0);
463 }
464 b.emit(Op::Jump(start), 0);
465 let ctx = self.loops.pop().unwrap();
466 for c in ctx.continues {
467 b.patch_jump(c, cont_target);
468 }
469 let end = b.current_pos();
470 if let Some(jf) = jfalse {
471 b.patch_jump(jf, end);
472 }
473 for br in ctx.breaks {
474 b.patch_jump(br, end);
475 }
476 Ok(())
477 }
478
479 fn compile_for_of(&mut self, b: &mut ChunkBuilder, declare: bool, target: &Expr, iter: &Expr, body: &Stmt) -> Result<(), String> {
480 self.compile_expr(b, iter)?;
481 b.emit(Op::CallBuiltin(ops::GETITER, 1), 0); self.loop_over(b, declare, target, body)
483 }
484
485 fn compile_for_in(&mut self, b: &mut ChunkBuilder, declare: bool, target: &Expr, object: &Expr, body: &Stmt) -> Result<(), String> {
486 self.compile_expr(b, object)?;
487 b.emit(Op::CallBuiltin(ops::FORIN_KEYS, 1), 0); b.emit(Op::CallBuiltin(ops::GETITER, 1), 0); self.loop_over(b, declare, target, body)
490 }
491
492 fn loop_over(&mut self, b: &mut ChunkBuilder, declare: bool, target: &Expr, body: &Stmt) -> Result<(), String> {
494 let start = b.current_pos();
495 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 {
499 breaks: Vec::new(),
500 continues: Vec::new(),
501 catches_continue: true,
502 });
503 self.compile_stmt(b, body)?;
504 b.emit(Op::Jump(start), 0);
505 let ctx = self.loops.pop().unwrap();
506 for c in ctx.continues {
507 b.patch_jump(c, start);
508 }
509 let done = b.current_pos();
510 b.patch_jump(jdone, done);
511 b.emit(Op::Pop, 0); let jafter = b.emit(Op::Jump(0), 0);
513 let break_target = b.current_pos();
514 b.emit(Op::Pop, 0); let end = b.current_pos();
516 b.patch_jump(jafter, end);
517 for br in ctx.breaks {
518 b.patch_jump(br, break_target);
519 }
520 Ok(())
521 }
522
523 fn compile_switch(&mut self, b: &mut ChunkBuilder, disc: &Expr, cases: &[SwitchCase]) -> Result<(), String> {
524 let disc_tmp = self.tmp_name("switch");
525 self.compile_expr(b, disc)?;
526 self.name_const(b, &disc_tmp);
527 b.emit(Op::Swap, 0);
528 b.emit(Op::CallBuiltin(ops::DECLARE, 2), 0);
529 b.emit(Op::Pop, 0);
530 let mut body_jumps: Vec<Option<usize>> = Vec::new();
532 let mut default_idx: Option<usize> = None;
533 for (i, case) in cases.iter().enumerate() {
534 match &case.test {
535 Some(t) => {
536 self.load_local(b, &disc_tmp);
537 self.compile_expr(b, t)?;
538 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
539 let j = b.emit(Op::JumpIfTrue(0), 0);
540 body_jumps.push(Some(j));
541 }
542 None => {
543 default_idx = Some(i);
544 body_jumps.push(None);
545 }
546 }
547 }
548 let no_match_jump = b.emit(Op::Jump(0), 0);
550 self.loops.push(LoopCtx {
551 breaks: Vec::new(),
552 continues: Vec::new(),
553 catches_continue: false,
554 });
555 let mut body_starts: Vec<usize> = Vec::new();
556 for case in cases {
557 body_starts.push(b.current_pos());
558 self.compile_stmts(b, &case.body)?;
559 }
560 let end = b.current_pos();
561 for (i, j) in body_jumps.iter().enumerate() {
563 if let Some(j) = j {
564 b.patch_jump(*j, body_starts[i]);
565 }
566 }
567 match default_idx {
568 Some(i) => b.patch_jump(no_match_jump, body_starts[i]),
569 None => b.patch_jump(no_match_jump, end),
570 }
571 let ctx = self.loops.pop().unwrap();
572 for br in ctx.breaks {
573 b.patch_jump(br, end);
574 }
575 Ok(())
576 }
577
578 fn compile_try(
579 &mut self,
580 b: &mut ChunkBuilder,
581 block: &[Stmt],
582 handler: &Option<(Option<Expr>, Vec<Stmt>)>,
583 finalizer: &Option<Vec<Stmt>>,
584 ) -> Result<(), String> {
585 let block_chunk = self.compile_block_chunk(block)?;
586 let handler_def = match handler {
587 Some((param, body)) => {
588 let param_name = match param {
589 Some(Expr::Ident(n)) => Some(n.clone()),
590 _ => None,
591 };
592 let hbody = self.compile_block_chunk(body)?;
593 Some((param_name, hbody))
594 }
595 None => None,
596 };
597 let final_chunk = match finalizer {
598 Some(f) => Some(self.compile_block_chunk(f)?),
599 None => None,
600 };
601 let id = self.tries.len();
602 self.tries.push(TryDef {
603 block: block_chunk,
604 handler: handler_def,
605 finalizer: final_chunk,
606 });
607 b.emit(Op::LoadInt(id as i64), 0);
608 b.emit(Op::CallBuiltin(ops::TRY, 1), 0);
609 b.emit(Op::Pop, 0);
610 Ok(())
611 }
612
613 fn compile_block_chunk(&mut self, stmts: &[Stmt]) -> Result<Chunk, String> {
614 let mut cb = ChunkBuilder::new();
615 self.hoist_funcs(&mut cb, stmts)?;
616 self.compile_stmts(&mut cb, stmts)?;
617 Ok(cb.build())
618 }
619
620 fn build_function(&mut self, name: &str, params: &[Param], body: &[Stmt]) -> Result<usize, String> {
622 let (slots, prologue) = self.lower_params(params)?;
623 let mut fb = ChunkBuilder::new();
624 self.hoist_funcs(&mut fb, &prologue)?;
626 self.hoist_funcs(&mut fb, body)?;
627 self.compile_stmts(&mut fb, &prologue)?;
628 self.compile_stmts(&mut fb, body)?;
629 let def = FuncDef {
630 name: name.to_string(),
631 params: slots,
632 chunk: fb.build(),
633 is_arrow: false,
634 };
635 self.functions.push((name.to_string(), def));
636 Ok(self.functions.len() - 1)
637 }
638
639 fn build_arrow(&mut self, params: &[Param], body: &FnBody) -> Result<usize, String> {
640 let stmts = match body {
641 FnBody::Block(b) => b.clone(),
642 FnBody::Expr(e) => vec![Stmt::from(StmtKind::Return(Some((**e).clone())))],
643 };
644 let id = self.build_function("", params, &stmts)?;
645 self.functions[id].1.is_arrow = true;
647 Ok(id)
648 }
649
650 fn lower_params(&mut self, params: &[Param]) -> Result<(Vec<ParamSlot>, Vec<Stmt>), String> {
653 let mut slots = Vec::new();
654 let mut prologue: Vec<Stmt> = Vec::new();
655 for (i, p) in params.iter().enumerate() {
656 if p.rest {
657 let name = match &p.pattern {
658 Expr::Ident(n) => n.clone(),
659 _ => return Err("SyntaxError: rest parameter must be an identifier".into()),
660 };
661 slots.push(ParamSlot {
662 name,
663 rest: true,
664 has_default: false,
665 });
666 continue;
667 }
668 match &p.pattern {
669 Expr::Ident(name) => {
670 slots.push(ParamSlot {
671 name: name.clone(),
672 rest: false,
673 has_default: p.default.is_some(),
674 });
675 if let Some(d) = &p.default {
676 prologue.push(default_stmt(name, d));
677 }
678 }
679 pattern => {
680 let synth = format!(".param{i}");
681 slots.push(ParamSlot {
682 name: synth.clone(),
683 rest: false,
684 has_default: p.default.is_some(),
685 });
686 if let Some(d) = &p.default {
687 prologue.push(default_stmt(&synth, d));
688 }
689 prologue.push(Stmt::from(StmtKind::Decl {
690 kind: DeclKind::Let,
691 decls: vec![Declarator {
692 target: pattern.clone(),
693 init: Some(Expr::Ident(synth)),
694 }],
695 }));
696 }
697 }
698 }
699 Ok((slots, prologue))
700 }
701
702 fn compile_expr(&mut self, b: &mut ChunkBuilder, e: &Expr) -> Result<(), String> {
704 match e {
705 Expr::Undefined => {
706 b.emit(Op::LoadUndef, 0);
707 }
708 Expr::Null => {
709 b.emit(Op::CallBuiltin(ops::LOAD_NULL, 0), 0);
710 }
711 Expr::True => {
712 b.emit(Op::LoadTrue, 0);
713 }
714 Expr::False => {
715 b.emit(Op::LoadFalse, 0);
716 }
717 Expr::Number(n) => {
718 b.emit(Op::LoadFloat(*n), 0);
719 }
720 Expr::Str(s) => self.strlit(b, s),
721 Expr::Template { quasis, exprs } => self.compile_template(b, quasis, exprs)?,
722 Expr::Ident(n) => self.load_local(b, n),
723 Expr::This => {
724 b.emit(Op::CallBuiltin(ops::THIS, 0), 0);
725 }
726 Expr::Array(items) => self.compile_array(b, items)?,
727 Expr::Object(props) => self.compile_object(b, props)?,
728 Expr::Spread(inner) => self.compile_expr(b, inner)?,
729 Expr::Logical(op, l, r) => self.compile_logical(b, *op, l, r)?,
730 Expr::Unary(op, e) => self.compile_unary(b, *op, e)?,
731 Expr::Binary(op, l, r) => self.compile_binary(b, *op, l, r)?,
732 Expr::Conditional { test, cons, alt } => {
733 self.compile_condition(b, test)?;
734 let jf = b.emit(Op::JumpIfFalse(0), 0);
735 self.compile_expr(b, cons)?;
736 let je = b.emit(Op::Jump(0), 0);
737 let els = b.current_pos();
738 b.patch_jump(jf, els);
739 self.compile_expr(b, alt)?;
740 let end = b.current_pos();
741 b.patch_jump(je, end);
742 }
743 Expr::Assign { target, value } => {
744 self.compile_expr(b, value)?;
745 b.emit(Op::Dup, 0); self.compile_bind(b, target, false)?;
747 }
748 Expr::Update { op, prefix, target } => self.compile_update(b, *op, *prefix, target)?,
749 Expr::Call { func, args, optional } => self.compile_call(b, func, args, *optional)?,
750 Expr::New { callee, args } => self.compile_new(b, callee, args)?,
751 Expr::Member { object, property, optional } => {
752 self.compile_member(b, object, property, *optional)?
753 }
754 Expr::Index { object, index, optional } => {
755 self.compile_index(b, object, index, *optional)?
756 }
757 Expr::Function { params, body, is_arrow, name } => {
758 let def_id = if *is_arrow {
759 self.build_arrow(params, body)?
760 } else {
761 let n = name.clone().unwrap_or_default();
762 let stmts = match body {
763 FnBody::Block(b) => b.clone(),
764 FnBody::Expr(e) => vec![Stmt::from(StmtKind::Return(Some((**e).clone())))],
765 };
766 self.build_function(&n, params, &stmts)?
767 };
768 self.emit_mkfunc(b, def_id);
769 }
770 Expr::Sequence(items) => {
771 for (i, it) in items.iter().enumerate() {
772 self.compile_expr(b, it)?;
773 if i + 1 < items.len() {
774 b.emit(Op::Pop, 0);
775 }
776 }
777 }
778 }
779 Ok(())
780 }
781
782 fn compile_template(&mut self, b: &mut ChunkBuilder, quasis: &[String], exprs: &[Expr]) -> Result<(), String> {
783 let mut n = 0;
784 for (i, q) in quasis.iter().enumerate() {
785 let k = b.add_constant(Value::str(q));
786 b.emit(Op::LoadConst(k), 0);
787 n += 1;
788 if i < exprs.len() {
789 self.compile_expr(b, &exprs[i])?;
790 b.emit(Op::CallBuiltin(ops::TOSTR, 1), 0);
791 n += 1;
792 }
793 }
794 b.emit(Op::CallBuiltin(ops::MKSTR, argc(n)?), 0);
795 Ok(())
796 }
797
798 fn compile_array(&mut self, b: &mut ChunkBuilder, items: &[Expr]) -> Result<(), String> {
799 if items.iter().any(|e| matches!(e, Expr::Spread(_))) {
800 for it in items {
802 match it {
803 Expr::Spread(inner) => {
804 b.emit(Op::LoadInt(1), 0);
805 self.compile_expr(b, inner)?;
806 }
807 _ => {
808 b.emit(Op::LoadInt(0), 0);
809 self.compile_expr(b, it)?;
810 }
811 }
812 }
813 b.emit(Op::CallBuiltin(ops::BUILD_ARGS, argc(items.len() * 2)?), 0);
814 } else {
815 for it in items {
816 self.compile_expr(b, it)?;
817 }
818 b.emit(Op::CallBuiltin(ops::MKARR, argc(items.len())?), 0);
819 }
820 Ok(())
821 }
822
823 fn compile_object(&mut self, b: &mut ChunkBuilder, props: &[Prop]) -> Result<(), String> {
824 for p in props {
826 match p {
827 Prop::KeyValue { key, value, .. } => {
828 b.emit(Op::LoadInt(0), 0);
829 self.compile_expr(b, key)?;
831 b.emit(Op::CallBuiltin(ops::TOSTR, 1), 0);
832 self.compile_expr(b, value)?;
833 }
834 Prop::Spread(src) => {
835 b.emit(Op::LoadInt(1), 0);
836 self.compile_expr(b, src)?;
837 b.emit(Op::LoadUndef, 0);
838 }
839 }
840 }
841 b.emit(Op::CallBuiltin(ops::MKOBJ, argc(props.len() * 3)?), 0);
842 Ok(())
843 }
844
845 fn compile_logical(&mut self, b: &mut ChunkBuilder, op: LogicalOp, l: &Expr, r: &Expr) -> Result<(), String> {
846 self.compile_expr(b, l)?;
847 b.emit(Op::Dup, 0);
848 let test_op = match op {
849 LogicalOp::And | LogicalOp::Or => ops::TRUTHY,
850 LogicalOp::Nullish => ops::NULLISH,
851 };
852 b.emit(Op::CallBuiltin(test_op, 1), 0);
853 let jump = match op {
854 LogicalOp::And => b.emit(Op::JumpIfFalse(0), 0), LogicalOp::Or => b.emit(Op::JumpIfTrue(0), 0), LogicalOp::Nullish => b.emit(Op::JumpIfFalse(0), 0), };
858 b.emit(Op::Pop, 0); self.compile_expr(b, r)?;
860 let end = b.current_pos();
861 b.patch_jump(jump, end);
862 Ok(())
863 }
864
865 fn compile_unary(&mut self, b: &mut ChunkBuilder, op: UnOp, e: &Expr) -> Result<(), String> {
866 match op {
867 UnOp::Neg => {
868 self.compile_expr(b, e)?;
869 b.emit(Op::Negate, 0);
870 }
871 UnOp::Not => {
872 self.compile_condition(b, e)?;
873 b.emit(Op::LogNot, 0);
874 }
875 UnOp::Pos => {
876 b.emit(Op::LoadInt(unop::POS), 0);
877 self.compile_expr(b, e)?;
878 b.emit(Op::CallBuiltin(ops::UNARY, 2), 0);
879 }
880 UnOp::BitNot => {
881 b.emit(Op::LoadInt(unop::BITNOT), 0);
882 self.compile_expr(b, e)?;
883 b.emit(Op::CallBuiltin(ops::UNARY, 2), 0);
884 }
885 UnOp::TypeOf => {
886 self.compile_expr(b, e)?;
887 b.emit(Op::CallBuiltin(ops::TYPEOF, 1), 0);
888 }
889 UnOp::Void => {
890 self.compile_expr(b, e)?;
891 b.emit(Op::Pop, 0);
892 b.emit(Op::LoadUndef, 0);
893 }
894 UnOp::Delete => match e {
895 Expr::Member { object, property, .. } => {
896 self.compile_expr(b, object)?;
897 self.name_const(b, property);
898 b.emit(Op::CallBuiltin(ops::DELPROP_NAME, 2), 0);
899 }
900 Expr::Index { object, index, .. } => {
901 self.compile_expr(b, object)?;
902 self.compile_expr(b, index)?;
903 b.emit(Op::CallBuiltin(ops::DELITEM, 2), 0);
904 }
905 _ => {
906 b.emit(Op::LoadTrue, 0);
907 }
908 },
909 }
910 Ok(())
911 }
912
913 fn compile_binary(&mut self, b: &mut ChunkBuilder, op: BinOp, l: &Expr, r: &Expr) -> Result<(), String> {
914 macro_rules! native {
917 ($opc:expr) => {{
918 self.compile_expr(b, l)?;
919 self.compile_expr(b, r)?;
920 b.emit($opc, 0);
921 return Ok(());
922 }};
923 }
924 match op {
925 BinOp::Add => native!(Op::Add),
926 BinOp::Sub => native!(Op::Sub),
927 BinOp::Mul => native!(Op::Mul),
928 BinOp::Div => {
929 self.compile_expr(b, l)?;
933 self.compile_expr(b, r)?;
934 b.emit(Op::CallBuiltin(ops::DIV, 2), 0);
935 return Ok(());
936 }
937 BinOp::Mod => native!(Op::Mod),
938 BinOp::Pow => native!(Op::Pow),
939 BinOp::Lt => native!(Op::NumLt),
940 BinOp::Le => native!(Op::NumLe),
941 BinOp::Gt => native!(Op::NumGt),
942 BinOp::Ge => native!(Op::NumGe),
943 BinOp::EqEqEq => {
944 self.compile_expr(b, l)?;
945 self.compile_expr(b, r)?;
946 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
947 }
948 BinOp::NeEqEq => {
949 self.compile_expr(b, l)?;
950 self.compile_expr(b, r)?;
951 b.emit(Op::CallBuiltin(ops::STRICT_EQ, 2), 0);
952 b.emit(Op::LogNot, 0);
953 }
954 BinOp::EqEq => {
955 self.compile_expr(b, l)?;
956 self.compile_expr(b, r)?;
957 b.emit(Op::CallBuiltin(ops::LOOSE_EQ, 2), 0);
958 }
959 BinOp::NeEq => {
960 self.compile_expr(b, l)?;
961 self.compile_expr(b, r)?;
962 b.emit(Op::CallBuiltin(ops::LOOSE_EQ, 2), 0);
963 b.emit(Op::LogNot, 0);
964 }
965 BinOp::In => {
966 self.compile_expr(b, l)?;
967 self.compile_expr(b, r)?;
968 b.emit(Op::CallBuiltin(ops::CONTAINS, 2), 0);
969 }
970 BinOp::InstanceOf => {
971 self.compile_expr(b, l)?;
972 self.compile_expr(b, r)?;
973 b.emit(Op::CallBuiltin(ops::INSTANCEOF, 2), 0);
974 }
975 BinOp::BitAnd => self.emit_bitwise(b, bop::BITAND, l, r)?,
976 BinOp::BitOr => self.emit_bitwise(b, bop::BITOR, l, r)?,
977 BinOp::BitXor => self.emit_bitwise(b, bop::BITXOR, l, r)?,
978 BinOp::Shl => self.emit_bitwise(b, bop::SHL, l, r)?,
979 BinOp::Shr => self.emit_bitwise(b, bop::SHR, l, r)?,
980 BinOp::UShr => self.emit_bitwise(b, bop::USHR, l, r)?,
981 }
982 Ok(())
983 }
984
985 fn emit_bitwise(&mut self, b: &mut ChunkBuilder, tag: i64, l: &Expr, r: &Expr) -> Result<(), String> {
986 b.emit(Op::LoadInt(tag), 0);
987 self.compile_expr(b, l)?;
988 self.compile_expr(b, r)?;
989 b.emit(Op::CallBuiltin(ops::BINOP, 3), 0);
990 Ok(())
991 }
992
993 fn compile_update(&mut self, b: &mut ChunkBuilder, op: UpdateOp, prefix: bool, target: &Expr) -> Result<(), String> {
994 let one = Expr::Number(1.0);
996 let bin = if matches!(op, UpdateOp::Inc) { BinOp::Add } else { BinOp::Sub };
997 if prefix {
998 let newv = Expr::Binary(bin, Box::new(target.clone()), Box::new(one));
1000 self.compile_expr(b, &newv)?;
1001 b.emit(Op::Dup, 0);
1002 self.compile_bind(b, target, false)?;
1003 } else {
1004 b.emit(Op::LoadInt(unop::POS), 0);
1007 self.compile_expr(b, target)?;
1008 b.emit(Op::CallBuiltin(ops::UNARY, 2), 0); b.emit(Op::Dup, 0); b.emit(Op::LoadFloat(1.0), 0);
1011 match op {
1012 UpdateOp::Inc => b.emit(Op::Add, 0),
1013 UpdateOp::Dec => b.emit(Op::Sub, 0),
1014 };
1015 self.compile_bind(b, target, false)?; }
1017 Ok(())
1018 }
1019
1020 fn compile_member(&mut self, b: &mut ChunkBuilder, object: &Expr, property: &str, optional: bool) -> Result<(), String> {
1021 self.compile_expr(b, object)?;
1022 if optional {
1023 let jshort = self.emit_optional_guard(b);
1024 self.name_const(b, property);
1025 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0);
1026 let end = b.current_pos();
1027 b.patch_jump(jshort, end);
1028 } else {
1029 self.name_const(b, property);
1030 b.emit(Op::CallBuiltin(ops::GETATTR, 2), 0);
1031 }
1032 Ok(())
1033 }
1034
1035 fn compile_index(&mut self, b: &mut ChunkBuilder, object: &Expr, index: &Expr, optional: bool) -> Result<(), String> {
1036 self.compile_expr(b, object)?;
1037 if optional {
1038 let jshort = self.emit_optional_guard(b);
1039 self.compile_expr(b, index)?;
1040 b.emit(Op::CallBuiltin(ops::GETITEM, 2), 0);
1041 let end = b.current_pos();
1042 b.patch_jump(jshort, end);
1043 } else {
1044 self.compile_expr(b, index)?;
1045 b.emit(Op::CallBuiltin(ops::GETITEM, 2), 0);
1046 }
1047 Ok(())
1048 }
1049
1050 fn emit_optional_guard(&mut self, b: &mut ChunkBuilder) -> usize {
1053 b.emit(Op::Dup, 0);
1054 b.emit(Op::CallBuiltin(ops::NULLISH, 1), 0);
1055 let jnull = b.emit(Op::JumpIfFalse(0), 0); b.emit(Op::Pop, 0);
1058 b.emit(Op::LoadUndef, 0);
1059 let jend = b.emit(Op::Jump(0), 0);
1060 let cont = b.current_pos();
1061 b.patch_jump(jnull, cont);
1062 jend
1063 }
1064
1065 fn compile_call(&mut self, b: &mut ChunkBuilder, func: &Expr, args: &[Expr], _optional: bool) -> Result<(), String> {
1066 let has_spread = args.iter().any(|a| matches!(a, Expr::Spread(_)));
1067 match func {
1068 Expr::Member { object, property, .. } => {
1069 self.compile_expr(b, object)?;
1070 self.name_const(b, property);
1071 if has_spread {
1072 self.compile_spread_args(b, args)?; b.emit(Op::CallBuiltin(ops::APPLY_METHOD, 3), 0);
1074 } else {
1075 for a in args {
1076 self.compile_expr(b, a)?;
1077 }
1078 b.emit(Op::CallBuiltin(ops::CALL_METHOD, argc(2 + args.len())?), 0);
1079 }
1080 }
1081 Expr::Index { object, index, .. } => {
1082 self.compile_expr(b, object)?; 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 {
1092 self.compile_spread_args(b, args)?;
1093 b.emit(Op::CallBuiltin(ops::APPLY, 2), 0);
1094 } else {
1095 for a in args {
1096 self.compile_expr(b, a)?;
1097 }
1098 b.emit(Op::CallBuiltin(ops::CALL_VALUE, argc(1 + args.len())?), 0);
1099 }
1100 }
1101 Expr::Ident(n) => {
1102 self.name_const(b, n);
1103 if has_spread {
1104 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);
1110 } else {
1111 for a in args {
1112 self.compile_expr(b, a)?;
1113 }
1114 b.emit(Op::CallBuiltin(ops::CALL, argc(1 + args.len())?), 0);
1115 }
1116 }
1117 _ => {
1118 self.compile_expr(b, func)?;
1119 if has_spread {
1120 self.compile_spread_args(b, args)?;
1121 b.emit(Op::CallBuiltin(ops::APPLY, 2), 0);
1122 } else {
1123 for a in args {
1124 self.compile_expr(b, a)?;
1125 }
1126 b.emit(Op::CallBuiltin(ops::CALL_VALUE, argc(1 + args.len())?), 0);
1127 }
1128 }
1129 }
1130 Ok(())
1131 }
1132
1133 fn compile_spread_args(&mut self, b: &mut ChunkBuilder, args: &[Expr]) -> Result<(), String> {
1135 for a in args {
1136 match a {
1137 Expr::Spread(inner) => {
1138 b.emit(Op::LoadInt(1), 0);
1139 self.compile_expr(b, inner)?;
1140 }
1141 _ => {
1142 b.emit(Op::LoadInt(0), 0);
1143 self.compile_expr(b, a)?;
1144 }
1145 }
1146 }
1147 b.emit(Op::CallBuiltin(ops::BUILD_ARGS, argc(args.len() * 2)?), 0);
1148 Ok(())
1149 }
1150
1151 fn compile_new(&mut self, b: &mut ChunkBuilder, callee: &Expr, args: &[Expr]) -> Result<(), String> {
1152 self.compile_expr(b, callee)?;
1153 for a in args {
1154 self.compile_expr(b, a)?;
1155 }
1156 b.emit(Op::CallBuiltin(ops::NEW, argc(1 + args.len())?), 0);
1157 Ok(())
1158 }
1159}
1160
1161fn default_stmt(name: &str, default: &Expr) -> Stmt {
1164 Stmt::from(StmtKind::If {
1165 test: Expr::Binary(
1166 BinOp::EqEqEq,
1167 Box::new(Expr::Ident(name.to_string())),
1168 Box::new(Expr::Undefined),
1169 ),
1170 cons: Box::new(Stmt::from(StmtKind::Expr(Expr::Assign {
1171 target: Box::new(Expr::Ident(name.to_string())),
1172 value: Box::new(default.clone()),
1173 }))),
1174 alt: None,
1175 })
1176}