difftreelog
feat forObj evaluation
in: master
4 files changed
crates/jrsonnet-evaluator/src/analyze.rsdiffbeforeafterboth410 /// Is `over` does not depend on any variable introduced by an earlier for-spec in this comprehension chain410 /// Is `over` does not depend on any variable introduced by an earlier for-spec in this comprehension chain411 loop_invariant: bool,411 loop_invariant: bool,412 },412 },413 #[cfg(feature = "exp-object-iteration")]414 ForObj {415 frame_shape: ClosureShape,416 key: LocalSlot,417 visibility: jrsonnet_ir::Visibility,418 value: LDestruct,419 over: LExpr,420 loop_invariant: bool,421 },413}422}414423415struct FrameAlloc<'s> {424struct FrameAlloc<'s> {1863 (r, rest)1872 (r, rest)1864 }1873 }1865 #[cfg(feature = "exp-object-iteration")]1874 #[cfg(feature = "exp-object-iteration")]1866 CompSpec::ForObjSpec(_) => todo!(),1875 CompSpec::ForObjSpec(data) => {1876 let mut over_taint = AnalysisResult::default();1877 let over_l = analyze(&data.over, stack, &mut over_taint);1878 let loop_invariant = over_taint.local_dependent_depth > outer_depth;1879 taint.taint_by(over_taint);18801881 let mut alloc = FrameAlloc::new(stack);1882 let closure = alloc.push_locals_closure();1883 let Some((_, key_slot)) = alloc.define_local(data.key.clone(), None) else {1884 stack.pop_closure(closure);1885 return go(idx + 1, specs, outer_depth, stack, taint, inside);1886 };1887 let Some(l_value) = alloc.alloc_destruct(&data.value) else {1888 stack.pop_closure(closure);1889 return go(idx + 1, specs, outer_depth, stack, taint, inside);1890 };1891 let mut pending = alloc.finish();18921893 let var_analysis = AnalysisResult::default();1894 pending.record_spec_init(&LDestruct::Full(key_slot), var_analysis);1895 pending.record_spec_init(&l_value, var_analysis);18961897 let body_frame = pending.finish();1898 let (r, mut rest) =1899 go(idx + 1, specs, outer_depth, body_frame.stack, taint, inside);1900 body_frame.finish();1901 let frame_shape = stack.pop_closure(closure);19021903 rest.insert(1904 0,1905 LCompSpec::ForObj {1906 frame_shape,1907 key: key_slot,1908 visibility: data.visibility,1909 value: l_value,1910 over: over_l,1911 loop_invariant,1912 },1913 );1914 (r, rest)1915 }1867 }1916 }1868 }1917 }1869 let outer_depth = stack.depth;1918 let outer_depth = stack.depth;1970 use super::*;2019 use super::*;197120201972 #[test]2021 #[test]2022 #[cfg(not(feature = "exp-null-coaelse"))]1973 fn snapshots() {2023 fn snapshots() {1974 glob!("analysis_tests/*.jsonnet", |path| {2024 glob!("analysis_tests/*.jsonnet", |path| {1975 let code = fs::read_to_string(path).expect("read test file");2025 let code = fs::read_to_string(path).expect("read test file");crates/jrsonnet-evaluator/src/evaluate/compspec.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/compspec.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/compspec.rs
@@ -6,6 +6,9 @@
destructure::{destruct, evaluate_locals_unbound, fill_letrec_binds},
evaluate_field_member_static, evaluate_field_member_unbound,
};
+#[cfg(feature = "exp-object-iteration")]
+use jrsonnet_interner::IStr;
+
use crate::{
Context, ObjValue, ObjValueBuilder, Result, Thunk, Val,
analyze::{
@@ -18,6 +21,12 @@
evaluate::{evaluate, evaluate_trivial},
};
+enum CachedOver {
+ Arr(ArrValue),
+ #[cfg(feature = "exp-object-iteration")]
+ Obj(ObjValue),
+}
+
trait CompCollector {
fn reserve(&mut self, _guaranteed: usize) {}
fn collect(&mut self, ctx: Context) -> Result<()>;
@@ -215,7 +224,7 @@
Ok(Val::arr(items))
}
-fn cache_overs(ctx: &Context, specs: &[LCompSpec]) -> Result<Vec<Option<ArrValue>>> {
+fn cache_overs(ctx: &Context, specs: &[LCompSpec]) -> Result<Vec<Option<CachedOver>>> {
specs
.iter()
.map(|spec| {
@@ -229,7 +238,23 @@
let Val::Arr(arr) = val else {
bail!(InComprehensionCanOnlyIterateOverArray)
};
- Some(arr)
+ Some(CachedOver::Arr(arr))
+ }
+ #[cfg(feature = "exp-object-iteration")]
+ LCompSpec::ForObj {
+ over,
+ loop_invariant: true,
+ ..
+ } => {
+ let val = evaluate(ctx.clone(), over)?;
+ let Val::Obj(obj) = val else {
+ bail!(TypeMismatch(
+ "object iteration over",
+ vec![jrsonnet_types::ValType::Obj],
+ val.value_type(),
+ ))
+ };
+ Some(CachedOver::Obj(obj))
}
_ => None,
})
@@ -240,7 +265,7 @@
fn evaluate_compspecs_eager(
ctx: Context,
specs: &[LCompSpec],
- cached_overs: &[Option<ArrValue>],
+ cached_overs: &[Option<CachedOver>],
idx: usize,
guaranteed_reserve: usize,
collector: &mut dyn CompCollector,
@@ -269,7 +294,7 @@
over,
..
} => {
- let arr = if let Some(cached) = &cached_overs[idx] {
+ let arr = if let Some(CachedOver::Arr(cached)) = &cached_overs[idx] {
cached.clone()
} else {
let arr_val = evaluate(ctx.clone(), over)?;
@@ -304,6 +329,10 @@
_ => unreachable!("eager compspecs are not possible with non-full patterns"),
}
}
+ #[cfg(feature = "exp-object-iteration")]
+ LCompSpec::ForObj { .. } => {
+ unreachable!("eager compspecs filter rejects ForObj");
+ }
}
Ok(())
}
@@ -311,7 +340,7 @@
fn evaluate_compspecs(
ctx: Context,
specs: &[LCompSpec],
- cached_overs: &[Option<ArrValue>],
+ cached_overs: &[Option<CachedOver>],
idx: usize,
guaranteed_reserve: usize,
collector: &mut dyn CompCollector,
@@ -340,7 +369,7 @@
over,
..
} => {
- let arr = if let Some(cached) = &cached_overs[idx] {
+ let arr = if let Some(CachedOver::Arr(cached)) = &cached_overs[idx] {
cached.clone()
} else {
let arr_val = evaluate(ctx.clone(), over)?;
@@ -365,6 +394,61 @@
)?;
}
}
+ #[cfg(feature = "exp-object-iteration")]
+ LCompSpec::ForObj {
+ frame_shape,
+ key,
+ visibility,
+ value,
+ over,
+ ..
+ } => {
+ use jrsonnet_ir::Visibility;
+ let obj = if let Some(CachedOver::Obj(cached)) = &cached_overs[idx] {
+ cached.clone()
+ } else {
+ let val = evaluate(ctx.clone(), over)?;
+ let Val::Obj(obj) = val else {
+ bail!(TypeMismatch(
+ "object iteration over",
+ vec![ValType::Obj],
+ val.value_type(),
+ ))
+ };
+ obj
+ };
+ let fields = obj.fields_with_visibility(
+ #[cfg(feature = "exp-preserve-order")]
+ false,
+ );
+ let pairs: Vec<(IStr, Visibility)> = fields
+ .into_iter()
+ .filter(|(_, v)| match visibility {
+ Visibility::Normal => v.is_visible(),
+ Visibility::Hidden => !v.is_visible(),
+ Visibility::Unhide => true,
+ })
+ .collect();
+ let inner_reserve = guaranteed_reserve.max(1) * pairs.len();
+ for (i, (field_name, _)) in pairs.into_iter().enumerate() {
+ let key_val = Val::string(field_name.clone());
+ let value_thunk = obj
+ .get_lazy(field_name.clone())
+ .expect("field exists, just enumerated");
+ let inner_ctx = ctx.pack_captures_sup_this(frame_shape).enter(|fill, ctx| {
+ fill.set(*key, Thunk::evaluated(key_val));
+ destruct(value, fill, value_thunk, &ctx);
+ });
+ evaluate_compspecs(
+ inner_ctx,
+ specs,
+ cached_overs,
+ idx + 1,
+ if i == 0 { inner_reserve } else { 0 },
+ collector,
+ )?;
+ }
+ }
}
Ok(())
}
crates/jrsonnet-evaluator/src/obj/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/obj/mod.rs
+++ b/crates/jrsonnet-evaluator/src/obj/mod.rs
@@ -909,6 +909,56 @@
out
}
+ pub fn fields_with_visibility(
+ &self,
+ #[cfg(feature = "exp-preserve-order")] preserve_order: bool,
+ ) -> Vec<(IStr, Visibility)> {
+ #[cfg(feature = "exp-preserve-order")]
+ if preserve_order {
+ let (mut fields, mut keys): (Vec<_>, Vec<_>) = self
+ .fields_visibility()
+ .into_iter()
+ .enumerate()
+ .map(|(idx, (k, d))| {
+ (
+ (
+ k,
+ d.exists_visible.expect("non-existing fields filtered out"),
+ ),
+ (d.sort_key(), idx),
+ )
+ })
+ .unzip();
+ keys.sort_unstable_by_key(|v| v.0);
+ for i in 0..fields.len() {
+ let x = fields[i].clone();
+ let mut j = i;
+ loop {
+ let k = keys[j].1;
+ keys[j].1 = j;
+ if k == i {
+ break;
+ }
+ fields[j] = fields[k].clone();
+ j = k;
+ }
+ fields[j] = x;
+ }
+ return fields;
+ }
+ let mut fields: Vec<_> = self
+ .fields_visibility()
+ .into_iter()
+ .map(|(k, d)| {
+ (
+ k,
+ d.exists_visible.expect("non-existing fields filtered out"),
+ )
+ })
+ .collect();
+ fields.sort_unstable_by(|a, b| a.0.cmp(&b.0));
+ fields
+ }
pub fn fields_ex(
&self,
include_hidden: bool,
crates/jrsonnet-peg-parser/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-peg-parser/src/lib.rs
+++ b/crates/jrsonnet-peg-parser/src/lib.rs
@@ -441,6 +441,7 @@
use crate::{ParserSettings, parse};
#[test]
+ #[cfg(not(feature = "exp-null-coaelse"))]
fn snapshots() {
glob!("tests/*.jsonnet", |path| {
let input = fs::read_to_string(path).expect("read test file");