difftreelog
refactor do not use eager compspec for destructuring
in: master
1 file changed
crates/jrsonnet-evaluator/src/evaluate/compspec.rsdiffbeforeafterboth1use std::rc::Rc;23use jrsonnet_types::ValType;45use super::{6 destructure::{self, evaluate_locals, evaluate_locals_unbound},7 evaluate_field_member_static, evaluate_field_member_unbound,8};9use crate::{10 Context, ContextBuilder, ObjValue, ObjValueBuilder, Pending, Result, Thunk, Val,11 analyze::{LArrComp, LBind, LCompSpec, LDestruct, LExpr, LFieldMember, LObjComp, LocalId},12 arr::ArrValue,13 bail,14 error::ErrorKind::*,15 evaluate::evaluate,16};1718trait CompCollector {19 fn reserve(&mut self, _guaranteed: usize) {}20 fn collect(&mut self, ctx: Context) -> Result<()>;21}2223struct EagerArrCollector<'a> {24 out: &'a mut Vec<Val>,25 value: &'a LExpr,26}27impl CompCollector for EagerArrCollector<'_> {28 fn reserve(&mut self, size_hint: usize) {29 self.out.reserve(size_hint);30 }31 fn collect(&mut self, ctx: Context) -> Result<()> {32 self.out.push(evaluate(ctx, self.value)?);33 Ok(())34 }35}3637struct LazyArrCollector<'a> {38 out: &'a mut Vec<Thunk<Val>>,39 value: &'a Rc<LExpr>,40}41impl CompCollector for LazyArrCollector<'_> {42 fn reserve(&mut self, size_hint: usize) {43 self.out.reserve(size_hint);44 }45 fn collect(&mut self, ctx: Context) -> Result<()> {46 let value_expr = self.value.clone();47 self.out.push(Thunk!(move || evaluate(ctx, &value_expr)));48 Ok(())49 }50}5152struct ObjCompCollectorStatic<'a> {53 builder: &'a mut ObjValueBuilder,54 locals: &'a [LBind],55 field: &'a LFieldMember,56}57impl CompCollector for ObjCompCollectorStatic<'_> {58 fn reserve(&mut self, guaranteed: usize) {59 self.builder.reserve_fields(guaranteed);60 }61 fn collect(&mut self, inner_ctx: Context) -> Result<()> {62 let value_ctx = evaluate_locals(inner_ctx.clone(), self.locals);63 evaluate_field_member_static(self.builder, inner_ctx, value_ctx, self.field)64 }65}6667struct ObjCompCollectorUnbound<'a> {68 builder: &'a mut ObjValueBuilder,69 locals: Rc<Vec<LBind>>,70 this_id: Option<LocalId>,71 field: &'a LFieldMember,72}73impl CompCollector for ObjCompCollectorUnbound<'_> {74 fn reserve(&mut self, guaranteed: usize) {75 self.builder.reserve_fields(guaranteed);76 }77 fn collect(&mut self, inner_ctx: Context) -> Result<()> {78 let uctx = evaluate_locals_unbound(inner_ctx.clone(), self.locals.clone(), self.this_id);79 evaluate_field_member_unbound(self.builder, inner_ctx, uctx, self.field)80 }81}8283pub fn evaluate_obj_comp(84 super_obj: Option<ObjValue>,85 ctx: Context,86 comp: &LObjComp,87) -> Result<Val> {88 let mut builder = ObjValueBuilder::new();89 if let Some(super_obj) = super_obj {90 builder.with_super(super_obj);91 }9293 let cached_overs = cache_overs(&ctx, &comp.compspecs)?;94 if comp.this.is_some() || comp.uses_super {95 evaluate_compspecs(96 ctx,97 &comp.compspecs,98 &cached_overs,99 0,100 0,101 &mut ObjCompCollectorUnbound {102 builder: &mut builder,103 locals: comp.locals.clone(),104 this_id: comp.this,105 field: &comp.field,106 },107 )?;108 } else {109 evaluate_compspecs(110 ctx,111 &comp.compspecs,112 &cached_overs,113 0,114 0,115 &mut ObjCompCollectorStatic {116 builder: &mut builder,117 locals: &comp.locals,118 field: &comp.field,119 },120 )?;121 }122123 Ok(Val::Obj(builder.build()))124}125126pub fn evaluate_arr_comp(ctx: Context, comp: &LArrComp) -> Result<Val> {127 let cached_overs = cache_overs(&ctx, &comp.compspecs)?;128129 // In eager evaluation, Context is not captured, thus updates in CoW fashion will likely to success130 'eager: {131 let mut out = Vec::new();132133 if evaluate_compspecs_eager(134 ctx.clone(),135 &comp.compspecs,136 &cached_overs,137 0,138 0,139 &mut EagerArrCollector {140 out: &mut out,141 value: &comp.value,142 },143 )144 .is_err()145 {146 break 'eager;147 }148 return Ok(Val::arr(out));149 }150151 let mut items: Vec<Thunk<Val>> = Vec::new();152 evaluate_compspecs(153 ctx,154 &comp.compspecs,155 &cached_overs,156 0,157 0,158 &mut LazyArrCollector {159 out: &mut items,160 value: &comp.value,161 },162 )?;163 Ok(Val::arr(items))164}165166fn cache_overs(ctx: &Context, specs: &[LCompSpec]) -> Result<Vec<Option<ArrValue>>> {167 specs168 .iter()169 .map(|spec| {170 Ok(match spec {171 LCompSpec::For {172 over,173 loop_invariant: true,174 ..175 } => {176 let val = evaluate(ctx.clone(), over)?;177 let Val::Arr(arr) = val else {178 bail!(InComprehensionCanOnlyIterateOverArray)179 };180 Some(arr)181 }182 _ => None,183 })184 })185 .collect::<Result<_>>()186}187188fn evaluate_compspecs_eager(189 ctx: Context,190 specs: &[LCompSpec],191 cached_overs: &[Option<ArrValue>],192 idx: usize,193 guaranteed_reserve: usize,194 collector: &mut dyn CompCollector,195) -> Result<()> {196 if idx >= specs.len() {197 collector.reserve(guaranteed_reserve);198 return collector.collect(ctx);199 }200 match &specs[idx] {201 LCompSpec::If(cond) => {202 let val = evaluate(ctx.clone(), cond)?;203 let Val::Bool(b) = val else {204 bail!(TypeMismatch(205 "if spec condition",206 vec![ValType::Bool],207 val.value_type()208 ))209 };210 if b {211 evaluate_compspecs_eager(ctx, specs, cached_overs, idx + 1, 0, collector)?;212 }213 }214 LCompSpec::For { destruct, over, .. } => {215 let arr = if let Some(cached) = &cached_overs[idx] {216 cached.clone()217 } else {218 let arr_val = evaluate(ctx.clone(), over)?;219 let Val::Arr(arr) = arr_val else {220 bail!(InComprehensionCanOnlyIterateOverArray)221 };222 arr223 };224 let inner_reserve = guaranteed_reserve.max(1) * arr.len() as usize;225 match destruct {226 LDestruct::Full(id) => {227 let id = *id;228 let mut inner_ctx = ContextBuilder::extend(ctx, 1).build();229 for (i, item) in arr.iter().enumerate() {230 // TODO: reuse one ContextBuilder for full evaluate_compspecs pipeline231 inner_ctx.cow_fill_binding(id, Thunk::evaluated(item?));232 evaluate_compspecs_eager(233 inner_ctx.clone(),234 specs,235 cached_overs,236 idx + 1,237 if i == 0 { inner_reserve } else { 0 },238 collector,239 )?;240 }241 }242 // TODO: Should not be eager? CoW won't work here243 #[cfg(feature = "exp-destruct")]244 _ => {245 for (i, item) in arr.iter().enumerate() {246 let item_val = item?;247 let mut inner_builder = ContextBuilder::extend(ctx.clone(), 1);248 let fctx = Pending::new();249 destructure::destruct(250 destruct,251 Thunk::evaluated(item_val),252 fctx.clone(),253 &mut inner_builder,254 );255 let inner_ctx = inner_builder.build().into_future(fctx);256 evaluate_compspecs_eager(257 inner_ctx,258 specs,259 cached_overs,260 idx + 1,261 if i == 0 { inner_reserve } else { 0 },262 collector,263 )?;264 }265 }266 }267 }268 }269 Ok(())270}271272fn evaluate_compspecs(273 ctx: Context,274 specs: &[LCompSpec],275 cached_overs: &[Option<ArrValue>],276 idx: usize,277 guaranteed_reserve: usize,278 collector: &mut dyn CompCollector,279) -> Result<()> {280 if idx >= specs.len() {281 collector.reserve(guaranteed_reserve);282 return collector.collect(ctx);283 }284 match &specs[idx] {285 LCompSpec::If(cond) => {286 let val = evaluate(ctx.clone(), cond)?;287 let Val::Bool(b) = val else {288 bail!(TypeMismatch(289 "if spec condition",290 vec![ValType::Bool],291 val.value_type()292 ))293 };294 if b {295 evaluate_compspecs(ctx, specs, cached_overs, idx + 1, 0, collector)?;296 }297 }298 LCompSpec::For { destruct, over, .. } => {299 let arr = if let Some(cached) = &cached_overs[idx] {300 cached.clone()301 } else {302 let arr_val = evaluate(ctx.clone(), over)?;303 let Val::Arr(arr) = arr_val else {304 bail!(InComprehensionCanOnlyIterateOverArray)305 };306 arr307 };308 let inner_reserve = guaranteed_reserve.max(1) * arr.len() as usize;309 for (i, item) in arr.iter().enumerate() {310 let item_val = item?;311 let mut inner_builder = ContextBuilder::extend(ctx.clone(), 1);312 let fctx = Pending::new();313 destructure::destruct(314 destruct,315 Thunk::evaluated(item_val),316 fctx.clone(),317 &mut inner_builder,318 );319 let inner_ctx = inner_builder.build().into_future(fctx);320 evaluate_compspecs(321 inner_ctx,322 specs,323 cached_overs,324 idx + 1,325 if i == 0 { inner_reserve } else { 0 },326 collector,327 )?;328 }329 }330 }331 Ok(())332}