1use std::collections::BTreeMap;
2use std::mem;
3
4#[cfg(feature = "macros")]
5use std::sync::Arc;
6
7use crate::compiler::instructions::{
8 Instruction, Instructions, LOOP_FLAG_RECURSIVE, LOOP_FLAG_WITH_LOOP_VAR, MAX_LOCALS,
9};
10use crate::environment::Environment;
11use crate::error::{Error, ErrorKind};
12use crate::output::{CaptureMode, Output};
13use crate::utils::{untrusted_size_hint, AutoEscape, UndefinedBehavior};
14use crate::value::namespace_object::Namespace;
15use crate::value::{ops, value_map_with_capacity, Kwargs, ObjectRepr, Value, ValueMap};
16use crate::vm::context::{Frame, Stack};
17use crate::vm::loop_object::{Loop, LoopState};
18use crate::vm::state::BlockStack;
19
20#[cfg(feature = "macros")]
21use crate::vm::closure_object::Closure;
22
23pub(crate) use crate::vm::context::Context;
24pub use crate::vm::state::State;
25
26#[cfg(feature = "macros")]
27mod closure_object;
28mod context;
29#[cfg(feature = "fuel")]
30mod fuel;
31mod loop_object;
32#[cfg(feature = "macros")]
33mod macro_object;
34#[cfg(feature = "multi_template")]
35mod module_object;
36mod state;
37
38#[cfg(feature = "multi_template")]
40const INCLUDE_RECURSION_COST: usize = 10;
41
42#[cfg(feature = "macros")]
44const MACRO_RECURSION_COST: usize = 4;
45
46#[cfg_attr(feature = "internal_debug", derive(Debug))]
48pub struct Vm<'env> {
49 env: &'env Environment<'env>,
50}
51
52pub(crate) fn prepare_blocks<'env, 'template>(
53 blocks: &'template BTreeMap<&'env str, Instructions<'env>>,
54) -> BTreeMap<&'env str, BlockStack<'template, 'env>> {
55 blocks
56 .iter()
57 .map(|(name, instr)| (*name, BlockStack::new(instr)))
58 .collect()
59}
60
61fn get_or_lookup_local<'a, F>(vec: &mut [Option<&'a Value>], idx: u8, f: F) -> Option<&'a Value>
62where
63 F: FnOnce() -> Option<&'a Value>,
64{
65 if idx == !0 {
66 f()
67 } else if let Some(Some(rv)) = vec.get(idx as usize) {
68 Some(*rv)
69 } else {
70 let val = some!(f());
71 vec[idx as usize] = Some(val);
72 Some(val)
73 }
74}
75
76impl<'env> Vm<'env> {
77 pub fn new(env: &'env Environment<'env>) -> Vm<'env> {
79 Vm { env }
80 }
81
82 pub fn eval<'template>(
87 &self,
88 instructions: &'template Instructions<'env>,
89 root: Value,
90 blocks: &'template BTreeMap<&'env str, Instructions<'env>>,
91 out: &mut Output,
92 auto_escape: AutoEscape,
93 ) -> Result<(Option<Value>, State<'template, 'env>), Error> {
94 let mut state = State::new(
95 Context::new_with_frame(self.env, ok!(Frame::new_checked(root))),
96 auto_escape,
97 instructions,
98 prepare_blocks(blocks),
99 );
100 self.eval_state(&mut state, out).map(|x| (x, state))
101 }
102
103 #[cfg(feature = "macros")]
105 pub fn eval_macro(
106 &self,
107 state: &State,
108 macro_id: usize,
109 out: &mut Output,
110 closure: Value,
111 caller: Option<Value>,
112 args: Vec<Value>,
113 ) -> Result<Option<Value>, Error> {
114 let (instructions, pc) = &state.macros[macro_id];
115 let context_base = state.ctx.clone_base();
116 let mut ctx = Context::new_with_frame(self.env, Frame::new(context_base));
117 ok!(ctx.push_frame(Frame::new(closure)));
118 if let Some(caller) = caller {
119 ctx.store("caller", caller);
120 }
121 ok!(ctx.incr_depth(state.ctx.depth() + MACRO_RECURSION_COST));
122 self.do_eval(
123 &mut State {
124 ctx,
125 current_block: None,
126 auto_escape: state.auto_escape(),
127 instructions,
128 blocks: BTreeMap::default(),
129 temps: state.temps.clone(),
130 loaded_templates: Default::default(),
131 #[cfg(feature = "macros")]
132 id: state.id,
133 #[cfg(feature = "macros")]
134 macros: state.macros.clone(),
135 #[cfg(feature = "macros")]
136 closure_tracker: state.closure_tracker.clone(),
137 #[cfg(feature = "fuel")]
138 fuel_tracker: state.fuel_tracker.clone(),
139 },
140 out,
141 Stack::from(args),
142 *pc,
143 )
144 }
145
146 #[inline(always)]
148 fn eval_state(
149 &self,
150 state: &mut State<'_, 'env>,
151 out: &mut Output,
152 ) -> Result<Option<Value>, Error> {
153 self.do_eval(state, out, Stack::default(), 0)
154 }
155
156 fn do_eval(
158 &self,
159 state: &mut State<'_, 'env>,
160 out: &mut Output,
161 stack: Stack,
162 pc: u32,
163 ) -> Result<Option<Value>, Error> {
164 #[cfg(feature = "stacker")]
165 {
166 stacker::maybe_grow(32 * 1024, 1024 * 1024, || {
167 self.eval_impl(state, out, stack, pc)
168 })
169 }
170 #[cfg(not(feature = "stacker"))]
171 {
172 self.eval_impl(state, out, stack, pc)
173 }
174 }
175
176 #[inline]
177 fn eval_impl(
178 &self,
179 state: &mut State<'_, 'env>,
180 out: &mut Output,
181 mut stack: Stack,
182 mut pc: u32,
183 ) -> Result<Option<Value>, Error> {
184 let initial_auto_escape = state.auto_escape;
185 let undefined_behavior = state.undefined_behavior();
186 let mut auto_escape_stack = vec![];
187 let mut next_loop_recursion_jump = None;
188 let mut loaded_filters = [None; MAX_LOCALS];
189 let mut loaded_tests = [None; MAX_LOCALS];
190
191 #[cfg(feature = "multi_template")]
195 let mut parent_instructions = None;
196
197 macro_rules! recurse_loop {
198 ($capture:expr, $loop_object:expr) => {{
199 let Some(jump_target) = $loop_object.recurse_jump_target else {
200 bail!(Error::new(
201 ErrorKind::InvalidOperation,
202 "cannot recurse outside of recursive loop",
203 ))
204 };
205 next_loop_recursion_jump = Some((pc + 1, $capture));
210 if $capture {
211 out.begin_capture(CaptureMode::Capture);
212 }
213 pc = jump_target;
214 continue;
215 }};
216 }
217
218 #[allow(clippy::while_let_loop)]
220 loop {
221 let instr = match state.instructions.get(pc) {
222 Some(instr) => instr,
223 #[cfg(not(feature = "multi_template"))]
224 None => break,
225 #[cfg(feature = "multi_template")]
226 None => {
227 state.instructions = match parent_instructions.take() {
233 Some(instr) => instr,
234 None => break,
235 };
236 out.end_capture(AutoEscape::None);
237 pc = 0;
238 loaded_filters = [None; MAX_LOCALS];
242 loaded_tests = [None; MAX_LOCALS];
243 continue;
244 }
245 };
246
247 let a;
251 let b;
252 let mut err;
253
254 macro_rules! func_binop {
255 ($method:ident) => {{
256 b = stack.pop();
257 a = stack.pop();
258 stack.push(ctx_ok!(ops::$method(&a, &b)));
259 }};
260 }
261
262 macro_rules! op_binop {
263 ($op:tt) => {{
264 b = stack.pop();
265 a = stack.pop();
266 stack.push(Value::from(a $op b));
267 }};
268 }
269
270 macro_rules! bail {
271 ($err:expr) => {{
272 err = $err;
273 process_err(&mut err, pc, state);
274 return Err(err);
275 }};
276 }
277
278 macro_rules! ctx_ok {
279 ($expr:expr) => {
280 match $expr {
281 Ok(rv) => rv,
282 Err(err) => bail!(err),
283 }
284 };
285 }
286
287 macro_rules! assert_valid {
288 ($expr:expr) => {{
289 let val = $expr;
290 match val.validate() {
291 Ok(val) => val,
292 Err(err) => bail!(err),
293 }
294 }};
295 }
296
297 #[cfg(feature = "fuel")]
300 if let Some(ref tracker) = state.fuel_tracker {
301 ctx_ok!(tracker.track(instr));
302 }
303
304 match instr {
305 Instruction::Swap => {
306 a = stack.pop();
307 b = stack.pop();
308 stack.push(a);
309 stack.push(b);
310 }
311 Instruction::EmitRaw(val) => {
312 ok!(out.write_str(val).map_err(Error::from));
315 }
316 Instruction::Emit => {
317 ctx_ok!(self.env.format(&stack.pop(), state, out));
318 }
319 Instruction::StoreLocal(name) => {
320 state.ctx.store(name, stack.pop());
321 }
322 Instruction::Lookup(name) => {
323 stack.push(assert_valid!(state
324 .lookup(name)
325 .unwrap_or(Value::UNDEFINED)));
326 }
327 Instruction::GetAttr(name) => {
328 a = stack.pop();
329 stack.push(match a.get_attr_fast(name) {
335 Some(value) => assert_valid!(value),
336 None => ctx_ok!(undefined_behavior.handle_undefined(a.is_undefined())),
337 });
338 }
339 Instruction::SetAttr(name) => {
340 b = stack.pop();
341 a = stack.pop();
342 if let Some(ns) = b.downcast_object_ref::<Namespace>() {
343 ns.set_value(name, a);
344 } else {
345 bail!(Error::new(
346 ErrorKind::InvalidOperation,
347 format!("can only assign to namespaces, not {}", b.kind())
348 ));
349 }
350 }
351 Instruction::GetItem => {
352 a = stack.pop();
353 b = stack.pop();
354 stack.push(match b.get_item_opt(&a) {
355 Some(value) => assert_valid!(value),
356 None => ctx_ok!(undefined_behavior.handle_undefined(b.is_undefined())),
357 });
358 }
359 Instruction::Slice => {
360 let step = stack.pop();
361 let stop = stack.pop();
362 b = stack.pop();
363 a = stack.pop();
364 if a.is_undefined() && matches!(undefined_behavior, UndefinedBehavior::Strict) {
365 bail!(Error::from(ErrorKind::UndefinedError));
366 }
367 stack.push(ctx_ok!(ops::slice(a, b, stop, step)));
368 }
369 Instruction::LoadConst(value) => {
370 stack.push(value.clone());
371 }
372 Instruction::BuildMap(pair_count) => {
373 let mut map = value_map_with_capacity(*pair_count);
374 stack.reverse_top(*pair_count * 2);
375 for _ in 0..*pair_count {
376 let key = stack.pop();
377 let value = stack.pop();
378 map.insert(key, value);
379 }
380 stack.push(Value::from_object(map))
381 }
382 Instruction::BuildKwargs(pair_count) => {
383 let mut map = value_map_with_capacity(*pair_count);
384 stack.reverse_top(*pair_count * 2);
385 for _ in 0..*pair_count {
386 let key = stack.pop();
387 let value = stack.pop();
388 map.insert(key, value);
389 }
390 stack.push(Kwargs::wrap(map))
391 }
392 Instruction::MergeKwargs(count) => {
393 let mut kwargs_sources = Vec::from_iter((0..*count).map(|_| stack.pop()));
394 kwargs_sources.reverse();
395 stack.push(ctx_ok!(self.merge_kwargs(kwargs_sources)));
396 }
397 Instruction::BuildList(n) => {
398 let count = n.unwrap_or_else(|| stack.pop().try_into().unwrap());
399 let mut v = Vec::with_capacity(untrusted_size_hint(count));
400 for _ in 0..count {
401 v.push(stack.pop());
402 }
403 v.reverse();
404 stack.push(Value::from_object(v))
405 }
406 Instruction::UnpackList(count) => {
407 ctx_ok!(self.unpack_list(&mut stack, *count));
408 }
409 Instruction::UnpackLists(count) => {
410 let lists = Vec::from_iter((0..*count).map(|_| stack.pop()));
411 let mut len = 0;
412 for list in lists.into_iter().rev() {
413 for item in ctx_ok!(list.try_iter()) {
414 stack.push(item);
415 len += 1;
416 }
417 }
418 stack.push(Value::from(len));
419 }
420 Instruction::Add => func_binop!(add),
421 Instruction::Sub => func_binop!(sub),
422 Instruction::Mul => func_binop!(mul),
423 Instruction::Div => func_binop!(div),
424 Instruction::IntDiv => func_binop!(int_div),
425 Instruction::Rem => func_binop!(rem),
426 Instruction::Pow => func_binop!(pow),
427 Instruction::Eq => op_binop!(==),
428 Instruction::Ne => op_binop!(!=),
429 Instruction::Gt => op_binop!(>),
430 Instruction::Gte => op_binop!(>=),
431 Instruction::Lt => op_binop!(<),
432 Instruction::Lte => op_binop!(<=),
433 Instruction::Not => {
434 a = stack.pop();
435 stack.push(Value::from(!a.is_true()));
436 }
437 Instruction::StringConcat => {
438 a = stack.pop();
439 b = stack.pop();
440 stack.push(ops::string_concat(b, &a));
441 }
442 Instruction::In => {
443 a = stack.pop();
444 b = stack.pop();
445 ctx_ok!(state.undefined_behavior().assert_iterable(&a));
448 stack.push(ctx_ok!(ops::contains(&a, &b)));
449 }
450 Instruction::Neg => {
451 a = stack.pop();
452 stack.push(ctx_ok!(ops::neg(&a)));
453 }
454 Instruction::PushWith => {
455 ctx_ok!(state.ctx.push_frame(Frame::default()));
456 }
457 Instruction::PopFrame => {
458 state.ctx.pop_frame();
459 }
460 Instruction::PopLoopFrame => {
461 let mut l = state.ctx.pop_frame().current_loop.unwrap();
462 if let Some((target, end_capture)) = l.current_recursion_jump.take() {
463 pc = target;
464 if end_capture {
465 stack.push(out.end_capture(state.auto_escape));
466 }
467 continue;
468 }
469 }
470 #[cfg(feature = "macros")]
471 Instruction::IsUndefined => {
472 a = stack.pop();
473 stack.push(Value::from(a.is_undefined()));
474 }
475 Instruction::PushLoop(flags) => {
476 a = stack.pop();
477 ctx_ok!(self.push_loop(state, a, *flags, pc, next_loop_recursion_jump.take()));
478 }
479 Instruction::Iterate(jump_target) => {
480 match state.ctx.current_loop().unwrap().next() {
481 Some(item) => stack.push(assert_valid!(item)),
482 None => {
483 pc = *jump_target;
484 continue;
485 }
486 };
487 }
488 Instruction::PushDidNotIterate => {
489 stack.push(Value::from(
490 state.ctx.current_loop().unwrap().did_not_iterate(),
491 ));
492 }
493 Instruction::Jump(jump_target) => {
494 pc = *jump_target;
495 continue;
496 }
497 Instruction::JumpIfFalse(jump_target) => {
498 a = stack.pop();
499 if !ctx_ok!(undefined_behavior.is_true(&a)) {
500 pc = *jump_target;
501 continue;
502 }
503 }
504 Instruction::JumpIfFalseOrPop(jump_target) => {
505 if !ctx_ok!(undefined_behavior.is_true(stack.peek())) {
506 pc = *jump_target;
507 continue;
508 } else {
509 stack.pop();
510 }
511 }
512 Instruction::JumpIfTrueOrPop(jump_target) => {
513 if ctx_ok!(undefined_behavior.is_true(stack.peek())) {
514 pc = *jump_target;
515 continue;
516 } else {
517 stack.pop();
518 }
519 }
520 Instruction::PushAutoEscape => {
521 a = stack.pop();
522 auto_escape_stack.push(state.auto_escape);
523 state.auto_escape = ctx_ok!(self.derive_auto_escape(a, initial_auto_escape));
524 }
525 Instruction::PopAutoEscape => {
526 state.auto_escape = auto_escape_stack.pop().unwrap();
527 }
528 Instruction::BeginCapture(mode) => {
529 out.begin_capture(*mode);
530 }
531 Instruction::EndCapture => {
532 stack.push(out.end_capture(state.auto_escape));
533 }
534 Instruction::ApplyFilter(name, arg_count, local_id) => {
535 let filter =
536 ctx_ok!(get_or_lookup_local(&mut loaded_filters, *local_id, || {
537 state.env().get_filter(name)
538 })
539 .ok_or_else(|| {
540 Error::new(
541 ErrorKind::UnknownFilter,
542 format!("filter {name} is unknown"),
543 )
544 }));
545 let args = stack.get_call_args(*arg_count);
546 let arg_count = args.len();
547 a = ctx_ok!(filter.call(state, args));
548 stack.drop_top(arg_count);
549 stack.push(a);
550 }
551 Instruction::PerformTest(name, arg_count, local_id) => {
552 let test = ctx_ok!(get_or_lookup_local(&mut loaded_tests, *local_id, || {
553 state.env().get_test(name)
554 })
555 .ok_or_else(|| {
556 Error::new(ErrorKind::UnknownTest, format!("test {name} is unknown"))
557 }));
558 let args = stack.get_call_args(*arg_count);
559 let arg_count = args.len();
560 a = ctx_ok!(test.call(state, args));
561 stack.drop_top(arg_count);
562 stack.push(Value::from(a.is_true()));
563 }
564 Instruction::CallFunction(name, arg_count) => {
565 let args = stack.get_call_args(*arg_count);
566 let rv = if *name == "super" {
568 if !args.is_empty() {
569 bail!(Error::new(
570 ErrorKind::InvalidOperation,
571 "super() takes no arguments",
572 ));
573 }
574 ctx_ok!(self.perform_super(state, out, true))
575 } else if let Some(func) = state.lookup(name) {
576 if let Some(loop_object) = func.downcast_object_ref::<Loop>() {
580 if args.len() != 1 {
581 bail!(Error::new(
582 ErrorKind::InvalidOperation,
583 "loop() takes one argument"
584 ));
585 }
586 recurse_loop!(true, loop_object);
587 } else {
588 ctx_ok!(func.call(state, args))
589 }
590 } else {
591 bail!(Error::new(
592 ErrorKind::UnknownFunction,
593 format!("{name} is unknown"),
594 ));
595 };
596 let arg_count = args.len();
597 stack.drop_top(arg_count);
598 stack.push(rv);
599 }
600 Instruction::CallMethod(name, arg_count) => {
601 let args = stack.get_call_args(*arg_count);
602 let arg_count = args.len();
603 a = ctx_ok!(args[0].call_method(state, name, &args[1..]));
604 stack.drop_top(arg_count);
605 stack.push(a);
606 }
607 Instruction::CallObject(arg_count) => {
608 let args = stack.get_call_args(*arg_count);
609 let arg_count = args.len();
610 a = ctx_ok!(args[0].call(state, &args[1..]));
611 stack.drop_top(arg_count);
612 stack.push(a);
613 }
614 Instruction::DupTop => {
615 stack.push(stack.peek().clone());
616 }
617 Instruction::DiscardTop => {
618 stack.pop();
619 }
620 Instruction::FastSuper => {
621 ctx_ok!(self.perform_super(state, out, false));
622 }
623 Instruction::FastRecurse => match state.ctx.current_loop() {
624 Some(l) => recurse_loop!(false, &l.object),
625 None => bail!(Error::new(ErrorKind::UnknownFunction, "loop is unknown")),
626 },
627 #[cfg(feature = "multi_template")]
651 Instruction::LoadBlocks => {
652 a = stack.pop();
653 if parent_instructions.is_some() {
654 bail!(Error::new(
655 ErrorKind::InvalidOperation,
656 "tried to extend a second time in a template"
657 ));
658 }
659 parent_instructions = Some(ctx_ok!(self.load_blocks(a, state)));
660 out.begin_capture(CaptureMode::Discard);
661 }
662 #[cfg(feature = "multi_template")]
663 Instruction::Include(ignore_missing) => {
664 a = stack.pop();
665 ctx_ok!(self.perform_include(a, state, out, *ignore_missing));
666 }
667 #[cfg(feature = "multi_template")]
668 Instruction::ExportLocals => {
669 let captured = stack.pop();
670 let locals = state.ctx.current_locals_mut();
671 let mut values = value_map_with_capacity(locals.len());
672 for (key, value) in locals.iter() {
673 values.insert(Value::from(*key), value.clone());
674 }
675 stack.push(Value::from_object(crate::vm::module_object::Module::new(
676 values, captured,
677 )));
678 }
679 #[cfg(feature = "multi_template")]
680 Instruction::CallBlock(name) => {
681 if parent_instructions.is_none() && !out.is_discarding() {
682 self.call_block(name, state, out)?;
683 }
684 }
685 #[cfg(feature = "macros")]
686 Instruction::BuildMacro(name, offset, flags) => {
687 self.build_macro(&mut stack, state, *offset, name, *flags);
688 }
689 #[cfg(feature = "macros")]
690 Instruction::Return => break,
691 #[cfg(feature = "macros")]
692 Instruction::Enclose(name) => {
693 if state.ctx.closure().is_none() {
697 let closure = Arc::new(Closure::default());
698 state.closure_tracker.track_closure(closure.clone());
699 state.ctx.reset_closure(Some(closure));
700 }
701 state.ctx.enclose(name);
702 }
703 #[cfg(feature = "macros")]
704 Instruction::GetClosure => {
705 stack.push(
706 state
707 .ctx
708 .closure()
709 .map_or(Value::UNDEFINED, |x| Value::from_dyn_object(x.clone())),
710 );
711 }
712 }
713 pc += 1;
714 }
715
716 Ok(stack.try_pop())
717 }
718
719 fn merge_kwargs(&self, values: Vec<Value>) -> Result<Value, Error> {
720 let mut rv = ValueMap::new();
721 for value in values {
722 ok!(self.env.undefined_behavior().assert_iterable(&value));
723 let iter = ok!(value
724 .as_object()
725 .filter(|x| x.repr() == ObjectRepr::Map)
726 .and_then(|x| x.try_iter_pairs())
727 .ok_or_else(|| {
728 Error::new(
729 ErrorKind::InvalidOperation,
730 format!(
731 "attempted to apply keyword arguments from non map (got {})",
732 value.kind()
733 ),
734 )
735 }));
736 for (key, value) in iter {
737 rv.insert(key, value);
738 }
739 }
740 Ok(Kwargs::wrap(rv))
741 }
742
743 #[cfg(feature = "multi_template")]
744 fn perform_include(
745 &self,
746 name: Value,
747 state: &mut State<'_, 'env>,
748 out: &mut Output,
749 ignore_missing: bool,
750 ) -> Result<(), Error> {
751 let obj = name.as_object();
752 let choices = obj
753 .as_ref()
754 .and_then(|d| d.try_iter())
755 .into_iter()
756 .flatten()
757 .chain(obj.is_none().then(|| name.clone()));
758
759 let mut templates_tried = vec![];
760 for choice in choices {
761 let name = ok!(choice.as_str().ok_or_else(|| {
762 Error::new(
763 ErrorKind::InvalidOperation,
764 "template name was not a string",
765 )
766 }));
767 let tmpl = match state.get_template(name) {
768 Ok(tmpl) => tmpl,
769 Err(err) => {
770 if err.kind() == ErrorKind::TemplateNotFound {
771 templates_tried.push(choice);
772 } else {
773 return Err(err);
774 }
775 continue;
776 }
777 };
778
779 let (new_instructions, new_blocks) = ok!(tmpl.instructions_and_blocks());
780 let old_escape = mem::replace(&mut state.auto_escape, tmpl.initial_auto_escape());
781 let old_instructions = mem::replace(&mut state.instructions, new_instructions);
782 let old_blocks = mem::replace(&mut state.blocks, prepare_blocks(new_blocks));
783 let old_loaded_templates = state.loaded_templates.clone();
787 ok!(state.ctx.incr_depth(INCLUDE_RECURSION_COST));
788 let rv;
789 #[cfg(feature = "macros")]
790 {
791 let old_closure = state.ctx.take_closure();
792 rv = self.eval_state(state, out);
793 state.ctx.reset_closure(old_closure);
794 }
795 #[cfg(not(feature = "macros"))]
796 {
797 rv = self.eval_state(state, out);
798 }
799 state.ctx.decr_depth(INCLUDE_RECURSION_COST);
800 state.loaded_templates = old_loaded_templates;
801 state.auto_escape = old_escape;
802 state.instructions = old_instructions;
803 state.blocks = old_blocks;
804 ok!(rv.map_err(|err| {
805 Error::new(
806 ErrorKind::BadInclude,
807 format!("error in \"{}\"", tmpl.name()),
808 )
809 .with_source(err)
810 }));
811 return Ok(());
812 }
813 if !templates_tried.is_empty() && !ignore_missing {
814 Err(Error::new(
815 ErrorKind::TemplateNotFound,
816 if templates_tried.len() == 1 {
817 format!(
818 "tried to include non-existing template {:?}",
819 templates_tried[0]
820 )
821 } else {
822 format!(
823 "tried to include one of multiple templates, none of which existed {}",
824 Value::from(templates_tried)
825 )
826 },
827 ))
828 } else {
829 Ok(())
830 }
831 }
832
833 fn perform_super(
834 &self,
835 state: &mut State<'_, 'env>,
836 out: &mut Output,
837 capture: bool,
838 ) -> Result<Value, Error> {
839 let name = ok!(state.current_block.ok_or_else(|| {
840 Error::new(ErrorKind::InvalidOperation, "cannot super outside of block")
841 }));
842
843 let block_stack = state.blocks.get_mut(name).unwrap();
844 if !block_stack.push() {
845 return Err(Error::new(
846 ErrorKind::InvalidOperation,
847 "no parent block exists",
848 ));
849 }
850
851 if capture {
852 out.begin_capture(CaptureMode::Capture);
853 }
854
855 let old_instructions = mem::replace(&mut state.instructions, block_stack.instructions());
856 ok!(state.ctx.push_frame(Frame::default()));
857 let rv = self.eval_state(state, out);
858 state.ctx.pop_frame();
859 state.instructions = old_instructions;
860 state.blocks.get_mut(name).unwrap().pop();
861
862 ok!(rv.map_err(|err| {
863 Error::new(ErrorKind::EvalBlock, "error in super block").with_source(err)
864 }));
865 if capture {
866 Ok(out.end_capture(state.auto_escape))
867 } else {
868 Ok(Value::UNDEFINED)
869 }
870 }
871
872 #[cfg(feature = "multi_template")]
873 fn load_blocks(
874 &self,
875 name: Value,
876 state: &mut State<'_, 'env>,
877 ) -> Result<&'env Instructions<'env>, Error> {
878 let Some(name) = name.as_str() else {
879 return Err(Error::new(
880 ErrorKind::InvalidOperation,
881 "template name was not a string",
882 ));
883 };
884 if state.loaded_templates.contains(&name) {
885 return Err(Error::new(
886 ErrorKind::InvalidOperation,
887 format!("cycle in template inheritance. {name:?} was referenced more than once"),
888 ));
889 }
890 let tmpl = ok!(state.get_template(name));
891 let (new_instructions, new_blocks) = ok!(tmpl.instructions_and_blocks());
892 state.loaded_templates.insert(new_instructions.name());
893 for (name, instr) in new_blocks.iter() {
894 state
895 .blocks
896 .entry(name)
897 .or_default()
898 .append_instructions(instr);
899 }
900 Ok(new_instructions)
901 }
902
903 #[cfg(feature = "multi_template")]
904 pub(crate) fn call_block(
905 &self,
906 name: &str,
907 state: &mut State<'_, 'env>,
908 out: &mut Output,
909 ) -> Result<Option<Value>, Error> {
910 if let Some((name, block_stack)) = state.blocks.get_key_value(name) {
911 let old_block = mem::replace(&mut state.current_block, Some(name));
912 let old_instructions =
913 mem::replace(&mut state.instructions, block_stack.instructions());
914 state.ctx.push_frame(Frame::default())?;
915 let rv = self.eval_state(state, out);
916 state.ctx.pop_frame();
917 state.instructions = old_instructions;
918 state.current_block = old_block;
919 rv
920 } else {
921 Err(Error::new(
922 ErrorKind::UnknownBlock,
923 format!("block '{}' not found", name),
924 ))
925 }
926 }
927
928 fn derive_auto_escape(
929 &self,
930 value: Value,
931 initial_auto_escape: AutoEscape,
932 ) -> Result<AutoEscape, Error> {
933 match (value.as_str(), value == Value::from(true)) {
934 (Some("html"), _) => Ok(AutoEscape::Html),
935 #[cfg(feature = "json")]
936 (Some("json"), _) => Ok(AutoEscape::Json),
937 (Some("none"), _) | (None, false) => Ok(AutoEscape::None),
938 (None, true) => Ok(if matches!(initial_auto_escape, AutoEscape::None) {
939 AutoEscape::Html
940 } else {
941 initial_auto_escape
942 }),
943 _ => Err(Error::new(
944 ErrorKind::InvalidOperation,
945 "invalid value to autoescape tag",
946 )),
947 }
948 }
949
950 fn push_loop(
951 &self,
952 state: &mut State<'_, 'env>,
953 iterable: Value,
954 flags: u8,
955 pc: u32,
956 current_recursion_jump: Option<(u32, bool)>,
957 ) -> Result<(), Error> {
958 let iter = ok!(state
959 .undefined_behavior()
960 .try_iter(iterable)
961 .map_err(|mut err| {
962 if let Some((jump_instr, _)) = current_recursion_jump {
963 process_err(&mut err, pc, state);
970 let mut call_err = Error::new(
971 ErrorKind::InvalidOperation,
972 "cannot recurse because of non-iterable value",
973 );
974 process_err(&mut call_err, jump_instr - 1, state);
975 call_err.with_source(err)
976 } else {
977 err
978 }
979 }));
980 let depth = state
981 .ctx
982 .current_loop()
983 .filter(|x| x.object.recurse_jump_target.is_some())
984 .map_or(0, |x| x.object.depth + 1);
985 state.ctx.push_frame(Frame {
986 current_loop: Some(LoopState::new(
987 iter,
988 depth,
989 flags & LOOP_FLAG_WITH_LOOP_VAR != 0,
990 (flags & LOOP_FLAG_RECURSIVE != 0).then_some(pc),
991 current_recursion_jump,
992 )),
993 ..Frame::default()
994 })
995 }
996
997 fn unpack_list(&self, stack: &mut Stack, count: usize) -> Result<(), Error> {
998 let top = stack.pop();
999 let iter = ok!(top
1000 .as_object()
1001 .and_then(|x| x.try_iter())
1002 .ok_or_else(|| Error::new(ErrorKind::CannotUnpack, "value is not iterable")));
1003
1004 let mut n = 0;
1005 for item in iter {
1006 stack.push(item);
1007 n += 1;
1008 }
1009
1010 if n == count {
1011 stack.reverse_top(n);
1012 Ok(())
1013 } else {
1014 Err(Error::new(
1015 ErrorKind::CannotUnpack,
1016 format!("sequence of wrong length (expected {}, got {})", count, n,),
1017 ))
1018 }
1019 }
1020
1021 #[cfg(feature = "macros")]
1022 fn build_macro(
1023 &self,
1024 stack: &mut Stack,
1025 state: &mut State,
1026 offset: u32,
1027 name: &str,
1028 flags: u8,
1029 ) {
1030 use crate::{compiler::instructions::MACRO_CALLER, vm::macro_object::Macro};
1031
1032 let arg_spec = stack.pop().try_iter().unwrap().collect();
1033 let closure = stack.pop();
1034 let macro_ref_id = state.macros.len();
1035 Arc::make_mut(&mut state.macros).push((state.instructions, offset));
1036 stack.push(Value::from_object(Macro {
1037 name: Value::from(name),
1038 arg_spec,
1039 macro_ref_id,
1040 state_id: state.id,
1041 closure,
1042 caller_reference: (flags & MACRO_CALLER) != 0,
1043 }));
1044 }
1045}
1046
1047#[inline(never)]
1048#[cold]
1049fn process_err(err: &mut Error, pc: u32, state: &State) {
1050 if err.line().is_none() {
1052 if let Some(span) = state.instructions.get_span(pc) {
1053 err.set_filename_and_span(state.instructions.name(), span);
1054 } else if let Some(lineno) = state.instructions.get_line(pc) {
1055 err.set_filename_and_line(state.instructions.name(), lineno);
1056 }
1057 }
1058 #[cfg(feature = "debug")]
1060 {
1061 if state.env().debug() && err.debug_info().is_none() {
1062 err.attach_debug_info(state.make_debug_info(pc, state.instructions));
1063 }
1064 }
1065}