difftreelog
feat(evaluator) tailstrict calls
in: master
4 files changed
crates/jsonnet-evaluator/src/error.rsdiffbeforeafterboth9910 RuntimeError(String),10 RuntimeError(String),11 StackOverflow,11 StackOverflow,12 FractionalIndex,13 DivisionByZero,12}14}131514#[derive(Clone, Debug)]16#[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.rsdiffbeforeafterboth2#![feature(type_alias_impl_trait)]2#![feature(type_alias_impl_trait)]3#![feature(debug_non_exhaustive)]3#![feature(debug_non_exhaustive)]4#![allow(macro_expanded_macro_exports_accessed_by_absolute_paths)]4#![allow(macro_expanded_macro_exports_accessed_by_absolute_paths)]5#![feature(stmt_expr_attributes)]5mod ctx;6mod ctx;6mod dynamic;7mod dynamic;7mod error;8mod error;51 }52 }52}53}5455pub struct EvaluationSettings {56 max_stack_frames: usize,57 max_stack_trace_size: usize,58}59impl Default for EvaluationSettings {60 fn default() -> Self {61 EvaluationSettings {62 max_stack_frames: 500,63 max_stack_trace_size: 20,64 }65 }66}536754pub struct FileData(String, LocExpr, Option<Val>);68pub struct FileData(String, LocExpr, Option<Val>);55#[derive(Default)]69#[derive(Default)]61 files: RefCell<HashMap<PathBuf, FileData>>,75 files: RefCell<HashMap<PathBuf, FileData>>,62 globals: RefCell<HashMap<String, Val>>,76 globals: RefCell<HashMap<String, Val>>,7778 settings: EvaluationSettings,63}79}648065thread_local! {81thread_local! {82 /// Contains state for currently executing file83 /// Global state is fine there66 pub static EVAL_STATE: RefCell<Option<EvaluationState>> = RefCell::new(None)84 pub(crate) static EVAL_STATE: RefCell<Option<EvaluationState>> = RefCell::new(None)67}85}86#[inline(always)]68pub(crate) fn with_state<T>(f: impl FnOnce(&EvaluationState) -> T) -> T {87pub(crate) fn with_state<T>(f: impl FnOnce(&EvaluationState) -> T) -> T {69 EVAL_STATE.with(|s| f(s.borrow().as_ref().unwrap()))88 EVAL_STATE.with(89 #[inline(always)]90 |s| f(s.borrow().as_ref().unwrap()),91 )70}92}71pub(crate) fn create_error<T>(err: Error) -> Result<T> {93pub(crate) fn create_error<T>(err: Error) -> Result<T> {72 with_state(|s| s.error(err))94 with_state(|s| s.error(err))73}95}96#[inline(always)]74pub(crate) fn push<T>(e: LocExpr, comment: String, f: impl FnOnce() -> Result<T>) -> Result<T> {97pub(crate) fn push<T>(e: LocExpr, comment: String, f: impl FnOnce() -> Result<T>) -> Result<T> {75 with_state(|s| s.push(e, comment, f))98 with_state(|s| s.push(e, comment, f))76}99}77100101/// Maintains stack trace and import resolution78#[derive(Default, Clone)]102#[derive(Default, Clone)]79pub struct EvaluationState(Rc<EvaluationStateInternals>);103pub struct EvaluationState(Rc<EvaluationStateInternals>);80impl EvaluationState {104impl EvaluationState {189 Context::new().extend(new_bindings, None, None, None)213 Context::new().extend(new_bindings, None, None, None)190 }214 }191215216 #[inline(always)]192 pub fn push<T>(&self, e: LocExpr, comment: String, f: impl FnOnce() -> Result<T>) -> Result<T> {217 pub fn push<T>(&self, e: LocExpr, comment: String, f: impl FnOnce() -> Result<T>) -> Result<T> {193 {218 {194 let mut stack = self.0.stack.borrow_mut();219 let mut stack = self.0.stack.borrow_mut();195 if stack.len() > 500 {220 if stack.len() > self.0.settings.max_stack_frames {196 drop(stack);221 drop(stack);197 return self.error(Error::StackOverflow);222 return self.error(Error::StackOverflow);198 } else {223 } else {240 .borrow()241 .iter()242 .rev()243 .take(self.0.settings.max_stack_trace_size)244 .cloned()245 .collect(),246 )crates/jsonnet-evaluator/src/val.rsdiffbeforeafterboth78}78}79impl FuncDesc {79impl FuncDesc {80 // TODO: Check for unset variables80 // TODO: Check for unset variables81 /// This function is always inlined to make tailstrict work82 #[inline(always)]81 pub fn evaluate(&self, args: Vec<(Option<String>, Val)>) -> Result<Val> {83 pub fn evaluate(&self, args: Vec<(Option<String>, Val)>) -> Result<Val> {82 let mut new_bindings: HashMap<String, LazyBinding> = HashMap::new();84 let mut new_bindings: HashMap<String, LazyBinding> = HashMap::new();83 let future_ctx = Context::new_future();85 let future_ctx = Context::new_future();