git.delta.rocks / jrsonnet / refs/commits / 33ac23bb6e15

difftreelog

feat detect infinite recursion in assertions

tlnxrzknYaroslav Bolyukin2026-07-31parent: #8b828f9.patch.diff
in: master

1 file changed

modifiedcrates/jrsonnet-evaluator/src/obj/mod.rsdiffbeforeafterboth
152#[derive(Trace, Debug)]152#[derive(Trace, Debug)]
153enum CacheValue {153enum CacheValue {
154 Cached(Result<Option<Val>>),154 Cached(Result<Option<Val>>),
155 Pending,155 Pending { in_current_assertion: Cell<bool> },
156}156}
157157
158pub type EnumFieldsHandler<'a> =158pub type EnumFieldsHandler<'a> =
252thread_local! {252thread_local! {
253 static RUNNING_ASSERTIONS: RefCell<FxHashSet<ObjValue>> = RefCell::default();253 static RUNNING_ASSERTIONS: RefCell<FxHashSet<ObjValue>> = RefCell::default();
254}254}
255fn is_asserting(obj: &ObjValue) -> bool {255fn is_asserting() -> bool {
256 RUNNING_ASSERTIONS.with_borrow(|v| v.contains(obj))256 RUNNING_ASSERTIONS.with_borrow(|v| !v.is_empty())
257}257}
258/// Returns false if already asserting258struct AssertionGuard(ObjValue);
259impl Drop for AssertionGuard {
259fn start_asserting(obj: &ObjValue) -> bool {260 fn drop(&mut self) {
260 RUNNING_ASSERTIONS.with_borrow_mut(|v| v.insert(obj.clone()))261 RUNNING_ASSERTIONS.with_borrow_mut(|v| {
262 let r = v.remove(&self.0);
263 debug_assert!(
264 r,
265 "finish_asserting was called before start_asserting or twice"
266 );
267 });
261}268 }
269}
270/// Returns `None` if already asserting
262fn finish_asserting(obj: &ObjValue) {271fn asserting(obj: &ObjValue) -> Option<AssertionGuard> {
263 RUNNING_ASSERTIONS.with_borrow_mut(|v| {272 RUNNING_ASSERTIONS
264 let r = v.remove(obj);273 .with_borrow_mut(|v| v.insert(obj.clone()).then(|| AssertionGuard(obj.clone())))
265 debug_assert!(
266 r,
267 "finish_asserting was called before start_asserting or twice"
268 );
269 });
270}274}
271275
272thread_local! {276thread_local! {
625 match cache.entry(cache_key.clone()) {629 match cache.entry(cache_key.clone()) {
626 Entry::Occupied(v) => match v.get() {630 Entry::Occupied(v) => match v.get() {
627 CacheValue::Cached(v) => return v.clone(),631 CacheValue::Cached(v) => return v.clone(),
628 CacheValue::Pending => {632 CacheValue::Pending {
633 in_current_assertion,
634 } => {
635 // During assertions we may encounter field that triggered the first assertion. This field is pending,
636 // but this time evaluation of this field would not trigger assertions in currently asserting object
637 // again; thus the evaluation result will be different, as it won't trigger the assertion failure side
638 // effect. The field vould be evaluated twice, yet the results should not be affected.
629 if !is_asserting(self) {639 if !is_asserting() || in_current_assertion.get() {
630 bail!(InfiniteRecursionDetected);640 bail!(InfiniteRecursionDetected);
631 }641 }
642 in_current_assertion.set(true);
632 }643 }
633 },644 },
634 Entry::Vacant(v) => {645 Entry::Vacant(v) => {
635 v.insert(CacheValue::Pending);646 v.insert(CacheValue::Pending {
647 // Should not default to is_asserting(), as evaluation of this field may trigger the other object
648 // assertions which would hit the same field again.
649 in_current_assertion: Cell::default(),
650 });
636 }651 }
637 }652 }
638 }653 };
639 let result = self.get_idx_uncached(key, core);654 let result = self.get_idx_uncached(key, core);
640 {655 {
641 let mut cache = self.0.value_cache.borrow_mut();656 let mut cache = self.0.value_cache.borrow_mut();
746 if self.0.assertions_ran.get() {761 if self.0.assertions_ran.get() {
747 return Ok(());762 return Ok(());
748 }763 }
749 if !start_asserting(self) {764 let Some(_asserting) = asserting(self) else {
750 return Ok(());765 return Ok(());
751 }766 };
752 for (idx, ele) in self.0.cores.iter().enumerate() {767 for (idx, ele) in self.0.cores.iter().enumerate() {
753 let sup_this = SupThis {768 let sup_this = SupThis {
754 sup: CoreIdx { idx },769 sup: CoreIdx { idx },
755 this: self.clone(),770 this: self.clone(),
756 };771 };
757 ele.0.run_assertions_core(sup_this).inspect_err(|_e| {772 ele.0.run_assertions_core(sup_this)?;
758 finish_asserting(self);
759 })?;
760 }773 }
761 finish_asserting(self);
762 self.0.assertions_ran.set(true);774 self.0.assertions_ran.set(true);
763 Ok(())775 Ok(())
764 }776 }