difftreelog
refactor! implement stack size limit using thread local
in: master
18 files changed
bindings/jsonnet/src/lib.rsdiffbeforeafterboth858586/// Set the maximum stack depth.86/// Set the maximum stack depth.87#[no_mangle]87#[no_mangle]88pub extern "C" fn jsonnet_max_stack(vm: &State, v: c_uint) {88pub extern "C" fn jsonnet_max_stack(_vm: &State, _v: c_uint) {89 vm.settings_mut().max_stack = v as usize;89 todo!()90}90}919192/// Set the number of objects required before a garbage collection cycle is allowed.92/// Set the number of objects required before a garbage collection cycle is allowed.cmds/jrsonnet/src/main.rsdiffbeforeafterboth556use clap::{AppSettings, IntoApp, Parser};6use clap::{AppSettings, IntoApp, Parser};7use clap_complete::Shell;7use clap_complete::Shell;8use jrsonnet_cli::{ConfigureState, GcOpts, GeneralOpts, ManifestOpts, OutputOpts};8use jrsonnet_cli::{ConfigureState, GeneralOpts, ManifestOpts, OutputOpts};9use jrsonnet_evaluator::{error::LocError, State};9use jrsonnet_evaluator::{error::LocError, State};101011#[cfg(feature = "mimalloc")]11#[cfg(feature = "mimalloc")]60 output: OutputOpts,60 output: OutputOpts,61 #[clap(flatten)]61 #[clap(flatten)]62 debug: DebugOpts,62 debug: DebugOpts,63 #[clap(flatten)]64 gc: GcOpts,65}63}666467fn main() {65fn main() {113}111}114112115fn main_catch(opts: Opts) -> bool {113fn main_catch(opts: Opts) -> bool {116 let _printer = opts.gc.stats_printer();117 let s = State::default();114 let s = State::default();118 if let Err(e) = main_real(&s, opts) {115 if let Err(e) = main_real(&s, opts) {119 if let Error::Evaluation(e) = e {116 if let Error::Evaluation(e) = e {127}124}128125129fn main_real(s: &State, opts: Opts) -> Result<(), Error> {126fn main_real(s: &State, opts: Opts) -> Result<(), Error> {130 opts.general.configure(s)?;127 let _guards = opts.general.configure(s)?;131 opts.manifest.configure(s)?;128 opts.manifest.configure(s)?;132129133 let input = opts.input.input.ok_or(Error::MissingInputArgument)?;130 let input = opts.input.input.ok_or(Error::MissingInputArgument)?;crates/jrsonnet-cli/src/lib.rsdiffbeforeafterboth3mod tla;3mod tla;4mod trace;4mod trace;556use std::{env, path::PathBuf};6use std::{env, marker::PhantomData, path::PathBuf};778use clap::Parser;8use clap::Parser;9use jrsonnet_evaluator::{error::Result, FileImportResolver, State};9use jrsonnet_evaluator::{10 error::Result, stack::StackDepthLimitOverrideGuard, FileImportResolver, State,11};10use jrsonnet_gcmodule::with_thread_object_space;12use jrsonnet_gcmodule::with_thread_object_space;11pub use manifest::*;13pub use manifest::*;12pub use stdlib::*;14pub use stdlib::*;13pub use tla::*;15pub use tla::*;14pub use trace::*;16pub use trace::*;151716pub trait ConfigureState {18pub trait ConfigureState {19 type Guards;2017 fn configure(&self, s: &State) -> Result<()>;21 fn configure(&self, s: &State) -> Result<Self::Guards>;18}22}192320#[derive(Parser)]24#[derive(Parser)]44 jpath: Vec<PathBuf>,48 jpath: Vec<PathBuf>,45}49}46impl ConfigureState for MiscOpts {50impl ConfigureState for MiscOpts {51 type Guards = StackDepthLimitOverrideGuard;47 fn configure(&self, s: &State) -> Result<()> {52 fn configure(&self, s: &State) -> Result<Self::Guards> {48 let mut library_paths = self.jpath.clone();53 let mut library_paths = self.jpath.clone();49 library_paths.reverse();54 library_paths.reverse();50 if let Some(path) = env::var_os("JSONNET_PATH") {55 if let Some(path) = env::var_os("JSONNET_PATH") {535854 s.set_import_resolver(Box::new(FileImportResolver::new(library_paths)));59 s.set_import_resolver(Box::new(FileImportResolver::new(library_paths)));556056 s.set_max_stack(self.max_stack);61 let _depth_limit = jrsonnet_evaluator::stack::limit_stack_depth(self.max_stack);57 Ok(())62 Ok(_depth_limit)58 }63 }59}64}606573 #[clap(flatten)]78 #[clap(flatten)]74 trace: TraceOpts,79 trace: TraceOpts,8081 #[clap(flatten)]82 gc: GcOpts,75}83}768477impl ConfigureState for GeneralOpts {85impl ConfigureState for GeneralOpts {86 type Guards = (87 <MiscOpts as ConfigureState>::Guards,88 <GcOpts as ConfigureState>::Guards,89 );78 fn configure(&self, s: &State) -> Result<()> {90 fn configure(&self, s: &State) -> Result<Self::Guards> {79 // Configure trace first, because tla-code/ext-code can throw91 // Configure trace first, because tla-code/ext-code can throw80 self.trace.configure(s)?;92 self.trace.configure(s)?;81 self.misc.configure(s)?;93 let misc_guards = self.misc.configure(s)?;82 self.tla.configure(s)?;94 self.tla.configure(s)?;83 self.std.configure(s)?;95 self.std.configure(s)?;96 let gc_guards = self.gc.configure(s)?;84 Ok(())97 Ok((misc_guards, gc_guards))85 }98 }86}99}87100100 #[clap(long)]113 #[clap(long)]101 gc_collect_before_printing_stats: bool,114 gc_collect_before_printing_stats: bool,102}115}103impl GcOpts {116impl ConfigureState for GcOpts {104 pub fn stats_printer(&self) -> (Option<GcStatsPrinter>, Option<LeakSpace>) {117 type Guards = (Option<GcStatsPrinter>, Option<LeakSpace>);118119 fn configure(&self, _s: &State) -> Result<Self::Guards> {105 // Constructed structs have side-effects in Drop impl120 // Constructed structs have side-effects in Drop impl106 #[allow(clippy::unnecessary_lazy_evaluations)]121 #[allow(clippy::unnecessary_lazy_evaluations)]107 (122 Ok((108 self.gc_print_stats.then(|| GcStatsPrinter {123 self.gc_print_stats.then(|| GcStatsPrinter {109 collect_before_printing_stats: self.gc_collect_before_printing_stats,124 collect_before_printing_stats: self.gc_collect_before_printing_stats,110 }),125 }),111 (!self.gc_collect_on_exit).then(|| LeakSpace {}),126 (!self.gc_collect_on_exit).then(|| LeakSpace(PhantomData)),112 )127 ))113 }128 }114}129}115130116pub struct LeakSpace {}131pub struct LeakSpace(PhantomData<()>);117132118impl Drop for LeakSpace {133impl Drop for LeakSpace {119 fn drop(&mut self) {134 fn drop(&mut self) {crates/jrsonnet-cli/src/manifest.rsdiffbeforeafterboth47 exp_preserve_order: bool,47 exp_preserve_order: bool,48}48}49impl ConfigureState for ManifestOpts {49impl ConfigureState for ManifestOpts {50 type Guards = ();50 fn configure(&self, s: &State) -> Result<()> {51 fn configure(&self, s: &State) -> Result<()> {51 if self.string {52 if self.string {52 s.set_manifest_format(ManifestFormat::String);53 s.set_manifest_format(ManifestFormat::String);crates/jrsonnet-cli/src/stdlib.rsdiffbeforeafterboth106 ext_code_file: Vec<ExtFile>,106 ext_code_file: Vec<ExtFile>,107}107}108impl ConfigureState for StdOpts {108impl ConfigureState for StdOpts {109 type Guards = ();109 fn configure(&self, s: &State) -> Result<()> {110 fn configure(&self, s: &State) -> Result<()> {110 if self.no_stdlib {111 if self.no_stdlib {111 return Ok(());112 return Ok(());crates/jrsonnet-cli/src/tla.rsdiffbeforeafterboth47 tla_code_file: Vec<ExtFile>,47 tla_code_file: Vec<ExtFile>,48}48}49impl ConfigureState for TLAOpts {49impl ConfigureState for TLAOpts {50 type Guards = ();50 fn configure(&self, s: &State) -> Result<()> {51 fn configure(&self, s: &State) -> Result<()> {51 for tla in self.tla_str.iter() {52 for tla in self.tla_str.iter() {52 s.add_tla_str((&tla.name as &str).into(), (&tla.value as &str).into());53 s.add_tla_str((&tla.name as &str).into(), (&tla.value as &str).into());crates/jrsonnet-cli/src/trace.rsdiffbeforeafterboth41 max_trace: usize,41 max_trace: usize,42}42}43impl ConfigureState for TraceOpts {43impl ConfigureState for TraceOpts {44 type Guards = ();44 fn configure(&self, s: &State) -> Result<()> {45 fn configure(&self, s: &State) -> Result<()> {45 let resolver = PathResolver::new_cwd_fallback();46 let resolver = PathResolver::new_cwd_fallback();46 match self47 match selfcrates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth20exp-preserve-order = []20exp-preserve-order = []21# Implements field destructuring21# Implements field destructuring22exp-destruct = ["jrsonnet-parser/exp-destruct"]22exp-destruct = ["jrsonnet-parser/exp-destruct"]23# Provide Typed for conversions to/from serde_json::Value type23# Improves performance, and implements some useful things using nightly-only features24serde_json = ["dep:serde_json"]24nightly = []252526[dependencies]26[dependencies]27jrsonnet-interner = { path = "../jrsonnet-interner", version = "0.4.2" }27jrsonnet-interner = { path = "../jrsonnet-interner", version = "0.4.2" }crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth223use jrsonnet_gcmodule::Trace;3use jrsonnet_gcmodule::Trace;4use jrsonnet_interner::IStr;4use jrsonnet_interner::IStr;5use jrsonnet_parser::{BinaryOpType, ExprLocation, Source, SourcePath, UnaryOpType};5use jrsonnet_parser::{BinaryOpType, ExprLocation, LocExpr, Source, SourcePath, UnaryOpType};6use jrsonnet_types::ValType;6use jrsonnet_types::ValType;7use thiserror::Error;7use thiserror::Error;889use crate::{stdlib::format::FormatError, typed::TypeLocError};9use crate::{function::CallLocation, stdlib::format::FormatError, typed::TypeLocError};101011fn format_found(list: &[IStr], what: &str) -> String {11fn format_found(list: &[IStr], what: &str) -> String {12 if list.is_empty() {12 if list.is_empty() {262 }262 }263}263}264265pub trait ErrorSource {266 fn to_location(self) -> Option<ExprLocation>;267}268impl ErrorSource for &LocExpr {269 fn to_location(self) -> Option<ExprLocation> {270 Some(self.1.clone())271 }272}273impl ErrorSource for &ExprLocation {274 fn to_location(self) -> Option<ExprLocation> {275 Some(self.clone())276 }277}278impl ErrorSource for CallLocation<'_> {279 fn to_location(self) -> Option<ExprLocation> {280 self.0.cloned()281 }282}264283265pub type Result<V, E = LocError> = std::result::Result<V, E>;284pub type Result<V, E = LocError> = std::result::Result<V, E>;285pub trait ResultExt: Sized {286 #[must_use]287 fn with_description<O: Into<String>>(self, msg: impl FnOnce() -> O) -> Self;288 #[must_use]289 fn description(self, msg: &str) -> Self {290 self.with_description(|| msg)291 }292293 #[must_use]294 fn with_description_src<O: Into<String>>(295 self,296 src: impl ErrorSource,297 msg: impl FnOnce() -> O,298 ) -> Self;299 #[must_use]300 fn description_src(self, src: impl ErrorSource, msg: &str) -> Self {301 self.with_description_src(src, || msg)302 }303}304impl<T> ResultExt for Result<T, LocError> {305 fn with_description<O: Into<String>>(mut self, msg: impl FnOnce() -> O) -> Self {306 if let Err(e) = &mut self {307 let trace = e.trace_mut();308 trace.0.push(StackTraceElement {309 location: None,310 desc: msg().into(),311 });312 }313 self314 }315316 fn with_description_src<O: Into<String>>(317 mut self,318 src: impl ErrorSource,319 msg: impl FnOnce() -> O,320 ) -> Self {321 if let Err(e) = &mut self {322 let trace = e.trace_mut();323 trace.0.push(StackTraceElement {324 location: src.to_location(),325 desc: msg().into(),326 });327 }328 self329 }330}266331267#[macro_export]332#[macro_export]268macro_rules! throw {333macro_rules! throw {crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth34pub fn evaluate_field_name(s: State, ctx: Context, field_name: &FieldName) -> Result<Option<IStr>> {34pub fn evaluate_field_name(s: State, ctx: Context, field_name: &FieldName) -> Result<Option<IStr>> {35 Ok(match field_name {35 Ok(match field_name {36 FieldName::Fixed(n) => Some(n.clone()),36 FieldName::Fixed(n) => Some(n.clone()),37 FieldName::Dyn(expr) => s.push(37 FieldName::Dyn(expr) => State::push(38 CallLocation::new(&expr.1),38 CallLocation::new(&expr.1),39 || "evaluating field name".to_string(),39 || "evaluating field name".to_string(),40 || {40 || {185 .with_visibility(*visibility)185 .with_visibility(*visibility)186 .with_location(value.1.clone())186 .with_location(value.1.clone())187 .bindable(187 .bindable(tb!(UnboundValue {188 s.clone(),189 tb!(UnboundValue {190 uctx: uctx.clone(),188 uctx: uctx.clone(),191 value: value.clone(),189 value: value.clone(),192 name: name.clone()190 name: name.clone()193 }),191 }))?;194 )?;195 }192 }196 Member::Field(FieldMember {193 Member::Field(FieldMember {234 .hide()231 .hide()235 .with_location(value.1.clone())232 .with_location(value.1.clone())236 .bindable(233 .bindable(tb!(UnboundMethod {237 s.clone(),238 tb!(UnboundMethod {239 uctx: uctx.clone(),234 uctx: uctx.clone(),240 value: value.clone(),235 value: value.clone(),241 params: params.clone(),236 params: params.clone(),242 name: name.clone()237 name: name.clone()243 }),238 }))?;244 )?;245 }239 }246 Member::BindStmt(_) => {}240 Member::BindStmt(_) => {}325 .with_location(obj.value.1.clone())319 .with_location(obj.value.1.clone())326 .with_add(obj.plus)320 .with_add(obj.plus)327 .bindable(321 .bindable(tb!(UnboundValue {328 s.clone(),329 tb!(UnboundValue {330 uctx,322 uctx,331 value: obj.value.clone(),323 value: obj.value.clone(),332 }),324 }))?;333 )?;334 }325 }335 v => throw!(FieldMustBeStringGot(v.value_type())),326 v => throw!(FieldMustBeStringGot(v.value_type())),364 if tailstrict {355 if tailstrict {365 body()?356 body()?366 } else {357 } else {367 s.push(loc, || format!("function <{}> call", f.name()), body)?358 State::push(loc, || format!("function <{}> call", f.name()), body)?368 }359 }369 }360 }370 v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type())),361 v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type())),374pub fn evaluate_assert(s: State, ctx: Context, assertion: &AssertStmt) -> Result<()> {365pub fn evaluate_assert(s: State, ctx: Context, assertion: &AssertStmt) -> Result<()> {375 let value = &assertion.0;366 let value = &assertion.0;376 let msg = &assertion.1;367 let msg = &assertion.1;377 let assertion_result = s.push(368 let assertion_result = State::push(378 CallLocation::new(&value.1),369 CallLocation::new(&value.1),379 || "assertion condition".to_owned(),370 || "assertion condition".to_owned(),380 || bool::from_untyped(evaluate(s.clone(), ctx.clone(), value)?, s.clone()),371 || bool::from_untyped(evaluate(s.clone(), ctx.clone(), value)?, s.clone()),381 )?;372 )?;382 if !assertion_result {373 if !assertion_result {383 s.push(374 State::push(384 CallLocation::new(&value.1),375 CallLocation::new(&value.1),385 || "assertion failure".to_owned(),376 || "assertion failure".to_owned(),386 || {377 || {432 Num(v) => Val::new_checked_num(*v)?,423 Num(v) => Val::new_checked_num(*v)?,433 BinaryOp(v1, o, v2) => evaluate_binary_op_special(s, ctx, v1, *o, v2)?,424 BinaryOp(v1, o, v2) => evaluate_binary_op_special(s, ctx, v1, *o, v2)?,434 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(s, ctx, v)?)?,425 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(s, ctx, v)?)?,435 Var(name) => s.push(426 Var(name) => State::push(436 CallLocation::new(loc),427 CallLocation::new(loc),437 || format!("variable <{name}> access"),428 || format!("variable <{name}> access"),438 || ctx.binding(name.clone())?.evaluate(s.clone()),429 || ctx.binding(name.clone())?.evaluate(s.clone()),442 evaluate(s.clone(), ctx.clone(), value)?,433 evaluate(s.clone(), ctx.clone(), value)?,443 evaluate(s.clone(), ctx, index)?,434 evaluate(s.clone(), ctx, index)?,444 ) {435 ) {445 (Val::Obj(v), Val::Str(key)) => s.push(436 (Val::Obj(v), Val::Str(key)) => State::push(446 CallLocation::new(loc),437 CallLocation::new(loc),447 || format!("field <{key}> access"),438 || format!("field <{key}> access"),448 || match v.get(s.clone(), key.clone()) {439 || match v.get(s.clone(), key.clone()) {571 evaluate_assert(s.clone(), ctx.clone(), assert)?;562 evaluate_assert(s.clone(), ctx.clone(), assert)?;572 evaluate(s, ctx, returned)?563 evaluate(s, ctx, returned)?573 }564 }574 ErrorStmt(e) => s.push(565 ErrorStmt(e) => State::push(575 CallLocation::new(loc),566 CallLocation::new(loc),576 || "error statement".to_owned(),567 || "error statement".to_owned(),577 || {568 || {585 cond_then,576 cond_then,586 cond_else,577 cond_else,587 } => {578 } => {588 if s.push(579 if State::push(589 CallLocation::new(loc),580 CallLocation::new(loc),590 || "if condition".to_owned(),581 || "if condition".to_owned(),591 || bool::from_untyped(evaluate(s.clone(), ctx.clone(), &cond.0)?, s.clone()),582 || bool::from_untyped(evaluate(s.clone(), ctx.clone(), &cond.0)?, s.clone()),607 desc: &'static str,598 desc: &'static str,608 ) -> Result<Option<T>> {599 ) -> Result<Option<T>> {609 if let Some(value) = expr {600 if let Some(value) = expr {610 Ok(Some(s.push(601 Ok(Some(State::push(611 loc,602 loc,612 || format!("slice {desc}"),603 || format!("slice {desc}"),613 || T::from_untyped(evaluate(s.clone(), ctx.clone(), value)?, s.clone()),604 || T::from_untyped(evaluate(s.clone(), ctx.clone(), value)?, s.clone()),630 let tmp = loc.clone().0;621 let tmp = loc.clone().0;631 let resolved_path = s.resolve_from(tmp.source_path(), path as &str)?;622 let resolved_path = s.resolve_from(tmp.source_path(), path as &str)?;632 match i {623 match i {633 Import(_) => s.push(624 Import(_) => State::push(634 CallLocation::new(loc),625 CallLocation::new(loc),635 || format!("import {:?}", path.clone()),626 || format!("import {:?}", path.clone()),636 || s.import_resolved(resolved_path),627 || s.import_resolved(resolved_path),crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth1//! jsonnet interpreter implementation1//! jsonnet interpreter implementation22#![cfg_attr(feature = "nightly", feature(thread_local))]3#![deny(unsafe_op_in_unsafe_fn)]3#![deny(unsafe_op_in_unsafe_fn)]4#![warn(4#![warn(5 clippy::all,5 clippy::all,51mod integrations;51mod integrations;52mod map;52mod map;53mod obj;53mod obj;54pub mod stack;54pub mod stdlib;55pub mod stdlib;55pub mod trace;56pub mod trace;56pub mod typed;57pub mod typed;676868pub use ctx::*;69pub use ctx::*;69pub use dynamic::*;70pub use dynamic::*;70use error::{Error::*, LocError, Result, StackTraceElement};71use error::{Error::*, LocError, Result, ResultExt};71pub use evaluate::*;72pub use evaluate::*;72use function::{CallLocation, TlaArg};73use function::{CallLocation, TlaArg};73use gc::{GcHashMap, TraceBox};74use gc::{GcHashMap, TraceBox};78pub use jrsonnet_parser as parser;79pub use jrsonnet_parser as parser;79use jrsonnet_parser::*;80use jrsonnet_parser::*;80pub use obj::*;81pub use obj::*;82use stack::check_depth;81use trace::{CompactFormat, TraceFormat};83use trace::{CompactFormat, TraceFormat};82pub use val::{ManifestFormat, Thunk, Val};84pub use val::{ManifestFormat, Thunk, Val};8385143145144/// Dynamically reconfigurable evaluation settings146/// Dynamically reconfigurable evaluation settings145pub struct EvaluationSettings {147pub struct EvaluationSettings {146 /// Limits recursion by limiting the number of stack frames147 pub max_stack: usize,148 /// Limits amount of stack trace items preserved148 /// Limits amount of stack trace items preserved149 pub max_trace: usize,149 pub max_trace: usize,150 /// TLA vars150 /// TLA vars162impl Default for EvaluationSettings {162impl Default for EvaluationSettings {163 fn default() -> Self {163 fn default() -> Self {164 Self {164 Self {165 max_stack: 200,166 max_trace: 20,165 max_trace: 20,167 context_initializer: Box::new(DummyContextInitializer),166 context_initializer: Box::new(DummyContextInitializer),168 tla_vars: HashMap::default(),167 tla_vars: HashMap::default(),180 }179 }181}180}182181183#[derive(Default)]184struct EvaluationData {185 /// Used for stack overflow detection, stacktrace is populated on unwind186 stack_depth: usize,187 /// Updated every time stack entry is popt188 stack_generation: usize,189190 breakpoints: Breakpoints,191192 /// Contains file source codes and evaluation results for imports and pretty-printed stacktraces193 files: GcHashMap<SourcePath, FileData>,194}195struct FileData {182struct FileData {196 string: Option<IStr>,183 string: Option<IStr>,197 bytes: Option<IBytes>,184 bytes: Option<IBytes>,221 }208 }222}209}223224#[allow(clippy::type_complexity)]225pub struct Breakpoint {226 loc: ExprLocation,227 collected: RefCell<HashMap<usize, (usize, Vec<Result<Val>>)>>,228}229#[derive(Default)]230struct Breakpoints(Vec<Rc<Breakpoint>>);231impl Breakpoints {232 fn insert(233 &self,234 stack_depth: usize,235 stack_generation: usize,236 loc: &ExprLocation,237 result: Result<Val>,238 ) -> Result<Val> {239 if self.0.is_empty() {240 return result;241 }242 for item in &self.0 {243 if item.loc.belongs_to(loc) {244 let mut collected = item.collected.borrow_mut();245 let (depth, vals) = collected.entry(stack_generation).or_default();246 if stack_depth > *depth {247 vals.clear();248 }249 vals.push(result.clone());250 }251 }252 result253 }254}255210256#[derive(Default)]211#[derive(Default)]257pub struct EvaluationStateInternals {212pub struct EvaluationStateInternals {258 /// Internal state213 /// Internal state259 data: RefCell<EvaluationData>,214 file_cache: RefCell<GcHashMap<SourcePath, FileData>>,260 /// Settings, safe to change at runtime215 /// Settings, safe to change at runtime261 settings: RefCell<EvaluationSettings>,216 settings: RefCell<EvaluationSettings>,262}217}268impl State {223impl State {269 /// Should only be called with path retrieved from [`resolve_path`], may panic otherwise224 /// Should only be called with path retrieved from [`resolve_path`], may panic otherwise270 pub fn import_resolved_str(&self, path: SourcePath) -> Result<IStr> {225 pub fn import_resolved_str(&self, path: SourcePath) -> Result<IStr> {271 let mut data = self.data_mut();226 let mut file_cache = self.file_cache();272 let mut file = data.files.raw_entry_mut().from_key(&path);227 let mut file = file_cache.raw_entry_mut().from_key(&path);273228274 let file = match file {229 let file = match file {275 RawEntryMut::Occupied(ref mut d) => d.get_mut(),230 RawEntryMut::Occupied(ref mut d) => d.get_mut(),303 }258 }304 /// Should only be called with path retrieved from [`resolve_path`], may panic otherwise259 /// Should only be called with path retrieved from [`resolve_path`], may panic otherwise305 pub fn import_resolved_bin(&self, path: SourcePath) -> Result<IBytes> {260 pub fn import_resolved_bin(&self, path: SourcePath) -> Result<IBytes> {306 let mut data = self.data_mut();261 let mut file_cache = self.file_cache();307 let mut file = data.files.raw_entry_mut().from_key(&path);262 let mut file = file_cache.raw_entry_mut().from_key(&path);308263309 let file = match file {264 let file = match file {310 RawEntryMut::Occupied(ref mut d) => d.get_mut(),265 RawEntryMut::Occupied(ref mut d) => d.get_mut(),330 }285 }331 /// Should only be called with path retrieved from [`resolve_path`], may panic otherwise286 /// Should only be called with path retrieved from [`resolve_path`], may panic otherwise332 pub fn import_resolved(&self, path: SourcePath) -> Result<Val> {287 pub fn import_resolved(&self, path: SourcePath) -> Result<Val> {333 let mut data = self.data_mut();288 let mut file_cache = self.file_cache();334 let mut file = data.files.raw_entry_mut().from_key(&path);289 let mut file = file_cache.raw_entry_mut().from_key(&path);335290336 let file = match file {291 let file = match file {337 RawEntryMut::Occupied(ref mut d) => d.get_mut(),292 RawEntryMut::Occupied(ref mut d) => d.get_mut(),383 throw!(InfiniteRecursionDetected)338 throw!(InfiniteRecursionDetected)384 }339 }385 file.evaluating = true;340 file.evaluating = true;386 // Dropping file here, as it borrows data, which may be used in evaluation341 // Dropping file cache guard here, as evaluation may use this map too387 drop(data);342 drop(file_cache);388 let res = evaluate(343 let res = evaluate(389 self.clone(),344 self.clone(),390 self.create_default_context(file_name),345 self.create_default_context(file_name),391 &parsed,346 &parsed,392 );347 );393348394 let mut data = self.data_mut();349 let mut file_cache = self.file_cache();395 let mut file = data.files.raw_entry_mut().from_key(&path);350 let mut file = file_cache.raw_entry_mut().from_key(&path);396351397 let file = match file {352 let file = match file {398 RawEntryMut::Occupied(ref mut d) => d.get_mut(),353 RawEntryMut::Occupied(ref mut d) => d.get_mut(),426381427 /// Executes code creating a new stack frame382 /// Executes code creating a new stack frame428 pub fn push<T>(383 pub fn push<T>(429 &self,430 e: CallLocation<'_>,384 e: CallLocation<'_>,431 frame_desc: impl FnOnce() -> String,385 frame_desc: impl FnOnce() -> String,432 f: impl FnOnce() -> Result<T>,386 f: impl FnOnce() -> Result<T>,433 ) -> Result<T> {387 ) -> Result<T> {434 {435 let mut data = self.data_mut();388 let _guard = check_depth()?;436 let stack_depth = &mut data.stack_depth;389437 if *stack_depth > self.max_stack() {438 // Error creation uses data, so i drop guard here439 drop(data);440 throw!(StackOverflow);441 }442 *stack_depth += 1;443 }444 let result = f();390 f().with_description_src(e, frame_desc)445 {446 let mut data = self.data_mut();447 data.stack_depth -= 1;448 data.stack_generation += 1;449 }450 if let Err(mut err) = result {451 err.trace_mut().0.push(StackTraceElement {452 location: e.0.cloned(),453 desc: frame_desc(),454 });455 return Err(err);456 }457 result458 }391 }459392460 /// Executes code creating a new stack frame393 /// Executes code creating a new stack frame464 frame_desc: impl FnOnce() -> String,397 frame_desc: impl FnOnce() -> String,465 f: impl FnOnce() -> Result<Val>,398 f: impl FnOnce() -> Result<Val>,466 ) -> Result<Val> {399 ) -> Result<Val> {467 {468 let mut data = self.data_mut();400 let _guard = check_depth()?;469 let stack_depth = &mut data.stack_depth;401470 if *stack_depth > self.max_stack() {471 // Error creation uses data, so i drop guard here472 drop(data);473 throw!(StackOverflow);474 }475 *stack_depth += 1;476 }477 let mut result = f();402 f().with_description_src(e, frame_desc)478 {479 let mut data = self.data_mut();480 data.stack_depth -= 1;481 data.stack_generation += 1;482 result = data483 .breakpoints484 .insert(data.stack_depth, data.stack_generation, e, result);485 }486 if let Err(mut err) = result {487 err.trace_mut().0.push(StackTraceElement {488 location: Some(e.clone()),489 desc: frame_desc(),490 });491 return Err(err);492 }493 result494 }403 }495 /// Executes code creating a new stack frame404 /// Executes code creating a new stack frame496 pub fn push_description<T>(405 pub fn push_description<T>(497 &self,498 frame_desc: impl FnOnce() -> String,406 frame_desc: impl FnOnce() -> String,499 f: impl FnOnce() -> Result<T>,407 f: impl FnOnce() -> Result<T>,500 ) -> Result<T> {408 ) -> Result<T> {501 {502 let mut data = self.data_mut();409 let _guard = check_depth()?;503 let stack_depth = &mut data.stack_depth;410504 if *stack_depth > self.max_stack() {505 // Error creation uses data, so i drop guard here506 drop(data);507 throw!(StackOverflow);508 }509 *stack_depth += 1;510 }511 let result = f();411 f().with_description(frame_desc)512 {513 let mut data = self.data_mut();514 data.stack_depth -= 1;515 data.stack_generation += 1;516 }517 if let Err(mut err) = result {518 err.trace_mut().0.push(StackTraceElement {519 location: None,520 desc: frame_desc(),521 });522 return Err(err);523 }524 result525 }412 }526413527 /// # Panics414 /// # Panics536 }423 }537424538 pub fn manifest(&self, val: Val) -> Result<IStr> {425 pub fn manifest(&self, val: Val) -> Result<IStr> {539 self.push_description(426 Self::push_description(540 || "manifestification".to_string(),427 || "manifestification".to_string(),541 || val.manifest(self.clone(), &self.manifest_format()),428 || val.manifest(self.clone(), &self.manifest_format()),542 )429 )551 /// If passed value is function then call with set TLA438 /// If passed value is function then call with set TLA552 pub fn with_tla(&self, val: Val) -> Result<Val> {439 pub fn with_tla(&self, val: Val) -> Result<Val> {553 Ok(match val {440 Ok(match val {554 Val::Func(func) => self.push_description(441 Val::Func(func) => State::push_description(555 || "during TLA call".to_owned(),442 || "during TLA call".to_owned(),556 || {443 || {557 func.evaluate(444 func.evaluate(573460574/// Internals461/// Internals575impl State {462impl State {576 fn data_mut(&self) -> RefMut<'_, EvaluationData> {463 fn file_cache(&self) -> RefMut<'_, GcHashMap<SourcePath, FileData>> {577 self.0.data.borrow_mut()464 self.0.file_cache.borrow_mut()578 }465 }579 pub fn settings(&self) -> Ref<'_, EvaluationSettings> {466 pub fn settings(&self) -> Ref<'_, EvaluationSettings> {580 self.0.settings.borrow()467 self.0.settings.borrow()677 self.settings_mut().max_trace = trace;564 self.settings_mut().max_trace = trace;678 }565 }679680 pub fn max_stack(&self) -> usize {681 self.settings().max_stack682 }683 pub fn set_max_stack(&self, trace: usize) {684 self.settings_mut().max_stack = trace;685 }686}566}687567crates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth577577578pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);578pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);579impl ObjMemberBuilder<ValueBuilder<'_>> {579impl ObjMemberBuilder<ValueBuilder<'_>> {580 /// Inserts value, replacing if it is already defined581 pub fn value_unchecked(self, value: Val) {582 let (receiver, name, member) =583 self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value)));584 let entry = receiver.0.map.entry(name);585 entry.insert(member);586 }587580 pub fn value(self, s: State, value: Val) -> Result<()> {588 pub fn value(self, value: Val) -> Result<()> {589 self.thunk(Thunk::evaluated(value))590 }591 pub fn thunk(self, value: Thunk<Val>) -> Result<()> {581 self.binding(s, MaybeUnbound::Bound(Thunk::evaluated(value)))592 self.binding(MaybeUnbound::Bound(value))582 }593 }583 pub fn bindable(594 pub fn bindable(self, bindable: TraceBox<dyn Unbound<Bound = Thunk<Val>>>) -> Result<()> {584 self,585 s: State,586 bindable: TraceBox<dyn Unbound<Bound = Thunk<Val>>>,587 ) -> Result<()> {588 self.binding(s, MaybeUnbound::Unbound(Cc::new(bindable)))595 self.binding(MaybeUnbound::Unbound(Cc::new(bindable)))589 }596 }590 pub fn binding(self, s: State, binding: MaybeUnbound) -> Result<()> {597 pub fn binding(self, binding: MaybeUnbound) -> Result<()> {591 let (receiver, name, member) = self.build_member(binding);598 let (receiver, name, member) = self.build_member(binding);592 let location = member.location.clone();599 let location = member.location.clone();593 let old = receiver.0.map.insert(name.clone(), member);600 let old = receiver.0.map.insert(name.clone(), member);594 if old.is_some() {601 if old.is_some() {595 s.push(602 State::push(596 CallLocation(location.as_ref()),603 CallLocation(location.as_ref()),597 || format!("field <{}> initializtion", name.clone()),604 || format!("field <{}> initializtion", name.clone()),598 || throw!(DuplicateFieldName(name.clone())),605 || throw!(DuplicateFieldName(name.clone())),crates/jrsonnet-evaluator/src/stack.rsdiffbeforeafterbothno changes
crates/jrsonnet-evaluator/src/stdlib/manifest.rsdiffbeforeafterboth111 buf.push_str(cur_padding);111 buf.push_str(cur_padding);112 escape_string_json_buf(&field, buf);112 escape_string_json_buf(&field, buf);113 buf.push_str(options.key_val_sep);113 buf.push_str(options.key_val_sep);114 s.push_description(114 State::push_description(115 || format!("field <{}> manifestification", field.clone()),115 || format!("field <{}> manifestification", field.clone()),116 || {116 || {117 let value = obj.get(s.clone(), field.clone())?.unwrap();117 let value = obj.get(s.clone(), field.clone())?.unwrap();crates/jrsonnet-evaluator/src/stdlib/mod.rsdiffbeforeafterboth10pub mod manifest;10pub mod manifest;111112pub fn std_format(s: State, str: IStr, vals: Val) -> Result<String> {12pub fn std_format(s: State, str: IStr, vals: Val) -> Result<String> {13 s.push(13 State::push(14 CallLocation::native(),14 CallLocation::native(),15 || format!("std.format of {str}"),15 || format!("std.format of {str}"),16 || {16 || {crates/jrsonnet-evaluator/src/typed/mod.rsdiffbeforeafterboth85}85}868687fn push_type_description(87fn push_type_description(88 s: State,89 error_reason: impl Fn() -> String,88 error_reason: impl Fn() -> String,90 path: impl Fn() -> ValuePathItem,89 path: impl Fn() -> ValuePathItem,91 item: impl Fn() -> Result<()>,90 item: impl Fn() -> Result<()>,92) -> Result<()> {91) -> Result<()> {93 s.push_description(error_reason, || match item() {92 State::push_description(error_reason, || match item() {94 Ok(_) => Ok(()),93 Ok(_) => Ok(()),95 Err(mut e) => {94 Err(mut e) => {96 if let Error::TypeError(e) = &mut e.error_mut() {95 if let Error::TypeError(e) = &mut e.error_mut() {170 Val::Arr(a) => {169 Val::Arr(a) => {171 for (i, item) in a.iter(s.clone()).enumerate() {170 for (i, item) in a.iter(s.clone()).enumerate() {172 push_type_description(171 push_type_description(173 s.clone(),174 || format!("array index {i}"),172 || format!("array index {i}"),175 || ValuePathItem::Index(i as u64),173 || ValuePathItem::Index(i as u64),176 || elem_type.check(s.clone(), &item.clone()?),174 || elem_type.check(s.clone(), &item.clone()?),184 Val::Arr(a) => {182 Val::Arr(a) => {185 for (i, item) in a.iter(s.clone()).enumerate() {183 for (i, item) in a.iter(s.clone()).enumerate() {186 push_type_description(184 push_type_description(187 s.clone(),188 || format!("array index {i}"),185 || format!("array index {i}"),189 || ValuePathItem::Index(i as u64),186 || ValuePathItem::Index(i as u64),190 || elem_type.check(s.clone(), &item.clone()?),187 || elem_type.check(s.clone(), &item.clone()?),199 for (k, v) in elems.iter() {196 for (k, v) in elems.iter() {200 if let Some(got_v) = obj.get(s.clone(), (*k).into())? {197 if let Some(got_v) = obj.get(s.clone(), (*k).into())? {201 push_type_description(198 push_type_description(202 s.clone(),203 || format!("property {k}"),199 || format!("property {k}"),204 || ValuePathItem::Field((*k).into()),200 || ValuePathItem::Field((*k).into()),205 || v.check(s.clone(), &got_v),201 || v.check(s.clone(), &got_v),crates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth299 cfg_attrs,299 cfg_attrs,300 } => {300 } => {301 let name = name.as_ref().map(|v| v.as_str()).unwrap_or("<unnamed>");301 let name = name.as_ref().map(|v| v.as_str()).unwrap_or("<unnamed>");302 let eval = quote! {s.push_description(302 let eval = quote! {jrsonnet_evaluator::State::push_description(303 || format!("argument <{}> evaluation", #name),303 || format!("argument <{}> evaluation", #name),304 || <#ty>::from_untyped(value.evaluate(s.clone())?, s.clone()),304 || <#ty>::from_untyped(value.evaluate(s.clone())?, s.clone()),305 )?};305 )?};crates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth137 builder137 builder138 .member(name.into())138 .member(name.into())139 .hide()139 .hide()140 .value(s.clone(), Val::Func(FuncVal::StaticBuiltin(builtin)))140 .value(Val::Func(FuncVal::StaticBuiltin(builtin)))141 .expect("no conflict");141 .expect("no conflict");142 }142 }143143144 builder144 builder145 .member("extVar".into())145 .member("extVar".into())146 .hide()146 .hide()147 .value(147 .value(Val::Func(FuncVal::Builtin(Cc::new(tb!(builtin_ext_var {148 s.clone(),149 Val::Func(FuncVal::Builtin(Cc::new(tb!(builtin_ext_var {150 settings: settings.clone()148 settings: settings.clone()151 })))),149 })))))152 )153 .expect("no conflict");150 .expect("no conflict");154 builder151 builder155 .member("native".into())152 .member("native".into())156 .hide()153 .hide()157 .value(154 .value(Val::Func(FuncVal::Builtin(Cc::new(tb!(builtin_native {158 s.clone(),159 Val::Func(FuncVal::Builtin(Cc::new(tb!(builtin_native {160 settings: settings.clone()155 settings: settings.clone()161 })))),156 })))))162 )163 .expect("no conflict");157 .expect("no conflict");164 builder158 builder165 .member("trace".into())159 .member("trace".into())166 .hide()160 .hide()167 .value(161 .value(Val::Func(FuncVal::Builtin(Cc::new(tb!(builtin_trace {168 s.clone(),169 Val::Func(FuncVal::Builtin(Cc::new(tb!(builtin_trace { settings })))),162 settings170 )163 })))))171 .expect("no conflict");164 .expect("no conflict");172165173 builder166 builder174 .member("id".into())167 .member("id".into())175 .hide()168 .hide()176 .value(s, Val::Func(FuncVal::Id))169 .value(Val::Func(FuncVal::Id))177 .expect("no conflict");170 .expect("no conflict");178171179 builder.build()172 builder.build()