--- a/bindings/jsonnet/src/val_make.rs +++ b/bindings/jsonnet/src/val_make.rs @@ -56,5 +56,5 @@ /// Make a `JsonnetJsonValue` representing an object. #[no_mangle] pub extern "C" fn jsonnet_json_make_object(_vm: &VM) -> *mut Val { - Box::into_raw(Box::new(Val::Obj(ObjValue::new_empty()))) + Box::into_raw(Box::new(Val::Obj(ObjValue::empty()))) } --- a/crates/jrsonnet-cli/src/tla.rs +++ b/crates/jrsonnet-cli/src/tla.rs @@ -1,5 +1,7 @@ use clap::Parser; -use jrsonnet_evaluator::{IStr, error::Result, function::TlaArg, gc::WithCapacityExt as _, rustc_hash::FxHashMap}; +use jrsonnet_evaluator::{ + error::Result, function::TlaArg, gc::WithCapacityExt as _, rustc_hash::FxHashMap, IStr, +}; use crate::{ExtFile, ExtStr}; --- a/crates/jrsonnet-evaluator/src/dynamic.rs +++ b/crates/jrsonnet-evaluator/src/dynamic.rs @@ -1,5 +1,4 @@ -use std::ptr::addr_of; -use std::{cell::OnceCell, hash::Hasher}; +use std::{cell::OnceCell, hash::Hasher, ptr::addr_of}; use educe::Educe; use jrsonnet_gcmodule::{Cc, Trace}; --- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs +++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs @@ -11,7 +11,18 @@ use self::destructure::destruct; use crate::{ - Context, Error, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result, ResultExt, SupThis, Unbound, Val, arr::ArrValue, bail, destructure::evaluate_dest, error::{ErrorKind::*, suggest_object_fields}, evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op}, function::{CallLocation, FuncDesc, FuncVal}, gc::WithCapacityExt as _, in_frame, typed::Typed, val::{CachedUnbound, IndexableVal, NumValue, StrValue, Thunk}, with_state + arr::ArrValue, + bail, + destructure::evaluate_dest, + error::{suggest_object_fields, ErrorKind::*}, + evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op}, + function::{CallLocation, FuncDesc, FuncVal}, + gc::WithCapacityExt as _, + in_frame, + typed::Typed, + val::{CachedUnbound, IndexableVal, NumValue, StrValue, Thunk}, + with_state, Context, Error, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result, + ResultExt, SupThis, Unbound, Val, }; pub mod destructure; pub mod operator; @@ -137,10 +148,7 @@ )), ]))); destruct(var, value, fctx.clone(), &mut new_bindings)?; - let ctx = ctx - .clone() - .extend(new_bindings, None, None, None) - .into_future(fctx); + let ctx = ctx.clone().extend_bindings(new_bindings).into_future(fctx); evaluate_comp(ctx, &specs[1..], callback)?; } --- a/crates/jrsonnet-evaluator/src/lib.rs +++ b/crates/jrsonnet-evaluator/src/lib.rs @@ -27,11 +27,11 @@ use std::{ any::Any, cell::{RefCell, RefMut}, + clone::Clone, collections::hash_map::Entry, - clone::Clone, fmt::{self, Debug}, + marker::PhantomData, rc::Rc, - marker::PhantomData, }; pub use ctx::*; --- a/crates/jrsonnet-evaluator/src/obj.rs +++ b/crates/jrsonnet-evaluator/src/obj.rs @@ -4,10 +4,12 @@ collections::hash_map::Entry, fmt::{self, Debug}, hash::{Hash, Hasher}, + mem, + ops::ControlFlow, }; use educe::Educe; -use jrsonnet_gcmodule::{cc_dyn, Cc, Trace, Weak}; +use jrsonnet_gcmodule::{cc_dyn, Acyclic, Cc, Trace, Weak}; use jrsonnet_interner::IStr; use jrsonnet_parser::{Span, Visibility}; use rustc_hash::{FxHashMap, FxHashSet}; @@ -74,7 +76,7 @@ pub struct SuperDepth(u32); impl SuperDepth { pub(super) fn deepen(&mut self) { - *self.0 += 1 + self.0 += 1 } } @@ -151,31 +153,56 @@ } #[allow(clippy::module_name_repetitions)] -#[derive(Trace)] +#[derive(Trace, Default)] #[trace(tracking(force))] pub struct OopObject { - // this: Option, - assertions: Cc>, - this_entries: Cc>, - value_cache: RefCell), CacheValue>>, + assertions: Vec, + this_entries: FxHashMap, } impl Debug for OopObject { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("OopObject") - // .field("assertions", &self.assertions) - // .field("assertions_ran", &self.assertions_ran) .field("this_entries", &self.this_entries) - // .field("value_cache", &self.value_cache) .finish_non_exhaustive() } } +impl OopObject { + fn is_empty(&self) -> bool { + self.assertions.is_empty() && self.this_entries.is_empty() + } +} -type EnumFieldsHandler<'a> = dyn FnMut(SuperDepth, FieldIndex, IStr, Visibility) -> bool + 'a; +type EnumFieldsHandler<'a> = + dyn FnMut(SuperDepth, FieldIndex, IStr, EnumFields) -> ControlFlow<()> + 'a; +pub enum EnumFields { + Normal(Visibility), + Omit, +} + #[derive(Trace, Clone)] -pub enum ValueProcess { - None, - SuperPlus, +pub enum GetFor { + // Return value + Final(Val), + // Continue iterating over cores, add current value to sum stack + SuperPlus(Val), + // Ignore the field value, stop at this layer instead + Omit, + NotFound, +} + +#[derive(Acyclic, Clone)] +pub enum FieldVisibility { + Found(Visibility), + Omit, + NotFound, +} + +#[derive(Acyclic, Clone)] +pub enum HasFieldIncludeHidden { + Exists, + NotFound, + Omit, } pub trait ObjectCore: Trace + Any + Debug { @@ -186,13 +213,12 @@ handler: &mut EnumFieldsHandler<'_>, ) -> bool; - fn has_field_include_hidden(&self, name: IStr) -> bool; + fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden; - fn get_for(&self, key: IStr, sup_this: SupThis) -> Result>; - // fn get_for_uncached(&self, key: IStr, this: ObjValue) -> Result>; - fn field_visibility(&self, field: IStr) -> Option; + fn get_for_core(&self, key: IStr, sup_this: SupThis) -> Result; + fn field_visibility_core(&self, field: IStr) -> FieldVisibility; - fn run_assertions_raw(&self, sup_this: SupThis) -> Result<()>; + fn run_assertions_core(&self, sup_this: SupThis) -> Result<()>; } #[derive(Clone, Trace)] @@ -220,13 +246,13 @@ cc_dyn!( #[derive(Clone, Debug)] - ObjCore, ObjectCore, + CcObjectCore, ObjectCore, pub fn new() {...} ); #[derive(Trace, Educe)] #[educe(Debug)] struct ObjValueInner { - cores: Vec, + cores: Vec, assertions_ran: Cell, value_cache: RefCell>, } @@ -251,6 +277,14 @@ }); } +thread_local! { + static EMPTY_OBJ: ObjValue = ObjValue(Cc::new(ObjValueInner { + cores: vec![], + assertions_ran: Cell::new(true), + value_cache: Default::default(), + })) +} + #[allow(clippy::module_name_repetitions)] #[derive(Clone, Trace, Debug, Educe)] #[educe(PartialEq, Hash, Eq)] @@ -258,6 +292,15 @@ #[educe(PartialEq(method(Cc::ptr_eq)), Hash(method(identity_hash)))] Cc, ); +impl ObjValue { + pub fn empty() -> Self { + EMPTY_OBJ.with(|v| v.clone()) + } + pub fn is_empty(&self) -> bool { + self.0.cores.is_empty() || self.len() == 0 + } +} + #[derive(Trace, Debug)] struct StandaloneSuperCore { sup: CoreIdx, @@ -269,53 +312,77 @@ super_depth: &mut SuperDepth, handler: &mut EnumFieldsHandler<'_>, ) -> bool { - self.this - .enum_fields_internal(super_depth, handler, self.sup) + self.this.enum_fields_idx(super_depth, handler, self.sup) } - fn has_field_include_hidden(&self, name: IStr) -> bool { - self.this.has_field_include_hidden_idx(name, self.sup) + fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden { + if self.this.has_field_include_hidden_idx(name, self.sup) { + HasFieldIncludeHidden::Exists + } else { + HasFieldIncludeHidden::NotFound + } } - fn get_for(&self, key: IStr, _sup_this: SupThis) -> Result> { + fn get_for_core(&self, key: IStr, _sup_this: SupThis) -> Result { let v = self.this.get_idx(key, self.sup)?; - Ok(v.map(|v| (v, ValueProcess::None))) + Ok(v.map_or(GetFor::NotFound, |v| GetFor::Final(v))) } - fn field_visibility(&self, field: IStr) -> Option { - self.this.field_visibility_idx(field, self.sup) + fn field_visibility_core(&self, field: IStr) -> FieldVisibility { + match self.this.field_visibility_idx(field, self.sup) { + Some(c) => FieldVisibility::Found(c), + None => FieldVisibility::NotFound, + } } - fn run_assertions_raw(&self, _sup_this: SupThis) -> Result<()> { + fn run_assertions_core(&self, _sup_this: SupThis) -> Result<()> { self.this.run_assertions() } } -#[derive(Debug, Trace)] -struct EmptyObject; -impl ObjectCore for EmptyObject { +#[derive(Debug, Acyclic)] +struct OmitFieldsCore { + omit: FxHashSet, +} +impl ObjectCore for OmitFieldsCore { fn enum_fields_core( &self, - _super_depth: &mut SuperDepth, - _handler: &mut EnumFieldsHandler<'_>, + super_depth: &mut SuperDepth, + handler: &mut EnumFieldsHandler<'_>, ) -> bool { + let mut fi = FieldIndex::default(); + for f in &self.omit { + if let ControlFlow::Break(()) = handler(*super_depth, fi, f.clone(), EnumFields::Omit) { + return false; + } + fi = fi.next(); + } true } - fn has_field_include_hidden(&self, _name: IStr) -> bool { - false + fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden { + if self.omit.contains(&name) { + return HasFieldIncludeHidden::Omit; + } + HasFieldIncludeHidden::NotFound } - fn get_for(&self, _key: IStr, _sup_this: SupThis) -> Result> { - Ok(None) + fn get_for_core(&self, key: IStr, _sup_this: SupThis) -> Result { + if self.omit.contains(&key) { + return Ok(GetFor::Omit); + } + Ok(GetFor::NotFound) } - fn run_assertions_raw(&self, _sup_this: SupThis) -> Result<()> { - Ok(()) + fn field_visibility_core(&self, field: IStr) -> FieldVisibility { + if self.omit.contains(&field) { + return FieldVisibility::Omit; + } + FieldVisibility::NotFound } - fn field_visibility(&self, _field: IStr) -> Option { - None + fn run_assertions_core(&self, _sup_this: SupThis) -> Result<()> { + Ok(()) } } @@ -363,10 +430,12 @@ if !self.sup.super_exists() { bail!(NoSuperFound) } - Ok(ObjValue::new(StandaloneSuperCore { + let mut out = ObjValue::builder(); + out.reserve_cores(1).extend_with_core(StandaloneSuperCore { sup: self.sup, this: self.this.clone(), - })) + }); + Ok(out.build()) } pub fn this(&self) -> &ObjValue { &self.this @@ -385,16 +454,6 @@ } impl ObjValue { - pub fn new(v: impl ObjectCore) -> Self { - Self(Cc::new(ObjValueInner { - cores: vec![ObjCore::new(v)], - assertions_ran: Cell::new(false), - value_cache: RefCell::new(FxHashMap::new()), - })) - } - pub fn new_empty() -> Self { - Self::new(EmptyObject) - } pub fn builder() -> ObjValueBuilder { ObjValueBuilder::new() } @@ -420,6 +479,12 @@ ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default()) } + pub fn extend(&mut self) -> ObjValueBuilder { + let mut out = ObjValueBuilder::new(); + out.with_super(self.clone()); + out + } + #[must_use] pub fn extend_from(&self, sup: Self) -> Self { let mut cores = sup.0.cores.clone(); @@ -442,16 +507,13 @@ .filter(|(_, (visible, _))| *visible) .count() } - pub fn is_empty(&self) -> bool { - self.len() == 0 - } /// For each field, calls callback. /// If callback returns false - ends iteration prematurely. /// /// Returns false if ended prematurely pub fn enum_fields(&self, handler: &mut EnumFieldsHandler<'_>) -> bool { let mut super_depth = SuperDepth::default(); - self.enum_fields_internal( + self.enum_fields_idx( &mut super_depth, handler, CoreIdx { @@ -459,7 +521,7 @@ }, ) } - fn enum_fields_internal( + fn enum_fields_idx( &self, super_depth: &mut SuperDepth, handler: &mut EnumFieldsHandler<'_>, @@ -483,10 +545,14 @@ ) } fn has_field_include_hidden_idx(&self, name: IStr, core: CoreIdx) -> bool { - self.0.cores[..core.idx] - .iter() - .rev() - .any(|v| v.0.has_field_include_hidden(name.clone())) + for ele in self.0.cores[..core.idx].iter().rev() { + match ele.0.has_field_include_hidden_core(name.clone()) { + HasFieldIncludeHidden::Exists => return true, + HasFieldIncludeHidden::NotFound => {} + HasFieldIncludeHidden::Omit => break, + } + } + false } pub fn has_field(&self, name: IStr) -> bool { match self.field_visibility(name) { @@ -544,16 +610,20 @@ sup: CoreIdx { idx: sup }, this: self.clone(), }; - if let Some((val, proc)) = core.0.get_for(key.clone(), sup_this)? { - match proc { - ValueProcess::None if add_stack.is_empty() => return Ok(Some(val)), - ValueProcess::None => { - add_stack.push(val); - break; - } - ValueProcess::SuperPlus => { - add_stack.push(val); - } + match core.0.get_for_core(key.clone(), sup_this)? { + GetFor::Final(val) if add_stack.is_empty() => return Ok(Some(val)), + GetFor::Final(val) => { + add_stack.push(val); + break; + } + GetFor::SuperPlus(val) => { + add_stack.push(val); + } + GetFor::Omit => { + break; + } + GetFor::NotFound => { + continue; } } } @@ -594,11 +664,14 @@ fn field_visibility_idx(&self, field: IStr, core: CoreIdx) -> Option { let mut exists = false; for ele in self.0.cores[..core.idx].iter().rev() { - let vis = ele.0.field_visibility(field.clone()); + let vis = ele.0.field_visibility_core(field.clone()); match vis { - Some(Visibility::Unhide | Visibility::Hidden) => return vis, - Some(Visibility::Normal) => exists = true, - None => {} + FieldVisibility::Found(vis @ (Visibility::Unhide | Visibility::Hidden)) => { + return Some(vis) + } + FieldVisibility::Found(Visibility::Normal) => exists = true, + FieldVisibility::NotFound => {} + FieldVisibility::Omit => break, } } exists.then_some(Visibility::Normal) @@ -616,7 +689,7 @@ sup: CoreIdx { idx }, this: self.clone(), }; - ele.0.run_assertions_raw(sup_this).inspect_err(|_e| { + ele.0.run_assertions_core(sup_this).inspect_err(|_e| { finish_asserting(self); })?; } @@ -664,17 +737,24 @@ self.enum_fields(&mut |depth, index, name, visibility| { let new_sort_key = FieldSortKey::new(depth, index); let entry = out.entry(name); + if matches!(visibility, EnumFields::Omit) { + if let Entry::Occupied(v) = entry { + v.remove(); + } + return ControlFlow::Continue(()); + } let (visible, _) = entry.or_insert((true, new_sort_key)); match visibility { - Visibility::Normal => {} - Visibility::Hidden => { + EnumFields::Omit => unreachable!(), + EnumFields::Normal(Visibility::Normal) => {} + EnumFields::Normal(Visibility::Hidden) => { *visible = false; } - Visibility::Unhide => { + EnumFields::Normal(Visibility::Unhide) => { *visible = true; } }; - false + return ControlFlow::Continue(()); }); out } @@ -776,12 +856,11 @@ impl OopObject { pub fn new( - this_entries: Cc>, - assertions: Cc>, + this_entries: FxHashMap, + assertions: Vec, ) -> Self { Self { this_entries, - value_cache: RefCell::new(FxHashMap::new()), assertions, } } @@ -794,11 +873,14 @@ handler: &mut EnumFieldsHandler<'_>, ) -> bool { for (name, member) in self.this_entries.iter() { - if handler( - *super_depth, - member.original_index, - name.clone(), - member.flags.visibility(), + if matches!( + handler( + *super_depth, + member.original_index, + name.clone(), + EnumFields::Normal(member.flags.visibility()), + ), + ControlFlow::Break(()) ) { return false; } @@ -806,28 +888,35 @@ true } - fn has_field_include_hidden(&self, name: IStr) -> bool { - self.this_entries.contains_key(&name) + fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden { + if self.this_entries.contains_key(&name) { + HasFieldIncludeHidden::Exists + } else { + HasFieldIncludeHidden::NotFound + } } - fn get_for(&self, key: IStr, sup_this: SupThis) -> Result> { + fn get_for_core(&self, key: IStr, sup_this: SupThis) -> Result { match self.this_entries.get(&key) { - Some(k) => Ok(Some(( - k.invoke.evaluate(sup_this)?, - if k.flags.add() { - ValueProcess::SuperPlus + Some(k) => { + let v = k.invoke.evaluate(sup_this)?; + Ok(if k.flags.add() { + GetFor::SuperPlus(v) } else { - ValueProcess::None - }, - ))), - None => Ok(None), + GetFor::Final(v) + }) + } + None => Ok(GetFor::NotFound), } } - fn field_visibility(&self, name: IStr) -> Option { - Some(self.this_entries.get(&name)?.flags.visibility()) + fn field_visibility_core(&self, name: IStr) -> FieldVisibility { + match self.this_entries.get(&name) { + Some(f) => FieldVisibility::Found(f.flags.visibility()), + None => FieldVisibility::NotFound, + } } - fn run_assertions_raw(&self, sup_this: SupThis) -> Result<()> { + fn run_assertions_core(&self, sup_this: SupThis) -> Result<()> { if self.assertions.is_empty() { return Ok(()); } @@ -840,9 +929,9 @@ #[allow(clippy::module_name_repetitions)] pub struct ObjValueBuilder { - sup: Option, - map: FxHashMap, - assertions: Vec, + sup: Vec, + + new: OopObject, next_field_index: FieldIndex, } impl ObjValueBuilder { @@ -851,23 +940,29 @@ } pub fn with_capacity(capacity: usize) -> Self { Self { - sup: None, - map: FxHashMap::with_capacity(capacity), - assertions: Vec::new(), + sup: vec![], + new: OopObject { + assertions: vec![], + this_entries: FxHashMap::with_capacity(capacity), + }, next_field_index: FieldIndex::default(), } } + pub fn reserve_cores(&mut self, capacity: usize) -> &mut Self { + self.sup.reserve_exact(capacity); + self + } pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self { - self.assertions.reserve_exact(capacity); + self.new.assertions.reserve_exact(capacity); self } pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self { - self.sup = Some(super_obj); + self.sup = super_obj.0.cores.clone(); self } pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self { - self.assertions.push(CcObjectAssertion::new(assertion)); + self.new.assertions.push(CcObjectAssertion::new(assertion)); self } pub fn field(&mut self, name: impl Into) -> ObjMemberBuilder> { @@ -892,12 +987,33 @@ Ok(self) } - pub fn build(self) -> ObjValue { - if self.sup.is_none() && self.map.is_empty() && self.assertions.is_empty() { - return ObjValue::new_empty(); + pub fn extend_with_core(&mut self, core: impl ObjectCore) { + self.commit(); + self.sup.push(CcObjectCore::new(core)); + } + + fn commit(&mut self) { + if !self.new.is_empty() { + self.sup.push(CcObjectCore::new(mem::take(&mut self.new))); + } + self.next_field_index = FieldIndex::default(); + } + + pub fn with_fields_omitted(&mut self, omit: FxHashSet) { + self.commit(); + self.sup.push(CcObjectCore::new(OmitFieldsCore { omit })); + } + + pub fn build(mut self) -> ObjValue { + self.commit(); + if self.sup.is_empty() { + return ObjValue::empty(); } - let res = ObjValue::new(OopObject::new(Cc::new(self.map), Cc::new(self.assertions))); - self.sup.map(|sup| res.extend_from(sup)).unwrap_or(res) + ObjValue(Cc::new(ObjValueInner { + cores: self.sup, + assertions_ran: Cell::new(false), + value_cache: Default::default(), + })) } } impl Default for ObjValueBuilder { @@ -968,7 +1084,7 @@ pub fn value(self, value: impl Into) { let (receiver, name, member) = self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value.into()))); - let entry = receiver.0.map.entry(name); + let entry = receiver.0.new.this_entries.entry(name); entry.insert_entry(member); } @@ -985,7 +1101,7 @@ pub fn binding(self, binding: MaybeUnbound) -> Result<()> { let (receiver, name, member) = self.build_member(binding); let location = member.location.clone(); - let old = receiver.0.map.insert(name.clone(), member); + let old = receiver.0.new.this_entries.insert(name.clone(), member); if old.is_some() { in_frame( CallLocation(location.as_ref()), --- a/crates/jrsonnet-stdlib/src/manifest/xml.rs +++ b/crates/jrsonnet-stdlib/src/manifest/xml.rs @@ -62,10 +62,10 @@ if let Val::Obj(attrs) = maybe_attrs { (true, attrs) } else { - (false, ObjValue::new_empty()) + (false, ObjValue::empty()) } } else { - (false, ObjValue::new_empty()) + (false, ObjValue::empty()) }; Ok(Self::Tag { tag, --- a/crates/jrsonnet-stdlib/src/misc.rs +++ b/crates/jrsonnet-stdlib/src/misc.rs @@ -172,7 +172,7 @@ let Some(patch) = patch.as_obj() else { return Ok(patch); }; - let target = target.as_obj().unwrap_or_else(|| ObjValue::new_empty()); + let target = target.as_obj().unwrap_or_else(|| ObjValue::empty()); let target_fields = target .fields( // FIXME: Makes no sense to preserve order for BTreeSet, it would be better to use IndexSet here? --- a/crates/jrsonnet-stdlib/src/objects.rs +++ b/crates/jrsonnet-stdlib/src/objects.rs @@ -1,8 +1,9 @@ use jrsonnet_evaluator::{ function::builtin, + gc::WithCapacityExt, rustc_hash::FxHashSet, val::{ArrValue, Val}, - IStr, MaybeUnbound, ObjValue, ObjValueBuilder, Thunk, + IStr, ObjValue, ObjValueBuilder, }; #[builtin] @@ -156,43 +157,11 @@ } #[builtin] -pub fn builtin_object_remove_key( - obj: ObjValue, - key: IStr, - - // Standard implementation uses std.objectFields without such argument, we can't - // assume order preservation should always be enabled/disabled - #[default(false)] - #[cfg(feature = "exp-preserve-order")] - preserve_order: bool, -) -> ObjValue { - let mut new_obj = ObjValueBuilder::with_capacity(obj.len() - 1); - let all_fields = obj.fields_ex( - true, - #[cfg(feature = "exp-preserve-order")] - preserve_order, - ); - let visible_fields = obj - .fields_ex( - false, - #[cfg(feature = "exp-preserve-order")] - preserve_order, - ) - .into_iter() - .collect::>(); - - for field in &all_fields { - if *field == key { - continue; - } - let mut b = new_obj.field(field.clone()); - if !visible_fields.contains(&field) { - b = b.hide(); - } - let _ = b.binding(MaybeUnbound::Bound(Thunk::result( - obj.get(field.clone()).transpose().expect("field exists"), - ))); - } +pub fn builtin_object_remove_key(obj: ObjValue, key: IStr) -> ObjValue { + let mut omit = FxHashSet::with_capacity(1); + omit.insert(key); - new_obj.build() + let mut out = ObjValueBuilder::new(); + out.with_super(obj).with_fields_omitted(omit); + out.build() } --- a/tests/tests/as_native.rs +++ b/tests/tests/as_native.rs @@ -1,4 +1,4 @@ -use jrsonnet_evaluator::{trace::PathResolver, FileImportResolver, Result, State}; +use jrsonnet_evaluator::{FileImportResolver, Result, State, trace::PathResolver}; use jrsonnet_stdlib::ContextInitializer; mod common; --- a/tests/tests/builtin.rs +++ b/tests/tests/builtin.rs @@ -1,11 +1,11 @@ mod common; use jrsonnet_evaluator::{ - function::{builtin, builtin::Builtin, CallLocation, FuncVal}, + ContextBuilder, ContextInitializer, FileImportResolver, Result, State, Thunk, Val, + function::{CallLocation, FuncVal, builtin, builtin::Builtin}, parser::Source, trace::PathResolver, typed::Typed, - ContextBuilder, ContextInitializer, FileImportResolver, Result, State, Thunk, Val, }; use jrsonnet_gcmodule::Trace; use jrsonnet_stdlib::ContextInitializer as StdContextInitializer; @@ -18,11 +18,8 @@ #[test] fn basic_function() -> Result<()> { let a: a = a {}; - let v = u32::from_untyped(a.call( - ContextBuilder::new().build(), - CallLocation::native(), - &(), - )?)?; + let v = + u32::from_untyped(a.call(ContextBuilder::new().build(), CallLocation::native(), &())?)?; ensure_eq!(v, 1); Ok(()) --- a/tests/tests/common.rs +++ b/tests/tests/common.rs @@ -1,8 +1,8 @@ use jrsonnet_evaluator::{ + ContextBuilder, ContextInitializer as ContextInitializerT, ObjValueBuilder, Result, Thunk, Val, bail, - function::{builtin, FuncVal}, + function::{FuncVal, builtin}, parser::Source, - ContextBuilder, ContextInitializer as ContextInitializerT, ObjValueBuilder, Result, Thunk, Val, }; use jrsonnet_gcmodule::Trace; --- a/tests/tests/golden.rs +++ b/tests/tests/golden.rs @@ -4,9 +4,9 @@ }; use jrsonnet_evaluator::{ + FileImportResolver, State, manifest::JsonFormat, trace::{CompactFormat, PathResolver, TraceFormat}, - FileImportResolver, State, }; use jrsonnet_stdlib::ContextInitializer; mod common; --- a/tests/tests/sanity.rs +++ b/tests/tests/sanity.rs @@ -1,7 +1,6 @@ use jrsonnet_evaluator::{ - bail, + FileImportResolver, Result, State, Val, bail, trace::{CompactFormat, PathResolver, TraceFormat}, - FileImportResolver, Result, State, Val, }; use jrsonnet_stdlib::ContextInitializer; --- a/tests/tests/std_native.rs +++ b/tests/tests/std_native.rs @@ -1,4 +1,4 @@ -use jrsonnet_evaluator::{function::builtin, trace::PathResolver, State}; +use jrsonnet_evaluator::{State, function::builtin, trace::PathResolver}; use jrsonnet_stdlib::ContextInitializer; #[builtin] @@ -14,9 +14,11 @@ state.context_initializer(std); let state = state.build(); - assert!(state - .evaluate_snippet("test", "std.native('example')(1, 3) == 4") - .unwrap() - .as_bool() - .expect("boolean output")); + assert!( + state + .evaluate_snippet("test", "std.native('example')(1, 3) == 4") + .unwrap() + .as_bool() + .expect("boolean output") + ); } --- a/tests/tests/suite.rs +++ b/tests/tests/suite.rs @@ -4,8 +4,8 @@ }; use jrsonnet_evaluator::{ + FileImportResolver, State, Val, trace::{CompactFormat, PathResolver, TraceFormat}, - FileImportResolver, State, Val, }; use jrsonnet_stdlib::ContextInitializer; --- a/tests/tests/typed_obj.rs +++ b/tests/tests/typed_obj.rs @@ -2,7 +2,7 @@ use std::fmt::Debug; -use jrsonnet_evaluator::{trace::PathResolver, typed::Typed, Result, State}; +use jrsonnet_evaluator::{Result, State, trace::PathResolver, typed::Typed}; use jrsonnet_stdlib::ContextInitializer; #[derive(Clone, Typed, PartialEq, Debug)]