1use std::borrow::Cow;
2use std::collections::{BTreeMap, HashSet};
3use std::fmt;
4
5#[cfg(feature = "macros")]
6use std::sync::Arc;
7
8use crate::environment::Environment;
9use crate::error::{Error, ErrorKind};
10use crate::value::Value;
11use crate::vm::loop_object::LoopState;
12
13#[cfg(feature = "macros")]
14use crate::vm::closure_object::Closure;
15
16type Locals<'env> = BTreeMap<&'env str, Value>;
17
18pub(crate) struct Frame<'env> {
19 pub(crate) locals: Locals<'env>,
20 pub(crate) ctx: Value,
21 pub(crate) current_loop: Option<LoopState>,
22
23 #[cfg(feature = "macros")]
29 pub(crate) closure: Option<Arc<Closure>>,
30}
31
32impl<'env> Default for Frame<'env> {
33 fn default() -> Frame<'env> {
34 Frame::new(Value::UNDEFINED)
35 }
36}
37
38impl<'env> Frame<'env> {
39 pub fn new(ctx: Value) -> Frame<'env> {
41 Frame {
42 locals: Locals::new(),
43 ctx,
44 current_loop: None,
45 #[cfg(feature = "macros")]
46 closure: None,
47 }
48 }
49
50 pub fn new_checked(root: Value) -> Result<Frame<'env>, Error> {
52 Ok(Frame::new(ok!(root.validate())))
53 }
54}
55
56#[cfg(feature = "internal_debug")]
57impl fmt::Debug for Frame<'_> {
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 let mut m = f.debug_map();
60 m.entry(&"locals", &self.locals);
61 if let Some(LoopState {
62 object: ref controller,
63 ..
64 }) = self.current_loop
65 {
66 m.entry(&"loop", controller);
67 }
68 if !self.ctx.is_undefined() {
69 m.entry(&"ctx", &self.ctx);
70 }
71 m.finish()
72 }
73}
74
75#[cfg_attr(feature = "internal_debug", derive(Debug))]
76pub(crate) struct Stack {
77 values: Vec<Value>,
78}
79
80impl Default for Stack {
81 fn default() -> Stack {
82 Stack {
83 values: Vec::with_capacity(16),
84 }
85 }
86}
87
88impl Stack {
89 pub fn push(&mut self, arg: Value) {
90 self.values.push(arg);
91 }
92
93 #[track_caller]
94 pub fn pop(&mut self) -> Value {
95 self.values.pop().unwrap()
96 }
97
98 pub fn reverse_top(&mut self, n: usize) {
99 let start = self.values.len() - n;
100 self.values[start..].reverse();
101 }
102
103 pub fn get_call_args(&mut self, n: Option<u16>) -> &[Value] {
104 let n = match n {
105 Some(n) => n as usize,
106 None => self.pop().as_usize().unwrap(),
107 };
108 &self.values[self.values.len() - n..]
109 }
110
111 pub fn drop_top(&mut self, n: usize) {
112 self.values.truncate(self.values.len() - n);
113 }
114
115 pub fn try_pop(&mut self) -> Option<Value> {
116 self.values.pop()
117 }
118
119 #[track_caller]
120 pub fn peek(&self) -> &Value {
121 self.values.last().unwrap()
122 }
123}
124
125impl From<Vec<Value>> for Stack {
126 fn from(values: Vec<Value>) -> Stack {
127 Stack { values }
128 }
129}
130
131pub(crate) struct Context<'env> {
132 env: &'env Environment<'env>,
133 stack: Vec<Frame<'env>>,
134 outer_stack_depth: usize,
135 recursion_limit: usize,
136}
137
138impl fmt::Debug for Context<'_> {
139 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140 let mut vars = Vec::from_iter(self.known_variables(false));
141 vars.sort();
142 f.debug_map()
143 .entries(vars.into_iter().map(|key| {
144 let value = self.load(&key).unwrap_or_default();
145 (key, value)
146 }))
147 .finish()
148 }
149}
150
151impl<'env> Context<'env> {
152 pub fn new(env: &'env Environment<'env>) -> Context<'env> {
154 Context {
155 env,
156 stack: Vec::with_capacity(32),
157 outer_stack_depth: 0,
158 recursion_limit: env.recursion_limit(),
159 }
160 }
161
162 pub fn new_with_frame(env: &'env Environment<'env>, frame: Frame<'env>) -> Context<'env> {
164 let mut rv = Context::new(env);
165 rv.stack.push(frame);
166 rv
167 }
168
169 #[inline(always)]
171 pub fn env(&self) -> &'env Environment<'env> {
172 self.env
173 }
174
175 pub fn store(&mut self, key: &'env str, value: Value) {
177 let top = self.stack.last_mut().unwrap();
178 #[cfg(feature = "macros")]
179 {
180 if let Some(ref closure) = top.closure {
181 closure.store(key, value.clone());
182 }
183 }
184 top.locals.insert(key, value);
185 }
186
187 #[cfg(feature = "macros")]
193 pub fn enclose(&mut self, key: &str) {
194 self.stack
195 .last_mut()
196 .unwrap()
197 .closure
198 .as_mut()
199 .unwrap()
200 .clone()
201 .store_if_missing(key, || self.load(key).unwrap_or(Value::UNDEFINED));
202 }
203
204 #[cfg(feature = "macros")]
206 pub fn closure(&mut self) -> Option<&Arc<Closure>> {
207 self.stack.last_mut().unwrap().closure.as_ref()
208 }
209
210 #[cfg(all(feature = "multi_template", feature = "macros"))]
220 pub fn take_closure(&mut self) -> Option<Arc<Closure>> {
221 self.stack.last_mut().unwrap().closure.take()
222 }
223
224 #[cfg(feature = "macros")]
226 pub fn reset_closure(&mut self, closure: Option<Arc<Closure>>) {
227 self.stack.last_mut().unwrap().closure = closure;
228 }
229
230 #[cfg(feature = "macros")]
232 pub fn clone_base(&self) -> Value {
233 self.stack
234 .first()
235 .map(|x| x.ctx.clone())
236 .unwrap_or_default()
237 }
238
239 pub fn load(&self, key: &str) -> Option<Value> {
241 for frame in self.stack.iter().rev() {
242 if let Some(value) = frame.locals.get(key) {
244 return Some(value.clone());
245 }
246
247 if let Some(ref l) = frame.current_loop {
249 if l.with_loop_var && key == "loop" {
250 return Some(Value::from_dyn_object(l.object.clone()));
251 }
252 }
253
254 if let Some(rv) = frame.ctx.get_attr_fast(key) {
257 return Some(rv);
258 }
259 }
260
261 self.env.get_global(key)
262 }
263
264 pub fn known_variables(&self, with_globals: bool) -> HashSet<Cow<'_, str>> {
266 let mut seen = HashSet::<Cow<'_, str>>::new();
267 for frame in self.stack.iter().rev() {
268 for key in frame.locals.keys() {
269 if !seen.contains(&Cow::Borrowed(*key)) {
270 seen.insert(Cow::Borrowed(key));
271 }
272 }
273
274 if let Some(ref l) = frame.current_loop {
275 if l.with_loop_var && !seen.contains("loop") {
276 seen.insert(Cow::Borrowed("loop"));
277 }
278 }
279
280 if let Ok(iter) = frame.ctx.try_iter() {
281 for key in iter {
282 if let Some(str_key) = key.as_str() {
283 if !seen.contains(&Cow::Borrowed(str_key))
284 && frame.ctx.get_item(&key).is_ok()
285 {
286 seen.insert(Cow::Owned(str_key.to_owned()));
287 }
288 }
289 }
290 }
291 }
292 if with_globals {
293 seen.extend(self.env.globals().map(|x| Cow::Borrowed(x.0)));
294 }
295 seen
296 }
297
298 pub fn push_frame(&mut self, layer: Frame<'env>) -> Result<(), Error> {
300 ok!(self.check_depth());
301 self.stack.push(layer);
302 Ok(())
303 }
304
305 #[track_caller]
307 pub fn pop_frame(&mut self) -> Frame {
308 self.stack.pop().unwrap()
309 }
310
311 #[track_caller]
313 pub fn exports(&self) -> &Locals<'env> {
314 &self.stack.first().unwrap().locals
315 }
316
317 #[track_caller]
319 #[cfg(feature = "multi_template")]
320 pub fn current_locals_mut(&mut self) -> &mut Locals<'env> {
321 &mut self.stack.last_mut().unwrap().locals
322 }
323
324 pub fn current_loop(&mut self) -> Option<&mut LoopState> {
326 self.stack
327 .iter_mut()
328 .rev()
329 .filter_map(|x| x.current_loop.as_mut())
330 .next()
331 }
332
333 pub fn depth(&self) -> usize {
335 self.outer_stack_depth + self.stack.len()
336 }
337
338 #[allow(unused)]
340 pub fn incr_depth(&mut self, delta: usize) -> Result<(), Error> {
341 self.outer_stack_depth += delta;
342 ok!(self.check_depth());
343 Ok(())
344 }
345
346 #[allow(unused)]
348 pub fn decr_depth(&mut self, delta: usize) {
349 self.outer_stack_depth -= delta;
350 }
351
352 fn check_depth(&self) -> Result<(), Error> {
353 if self.depth() > self.recursion_limit {
354 return Err(Error::new(
355 ErrorKind::InvalidOperation,
356 "recursion limit exceeded",
357 ));
358 }
359 Ok(())
360 }
361}