difftreelog
refactor closure interpreter
in: master
14 files changed
crates/jrsonnet-evaluator/src/arr/mod.rsdiffbeforeafterboth9910use crate::{Context, Result, Thunk, Val, analyze::LExpr, function::NativeFn, typed::IntoUntyped};10use crate::{11 Context, Result, Thunk, Val,12 analyze::{ClosureShape, LExpr},13 function::NativeFn,14 typed::IntoUntyped,15};111612mod spec;17mod spec;36 Self::new(())41 Self::new(())37 }42 }384339 pub fn expr(ctx: Context, exprs: Rc<Vec<LExpr>>) -> Self {44 pub fn expr(ctx: Context, shape: &ClosureShape, exprs: Rc<Vec<LExpr>>) -> Self {40 Self::new(ExprArray::new(ctx, exprs))45 Self::new(ExprArray::new(ctx, shape, exprs))41 }46 }424743 pub fn repeated(data: Self, repeats: u32) -> Option<Self> {48 pub fn repeated(data: Self, repeats: u32) -> Option<Self> {crates/jrsonnet-evaluator/src/arr/spec.rsdiffbeforeafterboth12use super::ArrValue;12use super::ArrValue;13use crate::{13use crate::{14 Context, Error, ObjValue, Result, Thunk, Val,14 Context, Error, ObjValue, Result, Thunk, Val,15 analyze::LExpr,15 analyze::{ClosureShape, LExpr},16 error::ErrorKind::InfiniteRecursionDetected,16 error::ErrorKind::InfiniteRecursionDetected,17 evaluate::evaluate,17 evaluate::evaluate,18 function::NativeFn,18 function::NativeFn,123 cached: Cc<RefCell<Vec<ArrayThunk>>>,123 cached: Cc<RefCell<Vec<ArrayThunk>>>,124}124}125impl ExprArray {125impl ExprArray {126 pub fn new(ctx: Context, src: Rc<Vec<LExpr>>) -> Self {126 pub fn new(outer: Context, shape: &ClosureShape, src: Rc<Vec<LExpr>>) -> Self {127 Self {127 Self {128 ctx,128 ctx: Context::enter_using(&outer, shape),129 cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting; src.len()])),129 cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting; src.len()])),130 src,130 src,131 }131 }crates/jrsonnet-evaluator/src/async_import.rsdiffbeforeafterboth4use jrsonnet_ir::{IStr, Source, SourcePath, visit::Visitor};4use jrsonnet_ir::{IStr, Source, SourcePath, visit::Visitor};5use rustc_hash::FxHashMap;5use rustc_hash::FxHashMap;667use crate::{AsPathLike, FileData, ImportResolver, ResolvePathOwned, State};7use crate::{AsPathLike, FileData, ImportResolver, ResolvePathOwned, Result, State};889pub struct Import {9pub struct Import {10 path: ResolvePathOwned,10 path: ResolvePathOwned,109 }109 }110 }110 }111 Job::ParseFile(path) => {111 Job::ParseFile(path) => {112 if let Some(file) = s.0.file_cache.borrow_mut().get_mut(&path) {112 if let Some(file) = s.0.file_cache.borrow_mut().get_mut(&path)113 if file.parsed.is_none() {113 && file.parsed.is_none()114 {114 let Some(code) = file.get_string() else {115 let Some(code) = file.get_string() else {115 continue;116 continue;128 }129 }129 }130 }130 }131 }131 }132 }132 }133 Job::ResolveImport { from, import } => {133 Job::ResolveImport { from, import } => {134 {134 {crates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth1use std::{clone::Clone, fmt::Debug};1use std::{2 cell::{Cell, OnceCell, RefCell},3 clone::Clone,4 fmt::{self, Debug},5};263use educe::Educe;7use educe::Educe;4use jrsonnet_gcmodule::{Cc, Trace};8use jrsonnet_gcmodule::{Cc, Trace};5use jrsonnet_interner::IStr;9use jrsonnet_interner::IStr;6107use crate::{Pending, Result, SupThis, Thunk, Val, analyze::LocalId, error, error::ErrorKind::*};11use crate::{12 Result, SupThis, Thunk, Val,13 analyze::{CaptureSlot, ClosureShape, LSlot, LocalId, LocalSlot},14 bail, error,15 error::ErrorKind::*,16};8179#[derive(Debug, Trace, Clone, Educe)]18#[derive(Debug, Trace, Clone, Educe)]10#[educe(PartialEq)]19#[educe(PartialEq)]11pub struct Context(#[educe(PartialEq(method = Cc::ptr_eq))] Cc<ContextInternal>);20pub struct Context(#[educe(PartialEq(method = Cc::ptr_eq))] pub(crate) Cc<ContextInternal>);122113#[derive(Debug, Trace, Clone)]22#[derive(Trace)]14struct ContextInternal {23pub(crate) struct ContextInternal {15 sup_this: Option<SupThis>,24 /// Immutable, packed at closure-create time.16 /// `bindings[i]` corresponds to `LocalId(offset + i)`.17 bindings: Vec<Option<Thunk<Val>>>,25 pub(crate) captures: Cc<Vec<Thunk<Val>>>,18 offset: u32,26 /// Filled during closure initialization27 pub(crate) locals: Cc<LocalsFrame>,19 parent: Option<Context>,28 pub(crate) sup_this: Option<SupThis>,20}29}213022impl Context {31impl Debug for ContextInternal {23 pub fn new_future() -> Pending<Self> {32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {24 Pending::new()33 f.debug_struct("ContextInternal")34 .field("captures", &self.captures.len())35 .field("locals", &self.locals)36 .field("sup_this", &self.sup_this.is_some())37 .finish()25 }38 }39}264041#[derive(Trace, Debug)]42pub(crate) struct IterFrame {27 pub fn sup_this(&self) -> Option<&SupThis> {43 slots: Vec<RefCell<Option<Thunk<Val>>>>,44 captured: Cell<bool>,45}46impl IterFrame {47 pub fn new(n: u16) -> IterFrame {28 self.0.sup_this.as_ref()48 let cells: Vec<RefCell<Option<Thunk<Val>>>> = (0..n).map(|_| RefCell::new(None)).collect();49 IterFrame {50 slots: cells,51 captured: Cell::new(false),52 }29 }53 }3031 pub fn try_sup_this(&self) -> Result<SupThis> {54 pub fn set(&self, slot: LocalSlot, value: Thunk<Val>) {32 self.055 *self.slots[slot.0 as usize].borrow_mut() = Some(value);33 .sup_this34 .clone()35 .ok_or_else(|| error!(CantUseSelfSupOutsideOfObject))36 }56 }57}375859#[derive(Trace, Debug)]60pub(crate) enum LocalsFrame {38 /// Update binding in `CoW` fashion. Only useful for eager comprehension61 Once1(OnceCell<Thunk<Val>>),62 /// Letrec/function/object/for frames - slots are filled during frame setup39 /// fast-path, as it requires Cc refcount to be 1; Use `ContextBuilder` otherwise.63 Once(Vec<OnceCell<Thunk<Val>>>),64 /// Comp-eager fast-path, cells are reset per iteration for the unique frames (i.e for the non-capturing thunks)40 pub(crate) fn cow_fill_binding(&mut self, id: LocalId, value: Thunk<Val>) {65 Iter(IterFrame),66}67impl LocalsFrame {68 pub fn set(&self, slot: LocalSlot, value: Thunk<Val>) {41 let mut value = Some(Some(value));69 match self {4243 self.0.update_with(|inner| {70 LocalsFrame::Once1(cell) => {44 let local_idx = (id.0 - inner.offset) as usize;71 debug_assert_eq!(slot.0, 0, "Once1 only holds slot 0");45 while inner.bindings.len() <= local_idx {72 cell.set(value)73 .map_err(|_| ())46 inner.bindings.push(None);74 .expect("slot already filled");47 }75 }48 inner.bindings[local_idx] = value.take().expect("called once");76 LocalsFrame::Once(cells) => {77 cells[slot.0 as usize]78 .set(value)79 .map_err(|_| ())80 .expect("slot already filled");49 });81 }82 LocalsFrame::Iter(_) => unreachable!("iter frame has different constructors"),83 }50 }84 }85}518687impl LocalsFrame {52 pub fn binding(&self, id: LocalId) -> Option<Thunk<Val>> {88 pub(crate) fn new_once(n: u16) -> Cc<Self> {53 let id_num = id.0;89 if n == 1 {54 if id_num >= self.0.offset {55 let local_idx = (id_num - self.0.offset) as usize;56 if let Some(Some(thunk)) = self.0.bindings.get(local_idx) {57 return Some(thunk.clone());90 return Cc::new(Self::Once1(OnceCell::new()));58 }59 }91 }60 if let Some(parent) = &self.0.parent {92 let cells: Vec<OnceCell<Thunk<Val>>> = (0..n).map(|_| OnceCell::new()).collect();61 return parent.binding(id);62 }93 Cc::new(Self::Once(cells))63 None64 }94 }95}659697pub(crate) struct IterContext {66 #[must_use]98 context: Context,99}100impl IterContext {67 pub fn into_future(self, ctx: Pending<Self>) -> Self {101 pub(crate) fn create(&self, build: impl FnOnce(&IterFrame)) -> Result<Context> {68 {102 if !Cc::is_unique(&self.context.0.locals) {69 ctx.clone().fill(self);103 bail!(EagerCompspecCaptured);70 }104 }71 ctx.unwrap()105 let LocalsFrame::Iter(frame) = &*self.context.0.locals else {106 unreachable!("IterContext is only created for Iter ctx");107 };108 if frame.captured.get() {109 bail!(EagerCompspecCaptured);110 }111 build(frame);112 Ok(self.context.clone())72 }113 }73}114}7411575#[derive(Clone)]116#[derive(Trace, Clone)]76pub struct ContextBuilder {117pub(crate) struct PackedContext {118 captures: Cc<Vec<Thunk<Val>>>,119 n_locals: u16,120}121impl PackedContext {122 pub fn enter(self, sup_this: SupThis, build: impl FnOnce(&LocalsFrame, &Context)) -> Context {123 let locals = LocalsFrame::new_once(self.n_locals);124 let val = Context(Cc::new(ContextInternal {125 captures: self.captures.clone(),126 locals,127 sup_this: Some(sup_this),128 }));129 build(&val.0.locals, &val);130 val131 }132}133#[derive(Trace, Clone, Educe, Debug)]134#[educe(PartialEq)]135pub(crate) struct PackedContextSupThis {136 #[educe(PartialEq(method = Cc::ptr_eq))]137 captures: Cc<Vec<Thunk<Val>>>,138 n_locals: u16,77 sup_this: Option<SupThis>,139 sup_this: Option<SupThis>,78 bindings: Vec<Option<Thunk<Val>>>,79 offset: u32,80 parent: Option<Context>,81}140}141impl PackedContextSupThis {142 pub fn enter(self, build: impl FnOnce(&LocalsFrame, &Context)) -> Context {143 let locals = LocalsFrame::new_once(self.n_locals);144 let val = Context(Cc::new(ContextInternal {145 captures: self.captures.clone(),146 locals,147 sup_this: self.sup_this,148 }));149 build(&val.0.locals, &val);150 val151 }152}8215383impl ContextBuilder {154impl Context {84 pub fn new() -> Self {155 #[inline]156 pub fn slot(&self, slot: LSlot) -> Thunk<Val> {85 Self {157 match slot {86 sup_this: None,158 LSlot::Local(i) => self.local(i),87 bindings: Vec::new(),88 offset: 0,159 LSlot::Capture(i) => self.capture(i),89 parent: None,90 }160 }91 }161 }92162 /// Read a local slot from the shared locals frame.93 pub(crate) fn extend(parent: Context, capacity: usize) -> Self {163 ///164 /// # Panics165 /// If the slot has not yet been filled. The analyzer guarantees166 /// that slot indices are in range and that letrec setup completes167 /// before the first read. A panic indicates an analyzer/runtime168 /// invariant violation, not a user error.169 #[inline]170 pub fn local(&self, slot: LocalSlot) -> Thunk<Val> {94 let offset = parent.0.offset + parent.0.bindings.len() as u32;171 match &*self.0.locals {172 LocalsFrame::Once1(cell) => {173 debug_assert_eq!(slot.0, 0, "Once1 only holds slot 0");174 cell.get().expect("local read before letrec init").clone()95 Self {175 }96 sup_this: parent.0.sup_this.clone(),176 LocalsFrame::Once(cells) => cells[slot.0 as usize]177 .get()178 .expect("local read before letrec init")179 .clone(),97 bindings: Vec::with_capacity(capacity),180 LocalsFrame::Iter(cells) => cells.slots[slot.0 as usize]98 offset,181 .borrow()99 parent: Some(parent),182 .as_ref()183 .expect("iter local read before iteration filled it")184 .clone(),100 }185 }101 }186 }102187103 pub(crate) fn bind(&mut self, id: LocalId, value: Thunk<Val>) {188 /// Read a captured slot from this closure's capture pack.104 debug_assert!(189 #[inline]105 id.0 >= self.offset,190 pub fn capture(&self, slot: CaptureSlot) -> Thunk<Val> {106 "cannot bind {id:?} below offset {}",107 self.offset,108 );109 let local_idx = (id.0 - self.offset) as usize;110 self.bindings.reserve(local_idx);111 while self.bindings.len() <= local_idx {112 self.bindings.push(None);191 (*self.0.captures)[slot.0 as usize].clone()113 }114 self.bindings[local_idx] = Some(value);115 }192 }116193117 pub(crate) fn build(self) -> Context {194 pub fn sup_this(&self) -> Option<&SupThis> {195 self.0.sup_this.as_ref()196 }197198 pub fn try_sup_this(&self) -> Result<SupThis> {118 Context(Cc::new(ContextInternal {199 self.0200 .sup_this201 .clone()202 .ok_or_else(|| error!(CantUseSelfSupOutsideOfObject))203 }204205 /// Build a root context: empty captures, externals filled into a206 /// fresh Once locals frame in declaration order. Used once at207 /// program entry to construct the context the analyzed root LIR208 /// runs against.209 pub(crate) fn root(externals: Vec<Thunk<Val>>) -> Self {119 sup_this: self.sup_this,210 let n: u16 = externals211 .len()212 .try_into()213 .expect("more than u16::MAX externals");214 let cells: Vec<OnceCell<Thunk<Val>>> = externals215 .into_iter()120 bindings: self.bindings,216 .map(|t| {217 let cell = OnceCell::new();218 cell.set(t).map_err(|_| ()).expect("fresh cell");219 cell220 })221 .collect();222 debug_assert_eq!(cells.len(), n as usize);121 offset: self.offset,223 let locals = Cc::new(LocalsFrame::Once(cells));224 Self(Cc::new(ContextInternal {225 captures: Cc::new(Vec::new()),122 parent: self.parent,226 locals,227 sup_this: None,123 }))228 }))124 }229 }125230126 pub(crate) fn build_sup_this(mut self, st: SupThis) -> Context {231 pub(crate) fn pack_captures(&self, shape: &ClosureShape) -> PackedContext {127 self.sup_this = Some(st);232 PackedContext {233 captures: Cc::new(pack_captures(self, &shape.captures)),128 self.build()234 n_locals: shape.n_locals,235 }129 }236 }237 pub(crate) fn pack_captures_sup_this(&self, shape: &ClosureShape) -> PackedContextSupThis {238 PackedContextSupThis {239 captures: Cc::new(pack_captures(self, &shape.captures)),240 n_locals: shape.n_locals,241 sup_this: self.0.sup_this.clone(),242 }243 }244245 pub(crate) fn enter_iter(246 parent: &Context,247 shape: &ClosureShape,248 cb: impl FnOnce(IterContext) -> Result<()>,249 ) -> Result<()> {250 let captures = Cc::new(pack_captures(parent, &shape.captures));251 let locals = IterFrame::new(shape.n_locals);252 cb(IterContext {253 context: Self(Cc::new(ContextInternal {254 captures,255 locals: Cc::new(LocalsFrame::Iter(locals)),256 sup_this: parent.0.sup_this.clone(),257 })),258 })259 }260261 pub(crate) fn enter_using(parent: &Context, shape: &ClosureShape) -> Self {262 debug_assert_eq!(shape.n_locals, 0);263 if shape.captures.is_empty() {264 if let LocalsFrame::Iter(i) = &*parent.0.locals {265 i.captured.set(true);266 }267 // Value never uses captures, thus evaluating it against the parent gives the same result268 return parent.clone();269 }270 let captures = Cc::new(pack_captures(parent, &shape.captures));271 Self(Cc::new(ContextInternal {272 captures,273 locals: parent.0.locals.clone(),274 sup_this: parent.0.sup_this.clone(),275 }))276 }130}277}131278132impl Default for ContextBuilder {279fn pack_captures(parent: &Context, sources: &[LSlot]) -> Vec<Thunk<Val>> {133 fn default() -> Self {134 Self::new()280 sources.iter().map(|src| parent.slot(*src)).collect()135 }136}281}137282138pub struct InitialContextBuilder {283pub struct InitialContextBuilder {139 builder: ContextBuilder,140 externals: Vec<(IStr, LocalId)>,284 externals: Vec<(IStr, LocalId)>,285 values: Vec<Thunk<Val>>,141 next_id: u32,286 next_id: u32,142}287}143288144impl InitialContextBuilder {289impl InitialContextBuilder {145 pub(crate) fn new() -> Self {290 pub(crate) fn new() -> Self {146 Self {291 Self {147 builder: ContextBuilder::new(),148 externals: Vec::new(),292 externals: Vec::new(),293 values: Vec::new(),149 next_id: 0,294 next_id: 0,150 }295 }151 }296 }155 let id = LocalId(self.next_id);300 let id = LocalId(self.next_id);156 self.next_id += 1;301 self.next_id += 1;157 self.externals.push((name, id));302 self.externals.push((name, id));158 self.builder.bind(id, value);303 self.values.push(value);159 }304 }160305161 pub(crate) fn build(self) -> (ContextBuilder, Vec<(IStr, LocalId)>) {306 pub(crate) fn build(self) -> (Vec<(IStr, LocalId)>, Vec<Thunk<Val>>) {162 (self.builder, self.externals)307 (self.externals, self.values)163 }308 }164}309}165310crates/jrsonnet-evaluator/src/dynamic.rsdiffbeforeafterboth1use std::{cell::OnceCell, hash::Hasher, ptr::addr_of};1use std::{hash::Hasher, ptr::addr_of};223use educe::Educe;4use jrsonnet_gcmodule::{Cc, Trace};3use jrsonnet_gcmodule::Cc;56use crate::{Result, bail, error::ErrorKind::InfiniteRecursionDetected, val::ThunkValue};78#[derive(Trace, Educe)]9#[educe(Clone)]10pub struct Pending<V: Trace + 'static>(pub Cc<OnceCell<V>>);11impl<T: Trace + 'static> Pending<T> {12 pub fn new() -> Self {13 Self(Cc::new(OnceCell::new()))14 }15 pub fn new_filled(v: T) -> Self {16 let cell = OnceCell::new();17 let _ = cell.set(v);18 Self(Cc::new(cell))19 }20 /// # Panics21 /// If wrapper is filled already22 pub fn fill(self, value: T) {23 self.024 .set(value)25 .map_err(|_| ())26 .expect("wrapper is filled already");27 }28}29impl<T: Trace + 'static + Clone> Pending<T> {30 /// # Panics31 /// If wrapper is not yet filled32 pub fn unwrap(&self) -> T {33 self.0.get().cloned().expect("pending was not filled")34 }35 pub fn try_get(&self) -> Option<T> {36 self.0.get().cloned()37 }38}3940impl<T: Trace + Clone> ThunkValue for Pending<T> {41 type Output = T;4243 fn get(&self) -> Result<Self::Output> {44 let Some(value) = self.0.get() else {45 // TODO: Other error?46 bail!(InfiniteRecursionDetected);47 };48 Ok(value.clone())49 }50}5152impl<T: Trace + 'static> Default for Pending<T> {53 fn default() -> Self {54 Self::new()55 }56}57458pub fn identity_hash<T, H: Hasher>(v: &Cc<T>, hasher: &mut H) {5pub fn identity_hash<T, H: Hasher>(v: &Cc<T>, hasher: &mut H) {59 hasher.write_usize(addr_of!(**v) as usize);6 hasher.write_usize(addr_of!(**v) as usize);crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth101011use crate::{11use crate::{12 ObjValue, ResolvePathOwned,12 ObjValue, ResolvePathOwned,13 analyze::Diagnostic,13 function::{CallLocation, FunctionSignature, ParamName},14 function::{CallLocation, FunctionSignature, ParamName},14 stdlib::format::FormatError,15 stdlib::format::FormatError,15 typed::TypeLocError,16 typed::TypeLocError,112 CantUseSelfSupOutsideOfObject,113 CantUseSelfSupOutsideOfObject,113114114 #[error("static analysis errors: {}", .0.iter().map(|d| d.message.as_str()).collect::<Vec<_>>().join("; "))]115 #[error("static analysis errors: {}", .0.iter().map(|d| d.message.as_str()).collect::<Vec<_>>().join("; "))]115 StaticAnalysisError(Vec<crate::analyze::Diagnostic>),116 StaticAnalysisError(Vec<Diagnostic>),116 #[error("no super found")]117 #[error("no super found")]117 NoSuperFound,118 NoSuperFound,118119119 #[error("for loop can only iterate over arrays")]120 #[error("for loop can only iterate over arrays")]120 InComprehensionCanOnlyIterateOverArray,121 InComprehensionCanOnlyIterateOverArray,122 #[error("(should not be visible) eager compspec evaluation failed due to captured context")]123 EagerCompspecCaptured,121124122 #[error("array out of bounds: {0} is not within [0,{1})")]125 #[error("array out of bounds: {0} is not within [0,{1})")]123 ArrayBoundsError(isize, u32),126 ArrayBoundsError(isize, u32),397 };400 };398}401}399400#[macro_export]401macro_rules! runtime_error {402 ($l:literal$(, $($tt:tt)*)?) => {403 $crate::error::Error::from($crate::error::ErrorKind::RuntimeError($crate::jrsonnet_macros::format_istr!($l$(, $($tt)*)?)))404 };405}406402crates/jrsonnet-evaluator/src/evaluate/compspec.rsdiffbeforeafterboth3use jrsonnet_types::ValType;3use jrsonnet_types::ValType;445use super::{5use super::{6 destructure::{self, evaluate_locals, evaluate_locals_unbound},6 destructure::{destruct, evaluate_locals_unbound, fill_letrec_binds},7 evaluate_field_member_static, evaluate_field_member_unbound,7 evaluate_field_member_static, evaluate_field_member_unbound,8};8};9use crate::{9use crate::{10 Context, ContextBuilder, ObjValue, ObjValueBuilder, Pending, Result, Thunk, Val,10 Context, ObjValue, ObjValueBuilder, Result, Thunk, Val,11 analyze::{LArrComp, LBind, LCompSpec, LDestruct, LExpr, LFieldMember, LObjComp, LocalId},11 analyze::{12 ClosureShape, LArrComp, LBind, LCompSpec, LDestruct, LExpr, LFieldMember, LObjComp,13 LocalSlot,14 },12 arr::ArrValue,15 arr::ArrValue,13 bail,16 bail,14 error::ErrorKind::*,17 error::ErrorKind::*,15 evaluate::evaluate,18 evaluate::{evaluate, evaluate_trivial},16};19};172018trait CompCollector {21trait CompCollector {222523struct EagerArrCollector<'a> {26struct EagerArrCollector<'a> {24 out: &'a mut Vec<Val>,27 out: &'a mut Vec<Val>,28 value_shape: &'a ClosureShape,25 value: &'a LExpr,29 value: &'a LExpr,26}30}27impl CompCollector for EagerArrCollector<'_> {31impl CompCollector for EagerArrCollector<'_> {28 fn reserve(&mut self, size_hint: usize) {32 fn reserve(&mut self, size_hint: usize) {29 self.out.reserve(size_hint);33 self.out.reserve(size_hint);30 }34 }31 fn collect(&mut self, ctx: Context) -> Result<()> {35 fn collect(&mut self, ctx: Context) -> Result<()> {36 if let Some(v) = evaluate_trivial(self.value) {37 self.out.push(v);38 return Ok(());39 }40 if let LExpr::Slot(slot) = self.value {41 self.out.push(ctx.slot(*slot).evaluate()?);42 return Ok(());43 }44 let env = Context::enter_using(&ctx, self.value_shape);32 self.out.push(evaluate(ctx, self.value)?);45 self.out.push(evaluate(env, self.value)?);33 Ok(())46 Ok(())34 }47 }35}48}364937struct LazyArrCollector<'a> {50struct LazyArrCollector<'a> {38 out: &'a mut Vec<Thunk<Val>>,51 out: &'a mut Vec<Thunk<Val>>,52 value_shape: &'a ClosureShape,39 value: &'a Rc<LExpr>,53 value: &'a Rc<LExpr>,40}54}41impl CompCollector for LazyArrCollector<'_> {55impl CompCollector for LazyArrCollector<'_> {42 fn reserve(&mut self, size_hint: usize) {56 fn reserve(&mut self, size_hint: usize) {43 self.out.reserve(size_hint);57 self.out.reserve(size_hint);44 }58 }45 fn collect(&mut self, ctx: Context) -> Result<()> {59 fn collect(&mut self, ctx: Context) -> Result<()> {60 if let Some(v) = evaluate_trivial(self.value) {61 self.out.push(Thunk::evaluated(v));62 return Ok(());63 }64 if let LExpr::Slot(slot) = self.value.as_ref() {65 self.out.push(ctx.slot(*slot));66 return Ok(());67 }68 let env = Context::enter_using(&ctx, self.value_shape);46 let value_expr = self.value.clone();69 let value_expr = self.value.clone();47 self.out.push(Thunk!(move || evaluate(ctx, &value_expr)));70 self.out.push(Thunk!(move || evaluate(env, &value_expr)));48 Ok(())71 Ok(())49 }72 }50}73}517452struct ObjCompCollectorStatic<'a> {75struct ObjCompCollectorStatic<'a> {53 builder: &'a mut ObjValueBuilder,76 builder: &'a mut ObjValueBuilder,77 frame_shape: &'a ClosureShape,54 locals: &'a [LBind],78 locals: &'a [LBind],55 field: &'a LFieldMember,79 field: &'a LFieldMember,56}80}59 self.builder.reserve_fields(guaranteed);83 self.builder.reserve_fields(guaranteed);60 }84 }61 fn collect(&mut self, inner_ctx: Context) -> Result<()> {85 fn collect(&mut self, inner_ctx: Context) -> Result<()> {86 // Build the object's A-frame fresh per iteration: captures from87 // the comp's iter ctx, locals = `this` (slot 0, unfilled in the88 // static path) + member-locals via letrec.62 let value_ctx = evaluate_locals(inner_ctx.clone(), self.locals);89 let value_ctx = inner_ctx90 .pack_captures_sup_this(self.frame_shape)91 .enter(|fill, ctx| {92 fill_letrec_binds(fill, &ctx, self.locals);93 });63 evaluate_field_member_static(self.builder, inner_ctx, value_ctx, self.field)94 evaluate_field_member_static(self.builder, inner_ctx, value_ctx, self.field)64 }95 }65}96}669767struct ObjCompCollectorUnbound<'a> {98struct ObjCompCollectorUnbound<'a> {68 builder: &'a mut ObjValueBuilder,99 builder: &'a mut ObjValueBuilder,100 frame_shape: Rc<ClosureShape>,69 locals: Rc<Vec<LBind>>,101 locals: Rc<Vec<LBind>>,70 this_id: Option<LocalId>,102 this_slot: Option<LocalSlot>,71 field: &'a LFieldMember,103 field: &'a LFieldMember,72}104}73impl CompCollector for ObjCompCollectorUnbound<'_> {105impl CompCollector for ObjCompCollectorUnbound<'_> {74 fn reserve(&mut self, guaranteed: usize) {106 fn reserve(&mut self, guaranteed: usize) {75 self.builder.reserve_fields(guaranteed);107 self.builder.reserve_fields(guaranteed);76 }108 }77 fn collect(&mut self, inner_ctx: Context) -> Result<()> {109 fn collect(&mut self, inner_ctx: Context) -> Result<()> {78 let uctx = evaluate_locals_unbound(inner_ctx.clone(), self.locals.clone(), self.this_id);110 let uctx = evaluate_locals_unbound(111 &inner_ctx,112 &self.frame_shape,113 self.this_slot,114 self.locals.clone(),115 );79 evaluate_field_member_unbound(self.builder, inner_ctx, uctx, self.field)116 evaluate_field_member_unbound(self.builder, inner_ctx, uctx, self.field)100 0,137 0,101 &mut ObjCompCollectorUnbound {138 &mut ObjCompCollectorUnbound {102 builder: &mut builder,139 builder: &mut builder,140 frame_shape: comp.frame_shape.clone(),103 locals: comp.locals.clone(),141 locals: comp.locals.clone(),104 this_id: comp.this,142 this_slot: comp.this,105 field: &comp.field,143 field: &comp.field,106 },144 },107 )?;145 )?;114 0,152 0,115 &mut ObjCompCollectorStatic {153 &mut ObjCompCollectorStatic {116 builder: &mut builder,154 builder: &mut builder,155 frame_shape: &comp.frame_shape,117 locals: &comp.locals,156 locals: &comp.locals,118 field: &comp.field,157 field: &comp.field,119 },158 },126pub fn evaluate_arr_comp(ctx: Context, comp: &LArrComp) -> Result<Val> {165pub fn evaluate_arr_comp(ctx: Context, comp: &LArrComp) -> Result<Val> {127 let cached_overs = cache_overs(&ctx, &comp.compspecs)?;166 let cached_overs = cache_overs(&ctx, &comp.compspecs)?;128167129 // In eager evaluation, Context is not captured, thus updates in CoW fashion will likely to success168 // Eager fast-path: when the comp has only `if` and `for { destruct: Full(_) }`169 // specs, allocate one Iter A-frame per for-spec and re-set the slot170 // per iteration as long as the frame's refcount stays at 1.130 'eager: {171 'eager: {131 let mut out = Vec::new();172 let mut out = Vec::new();132173147 0,188 0,148 &mut EagerArrCollector {189 &mut EagerArrCollector {149 out: &mut out,190 out: &mut out,191 value_shape: &comp.value_shape,150 value: &comp.value,192 value: &comp.value,151 },193 },152 )194 )166 0,208 0,167 &mut LazyArrCollector {209 &mut LazyArrCollector {168 out: &mut items,210 out: &mut items,211 value_shape: &comp.value_shape,169 value: &comp.value,212 value: &comp.value,170 },213 },171 )?;214 )?;221 }264 }222 }265 }223 LCompSpec::For { destruct, over, .. } => {266 LCompSpec::For {267 frame_shape,268 destruct,269 over,270 ..232 };280 };233 let inner_reserve = guaranteed_reserve.max(1) * arr.len() as usize;281 let inner_reserve = guaranteed_reserve.max(1) * arr.len() as usize;234 match destruct {282 match destruct {235 LDestruct::Full(id) => {283 LDestruct::Full(slot) => {236 let id = *id;284 Context::enter_iter(&ctx, frame_shape, |it| {237 let mut inner_ctx = ContextBuilder::extend(ctx, 1).build();238 for (i, item) in arr.iter().enumerate() {285 for (i, item) in arr.iter().enumerate() {239 // TODO: reuse one ContextBuilder for full evaluate_compspecs pipeline286 let item = item?;240 inner_ctx.cow_fill_binding(id, Thunk::evaluated(item?));287 let ctx = it.create(|f| {288 f.set(*slot, Thunk::evaluated(item));289 })?;241 evaluate_compspecs_eager(290 evaluate_compspecs_eager(242 inner_ctx.clone(),291 ctx,243 specs,292 specs,244 cached_overs,293 cached_overs,245 idx + 1,294 idx + 1,246 if i == 0 { inner_reserve } else { 0 },295 if i == 0 { inner_reserve } else { 0 },247 collector,296 collector,248 )?;297 )?;249 }298 }299 Ok(())300 })?;250 }301 }251 // TODO: Should not be eager? CoW won't work here302 // TODO: Should not be eager? CoW won't work here252 #[cfg(feature = "exp-destruct")]303 #[cfg(feature = "exp-destruct")]284 }335 }285 }336 }286 LCompSpec::For { destruct, over, .. } => {337 LCompSpec::For {338 frame_shape,339 destruct: dst,340 over,341 ..342 } => {295 };351 };296 let inner_reserve = guaranteed_reserve.max(1) * arr.len() as usize;352 let inner_reserve = guaranteed_reserve.max(1) * arr.len() as usize;297 for (i, item) in arr.iter().enumerate() {353 for (i, item) in arr.iter().enumerate() {298 let item_val = item?;354 let item = item?;299 let mut inner_builder = ContextBuilder::extend(ctx.clone(), 1);355 let inner_ctx = ctx.pack_captures_sup_this(frame_shape).enter(|fill, ctx| {300 let fctx = Pending::new();301 destructure::destruct(302 destruct,356 destruct(dst, fill, Thunk::evaluated(item), &ctx);303 Thunk::evaluated(item_val),304 fctx.clone(),305 &mut inner_builder,306 );307 let inner_ctx = inner_builder.build().into_future(fctx);357 });308 evaluate_compspecs(358 evaluate_compspecs(309 inner_ctx,359 inner_ctx,310 specs,360 specs,crates/jrsonnet-evaluator/src/evaluate/destructure.rsdiffbeforeafterboth3use jrsonnet_gcmodule::Trace;3use jrsonnet_gcmodule::Trace;445use crate::{5use crate::{6 Context, ContextBuilder, Pending, Result, SupThis, Thunk, Unbound, Val,6 Context, LocalsFrame, PackedContext, Result, SupThis, Thunk, Unbound, Val,7 analyze::{LBind, LDestruct, LDestructField, LDestructRest, LExpr, LocalId},7 analyze::{8 ClosureShape, LBind, LDestruct, LDestructField, LDestructRest, LExpr, LLocalExpr, LocalSlot,9 },8 bail,10 bail,9 evaluate::evaluate,11 evaluate::evaluate,15 rest: Option<&LDestructRest>,17 rest: Option<&LDestructRest>,16 end: &[LDestruct],18 end: &[LDestruct],171920 fill: &LocalsFrame,18 value: Thunk<Val>,21 value: Thunk<Val>,19 fctx: Pending<Context>,20 builder: &mut ContextBuilder,22 a_ctx: &Context,21) {23) {22 let min_len = start.len() + end.len();24 let min_len = start.len() + end.len();23 let has_rest = rest.is_some();25 let has_rest = rest.is_some();44 let full = full.clone();46 let full = full.clone();45 destruct(47 destruct(46 d,48 d,49 fill,47 Thunk!(move || Ok(full.evaluate()?.get(i as u32)?.expect("length is checked"))),50 Thunk!(move || Ok(full.evaluate()?.get(i as u32)?.expect("length is checked"))),48 fctx.clone(),51 a_ctx,49 builder,50 );52 );51 }53 }525453 let start_len = start.len() as u32;55 let start_len = start.len() as u32;54 let end_len = end.len() as u32;56 let end_len = end.len() as u32;555756 if let Some(crate::analyze::LDestructRest::Keep(id)) = rest {58 if let Some(LDestructRest::Keep(slot)) = rest {57 let full = full.clone();59 let full = full.clone();58 builder.bind(60 fill.set(59 *id,61 *slot,60 Thunk!(move || {62 Thunk!(move || {61 let full = full.evaluate()?;63 let full = full.evaluate()?;62 let to = full.len() - end_len;64 let to = full.len() - end_len;73 let full = full.clone();75 let full = full.clone();74 destruct(76 destruct(75 d,77 d,78 fill,76 Thunk!(move || {79 Thunk!(move || {77 let full = full.evaluate()?;80 let full = full.evaluate()?;78 Ok(full81 Ok(full79 .get(full.len() - end_len + i as u32)?82 .get(full.len() - end_len + i as u32)?80 .expect("length is checked"))83 .expect("length is checked"))81 }),84 }),82 fctx.clone(),85 a_ctx,83 builder,84 );86 );85 }87 }86}88}90 fields: &[LDestructField],92 fields: &[LDestructField],91 rest: Option<&LDestructRest>,93 rest: Option<&LDestructRest>,929495 fill: &LocalsFrame,93 value: Thunk<Val>,96 value: Thunk<Val>,94 fctx: Pending<Context>,95 builder: &mut ContextBuilder,97 a_ctx: &Context,96) {98) {97 use jrsonnet_interner::IStr;99 use jrsonnet_interner::IStr;98 use rustc_hash::FxHashSet;100 use rustc_hash::FxHashSet;124 Ok(obj)126 Ok(obj)125 });127 });126128127 if let Some(crate::analyze::LDestructRest::Keep(id)) = rest {129 if let Some(LDestructRest::Keep(slot)) = rest {128 let full = full.clone();130 let full = full.clone();129 builder.bind(131 fill.set(130 *id,132 *slot,131 Thunk!(move || {133 Thunk!(move || {132 let full = full.evaluate()?;134 let full = full.evaluate()?;133 let mut out = ObjValueBuilder::new();135 let mut out = ObjValueBuilder::new();140142141 for field in fields {143 for field in fields {142 let field_name = field.name.clone();144 let field_name = field.name.clone();143 let default: Option<(Pending<Context>, Rc<LExpr>)> =145 let default_thunk: Option<Thunk<Val>> = field144 field.default.as_ref().map(|e| (fctx.clone(), e.clone()));146 .default147 .as_ref()148 .map(|(shape, expr)| build_b_thunk(a_ctx, shape, expr.clone()));149145 let field_full = full.clone();150 let field_full = full.clone();146 let value_thunk = Thunk!(move || {151 let value_thunk = Thunk!(move || {147 let obj = field_full.evaluate()?;152 let obj = field_full.evaluate()?;148 obj.get(field_name)?.map_or_else(153 obj.get(field_name)?.map_or_else(149 || {154 || default_thunk.as_ref().expect("shape is checked").evaluate(),150 let (fctx, expr) = default.as_ref().expect("shape is checked");151 evaluate(fctx.unwrap(), expr)152 },153 Ok,155 Ok,154 )156 )155 });157 });156158157 if let Some(into) = &field.into {159 if let Some(into) = &field.into {158 destruct(into, value_thunk, fctx.clone(), builder);160 destruct(into, fill, value_thunk, a_ctx);159 } else {161 } else {160 unreachable!("analyzer lowers object-destruct shorthands into `into`");162 unreachable!("analyzer lowers object-destruct shorthands into `into`");161 }163 }162 }164 }163}165}164166165/// Bind a pre-built thunk to an [`LDestruct`] pattern, inserting one166/// binding per [`LocalId`] the pattern introduces.167///168/// `fctx` is needed for object-destruct defaults (feature `exp-destruct`).169#[allow(unused_variables)]167#[allow(unused_variables)]170pub fn destruct(168pub fn destruct(d: &LDestruct, fill: &LocalsFrame, value: Thunk<Val>, a_ctx: &Context) {171 d: &LDestruct,172 value: Thunk<Val>,173 fctx: Pending<Context>,174 builder: &mut ContextBuilder,175) {176 match d {169 match d {177 LDestruct::Full(id) => builder.bind(*id, value),170 LDestruct::Full(slot) => fill.set(*slot, value),178 #[cfg(feature = "exp-destruct")]171 #[cfg(feature = "exp-destruct")]179 LDestruct::Skip => {}172 LDestruct::Skip => {}180 #[cfg(feature = "exp-destruct")]173 #[cfg(feature = "exp-destruct")]181 LDestruct::Array { start, rest, end } => {174 LDestruct::Array { start, rest, end } => {182 destruct_array(start, rest.as_ref(), end, value, fctx, builder)175 destruct_array(start, rest.as_ref(), end, fill, value, a_ctx)183 }176 }184 #[cfg(feature = "exp-destruct")]177 #[cfg(feature = "exp-destruct")]185 LDestruct::Object { fields, rest } => {178 LDestruct::Object { fields, rest } => destruct_object(fields, rest.as_ref(), fill, value, a_ctx),186 destruct_object(fields, rest.as_ref(), value, fctx, builder)187 }188 }179 }189}180}190181191/// Bind one [`LBind`] as a lazy thunk that evaluates in the given192/// future context. Mirrors the old `evaluate_dest` — one entry per193/// binding in a `local … ;` frame.194pub fn evaluate_dest(bind: &LBind, fctx: Pending<Context>, builder: &mut ContextBuilder) {182pub fn build_b_thunk(a_ctx: &Context, shape: &ClosureShape, expr: Rc<LExpr>) -> Thunk<Val> {195 let value = bind.value.clone();183 let env = Context::enter_using(a_ctx, shape);196 let fctx_clone = fctx.clone();197 let thunk = Thunk!(move || {184 Thunk!(move || evaluate(env, &expr))198 let ctx = fctx_clone.unwrap();199 evaluate(ctx, &value)200 });201 destruct(&bind.destruct, thunk, fctx, builder);202}185}203186pub fn build_b_thunk_uno(a_ctx: &Context, shape: Rc<(ClosureShape, LExpr)>) -> Thunk<Val> {204/// Bind each LBind's value as a lazy thunk. Mutually recursive locals187 let env = Context::enter_using(a_ctx, &shape.0);205/// resolve lazily through the shared Pending<Context>.188 Thunk!(move || evaluate(env, &shape.1))189}190206pub fn evaluate_locals(parent: Context, binds: &[LBind]) -> Context {191pub fn fill_letrec_binds(fill: &LocalsFrame, ctx: &Context, binds: &[LBind]) {207 if binds.is_empty() {192 for bind in binds {193 let value_thunk = build_b_thunk(ctx, &bind.value_shape, bind.value.clone());194 destruct(&bind.destruct, fill, value_thunk, ctx);195 }196}197208 return parent;198pub fn evaluate_local_expr(parent: Context, l: &LLocalExpr) -> Result<Val> {209 }210 let fctx = Context::new_future();211 let mut builder =199 let ctx = parent200 .pack_captures_sup_this(&l.frame_shape)212 ContextBuilder::extend(parent, binds.iter().map(|b| b.destruct.ids().len()).sum());201 .enter(|fill, ctx| {213 for bind in binds {202 fill_letrec_binds(fill, ctx, &l.binds);203 });214 evaluate_dest(bind, fctx.clone(), &mut builder);204 evaluate(ctx, &l.body)215 }205}216 builder.build().into_future(fctx)217}218206219pub trait CloneableUnbound<T>: Unbound<Bound = T> + Clone {}207pub trait CloneableUnbound<T>: Unbound<Bound = T> + Clone {}220impl<V, T> CloneableUnbound<T> for V where V: Unbound<Bound = T> + Clone {}208impl<V, T> CloneableUnbound<T> for V where V: Unbound<Bound = T> + Clone {}221209222pub fn evaluate_locals_unbound(210pub fn evaluate_locals_unbound(223 fctx: Context,211 outer: &Context,212 frame_shape: &ClosureShape,213 this_slot: Option<LocalSlot>,224 locals: Rc<Vec<LBind>>,214 locals: Rc<Vec<LBind>>,225 this_id: Option<LocalId>,226) -> impl CloneableUnbound<Context> {215) -> impl CloneableUnbound<Context> {227 #[derive(Trace, Clone)]216 #[derive(Trace, Clone)]228 struct UnboundLocals {217 struct UnboundLocals {229 fctx: Context,218 captures: PackedContext,219 this_slot: Option<LocalSlot>,230 locals: Rc<Vec<LBind>>,220 locals: Rc<Vec<LBind>>,231 this_id: Option<LocalId>,232 }221 }233 impl Unbound for UnboundLocals {222 impl Unbound for UnboundLocals {234 type Bound = Context;223 type Bound = Context;235224236 fn bind(&self, sup_this: SupThis) -> Result<Context> {225 fn bind(&self, sup_this: SupThis) -> Result<Context> {237 let parent = self.fctx.clone();226 Ok(self.captures.clone().enter(sup_this, |fill, ctx| {238239 let fctx = Context::new_future();240 let mut builder = ContextBuilder::extend(241 parent,242 self.locals.iter().map(|b| b.destruct.ids().len()).sum(),243 );244 for b in self.locals.iter() {245 evaluate_dest(b, fctx.clone(), &mut builder);246 }247 if let Some(this_id) = self.this_id {227 if let Some(slot) = self.this_slot {228 let this_obj = ctx.sup_this().expect("sup_this set above").this().clone();248 builder.bind(this_id, Thunk::evaluated(Val::Obj(sup_this.this().clone())));229 fill.set(slot, Thunk::evaluated(Val::Obj(this_obj)));249 }230 }250 let ctx = builder.build_sup_this(sup_this).into_future(fctx);231 fill_letrec_binds(fill, ctx, &self.locals);251 Ok(ctx)232 }))252 }233 }253 }234 }254235255 UnboundLocals {236 UnboundLocals {256 fctx,237 captures: outer.pack_captures(frame_shape),238 this_slot,257 locals,239 locals,258 this_id,259 }240 }260}241}261242crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth778use self::{8use self::{9 compspec::{evaluate_arr_comp, evaluate_obj_comp},9 compspec::{evaluate_arr_comp, evaluate_obj_comp},10 destructure::{evaluate_locals, evaluate_locals_unbound},10 destructure::{build_b_thunk_uno, evaluate_local_expr, evaluate_locals_unbound},11 operator::evaluate_binary_op_special,11 operator::evaluate_binary_op_special,12};12};13use crate::{13use crate::{14 Context, Error, ObjValue, ObjValueBuilder, ObjectAssertion, Result, ResultExt as _, SupThis,14 Context, Error, ObjValue, ObjValueBuilder, ObjectAssertion, Result, ResultExt as _, SupThis,15 Unbound, Val,15 Unbound, Val,16 analyze::{16 analyze::{17 LArgsDesc, LAssertStmt, LExpr, LFieldMember, LFieldName, LFunction, LIndexPart, LObjBody,17 ClosureShape, LArgsDesc, LAssertStmt, LExpr, LFieldMember, LFieldName, LFunction,18 LObjMembers,18 LIndexPart, LObjAsserts, LObjBody, LObjMembers, LSlot,19 },19 },20 arr::ArrValue,20 bail,21 bail, error,21 error::{ErrorKind::*, suggest_object_fields},22 error::{ErrorKind::*, suggest_object_fields},22 evaluate::operator::evaluate_unary_op,23 evaluate::{destructure::fill_letrec_binds, operator::evaluate_unary_op},23 function::{CallLocation, FuncDesc, FuncVal, prepared::PreparedFuncVal},24 function::{CallLocation, FuncDesc, FuncVal, prepared::PreparedFuncVal},24 in_frame, runtime_error,25 in_frame,25 typed::FromUntyped as _,26 typed::FromUntyped as _,26 val::{CachedUnbound, Thunk},27 val::{CachedUnbound, Thunk},27 with_state,28 with_state,62 })63 })63}64}646565/// Evaluate a method definition.66pub fn evaluate_method(ctx: Context, name: IStr, func: &Rc<LFunction>) -> Val {66pub fn evaluate_method(ctx: Context, name: IStr, func: &Rc<LFunction>) -> Val {67 Val::Func(FuncVal::Normal(Cc::new(FuncDesc {67 Val::Func(FuncVal::Normal(Cc::new(FuncDesc {68 name,68 name,69 ctx,69 body_captures: ctx.pack_captures_sup_this(&func.body_shape),70 func: func.clone(),70 func: func.clone(),71 })))71 })))72}72}91}91}929293pub fn evaluate_thunk(ctx: Context, expr: Rc<LExpr>, tailstrict: bool) -> Result<Thunk<Val>> {93pub fn evaluate_thunk(ctx: Context, expr: Rc<LExpr>, tailstrict: bool) -> Result<Thunk<Val>> {94 match &*expr {95 LExpr::Slot(LSlot::Local(i)) => return Ok(ctx.local(*i)),96 LExpr::Slot(LSlot::Capture(i)) => return Ok(ctx.capture(*i)),97 _ => {98 if let Some(v) = evaluate_trivial(&expr) {99 return Ok(Thunk::evaluated(v));100 }101 }102 }94 Ok(if tailstrict {103 Ok(if tailstrict {95 Thunk::evaluated(evaluate(ctx, &expr)?)104 Thunk::evaluated(evaluate(ctx, &expr)?)96 } else {105 } else {106 }115 }107}116}108109pub fn evaluate_named(name: &IStr, ctx: Context, expr: &LExpr) -> Result<Val> {110 if let LExpr::Function(f) = &expr {111 return Ok(evaluate_method(112 ctx,113 f.name.clone().unwrap_or_else(|| name.clone()),114 f,115 ));116 }117 evaluate(ctx, expr)118}119117120pub fn evaluate(ctx: Context, expr: &LExpr) -> Result<Val> {118pub fn evaluate(ctx: Context, expr: &LExpr) -> Result<Val> {121 Ok(match expr {119 Ok(match expr {122 LExpr::Null => Val::Null,120 LExpr::Null => Val::Null,123 LExpr::Bool(b) => Val::Bool(*b),121 LExpr::Bool(b) => Val::Bool(*b),124 LExpr::Str(s) => Val::string(s.clone()),122 LExpr::Str(s) => Val::string(s.clone()),125 LExpr::Num(n) => Val::Num(*n),123 LExpr::Num(n) => Val::Num(*n),126 LExpr::Local(id) => {124 LExpr::Slot(slot) => ctx.slot(*slot).evaluate()?,127 let Some(thunk) = ctx.binding(*id) else {128 bail!("should not happen: unbound local {id:?}");129 };130 thunk.evaluate()?131 }132 LExpr::BadLocal(name) => panic!("unresolvable reference: {name}"),125 LExpr::BadLocal(name) => panic!("unresolvable reference: {name}"),133 LExpr::Arr(items) => Val::Arr(crate::arr::ArrValue::expr(ctx, items.clone())),126 LExpr::Arr { shape, items } => Val::Arr(ArrValue::expr(ctx, shape, items.clone())),134 LExpr::UnaryOp(op, value) => {127 LExpr::UnaryOp(op, value) => {135 let value = evaluate(ctx, value)?;128 let value = evaluate(ctx, value)?;136 evaluate_unary_op(*op, &value)?129 evaluate_unary_op(*op, &value)?137 }130 }138 LExpr::BinaryOp { lhs, op, rhs } => evaluate_binary_op_special(ctx, lhs, *op, rhs)?,131 LExpr::BinaryOp { lhs, op, rhs } => evaluate_binary_op_special(ctx, lhs, *op, rhs)?,139 LExpr::LocalExpr { binds, body } => {132 LExpr::LocalExpr(local_expr) => evaluate_local_expr(ctx, local_expr)?,140 let ctx = evaluate_locals(ctx, binds);141 evaluate(ctx, body)?142 }143 LExpr::IfElse {133 LExpr::IfElse {144 cond,134 cond,145 cond_then,135 cond_then,176 func.name.clone().unwrap_or_else(names::anonymous),166 func.name.clone().unwrap_or_else(names::anonymous),177 func,167 func,178 ),168 ),169 LExpr::IdentityFunction => Val::Func(FuncVal::identity()),179 LExpr::Apply {170 LExpr::Apply {180 applicable,171 applicable,181 args,172 args,240 let n = v.as_num().ok_or_else(|| -> crate::Error {231 let n = v.as_num().ok_or_else(|| -> crate::Error {241 TypeMismatch("slice step", vec![ValType::Num], v.value_type()).into()232 TypeMismatch("slice step", vec![ValType::Num], v.value_type()).into()242 })?;233 })?;243 BoundedUsize::new(n as usize)234 BoundedUsize::new(n as usize).ok_or_else(|| error!("slice step must be >= 1"))244 .ok_or_else(|| runtime_error!("slice step must be >= 1"))245 })235 })246 .transpose()?;236 .transpose()?;247 Val::from(indexable.slice(start, end, step)?)237 Val::from(indexable.slice(start, end, step)?)278 bail!(OnlyFunctionsCanBeCalledGot(func_val.value_type()))268 bail!(OnlyFunctionsCanBeCalledGot(func_val.value_type()))279 };269 };270271 if func.is_identity() && args.names.is_empty() && args.unnamed.len() == 1 {272 return evaluate_thunk(ctx, args.unnamed[0].clone(), tailstrict)?.evaluate();273 }280274281 let name = func.name();275 let name = func.name();276277 if args.names.is_empty() && args.unnamed.len() == 1 && func.params().len() == 1 {278 use crate::function::prepared::PreparedCall;279 let prepared_inline = PreparedCall::empty();280 let arg = evaluate_thunk(ctx, args.unnamed[0].clone(), tailstrict)?;281 let arg_slice = std::slice::from_ref(&arg);282 return in_frame(283 loc,284 || format!("function <{name}> call"),285 || {286 func.evaluate_prepared(287 &prepared_inline,288 CallLocation::native(),289 arg_slice,290 &[],291 tailstrict,292 )293 },294 );295 }296282 let unnamed = args297 let unnamed = args283 .unnamed298 .unnamed286 .map(|e| evaluate_thunk(ctx.clone(), e, tailstrict))301 .map(|e| evaluate_thunk(ctx.clone(), e, tailstrict))287 .collect::<Result<Vec<_>>>()?;302 .collect::<Result<Vec<_>>>()?;303304 // Fast path: positional-only multi-arg call fully covering the305 // params, no defaults.306 if args.names.is_empty() && unnamed.len() == func.params().len() {307 use crate::function::prepared::PreparedCall;308 let prepared_inline = PreparedCall::empty();309 return in_frame(310 loc,311 || format!("function <{name}> call"),312 || {313 func.evaluate_prepared(314 &prepared_inline,315 CallLocation::native(),316 &unnamed,317 &[],318 tailstrict,319 )320 },321 );322 }288323289 let named = args324 let named = args290 .values325 .values302}337}303338304fn evaluate_index(ctx: Context, indexable: &LExpr, parts: &[LIndexPart]) -> Result<Val> {339fn evaluate_index(ctx: Context, indexable: &LExpr, parts: &[LIndexPart]) -> Result<Val> {305 let mut value = if let LExpr::Super = indexable {340 let mut value = if matches!(indexable, LExpr::Super) {306 let sup_this = ctx.try_sup_this()?;341 let sup_this = ctx.try_sup_this()?;307 // First part must be evaluated to get the super field name342 // First part must be evaluated to get the super field name308 if parts.is_empty() {343 if parts.is_empty() {422 #[derive(Trace)]457 #[derive(Trace)]423 struct UnboundValue<B: Trace> {458 struct UnboundValue<B: Trace> {424 uctx: B,459 uctx: B,425 value: Rc<LExpr>,460 value: Rc<(ClosureShape, LExpr)>,426 name: IStr,461 name: IStr,427 }462 }428 impl<B: Unbound<Bound = Context>> Unbound for UnboundValue<B> {463 impl<B: Unbound<Bound = Context>> Unbound for UnboundValue<B> {429 type Bound = Val;464 type Bound = Val;430 fn bind(&self, sup_this: SupThis) -> Result<Val> {465 fn bind(&self, sup_this: SupThis) -> Result<Val> {431 evaluate(self.uctx.bind(sup_this)?, &self.value)466 let a_ctx = self.uctx.bind(sup_this)?;467 let b_ctx = Context::enter_using(&a_ctx, &self.value.0);468 evaluate(b_ctx, &self.value.1)432 }469 }433 }470 }434471468 return Ok(());505 return Ok(());469 };506 };470507471 let value = value.clone();508 let thunk = build_b_thunk_uno(&value_ctx, value.clone());472 builder509 builder473 .field(name)510 .field(name)474 .with_add(*plus)511 .with_add(*plus)475 .with_visibility(*visibility)512 .with_visibility(*visibility)476 .try_thunk(Thunk!(move || { evaluate(value_ctx, &value) }))?;513 .try_thunk(thunk)?;477 Ok(())514 Ok(())478}515}479516491528492 if needs_unbound {529 if needs_unbound {493 let uctx = CachedUnbound::new(evaluate_locals_unbound(530 let uctx = CachedUnbound::new(evaluate_locals_unbound(531 &ctx,494 ctx.clone(),532 &members.frame_shape,533 members.this,495 members.locals.clone(),534 members.locals.clone(),496 members.this,497 ));535 ));498 for field in &members.fields {536 for field in &members.fields {499 evaluate_field_member_unbound(&mut builder, ctx.clone(), uctx.clone(), field)?;537 evaluate_field_member_unbound(&mut builder, ctx.clone(), uctx.clone(), field)?;500 }538 }501 if !members.asserts.is_empty() {539 if let Some(asserts_block) = &members.asserts {502 builder.assert(evaluate_object_assertions_unbound(540 builder.assert(evaluate_object_assertions_unbound(503 uctx,541 uctx,504 members.asserts.clone(),542 asserts_block.clone(),505 ));543 ));506 }544 }507 } else {545 } else {508 let field_ctx = ctx;509 let value_ctx = evaluate_locals(field_ctx.clone(), &members.locals);546 let a_ctx = ctx547 .pack_captures_sup_this(&members.frame_shape)548 .enter(|fill, ctx| {549 fill_letrec_binds(fill, &ctx, &members.locals);550 });510 for field in &members.fields {551 for field in &members.fields {511 evaluate_field_member_static(552 evaluate_field_member_static(&mut builder, ctx.clone(), a_ctx.clone(), field)?;512 &mut builder,513 field_ctx.clone(),514 value_ctx.clone(),515 field,516 )?;517 }553 }518 if !members.asserts.is_empty() {554 if let Some(asserts_block) = &members.asserts {519 builder.assert(evaluate_object_assertions_static(555 builder.assert(evaluate_object_assertions_static(520 value_ctx,556 a_ctx,521 members.asserts.clone(),557 asserts_block.clone(),522 ));558 ));523 }559 }524 }560 }529pub fn evaluate_assert(ctx: Context, assertion: &LAssertStmt) -> Result<()> {565pub fn evaluate_assert(ctx: Context, assertion: &LAssertStmt) -> Result<()> {530 let LAssertStmt { cond, message } = assertion;566 let LAssertStmt { cond, message } = assertion;531 let assertion_result = in_frame(567 let assertion_result = in_frame(532 CallLocation::native(),568 CallLocation::new(&cond.span),533 || "assertion condition".to_owned(),569 || "assertion condition".to_owned(),534 || bool::from_untyped(evaluate(ctx.clone(), cond)?),570 || bool::from_untyped(evaluate(ctx.clone(), cond)?),535 )?;571 )?;550586551fn evaluate_object_assertions_unbound<B: Unbound<Bound = Context>>(587fn evaluate_object_assertions_unbound<B: Unbound<Bound = Context>>(552 uctx: B,588 uctx: B,553 asserts: Rc<Vec<LAssertStmt>>,589 asserts: Rc<LObjAsserts>,554) -> impl ObjectAssertion {590) -> impl ObjectAssertion {555 #[derive(Trace)]591 #[derive(Trace)]556 struct ObjectAssert<B: Trace> {592 struct ObjectAssert<B: Trace> {557 uctx: B,593 uctx: B,558 asserts: Rc<Vec<LAssertStmt>>,594 asserts: Rc<LObjAsserts>,559 }595 }560 impl<B: Unbound<Bound = Context>> ObjectAssertion for ObjectAssert<B> {596 impl<B: Unbound<Bound = Context>> ObjectAssertion for ObjectAssert<B> {561 fn run(&self, sup_this: SupThis) -> Result<()> {597 fn run(&self, sup_this: SupThis) -> Result<()> {562 let ctx = self.uctx.bind(sup_this)?;598 let a_ctx = self.uctx.bind(sup_this)?;599 let assert_env = Context::enter_using(&a_ctx, &self.asserts.shape);563 for assert in &*self.asserts {600 for assert in &self.asserts.asserts {564 evaluate_assert(ctx.clone(), assert)?;601 evaluate_assert(assert_env.clone(), assert)?;565 }602 }566 Ok(())603 Ok(())567 }604 }568 }605 }569 ObjectAssert { uctx, asserts }606 ObjectAssert { uctx, asserts }570}607}571fn evaluate_object_assertions_static(608fn evaluate_object_assertions_static(572 ctx: Context,609 a_ctx: Context,573 asserts: Rc<Vec<LAssertStmt>>,610 asserts: Rc<LObjAsserts>,574) -> impl ObjectAssertion {611) -> impl ObjectAssertion {575 #[derive(Trace)]612 #[derive(Trace)]576 struct ObjectAssert {613 struct ObjectAssert {577 ctx: Context,614 assert_env: Context,578 asserts: Rc<Vec<LAssertStmt>>,615 asserts: Rc<LObjAsserts>,579 }616 }580 impl ObjectAssertion for ObjectAssert {617 impl ObjectAssertion for ObjectAssert {581 fn run(&self, _sup_this: SupThis) -> Result<()> {618 fn run(&self, _sup_this: SupThis) -> Result<()> {582 for assert in &*self.asserts {619 for assert in &self.asserts.asserts {583 evaluate_assert(self.ctx.clone(), assert)?;620 evaluate_assert(self.assert_env.clone(), assert)?;584 }621 }585 Ok(())622 Ok(())586 }623 }587 }624 }625 let assert_env = Context::enter_using(&a_ctx, &asserts.shape);588 ObjectAssert { ctx, asserts }626 ObjectAssert {627 assert_env,628 asserts,629 }589}630}590631crates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth11 prepared::{PreparedCall, parse_prepared_builtin_call},11 prepared::{PreparedCall, parse_prepared_builtin_call},12};12};13use crate::{13use crate::{14 Context, ContextBuilder, Result, Thunk, Val,14 PackedContextSupThis, Result, Thunk, Val,15 analyze::{LDestruct, LExpr, LFunction},15 analyze::LFunction,16 evaluate::{destructure::destruct, ensure_sufficient_stack, evaluate, evaluate_trivial},16 evaluate::{17 destructure::{build_b_thunk, destruct},18 ensure_sufficient_stack, evaluate, evaluate_trivial,19 },17 function::builtin::BuiltinFunc,20 function::builtin::BuiltinFunc,18};21};56 /// { a() = ... }59 /// { a() = ... }57 /// ```60 /// ```58 pub name: IStr,61 pub name: IStr,59 /// Context, in which this function was evaluated.60 ///61 /// # Example62 /// In63 /// ```jsonnet64 /// local a = 2;65 /// function() ...66 /// ```67 /// context will contain `a`.68 pub ctx: Context,62 pub(crate) body_captures: PackedContextSupThis,696370 #[educe(PartialEq(method = Rc::ptr_eq))]64 #[educe(PartialEq(method = Rc::ptr_eq))]71 pub func: Rc<LFunction>,65 pub func: Rc<LFunction>,82 named: &[Thunk<Val>],76 named: &[Thunk<Val>],83 prepared: &PreparedCall,77 prepared: &PreparedCall,84 ) -> Result<Val> {78 ) -> Result<Val> {85 let has_defaults = !prepared.defaults().is_empty();79 let body_ctx = self.body_captures.clone().enter(|fill, ctx| {86 let mut builder = ContextBuilder::extend(self.ctx.clone(), self.func.params.len());80 // Place each provided arg-thunk into its destructured slots.8788 let fctx = Context::new_future();89 for (param_idx, thunk) in unnamed.iter().enumerate() {81 for (param_idx, thunk) in unnamed.iter().enumerate() {90 destruct(82 destruct(91 &self.func.params[param_idx].destruct,83 &self.func.params[param_idx].destruct,84 fill,92 thunk.clone(),85 thunk.clone(),93 fctx.clone(),94 &mut builder,86 &ctx,95 );87 );96 }88 }9798 for &(param_idx, arg_idx) in prepared.named() {89 for &(param_idx, arg_idx) in prepared.named() {99 destruct(90 destruct(100 &self.func.params[param_idx].destruct,91 &self.func.params[param_idx].destruct,92 fill,101 named[arg_idx].clone(),93 named[arg_idx].clone(),102 fctx.clone(),103 &mut builder,94 &ctx,104 );95 );105 }96 }10697107 if has_defaults {108 for ¶m_idx in prepared.defaults() {98 for ¶m_idx in prepared.defaults() {109 let param = &self.func.params[param_idx];99 let param = &self.func.params[param_idx];110 if let Some(default_expr) = ¶m.default {100 let (shape, expr) = param.default.as_ref().expect("default exists");111 let default_expr = default_expr.clone();112 let fctxc = fctx.clone();113 let thunk = Thunk!(move || {101 let thunk = build_b_thunk(&ctx, shape, expr.clone());114 let ctx = fctxc.unwrap();115 evaluate(ctx, &default_expr)116 });117 destruct(¶m.destruct, thunk, fctx.clone(), &mut builder);102 destruct(¶m.destruct, fill, thunk, &ctx);118 }119 }103 }120 };121 let ctx = builder.build().into_future(fctx);104 });105122 ensure_sufficient_stack(|| evaluate(ctx, &self.func.body))106 ensure_sufficient_stack(|| evaluate(body_ctx, &self.func.body))123 }107 }124108125 pub fn evaluate_trivial(&self) -> Option<Val> {109 pub fn evaluate_trivial(&self) -> Option<Val> {157 Self::Builtin(BuiltinFunc::new(builtin))141 Self::Builtin(BuiltinFunc::new(builtin))158 }142 }143144 pub fn identity() -> Self {145 Self::builtin(builtin_id {})146 }159147160 pub fn params(&self) -> FunctionSignature {148 pub fn params(&self) -> FunctionSignature {161 match self {149 match self {193 }181 }194182195 /// Is this function an identity function.183 /// Is this function an identity function.196 ///197 /// Currently only works for builtin `std.id`, aka `Self::Id` value, and `function(x) x`.198 ///184 ///199 /// This function should only be used for optimization, not for the conditional logic, i.e code should work with syntetic identity function too185 /// This function should only be used for optimization, not for the conditional logic, i.e code should work with syntetic identity function too200 pub fn is_identity(&self) -> bool {186 pub fn is_identity(&self) -> bool {201 match self {187 match self {202 Self::Builtin(b) => b.as_any().downcast_ref::<builtin_id>().is_some(),188 Self::Builtin(b) => b.as_any().downcast_ref::<builtin_id>().is_some(),203 Self::Normal(desc) => {189 Self::Normal(_) => false,204 if desc.func.params.len() != 1 {205 return false;206 }207 let param = &desc.func.params[0];208 if param.default.is_some() {209 return false;210 }211 #[allow(irrefutable_let_patterns, reason = "refutable with exp-destruct")]212 let LDestruct::Full(id) = ¶m.destruct else {213 return false;214 };215 matches!(&*desc.func.body, LExpr::Local(v) if v == id)216 }217 }190 }218 }191 }219192crates/jrsonnet-evaluator/src/function/prepared.rsdiffbeforeafterboth46 pub fn defaults(&self) -> &[usize] {46 pub fn defaults(&self) -> &[usize] {47 &self.defaults47 &self.defaults48 }48 }49 pub const fn empty() -> Self {50 Self {51 named: Vec::new(),52 defaults: Vec::new(),53 }54 }49}55}505651pub fn prepare_call(57pub fn prepare_call(crates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth12};12};131314use crate::{14use crate::{15 Error as JrError, ObjValue, ObjValueBuilder, Result, Val, in_description_frame, runtime_error,15 Error as JrError, ObjValue, ObjValueBuilder, Result, Val, error, in_description_frame,16};16};171718impl<'de> Deserialize<'de> for Val {18impl<'de> Deserialize<'de> for Val {629 where629 where630 T: std::fmt::Display,630 T: std::fmt::Display,631 {631 {632 runtime_error!("serde: {msg}")632 error!("serde: {msg}")633 }633 }634}634}635635crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth58pub use val::{Thunk, Val};58pub use val::{Thunk, Val};595960pub mod analyze;60pub mod analyze;61use self::analyze::{LExpr, analyze_root};61use crate::gc::WithCapacityExt as _;62use crate::gc::WithCapacityExt as _;626363#[allow(clippy::needless_return)]64#[allow(clippy::needless_return)]408 file.evaluating = true;409 file.evaluating = true;409 // Dropping file cache guard here, as evaluation may use this map too410 // Dropping file cache guard here, as evaluation may use this map too410 drop(file_cache);411 drop(file_cache);411 let (ctx, externals) = self.create_default_context(file_name.clone()).build();412 let (externals, thunks) = self.create_default_context(file_name).build();412 let report = analyze::analyze_root(&parsed, externals);413 let report = analyze_root(&parsed, externals);413 if report.errored {414 if report.errored {414 return Err(StaticAnalysisError(report.diagnostics_list).into());415 return Err(StaticAnalysisError(report.diagnostics_list).into());415 }416 }417 debug_assert_eq!(report.root_shape.n_locals as usize, thunks.len());418 debug_assert!(report.root_shape.captures.is_empty());419 let ctx = Context::root(thunks);416 let res = evaluate::evaluate(ctx.build(), &report.lir);420 let res = evaluate::evaluate(ctx, &report.lir);417421418 let mut file_cache = self.file_cache();422 let mut file_cache = self.file_cache();419 let mut file = file_cache.entry(path);423 let mut file = file_cache.entry(path);501 }505 }502}506}507508pub struct PreparedSnippet {509 lir: LExpr,510 thunks: Vec<Thunk<Val>>,511}503512504/// Raw methods evaluate passed values but don't perform TLA execution513/// Raw methods evaluate passed values but don't perform TLA execution505impl State {514impl State {515 /// Parses and analyses the given snippet with a custom context516 /// modifier.517 pub fn prepare_snippet_with(518 &self,519 name: impl Into<IStr>,520 code: impl Into<IStr>,521 context_initializer: &dyn ContextInitializer,522 ) -> Result<PreparedSnippet> {523 let code = code.into();524 let source = Source::new_virtual(name.into(), code.clone());525 let parsed = parse_jsonnet(&code, source.clone()).map_err(|e| ImportSyntaxError {526 path: source.clone(),527 error: Box::new(e),528 })?;529 let (externals, thunks) = self530 .create_default_context_with(source, context_initializer)531 .build();532 let report = analyze_root(&parsed, externals);533 if report.errored {534 return Err(StaticAnalysisError(report.diagnostics_list).into());535 }536 debug_assert_eq!(report.root_shape.n_locals as usize, thunks.len());537 debug_assert!(report.root_shape.captures.is_empty());538 Ok(PreparedSnippet {539 lir: report.lir,540 thunks,541 })542 }543 /// Parses and analyses the given snippet544 pub fn prepare_snippet(545 &self,546 name: impl Into<IStr>,547 code: impl Into<IStr>,548 ) -> Result<PreparedSnippet> {549 self.prepare_snippet_with(name, code, &())550 }551 pub fn evaluate_prepared_snippet(&self, prepared: &PreparedSnippet) -> Result<Val> {552 let ctx = Context::root(prepared.thunks.clone());553 evaluate::evaluate(ctx, &prepared.lir)554 }555 /// Parses and evaluates the given snippet with custom context modifier556 pub fn evaluate_snippet_with(557 &self,558 name: impl Into<IStr>,559 code: impl Into<IStr>,560 context_initializer: &dyn ContextInitializer,561 ) -> Result<Val> {562 let prepared = self.prepare_snippet_with(name, code, context_initializer)?;563 self.evaluate_prepared_snippet(&prepared)564 }506 /// Parses and evaluates the given snippet565 /// Parses and evaluates the given snippet507 pub fn evaluate_snippet(&self, name: impl Into<IStr>, code: impl Into<IStr>) -> Result<Val> {566 pub fn evaluate_snippet(&self, name: impl Into<IStr>, code: impl Into<IStr>) -> Result<Val> {508 self.evaluate_snippet_with(name, code, &())567 self.evaluate_snippet_with(name, code, &())509 }568 }510 /// Parses and evaluates the given snippet with custom context modifier511 pub fn evaluate_snippet_with(512 &self,513 name: impl Into<IStr>,514 code: impl Into<IStr>,515 context_initializer: &dyn ContextInitializer,516 ) -> Result<Val> {517 let code = code.into();518 let source = Source::new_virtual(name.into(), code.clone());519 let parsed = parse_jsonnet(&code, source.clone()).map_err(|e| ImportSyntaxError {520 path: source.clone(),521 error: Box::new(e),522 })?;523 let (ctx, externals) = self524 .create_default_context_with(source.clone(), context_initializer)525 .build();526 let report = analyze::analyze_root(&parsed, externals);527 if report.errored {528 return Err(StaticAnalysisError(report.diagnostics_list).into());529 }530 evaluate::evaluate(ctx.build(), &report.lir)531 }532}569}533570534/// Settings utilities571/// Settings utilitiestests/benches/cpp_test_suite.rsdiffbeforeafterboth1use std::{collections::HashMap, fs::read_dir, hint::black_box, path::Path};1use std::{collections::HashMap, fs, fs::read_dir, hint::black_box, path::Path};223use criterion::{Criterion, criterion_group, criterion_main};3use criterion::{Criterion, criterion_group, criterion_main};4use jrsonnet_evaluator::{4use jrsonnet_evaluator::{5 FileImportResolver, State, apply_tla,5 FileImportResolver, State, apply_tla, manifest::JsonFormat, stack::limit_stack_depth,6 manifest::{BlackBoxFormat, JsonFormat},7 stack::limit_stack_depth,8 trace::PathResolver,6 trace::PathResolver,9};7};12static GLOBAL: mimallocator::Mimalloc = mimallocator::Mimalloc;10static GLOBAL: mimallocator::Mimalloc = mimallocator::Mimalloc;131114fn bench_entry(c: &mut Criterion, path: &Path) {12fn bench_entry(c: &mut Criterion, path: &Path) {13 let name = path14 .file_name()15 .expect("file path")16 .to_str()17 .expect("name is utf-8")18 .to_owned();19 let code = fs::read_to_string(path).expect("read bench source");2015 c.bench_function(21 c.bench_function(&name, |b| {16 path.file_name()17 .expect("file path")18 .to_str()28 .import_resolver(FileImportResolver::new(vec![]));28 .import_resolver(FileImportResolver::new(vec![]));2930 let s = s.build();29 let s = s.build();31 let _s = s.enter();30 let _entered = s.enter();3132 // Parse + analysis happen once; each iter only measures33 // evaluation + manifestation.34 let prepared = s35 .prepare_snippet(name.clone(), code.clone())36 .expect("prepared");323733 b.iter(|| {38 b.iter(|| {34 let imported = s.import(path).expect("evaluated");39 let imported = s.evaluate_prepared_snippet(&prepared).expect("evaluated");35 let res = apply_tla(&HashMap::new(), imported).expect("tla applied");40 let res = apply_tla(&HashMap::new(), imported).expect("tla applied");36 black_box(res.manifest(JsonFormat::cli(3)).expect("manifested"));41 black_box(res.manifest(JsonFormat::cli(3)).expect("manifested"));37 });42 });