use std::borrow::Cow;
use std::collections::{BTreeMap, HashSet};
use std::fmt;
use std::sync::Arc;
use crate::environment::Environment;
use crate::error::{Error, ErrorKind};
use crate::value::{Value, ValueIter};
use crate::vm::loop_object::Loop;
#[cfg(feature = "macros")]
use crate::vm::closure_object::Closure;
type Locals<'env> = BTreeMap<&'env str, Value>;
pub(crate) struct LoopState {
pub(crate) with_loop_var: bool,
pub(crate) recurse_jump_target: Option<usize>,
pub(crate) current_recursion_jump: Option<(usize, bool)>,
pub(crate) iterator: ValueIter,
pub(crate) object: Arc<Loop>,
}
pub(crate) struct Frame<'env> {
pub(crate) locals: Locals<'env>,
pub(crate) ctx: Value,
pub(crate) current_loop: Option<LoopState>,
#[cfg(feature = "macros")]
pub(crate) closure: Option<Arc<Closure>>,
}
impl<'env> Default for Frame<'env> {
fn default() -> Frame<'env> {
Frame::new(Value::UNDEFINED)
}
}
impl<'env> Frame<'env> {
pub fn new(ctx: Value) -> Frame<'env> {
Frame {
locals: Locals::new(),
ctx,
current_loop: None,
#[cfg(feature = "macros")]
closure: None,
}
}
pub fn new_checked(root: Value) -> Result<Frame<'env>, Error> {
Ok(Frame::new(ok!(root.validate())))
}
}
#[cfg(feature = "internal_debug")]
impl<'env> fmt::Debug for Frame<'env> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut m = f.debug_map();
m.entry(&"locals", &self.locals);
if let Some(LoopState {
object: ref controller,
..
}) = self.current_loop
{
m.entry(&"loop", controller);
}
if !self.ctx.is_undefined() {
m.entry(&"ctx", &self.ctx);
}
m.finish()
}
}
#[cfg_attr(feature = "internal_debug", derive(Debug))]
pub(crate) struct Stack {
values: Vec<Value>,
}
impl Default for Stack {
fn default() -> Stack {
Stack {
values: Vec::with_capacity(16),
}
}
}
impl Stack {
pub fn push(&mut self, arg: Value) {
self.values.push(arg);
}
#[track_caller]
pub fn pop(&mut self) -> Value {
self.values.pop().unwrap()
}
pub fn reverse_top(&mut self, n: usize) {
let start = self.values.len() - n;
self.values[start..].reverse();
}
pub fn get_call_args(&mut self, n: Option<u16>) -> &[Value] {
let n = match n {
Some(n) => n as usize,
None => self.pop().as_usize().unwrap(),
};
&self.values[self.values.len() - n..]
}
pub fn drop_top(&mut self, n: usize) {
self.values.truncate(self.values.len() - n);
}
pub fn try_pop(&mut self) -> Option<Value> {
self.values.pop()
}
#[track_caller]
pub fn peek(&self) -> &Value {
self.values.last().unwrap()
}
}
impl From<Vec<Value>> for Stack {
fn from(values: Vec<Value>) -> Stack {
Stack { values }
}
}
pub(crate) struct Context<'env> {
stack: Vec<Frame<'env>>,
outer_stack_depth: usize,
recursion_limit: usize,
}
impl<'env> fmt::Debug for Context<'env> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn dump<'a>(
m: &mut std::fmt::DebugMap,
seen: &mut HashSet<Cow<'a, str>>,
ctx: &'a Context<'a>,
) -> fmt::Result {
for frame in ctx.stack.iter().rev() {
for (key, value) in frame.locals.iter() {
if !seen.contains(&Cow::Borrowed(*key)) {
m.entry(&key, value);
seen.insert(Cow::Borrowed(key));
}
}
if let Some(ref l) = frame.current_loop {
if l.with_loop_var && !seen.contains("loop") {
m.entry(&"loop", &l.object);
seen.insert(Cow::Borrowed("loop"));
}
}
if let Ok(iter) = frame.ctx.try_iter() {
for key in iter {
if let Some(str_key) = key.as_str() {
if !seen.contains(&Cow::Borrowed(str_key)) {
if let Ok(value) = frame.ctx.get_item(&key) {
m.entry(&str_key, &value);
seen.insert(Cow::Owned(str_key.to_owned()));
}
}
}
}
}
}
Ok(())
}
let mut m = f.debug_map();
let mut seen = HashSet::new();
ok!(dump(&mut m, &mut seen, self));
m.finish()
}
}
impl<'env> Context<'env> {
pub fn new(recursion_limit: usize) -> Context<'env> {
Context {
stack: Vec::with_capacity(32),
outer_stack_depth: 0,
recursion_limit,
}
}
pub fn new_with_frame(frame: Frame<'env>, recursion_limit: usize) -> Context<'env> {
let mut rv = Context::new(recursion_limit);
rv.stack.push(frame);
rv
}
pub fn store(&mut self, key: &'env str, value: Value) {
let top = self.stack.last_mut().unwrap();
#[cfg(feature = "macros")]
{
if let Some(ref closure) = top.closure {
closure.store(key, value.clone());
}
}
top.locals.insert(key, value);
}
#[cfg(feature = "macros")]
pub fn enclose(&mut self, env: &Environment, key: &str) {
self.stack
.last_mut()
.unwrap()
.closure
.as_mut()
.unwrap()
.clone()
.store_if_missing(key, || self.load(env, key).unwrap_or(Value::UNDEFINED));
}
#[cfg(feature = "macros")]
pub fn closure(&mut self) -> Option<&Arc<Closure>> {
self.stack.last_mut().unwrap().closure.as_ref()
}
#[cfg(all(feature = "multi_template", feature = "macros"))]
pub fn take_closure(&mut self) -> Option<Arc<Closure>> {
self.stack.last_mut().unwrap().closure.take()
}
#[cfg(feature = "macros")]
pub fn reset_closure(&mut self, closure: Option<Arc<Closure>>) {
self.stack.last_mut().unwrap().closure = closure;
}
#[cfg(feature = "macros")]
pub fn clone_base(&self) -> Value {
self.stack
.first()
.map(|x| x.ctx.clone())
.unwrap_or_default()
}
pub fn load(&self, env: &Environment, key: &str) -> Option<Value> {
for frame in self.stack.iter().rev() {
if let Some(value) = frame.locals.get(key) {
return Some(value.clone());
}
if let Some(ref l) = frame.current_loop {
if l.with_loop_var && key == "loop" {
return Some(Value::from_dyn_object(l.object.clone()));
}
}
if let Some(rv) = frame.ctx.get_attr_fast(key) {
return Some(rv);
}
}
env.get_global(key)
}
pub fn push_frame(&mut self, layer: Frame<'env>) -> Result<(), Error> {
ok!(self.check_depth());
self.stack.push(layer);
Ok(())
}
#[track_caller]
pub fn pop_frame(&mut self) -> Frame {
self.stack.pop().unwrap()
}
#[track_caller]
pub fn exports(&self) -> &Locals<'env> {
&self.stack.first().unwrap().locals
}
#[track_caller]
#[cfg(feature = "multi_template")]
pub fn current_locals_mut(&mut self) -> &mut Locals<'env> {
&mut self.stack.last_mut().unwrap().locals
}
pub fn current_loop(&mut self) -> Option<&mut LoopState> {
self.stack
.iter_mut()
.rev()
.filter_map(|x| x.current_loop.as_mut())
.next()
}
pub fn depth(&self) -> usize {
self.outer_stack_depth + self.stack.len()
}
#[allow(unused)]
pub fn incr_depth(&mut self, delta: usize) -> Result<(), Error> {
self.outer_stack_depth += delta;
ok!(self.check_depth());
Ok(())
}
#[allow(unused)]
pub fn decr_depth(&mut self, delta: usize) {
self.outer_stack_depth -= delta;
}
fn check_depth(&self) -> Result<(), Error> {
if self.depth() > self.recursion_limit {
return Err(Error::new(
ErrorKind::InvalidOperation,
"recursion limit exceeded",
));
}
Ok(())
}
}