difftreelog
feat(evaluator) tailstrict calls
in: master
4 files changed
crates/jsonnet-evaluator/src/error.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/error.rs
+++ b/crates/jsonnet-evaluator/src/error.rs
@@ -9,6 +9,8 @@
RuntimeError(String),
StackOverflow,
+ FractionalIndex,
+ DivisionByZero,
}
#[derive(Clone, Debug)]
crates/jsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth135135136 // Num X Num136 // Num X Num137 (Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Num(v1 * v2),137 (Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Num(v1 * v2),138 (Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => Val::Num(v1 / v2),138 (Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => {139 if *v2 <= f64::EPSILON {140 create_error(crate::Error::DivisionByZero)?141 }142 Val::Num(v1 / v2)143 }139144140 (Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::Num(v1 - v2),145 (Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::Num(v1 - v2),141146305 })310 })306}311}307312313#[inline(always)]308pub fn evaluate(context: Context, expr: &LocExpr) -> Result<Val> {314pub fn evaluate(context: Context, expr: &LocExpr) -> Result<Val> {309 use Expr::*;315 use Expr::*;310 let locexpr = expr.clone();316 let locexpr = expr.clone();356 create_error(crate::Error::NoSuchField(s))?362 create_error(crate::Error::NoSuchField(s))?357 }363 }358 }364 }359 (Val::Arr(v), Val::Num(n)) => v365 (Val::Arr(v), Val::Num(n)) => {366 if n.fract() > f64::EPSILON {367 create_error(crate::Error::FractionalIndex)?368 }360 .get(n as usize)369 v.get(n as usize)361 .unwrap_or_else(|| panic!("out of bounds"))370 .unwrap_or_else(|| panic!("out of bounds"))362 .clone()371 .clone()363 .unwrap_if_lazy()?,372 .unwrap_if_lazy()?373 }364 (Val::Str(s), Val::Num(n)) => {374 (Val::Str(s), Val::Num(n)) => {365 Val::Str(s.chars().skip(n as usize).take(1).collect())375 Val::Str(s.chars().skip(n as usize).take(1).collect())366 }376 }511 panic!("bad trace call");521 panic!("bad trace call");512 }522 }513 }523 }524 ("std", "pow") => {525 assert_eq!(args.len(), 2);526 if let (Val::Num(a), Val::Num(b)) = (527 evaluate(context.clone(), &args[0].1)?,528 evaluate(context, &args[1].1)?,529 ) {530 Val::Num(a.powf(b))531 } else {532 panic!("bad pow call");533 }534 }514 (ns, name) => panic!("Intristic not found: {}.{}", ns, name),535 (ns, name) => panic!("Intristic not found: {}.{}", ns, name),515 },536 },516 Val::Func(f) => push(locexpr, "function call".to_owned(), || {537 Val::Func(f) => {538 let body = #[inline(always)]539 || {517 f.evaluate(540 f.evaluate(518 args.clone()541 args.clone()519 .into_iter()542 .into_iter()520 .map(move |a| {543 .map(544 #[inline(always)]545 move |a| {521 (546 Ok((522 a.clone().0,547 a.clone().0,523 if *tailstrict {548 if *tailstrict {524 Val::Lazy(LazyVal::new_resolved(549 Val::Lazy(LazyVal::new_resolved(evaluate(525 evaluate(context.clone(), &a.1).unwrap(),550 context.clone(),551 &a.1,526 ))552 )?))527 } else {553 } else {528 Val::Lazy(lazy_val!(554 Val::Lazy(lazy_val!(529 closure!(clone context, clone a, || evaluate(context.clone(), &a.clone().1))555 closure!(clone context, clone a, || evaluate(context.clone(), &a.clone().1))530 ))556 ))531 },557 },532 )558 ))533 })559 },534 .collect(),560 )561 .collect::<Result<Vec<_>>>()?,535 )562 )536 })?,563 };564 if *tailstrict {565 body()?566 } else {567 push(locexpr, "function call".to_owned(), body)?568 }569 }537 _ => panic!("{:?} is not a function", value),570 _ => panic!("{:?} is not a function", value),538 }571 }539 }572 }578 )?611 )?579 } else {612 } else {580 match cond_else {613 match cond_else {581 Some(v) => push(v.clone(), "if condition 'else' branch".to_owned(), || {614 Some(v) => evaluate(context, v)?,582 evaluate(context, v)583 })?,584 None => Val::Null,615 None => Val::Null,585 }616 }586 }617 }crates/jsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/lib.rs
+++ b/crates/jsonnet-evaluator/src/lib.rs
@@ -2,6 +2,7 @@
#![feature(type_alias_impl_trait)]
#![feature(debug_non_exhaustive)]
#![allow(macro_expanded_macro_exports_accessed_by_absolute_paths)]
+#![feature(stmt_expr_attributes)]
mod ctx;
mod dynamic;
mod error;
@@ -51,6 +52,19 @@
}
}
+pub struct EvaluationSettings {
+ max_stack_frames: usize,
+ max_stack_trace_size: usize,
+}
+impl Default for EvaluationSettings {
+ fn default() -> Self {
+ EvaluationSettings {
+ max_stack_frames: 500,
+ max_stack_trace_size: 20,
+ }
+ }
+}
+
pub struct FileData(String, LocExpr, Option<Val>);
#[derive(Default)]
pub struct EvaluationStateInternals {
@@ -60,21 +74,31 @@
/// printing stacktraces
files: RefCell<HashMap<PathBuf, FileData>>,
globals: RefCell<HashMap<String, Val>>,
+
+ settings: EvaluationSettings,
}
thread_local! {
- pub static EVAL_STATE: RefCell<Option<EvaluationState>> = RefCell::new(None)
+ /// Contains state for currently executing file
+ /// Global state is fine there
+ pub(crate) static EVAL_STATE: RefCell<Option<EvaluationState>> = RefCell::new(None)
}
+#[inline(always)]
pub(crate) fn with_state<T>(f: impl FnOnce(&EvaluationState) -> T) -> T {
- EVAL_STATE.with(|s| f(s.borrow().as_ref().unwrap()))
+ EVAL_STATE.with(
+ #[inline(always)]
+ |s| f(s.borrow().as_ref().unwrap()),
+ )
}
pub(crate) fn create_error<T>(err: Error) -> Result<T> {
with_state(|s| s.error(err))
}
+#[inline(always)]
pub(crate) fn push<T>(e: LocExpr, comment: String, f: impl FnOnce() -> Result<T>) -> Result<T> {
with_state(|s| s.push(e, comment, f))
}
+/// Maintains stack trace and import resolution
#[derive(Default, Clone)]
pub struct EvaluationState(Rc<EvaluationStateInternals>);
impl EvaluationState {
@@ -189,10 +213,11 @@
Context::new().extend(new_bindings, None, None, None)
}
+ #[inline(always)]
pub fn push<T>(&self, e: LocExpr, comment: String, f: impl FnOnce() -> Result<T>) -> Result<T> {
{
let mut stack = self.0.stack.borrow_mut();
- if stack.len() > 500 {
+ if stack.len() > self.0.settings.max_stack_frames {
drop(stack);
return self.error(Error::StackOverflow);
} else {
@@ -209,7 +234,16 @@
}
}
pub fn stack_trace(&self) -> StackTrace {
- StackTrace(self.0.stack.borrow().iter().rev().cloned().collect())
+ StackTrace(
+ self.0
+ .stack
+ .borrow()
+ .iter()
+ .rev()
+ .take(self.0.settings.max_stack_trace_size)
+ .cloned()
+ .collect(),
+ )
}
pub fn error<T>(&self, err: Error) -> Result<T> {
Err(LocError(err, self.stack_trace()))
crates/jsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/val.rs
+++ b/crates/jsonnet-evaluator/src/val.rs
@@ -78,6 +78,8 @@
}
impl FuncDesc {
// TODO: Check for unset variables
+ /// This function is always inlined to make tailstrict work
+ #[inline(always)]
pub fn evaluate(&self, args: Vec<(Option<String>, Val)>) -> Result<Val> {
let mut new_bindings: HashMap<String, LazyBinding> = HashMap::new();
let future_ctx = Context::new_future();