1use std::cell::RefCell;
2use std::collections::BTreeMap;
3use std::mem;
4
5use crate::compiler::ast;
6use crate::compiler::instructions::{
7 Instruction, Instructions, LocalId, LOOP_FLAG_RECURSIVE, LOOP_FLAG_WITH_LOOP_VAR, MAX_LOCALS,
8};
9use crate::compiler::tokens::Span;
10use crate::output::CaptureMode;
11use crate::value::ops::neg;
12use crate::value::{Kwargs, UndefinedType, Value, ValueMap, ValueRepr};
13
14#[cfg(test)]
15use similar_asserts::assert_eq;
16
17#[cfg(feature = "macros")]
18type Caller<'source> = ast::Spanned<ast::Macro<'source>>;
19
20#[cfg(not(feature = "macros"))]
21type Caller<'source> = std::marker::PhantomData<&'source ()>;
22
23fn get_local_id<'source>(ids: &mut BTreeMap<&'source str, LocalId>, name: &'source str) -> LocalId {
25 if let Some(id) = ids.get(name) {
26 *id
27 } else if ids.len() >= MAX_LOCALS {
28 !0
29 } else {
30 let next_id = ids.len() as LocalId;
31 ids.insert(name, next_id);
32 next_id
33 }
34}
35
36enum PendingBlock {
39 Branch {
40 jump_instr: u32,
41 },
42 Loop {
43 iter_instr: u32,
44 jump_instrs: Vec<u32>,
45 },
46 ScBool {
47 jump_instrs: Vec<u32>,
48 },
49}
50
51const CODEGEN_POOL_MAX_ITEMS: usize = 64;
52const CODEGEN_POOLED_MAX_CAPACITY: usize = 64;
53
54thread_local! {
55 static PENDING_BLOCK_POOL: RefCell<Vec<Vec<PendingBlock>>> = const { RefCell::new(Vec::new()) };
56 static SPAN_STACK_POOL: RefCell<Vec<Vec<Span>>> = const { RefCell::new(Vec::new()) };
57}
58
59#[inline(always)]
60fn take_pending_block_buffer() -> Vec<PendingBlock> {
61 let mut buf = PENDING_BLOCK_POOL
62 .with(|pool| pool.borrow_mut().pop())
63 .unwrap_or_else(|| Vec::with_capacity(8));
64 buf.clear();
65 buf
66}
67
68#[inline(always)]
69fn take_span_stack_buffer() -> Vec<Span> {
70 let mut buf = SPAN_STACK_POOL
71 .with(|pool| pool.borrow_mut().pop())
72 .unwrap_or_else(|| Vec::with_capacity(8));
73 buf.clear();
74 buf
75}
76
77#[inline(always)]
78fn recycle_pending_block_buffer(mut buf: Vec<PendingBlock>) {
79 if buf.capacity() <= CODEGEN_POOLED_MAX_CAPACITY {
80 buf.clear();
81 PENDING_BLOCK_POOL.with(|pool| {
82 let mut pool = pool.borrow_mut();
83 if pool.len() < CODEGEN_POOL_MAX_ITEMS {
84 pool.push(buf);
85 }
86 });
87 }
88}
89
90#[inline(always)]
91fn recycle_span_stack_buffer(mut buf: Vec<Span>) {
92 if buf.capacity() <= CODEGEN_POOLED_MAX_CAPACITY {
93 buf.clear();
94 SPAN_STACK_POOL.with(|pool| {
95 let mut pool = pool.borrow_mut();
96 if pool.len() < CODEGEN_POOL_MAX_ITEMS {
97 pool.push(buf);
98 }
99 });
100 }
101}
102
103pub struct CodeGenerator<'source> {
105 instructions: Instructions<'source>,
106 blocks: BTreeMap<&'source str, Instructions<'source>>,
107 pending_block: Vec<PendingBlock>,
108 current_line: u16,
109 span_stack: Vec<Span>,
110 filter_local_ids: BTreeMap<&'source str, LocalId>,
111 test_local_ids: BTreeMap<&'source str, LocalId>,
112 raw_template_bytes: usize,
113}
114
115impl<'source> CodeGenerator<'source> {
116 pub fn new(file: &'source str, source: &'source str) -> CodeGenerator<'source> {
118 CodeGenerator {
119 instructions: Instructions::new(file, source),
120 blocks: BTreeMap::new(),
121 pending_block: take_pending_block_buffer(),
122 current_line: 0,
123 span_stack: take_span_stack_buffer(),
124 filter_local_ids: BTreeMap::new(),
125 test_local_ids: BTreeMap::new(),
126 raw_template_bytes: 0,
127 }
128 }
129
130 pub fn set_line(&mut self, lineno: u16) {
132 self.current_line = lineno;
133 }
134
135 pub fn set_line_from_span(&mut self, span: Span) {
137 self.set_line(span.start_line);
138 }
139
140 pub fn push_span(&mut self, span: Span) {
142 self.span_stack.push(span);
143 self.set_line_from_span(span);
144 }
145
146 pub fn pop_span(&mut self) {
148 self.span_stack.pop();
149 }
150
151 pub fn add(&mut self, instr: Instruction<'source>) -> u32 {
153 if let Some(span) = self.span_stack.last() {
154 if span.start_line == self.current_line {
155 return self.instructions.add_with_span(instr, *span);
156 }
157 }
158 self.instructions.add_with_line(instr, self.current_line)
159 }
160
161 pub fn add_with_span(&mut self, instr: Instruction<'source>, span: Span) -> u32 {
163 self.instructions.add_with_span(instr, span)
164 }
165
166 pub fn next_instruction(&self) -> u32 {
168 self.instructions.len() as u32
169 }
170
171 #[cfg(feature = "multi_template")]
173 fn new_subgenerator(&self) -> CodeGenerator<'source> {
174 let mut sub = CodeGenerator::new(self.instructions.name(), self.instructions.source());
175 sub.current_line = self.current_line;
176 sub.span_stack = self.span_stack.last().copied().into_iter().collect();
177 sub
178 }
179
180 #[cfg(feature = "multi_template")]
182 fn finish_subgenerator(&mut self, sub: CodeGenerator<'source>) -> Instructions<'source> {
183 self.current_line = sub.current_line;
184 let (instructions, blocks) = sub.finish();
185 self.blocks.extend(blocks);
186 instructions
187 }
188
189 pub fn start_for_loop(&mut self, with_loop_var: bool, recursive: bool) {
191 let mut flags = 0;
192 if with_loop_var {
193 flags |= LOOP_FLAG_WITH_LOOP_VAR;
194 }
195 if recursive {
196 flags |= LOOP_FLAG_RECURSIVE;
197 }
198 self.add(Instruction::PushLoop(flags));
199 let instr = self.add(Instruction::Iterate(!0));
200 self.pending_block.push(PendingBlock::Loop {
201 iter_instr: instr,
202 jump_instrs: Vec::new(),
203 });
204 }
205
206 pub fn end_for_loop(&mut self, push_did_not_iterate: bool) {
208 if let Some(PendingBlock::Loop {
209 iter_instr,
210 jump_instrs,
211 }) = self.pending_block.pop()
212 {
213 self.add(Instruction::Jump(iter_instr));
214 let loop_end = self.next_instruction();
215 if push_did_not_iterate {
216 self.add(Instruction::PushDidNotIterate);
217 };
218 self.add(Instruction::PopLoopFrame);
219 for instr in jump_instrs.into_iter().chain(Some(iter_instr)) {
220 match self.instructions.get_mut(instr) {
221 Some(&mut Instruction::Iterate(ref mut jump_target))
222 | Some(&mut Instruction::Jump(ref mut jump_target)) => {
223 *jump_target = loop_end;
224 }
225 _ => unreachable!(),
226 }
227 }
228 } else {
229 unreachable!()
230 }
231 }
232
233 pub fn start_if(&mut self) {
235 let jump_instr = self.add(Instruction::JumpIfFalse(!0));
236 self.pending_block.push(PendingBlock::Branch { jump_instr });
237 }
238
239 pub fn start_else(&mut self) {
241 let jump_instr = self.add(Instruction::Jump(!0));
242 self.end_condition(jump_instr + 1);
243 self.pending_block.push(PendingBlock::Branch { jump_instr });
244 }
245
246 pub fn end_if(&mut self) {
248 self.end_condition(self.next_instruction());
249 }
250
251 pub fn start_sc_bool(&mut self) {
253 self.pending_block.push(PendingBlock::ScBool {
254 jump_instrs: Vec::new(),
255 });
256 }
257
258 pub fn sc_bool(&mut self, and: bool) {
260 if let Some(&mut PendingBlock::ScBool {
261 ref mut jump_instrs,
262 }) = self.pending_block.last_mut()
263 {
264 jump_instrs.push(self.instructions.add(if and {
265 Instruction::JumpIfFalseOrPop(!0)
266 } else {
267 Instruction::JumpIfTrueOrPop(!0)
268 }));
269 } else {
270 unreachable!();
271 }
272 }
273
274 pub fn end_sc_bool(&mut self) {
276 let end = self.next_instruction();
277 if let Some(PendingBlock::ScBool { jump_instrs }) = self.pending_block.pop() {
278 for instr in jump_instrs {
279 match self.instructions.get_mut(instr) {
280 Some(&mut Instruction::JumpIfFalseOrPop(ref mut target))
281 | Some(&mut Instruction::JumpIfTrueOrPop(ref mut target)) => {
282 *target = end;
283 }
284 _ => unreachable!(),
285 }
286 }
287 }
288 }
289
290 fn end_condition(&mut self, new_jump_instr: u32) {
291 match self.pending_block.pop() {
292 Some(PendingBlock::Branch { jump_instr }) => {
293 match self.instructions.get_mut(jump_instr) {
294 Some(&mut Instruction::JumpIfFalse(ref mut target))
295 | Some(&mut Instruction::Jump(ref mut target)) => {
296 *target = new_jump_instr;
297 }
298 _ => {}
299 }
300 }
301 _ => unreachable!(),
302 }
303 }
304
305 pub fn compile_stmt(&mut self, stmt: &ast::Stmt<'source>) {
307 match stmt {
308 ast::Stmt::Template(t) => {
309 self.set_line_from_span(t.span());
310 for node in &t.children {
311 self.compile_stmt(node);
312 }
313 }
314 ast::Stmt::EmitExpr(expr) => {
315 self.compile_emit_expr(expr);
316 }
317 ast::Stmt::EmitRaw(raw) => {
318 self.set_line_from_span(raw.span());
319 self.add(Instruction::EmitRaw(raw.raw));
320 self.raw_template_bytes += raw.raw.len();
321 }
322 ast::Stmt::ForLoop(for_loop) => {
323 self.compile_for_loop(for_loop);
324 }
325 ast::Stmt::IfCond(if_cond) => {
326 self.compile_if_stmt(if_cond);
327 }
328 ast::Stmt::WithBlock(with_block) => {
329 self.set_line_from_span(with_block.span());
330 self.add(Instruction::PushWith);
331 for (target, expr) in &with_block.assignments {
332 self.compile_expr(expr);
333 self.compile_assignment(target);
334 }
335 for node in &with_block.body {
336 self.compile_stmt(node);
337 }
338 self.add(Instruction::PopFrame);
339 }
340 ast::Stmt::Set(set) => {
341 self.set_line_from_span(set.span());
342 self.compile_expr(&set.expr);
343 self.compile_assignment(&set.target);
344 }
345 ast::Stmt::SetBlock(set_block) => {
346 self.set_line_from_span(set_block.span());
347 self.add(Instruction::BeginCapture(CaptureMode::Capture));
348 for node in &set_block.body {
349 self.compile_stmt(node);
350 }
351 self.add(Instruction::EndCapture);
352 if let Some(ref filter) = set_block.filter {
353 self.compile_expr(filter);
354 }
355 self.compile_assignment(&set_block.target);
356 }
357 ast::Stmt::AutoEscape(auto_escape) => {
358 self.set_line_from_span(auto_escape.span());
359 self.compile_expr(&auto_escape.enabled);
360 self.add(Instruction::PushAutoEscape);
361 for node in &auto_escape.body {
362 self.compile_stmt(node);
363 }
364 self.add(Instruction::PopAutoEscape);
365 }
366 ast::Stmt::FilterBlock(filter_block) => {
367 self.set_line_from_span(filter_block.span());
368 self.add(Instruction::BeginCapture(CaptureMode::Capture));
369 for node in &filter_block.body {
370 self.compile_stmt(node);
371 }
372 self.add(Instruction::EndCapture);
373 self.compile_expr(&filter_block.filter);
374 self.add(Instruction::Emit);
375 }
376 #[cfg(feature = "multi_template")]
377 ast::Stmt::Block(block) => {
378 self.compile_block(block);
379 }
380 #[cfg(feature = "multi_template")]
381 ast::Stmt::Import(import) => {
382 self.add(Instruction::BeginCapture(CaptureMode::Capture));
383 self.add(Instruction::PushWith);
384 self.compile_expr(&import.expr);
385 self.add_with_span(Instruction::Include(false), import.span());
386 self.add(Instruction::EndCapture);
387 self.add(Instruction::ExportLocals);
388 self.add(Instruction::PopFrame);
389 self.compile_assignment(&import.name);
390 }
391 #[cfg(feature = "multi_template")]
392 ast::Stmt::FromImport(from_import) => {
393 self.add(Instruction::BeginCapture(CaptureMode::Discard));
394 self.add(Instruction::PushWith);
395 self.compile_expr(&from_import.expr);
396 self.add_with_span(Instruction::Include(false), from_import.span());
397 for (name, _) in &from_import.names {
398 self.compile_expr(name);
399 }
400 self.add(Instruction::PopFrame);
401 for (name, alias) in from_import.names.iter().rev() {
402 self.compile_assignment(alias.as_ref().unwrap_or(name));
403 }
404 self.add(Instruction::EndCapture);
405 }
406 #[cfg(feature = "multi_template")]
407 ast::Stmt::Extends(extends) => {
408 self.set_line_from_span(extends.span());
409 self.compile_expr(&extends.name);
410 self.add_with_span(Instruction::LoadBlocks, extends.span());
411 }
412 #[cfg(feature = "multi_template")]
413 ast::Stmt::Include(include) => {
414 self.set_line_from_span(include.span());
415 self.compile_expr(&include.name);
416 self.add_with_span(Instruction::Include(include.ignore_missing), include.span());
417 }
418 #[cfg(feature = "macros")]
419 ast::Stmt::Macro(macro_decl) => {
420 self.compile_macro(macro_decl);
421 }
422 #[cfg(feature = "macros")]
423 ast::Stmt::CallBlock(call_block) => {
424 self.compile_call_block(call_block);
425 }
426 #[cfg(feature = "loop_controls")]
427 ast::Stmt::Continue(cont) => {
428 self.set_line_from_span(cont.span());
429 for pending_block in self.pending_block.iter().rev() {
430 if let PendingBlock::Loop { iter_instr, .. } = pending_block {
431 self.add(Instruction::Jump(*iter_instr));
432 break;
433 }
434 }
435 }
436 #[cfg(feature = "loop_controls")]
437 ast::Stmt::Break(brk) => {
438 self.set_line_from_span(brk.span());
439 let instr = self.add(Instruction::Jump(0));
440 for pending_block in self.pending_block.iter_mut().rev() {
441 if let &mut PendingBlock::Loop {
442 ref mut jump_instrs,
443 ..
444 } = pending_block
445 {
446 jump_instrs.push(instr);
447 break;
448 }
449 }
450 }
451 ast::Stmt::Do(do_tag) => {
452 self.compile_do(do_tag);
453 }
454 }
455 }
456
457 #[cfg(feature = "multi_template")]
458 fn compile_block(&mut self, block: &ast::Spanned<ast::Block<'source>>) {
459 self.set_line_from_span(block.span());
460 let mut sub = self.new_subgenerator();
461 for node in &block.body {
462 sub.compile_stmt(node);
463 }
464 let instructions = self.finish_subgenerator(sub);
465 self.blocks.insert(block.name, instructions);
466 self.add(Instruction::CallBlock(block.name));
467 }
468
469 #[cfg(feature = "macros")]
470 fn compile_macro_expression(&mut self, macro_decl: &ast::Spanned<ast::Macro<'source>>) {
471 use crate::compiler::instructions::MACRO_CALLER;
472 self.set_line_from_span(macro_decl.span());
473 let instr = self.add(Instruction::Jump(!0));
474 let mut defaults_iter = macro_decl.defaults.iter().rev();
475 for arg in macro_decl.args.iter().rev() {
476 if let Some(default) = defaults_iter.next() {
477 self.add(Instruction::DupTop);
478 self.add(Instruction::IsUndefined);
479 self.start_if();
480 self.add(Instruction::DiscardTop);
481 self.compile_expr(default);
482 self.end_if();
483 }
484 self.compile_assignment(arg);
485 }
486 for node in ¯o_decl.body {
487 self.compile_stmt(node);
488 }
489 self.add(Instruction::Return);
490 let mut undeclared = crate::compiler::meta::find_macro_closure(macro_decl);
491 let caller_reference = undeclared.remove("caller");
492 let macro_instr = self.next_instruction();
493 for name in &undeclared {
494 self.add(Instruction::Enclose(name));
495 }
496 self.add(Instruction::GetClosure);
497 self.add(Instruction::LoadConst(Value::from_object(
498 macro_decl
499 .args
500 .iter()
501 .map(|x| match x {
502 ast::Expr::Var(var) => Value::from(var.id),
503 _ => unreachable!(),
504 })
505 .collect::<Vec<Value>>(),
506 )));
507 let mut flags = 0;
508 if caller_reference {
509 flags |= MACRO_CALLER;
510 }
511 self.add(Instruction::BuildMacro(macro_decl.name, instr + 1, flags));
512 if let Some(&mut Instruction::Jump(ref mut target)) = self.instructions.get_mut(instr) {
513 *target = macro_instr;
514 } else {
515 unreachable!();
516 }
517 }
518
519 #[cfg(feature = "macros")]
520 fn compile_macro(&mut self, macro_decl: &ast::Spanned<ast::Macro<'source>>) {
521 self.compile_macro_expression(macro_decl);
522 self.add(Instruction::StoreLocal(macro_decl.name));
523 }
524
525 #[cfg(feature = "macros")]
526 fn compile_call_block(&mut self, call_block: &ast::Spanned<ast::CallBlock<'source>>) {
527 self.compile_call(&call_block.call, Some(&call_block.macro_decl));
528 self.add(Instruction::Emit);
529 }
530
531 fn compile_do(&mut self, do_tag: &ast::Spanned<ast::Do<'source>>) {
532 self.compile_call(&do_tag.call, None);
533 }
534
535 fn compile_if_stmt(&mut self, if_cond: &ast::Spanned<ast::IfCond<'source>>) {
536 self.set_line_from_span(if_cond.span());
537 self.push_span(if_cond.expr.span());
538 self.compile_expr(&if_cond.expr);
539 self.start_if();
540 self.pop_span();
541 for node in &if_cond.true_body {
542 self.compile_stmt(node);
543 }
544 if !if_cond.false_body.is_empty() {
545 self.start_else();
546 for node in &if_cond.false_body {
547 self.compile_stmt(node);
548 }
549 }
550 self.end_if();
551 }
552
553 fn compile_emit_expr(&mut self, expr: &ast::Spanned<ast::EmitExpr<'source>>) {
554 if let ast::Expr::Call(call) = &expr.expr {
555 self.set_line_from_span(expr.expr.span());
556 match call.identify_call() {
557 ast::CallType::Function(name) => {
558 if name == "super" && call.args.is_empty() {
559 self.add_with_span(Instruction::FastSuper, call.span());
560 return;
561 } else if name == "loop" && call.args.len() == 1 {
562 self.compile_call_args(std::slice::from_ref(&call.args[0]), 0, None);
563 self.add_with_span(Instruction::FastRecurse, call.span());
564 return;
565 }
566 }
567 #[cfg(feature = "multi_template")]
568 ast::CallType::Block(name) => {
569 self.add(Instruction::CallBlock(name));
570 return;
571 }
572 _ => {}
573 }
574 }
575 self.push_span(expr.expr.span());
576 self.compile_expr(&expr.expr);
577 self.add(Instruction::Emit);
578 self.pop_span();
579 }
580
581 fn compile_for_loop(&mut self, for_loop: &ast::Spanned<ast::ForLoop<'source>>) {
582 self.set_line_from_span(for_loop.span());
583
584 if let Some(ref filter_expr) = for_loop.filter_expr {
589 self.add(Instruction::LoadConst(Value::from(0usize)));
590 self.push_span(filter_expr.span());
591 self.compile_expr(&for_loop.iter);
592 self.start_for_loop(false, false);
593 self.add(Instruction::DupTop);
594 self.compile_assignment(&for_loop.target);
595 self.compile_expr(filter_expr);
596 self.start_if();
597 self.add(Instruction::Swap);
598 self.add(Instruction::LoadConst(Value::from(1usize)));
599 self.add(Instruction::Add);
600 self.start_else();
601 self.add(Instruction::DiscardTop);
602 self.end_if();
603 self.pop_span();
604 self.end_for_loop(false);
605 self.add(Instruction::BuildList(None));
606 self.start_for_loop(true, for_loop.recursive);
607 } else {
608 self.push_span(for_loop.iter.span());
609 self.compile_expr(&for_loop.iter);
610 self.start_for_loop(true, for_loop.recursive);
611 self.pop_span();
612 }
613
614 self.compile_assignment(&for_loop.target);
615 for node in &for_loop.body {
616 self.compile_stmt(node);
617 }
618 self.end_for_loop(!for_loop.else_body.is_empty());
619 if !for_loop.else_body.is_empty() {
620 self.start_if();
621 for node in &for_loop.else_body {
622 self.compile_stmt(node);
623 }
624 self.end_if();
625 };
626 }
627
628 pub fn compile_assignment(&mut self, expr: &ast::Expr<'source>) {
630 match expr {
631 ast::Expr::Var(var) => {
632 self.add(Instruction::StoreLocal(var.id));
633 }
634 ast::Expr::List(list) => {
635 self.push_span(list.span());
636 self.add(Instruction::UnpackList(list.items.len()));
637 for expr in &list.items {
638 self.compile_assignment(expr);
639 }
640 self.pop_span();
641 }
642 ast::Expr::GetAttr(attr) => {
643 self.push_span(attr.span());
644 self.compile_expr(&attr.expr);
645 self.add(Instruction::SetAttr(attr.name));
646 }
647 _ => unreachable!(),
648 }
649 }
650
651 pub fn compile_expr(&mut self, expr: &ast::Expr<'source>) {
653 if let Some(v) = expr.as_const() {
655 self.set_line_from_span(expr.span());
656 self.add(Instruction::LoadConst(v.clone()));
657 return;
658 }
659
660 match expr {
661 ast::Expr::Var(v) => {
662 self.set_line_from_span(v.span());
663 self.add(Instruction::Lookup(v.id));
664 }
665 ast::Expr::Const(_) => unreachable!(), ast::Expr::Slice(s) => {
667 self.push_span(s.span());
668 self.compile_expr(&s.expr);
669 if let Some(ref start) = s.start {
670 self.compile_expr(start);
671 } else {
672 self.add(Instruction::LoadConst(Value::from(())));
673 }
674 if let Some(ref stop) = s.stop {
675 self.compile_expr(stop);
676 } else {
677 self.add(Instruction::LoadConst(Value::from(())));
678 }
679 if let Some(ref step) = s.step {
680 self.compile_expr(step);
681 } else {
682 self.add(Instruction::LoadConst(Value::from(())));
683 }
684 self.add(Instruction::Slice);
685 self.pop_span();
686 }
687 ast::Expr::UnaryOp(c) => {
688 self.set_line_from_span(c.span());
689 match c.op {
690 ast::UnaryOpKind::Not => {
691 self.compile_expr(&c.expr);
692 self.add(Instruction::Not);
693 }
694 ast::UnaryOpKind::Neg => {
695 if let ast::Expr::Const(ref c) = c.expr {
699 if let Ok(negated) = neg(&c.value) {
700 self.add(Instruction::LoadConst(negated));
701 return;
702 }
703 }
704 self.compile_expr(&c.expr);
705 self.add_with_span(Instruction::Neg, c.span());
706 }
707 }
708 }
709 ast::Expr::BinOp(c) => {
710 self.compile_bin_op(c);
711 }
712 ast::Expr::IfExpr(i) => {
713 self.set_line_from_span(i.span());
714 self.compile_expr(&i.test_expr);
715 self.start_if();
716 self.compile_expr(&i.true_expr);
717 self.start_else();
718 if let Some(ref false_expr) = i.false_expr {
719 self.compile_expr(false_expr);
720 } else {
721 self.add(Instruction::LoadConst(
725 ValueRepr::Undefined(UndefinedType::Silent).into(),
726 ));
727 }
728 self.end_if();
729 }
730 ast::Expr::Filter(f) => {
731 self.push_span(f.span());
732 if let Some(ref expr) = f.expr {
733 self.compile_expr(expr);
734 }
735 let arg_count = self.compile_call_args(&f.args, 1, None);
736 let local_id = get_local_id(&mut self.filter_local_ids, f.name);
737 self.add(Instruction::ApplyFilter(f.name, arg_count, local_id));
738 self.pop_span();
739 }
740 ast::Expr::Test(f) => {
741 self.push_span(f.span());
742 self.compile_expr(&f.expr);
743 let arg_count = self.compile_call_args(&f.args, 1, None);
744 let local_id = get_local_id(&mut self.test_local_ids, f.name);
745 self.add(Instruction::PerformTest(f.name, arg_count, local_id));
746 self.pop_span();
747 }
748 ast::Expr::GetAttr(g) => {
749 self.push_span(g.span());
750 self.compile_expr(&g.expr);
751 self.add(Instruction::GetAttr(g.name));
752 self.pop_span();
753 }
754 ast::Expr::GetItem(g) => {
755 self.push_span(g.span());
756 self.compile_expr(&g.expr);
757 self.compile_expr(&g.subscript_expr);
758 self.add(Instruction::GetItem);
759 self.pop_span();
760 }
761 ast::Expr::Call(c) => {
762 self.compile_call(c, None);
763 }
764 ast::Expr::List(l) => {
765 self.set_line_from_span(l.span());
766 for item in &l.items {
767 self.compile_expr(item);
768 }
769 self.add(Instruction::BuildList(Some(l.items.len())));
770 }
771 ast::Expr::Map(m) => {
772 self.set_line_from_span(m.span());
773 assert_eq!(m.keys.len(), m.values.len());
774 for (key, value) in m.keys.iter().zip(m.values.iter()) {
775 self.compile_expr(key);
776 self.compile_expr(value);
777 }
778 self.add(Instruction::BuildMap(m.keys.len()));
779 }
780 }
781 }
782
783 fn compile_call(
784 &mut self,
785 c: &ast::Spanned<ast::Call<'source>>,
786 caller: Option<&Caller<'source>>,
787 ) {
788 self.push_span(c.span());
789 match c.identify_call() {
790 ast::CallType::Function(name) => {
791 let arg_count = self.compile_call_args(&c.args, 0, caller);
792 self.add(Instruction::CallFunction(name, arg_count));
793 }
794 #[cfg(feature = "multi_template")]
795 ast::CallType::Block(name) => {
796 self.add(Instruction::BeginCapture(CaptureMode::Capture));
797 self.add(Instruction::CallBlock(name));
798 self.add(Instruction::EndCapture);
799 }
800 ast::CallType::Method(expr, name) => {
801 self.compile_expr(expr);
802 let arg_count = self.compile_call_args(&c.args, 1, caller);
803 self.add(Instruction::CallMethod(name, arg_count));
804 }
805 ast::CallType::Object(expr) => {
806 self.compile_expr(expr);
807 let arg_count = self.compile_call_args(&c.args, 1, caller);
808 self.add(Instruction::CallObject(arg_count));
809 }
810 };
811 self.pop_span();
812 }
813
814 fn compile_call_args(
815 &mut self,
816 args: &[ast::CallArg<'source>],
817 extra_args: usize,
818 caller: Option<&Caller<'source>>,
819 ) -> Option<u16> {
820 let mut pending_args = extra_args;
821 let mut num_args_batches = 0;
822 let mut has_kwargs = caller.is_some();
823 let mut static_kwargs = caller.is_none();
824
825 for arg in args {
826 match arg {
827 ast::CallArg::Pos(expr) => {
828 self.compile_expr(expr);
829 pending_args += 1;
830 }
831 ast::CallArg::PosSplat(expr) => {
832 if pending_args > 0 {
833 self.add(Instruction::BuildList(Some(pending_args)));
834 pending_args = 0;
835 num_args_batches += 1;
836 }
837 self.compile_expr(expr);
838 num_args_batches += 1;
839 }
840 ast::CallArg::Kwarg(_, expr) => {
841 if !matches!(expr, ast::Expr::Const(_)) {
842 static_kwargs = false;
843 }
844 has_kwargs = true;
845 }
846 ast::CallArg::KwargSplat(_) => {
847 static_kwargs = false;
848 has_kwargs = true;
849 }
850 }
851 }
852
853 if has_kwargs {
854 let mut pending_kwargs = 0;
855 let mut num_kwargs_batches = 0;
856 let mut collected_kwargs = ValueMap::new();
857 for arg in args {
858 match arg {
859 ast::CallArg::Kwarg(key, value) => {
860 if static_kwargs {
861 if let ast::Expr::Const(c) = value {
862 collected_kwargs.insert(Value::from(*key), c.value.clone());
863 } else {
864 unreachable!();
865 }
866 } else {
867 self.add(Instruction::LoadConst(Value::from(*key)));
868 self.compile_expr(value);
869 pending_kwargs += 1;
870 }
871 }
872 ast::CallArg::KwargSplat(expr) => {
873 if pending_kwargs > 0 {
874 self.add(Instruction::BuildKwargs(pending_kwargs));
875 num_kwargs_batches += 1;
876 pending_kwargs = 0;
877 }
878 self.compile_expr(expr);
879 num_kwargs_batches += 1;
880 }
881 ast::CallArg::Pos(_) | ast::CallArg::PosSplat(_) => {}
882 }
883 }
884
885 if !collected_kwargs.is_empty() {
886 self.add(Instruction::LoadConst(Kwargs::wrap(collected_kwargs)));
887 } else {
888 #[cfg(feature = "macros")]
892 {
893 if let Some(caller) = caller {
894 self.add(Instruction::LoadConst(Value::from("caller")));
895 self.compile_macro_expression(caller);
896 pending_kwargs += 1
897 }
898 }
899 if num_kwargs_batches > 0 {
900 if pending_kwargs > 0 {
901 self.add(Instruction::BuildKwargs(pending_kwargs));
902 num_kwargs_batches += 1;
903 }
904 self.add(Instruction::MergeKwargs(num_kwargs_batches));
905 } else {
906 self.add(Instruction::BuildKwargs(pending_kwargs));
907 }
908 }
909 pending_args += 1;
910 }
911
912 if num_args_batches > 0 {
913 if pending_args > 0 {
914 self.add(Instruction::BuildList(Some(pending_args)));
915 num_args_batches += 1;
916 }
917 self.add(Instruction::UnpackLists(num_args_batches));
918 None
919 } else {
920 assert!(pending_args as u16 as usize == pending_args);
921 Some(pending_args as u16)
922 }
923 }
924
925 fn compile_bin_op(&mut self, c: &ast::Spanned<ast::BinOp<'source>>) {
926 self.push_span(c.span());
927 let instr = match c.op {
928 ast::BinOpKind::Eq => Instruction::Eq,
929 ast::BinOpKind::Ne => Instruction::Ne,
930 ast::BinOpKind::Lt => Instruction::Lt,
931 ast::BinOpKind::Lte => Instruction::Lte,
932 ast::BinOpKind::Gt => Instruction::Gt,
933 ast::BinOpKind::Gte => Instruction::Gte,
934 ast::BinOpKind::ScAnd | ast::BinOpKind::ScOr => {
935 self.start_sc_bool();
936 self.compile_expr(&c.left);
937 self.sc_bool(matches!(c.op, ast::BinOpKind::ScAnd));
938 self.compile_expr(&c.right);
939 self.end_sc_bool();
940 self.pop_span();
941 return;
942 }
943 ast::BinOpKind::Add => Instruction::Add,
944 ast::BinOpKind::Sub => Instruction::Sub,
945 ast::BinOpKind::Mul => Instruction::Mul,
946 ast::BinOpKind::Div => Instruction::Div,
947 ast::BinOpKind::FloorDiv => Instruction::IntDiv,
948 ast::BinOpKind::Rem => Instruction::Rem,
949 ast::BinOpKind::Pow => Instruction::Pow,
950 ast::BinOpKind::Concat => Instruction::StringConcat,
951 ast::BinOpKind::In => Instruction::In,
952 };
953 self.compile_expr(&c.left);
954 self.compile_expr(&c.right);
955 self.add(instr);
956 self.pop_span();
957 }
958
959 pub fn buffer_size_hint(&self) -> usize {
963 (self.raw_template_bytes * 2).next_power_of_two()
968 }
969
970 pub fn finish(
972 mut self,
973 ) -> (
974 Instructions<'source>,
975 BTreeMap<&'source str, Instructions<'source>>,
976 ) {
977 assert!(self.pending_block.is_empty());
978 recycle_pending_block_buffer(mem::take(&mut self.pending_block));
979 recycle_span_stack_buffer(mem::take(&mut self.span_stack));
980 (self.instructions, self.blocks)
981 }
982}