difftreelog
feat OOP-aware objectRemoveKey
in: master
17 files changed
bindings/jsonnet/src/val_make.rsdiffbeforeafterboth56/// Make a `JsonnetJsonValue` representing an object.56/// Make a `JsonnetJsonValue` representing an object.57#[no_mangle]57#[no_mangle]58pub extern "C" fn jsonnet_json_make_object(_vm: &VM) -> *mut Val {58pub extern "C" fn jsonnet_json_make_object(_vm: &VM) -> *mut Val {59 Box::into_raw(Box::new(Val::Obj(ObjValue::new_empty())))59 Box::into_raw(Box::new(Val::Obj(ObjValue::empty())))60}60}6161crates/jrsonnet-cli/src/tla.rsdiffbeforeafterboth1use clap::Parser;1use clap::Parser;2use jrsonnet_evaluator::{IStr, error::Result, function::TlaArg, gc::WithCapacityExt as _, rustc_hash::FxHashMap};2use jrsonnet_evaluator::{3 error::Result, function::TlaArg, gc::WithCapacityExt as _, rustc_hash::FxHashMap, IStr,4};354use crate::{ExtFile, ExtStr};6use crate::{ExtFile, ExtStr};crates/jrsonnet-evaluator/src/dynamic.rsdiffbeforeafterboth1use std::ptr::addr_of;2use std::{cell::OnceCell, hash::Hasher};1use std::{cell::OnceCell, hash::Hasher, ptr::addr_of};324use educe::Educe;3use educe::Educe;5use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_gcmodule::{Cc, Trace};crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth111112use self::destructure::destruct;12use self::destructure::destruct;13use crate::{13use crate::{14 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_state14 arr::ArrValue,15 bail,16 destructure::evaluate_dest,17 error::{suggest_object_fields, ErrorKind::*},18 evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},19 function::{CallLocation, FuncDesc, FuncVal},20 gc::WithCapacityExt as _,21 in_frame,22 typed::Typed,23 val::{CachedUnbound, IndexableVal, NumValue, StrValue, Thunk},24 with_state, Context, Error, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result,25 ResultExt, SupThis, Unbound, Val,15};26};16pub mod destructure;27pub mod destructure;17pub mod operator;28pub mod operator;137 )),148 )),138 ])));149 ])));139 destruct(var, value, fctx.clone(), &mut new_bindings)?;150 destruct(var, value, fctx.clone(), &mut new_bindings)?;140 let ctx = ctx151 let ctx = ctx.clone().extend_bindings(new_bindings).into_future(fctx);141 .clone()142 .extend(new_bindings, None, None, None)143 .into_future(fctx);144152145 evaluate_comp(ctx, &specs[1..], callback)?;153 evaluate_comp(ctx, &specs[1..], callback)?;crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth27use std::{27use std::{28 any::Any,28 any::Any,29 cell::{RefCell, RefMut},29 cell::{RefCell, RefMut},30 clone::Clone,30 collections::hash_map::Entry,31 collections::hash_map::Entry,31 clone::Clone,32 fmt::{self, Debug},32 fmt::{self, Debug},33 rc::Rc,33 marker::PhantomData,34 marker::PhantomData,34 rc::Rc,35};35};363637pub use ctx::*;37pub use ctx::*;crates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth4 collections::hash_map::Entry,4 collections::hash_map::Entry,5 fmt::{self, Debug},5 fmt::{self, Debug},6 hash::{Hash, Hasher},6 hash::{Hash, Hasher},7 mem,8 ops::ControlFlow,7};9};8109use educe::Educe;11use educe::Educe;10use jrsonnet_gcmodule::{cc_dyn, Cc, Trace, Weak};12use jrsonnet_gcmodule::{cc_dyn, Acyclic, Cc, Trace, Weak};11use jrsonnet_interner::IStr;13use jrsonnet_interner::IStr;12use jrsonnet_parser::{Span, Visibility};14use jrsonnet_parser::{Span, Visibility};13use rustc_hash::{FxHashMap, FxHashSet};15use rustc_hash::{FxHashMap, FxHashSet};74 pub struct SuperDepth(u32);76 pub struct SuperDepth(u32);75 impl SuperDepth {77 impl SuperDepth {76 pub(super) fn deepen(&mut self) {78 pub(super) fn deepen(&mut self) {77 *self.0 += 179 self.0 += 178 }80 }79 }81 }8082151}153}152154153#[allow(clippy::module_name_repetitions)]155#[allow(clippy::module_name_repetitions)]154#[derive(Trace)]156#[derive(Trace, Default)]155#[trace(tracking(force))]157#[trace(tracking(force))]156pub struct OopObject {158pub struct OopObject {157 // this: Option<ObjValue>,159 assertions: Vec<CcObjectAssertion>,158 assertions: Cc<Vec<CcObjectAssertion>>,159 this_entries: Cc<FxHashMap<IStr, ObjMember>>,160 this_entries: FxHashMap<IStr, ObjMember>,160 value_cache: RefCell<FxHashMap<(IStr, Option<WeakObjValue>), CacheValue>>,161}161}162impl Debug for OopObject {162impl Debug for OopObject {163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {164 f.debug_struct("OopObject")164 f.debug_struct("OopObject")165 // .field("assertions", &self.assertions)166 // .field("assertions_ran", &self.assertions_ran)167 .field("this_entries", &self.this_entries)165 .field("this_entries", &self.this_entries)168 // .field("value_cache", &self.value_cache)169 .finish_non_exhaustive()166 .finish_non_exhaustive()170 }167 }171}168}169impl OopObject {170 fn is_empty(&self) -> bool {171 self.assertions.is_empty() && self.this_entries.is_empty()172 }173}172174173type EnumFieldsHandler<'a> = dyn FnMut(SuperDepth, FieldIndex, IStr, Visibility) -> bool + 'a;175type EnumFieldsHandler<'a> =176 dyn FnMut(SuperDepth, FieldIndex, IStr, EnumFields) -> ControlFlow<()> + 'a;174177178pub enum EnumFields {179 Normal(Visibility),180 Omit,181}182175#[derive(Trace, Clone)]183#[derive(Trace, Clone)]176pub enum ValueProcess {184pub enum GetFor {177 None,185 // Return value186 Final(Val),178 SuperPlus,187 // Continue iterating over cores, add current value to sum stack188 SuperPlus(Val),189 // Ignore the field value, stop at this layer instead190 Omit,191 NotFound,179}192}180193194#[derive(Acyclic, Clone)]195pub enum FieldVisibility {196 Found(Visibility),197 Omit,198 NotFound,199}200201#[derive(Acyclic, Clone)]202pub enum HasFieldIncludeHidden {203 Exists,204 NotFound,205 Omit,206}207181pub trait ObjectCore: Trace + Any + Debug {208pub trait ObjectCore: Trace + Any + Debug {182 // If callback returns false, iteration stops, and this call returns false.209 // If callback returns false, iteration stops, and this call returns false.183 fn enum_fields_core(210 fn enum_fields_core(186 handler: &mut EnumFieldsHandler<'_>,213 handler: &mut EnumFieldsHandler<'_>,187 ) -> bool;214 ) -> bool;188215189 fn has_field_include_hidden(&self, name: IStr) -> bool;216 fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden;190217191 fn get_for(&self, key: IStr, sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>>;218 fn get_for_core(&self, key: IStr, sup_this: SupThis) -> Result<GetFor>;192 // fn get_for_uncached(&self, key: IStr, this: ObjValue) -> Result<Option<(Val, ValueProcess)>>;219 fn field_visibility_core(&self, field: IStr) -> FieldVisibility;193 fn field_visibility(&self, field: IStr) -> Option<Visibility>;194220195 fn run_assertions_raw(&self, sup_this: SupThis) -> Result<()>;221 fn run_assertions_core(&self, sup_this: SupThis) -> Result<()>;196}222}197223198#[derive(Clone, Trace)]224#[derive(Clone, Trace)]220246221cc_dyn!(247cc_dyn!(222 #[derive(Clone, Debug)]248 #[derive(Clone, Debug)]223 ObjCore, ObjectCore,249 CcObjectCore, ObjectCore,224 pub fn new() {...}250 pub fn new() {...}225);251);226#[derive(Trace, Educe)]252#[derive(Trace, Educe)]227#[educe(Debug)]253#[educe(Debug)]228struct ObjValueInner {254struct ObjValueInner {229 cores: Vec<ObjCore>,255 cores: Vec<CcObjectCore>,230 assertions_ran: Cell<bool>,256 assertions_ran: Cell<bool>,231 value_cache: RefCell<FxHashMap<(IStr, CoreIdx), CacheValue>>,257 value_cache: RefCell<FxHashMap<(IStr, CoreIdx), CacheValue>>,232}258}251 });277 });252}278}253279280thread_local! {281 static EMPTY_OBJ: ObjValue = ObjValue(Cc::new(ObjValueInner {282 cores: vec![],283 assertions_ran: Cell::new(true),284 value_cache: Default::default(),285 }))286}287254#[allow(clippy::module_name_repetitions)]288#[allow(clippy::module_name_repetitions)]255#[derive(Clone, Trace, Debug, Educe)]289#[derive(Clone, Trace, Debug, Educe)]256#[educe(PartialEq, Hash, Eq)]290#[educe(PartialEq, Hash, Eq)]257pub struct ObjValue(291pub struct ObjValue(258 #[educe(PartialEq(method(Cc::ptr_eq)), Hash(method(identity_hash)))] Cc<ObjValueInner>,292 #[educe(PartialEq(method(Cc::ptr_eq)), Hash(method(identity_hash)))] Cc<ObjValueInner>,259);293);260294295impl ObjValue {296 pub fn empty() -> Self {297 EMPTY_OBJ.with(|v| v.clone())298 }299 pub fn is_empty(&self) -> bool {300 self.0.cores.is_empty() || self.len() == 0301 }302}303261#[derive(Trace, Debug)]304#[derive(Trace, Debug)]262struct StandaloneSuperCore {305struct StandaloneSuperCore {263 sup: CoreIdx,306 sup: CoreIdx,269 super_depth: &mut SuperDepth,312 super_depth: &mut SuperDepth,270 handler: &mut EnumFieldsHandler<'_>,313 handler: &mut EnumFieldsHandler<'_>,271 ) -> bool {314 ) -> bool {272 self.this315 self.this.enum_fields_idx(super_depth, handler, self.sup)273 .enum_fields_internal(super_depth, handler, self.sup)274 }316 }275317276 fn has_field_include_hidden(&self, name: IStr) -> bool {318 fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden {277 self.this.has_field_include_hidden_idx(name, self.sup)319 if self.this.has_field_include_hidden_idx(name, self.sup) {320 HasFieldIncludeHidden::Exists321 } else {322 HasFieldIncludeHidden::NotFound323 }278 }324 }279325280 fn get_for(&self, key: IStr, _sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>> {326 fn get_for_core(&self, key: IStr, _sup_this: SupThis) -> Result<GetFor> {281 let v = self.this.get_idx(key, self.sup)?;327 let v = self.this.get_idx(key, self.sup)?;282 Ok(v.map(|v| (v, ValueProcess::None)))328 Ok(v.map_or(GetFor::NotFound, |v| GetFor::Final(v)))283 }329 }284330285 fn field_visibility(&self, field: IStr) -> Option<Visibility> {331 fn field_visibility_core(&self, field: IStr) -> FieldVisibility {286 self.this.field_visibility_idx(field, self.sup)332 match self.this.field_visibility_idx(field, self.sup) {333 Some(c) => FieldVisibility::Found(c),334 None => FieldVisibility::NotFound,335 }287 }336 }288337289 fn run_assertions_raw(&self, _sup_this: SupThis) -> Result<()> {338 fn run_assertions_core(&self, _sup_this: SupThis) -> Result<()> {290 self.this.run_assertions()339 self.this.run_assertions()291 }340 }292}341}293342294#[derive(Debug, Trace)]343#[derive(Debug, Acyclic)]295struct EmptyObject;344struct OmitFieldsCore {345 omit: FxHashSet<IStr>,346}296impl ObjectCore for EmptyObject {347impl ObjectCore for OmitFieldsCore {297 fn enum_fields_core(348 fn enum_fields_core(298 &self,349 &self,299 _super_depth: &mut SuperDepth,350 super_depth: &mut SuperDepth,300 _handler: &mut EnumFieldsHandler<'_>,351 handler: &mut EnumFieldsHandler<'_>,301 ) -> bool {352 ) -> bool {353 let mut fi = FieldIndex::default();354 for f in &self.omit {355 if let ControlFlow::Break(()) = handler(*super_depth, fi, f.clone(), EnumFields::Omit) {356 return false;357 }358 fi = fi.next();359 }302 true360 true303 }361 }304362305 fn has_field_include_hidden(&self, _name: IStr) -> bool {363 fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden {306 false364 if self.omit.contains(&name) {365 return HasFieldIncludeHidden::Omit;366 }367 HasFieldIncludeHidden::NotFound307 }368 }308369309 fn get_for(&self, _key: IStr, _sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>> {370 fn get_for_core(&self, key: IStr, _sup_this: SupThis) -> Result<GetFor> {371 if self.omit.contains(&key) {310 Ok(None)372 return Ok(GetFor::Omit);373 }374 Ok(GetFor::NotFound)311 }375 }312376313 fn run_assertions_raw(&self, _sup_this: SupThis) -> Result<()> {377 fn field_visibility_core(&self, field: IStr) -> FieldVisibility {378 if self.omit.contains(&field) {314 Ok(())379 return FieldVisibility::Omit;380 }381 FieldVisibility::NotFound315 }382 }316383317 fn field_visibility(&self, _field: IStr) -> Option<Visibility> {384 fn run_assertions_core(&self, _sup_this: SupThis) -> Result<()> {318 None385 Ok(())319 }386 }320}387}321388363 if !self.sup.super_exists() {430 if !self.sup.super_exists() {364 bail!(NoSuperFound)431 bail!(NoSuperFound)365 }432 }366 Ok(ObjValue::new(StandaloneSuperCore {433 let mut out = ObjValue::builder();434 out.reserve_cores(1).extend_with_core(StandaloneSuperCore {367 sup: self.sup,435 sup: self.sup,368 this: self.this.clone(),436 this: self.this.clone(),369 }))437 });438 Ok(out.build())370 }439 }371 pub fn this(&self) -> &ObjValue {440 pub fn this(&self) -> &ObjValue {372 &self.this441 &self.this385}454}386455387impl ObjValue {456impl ObjValue {388 pub fn new(v: impl ObjectCore) -> Self {389 Self(Cc::new(ObjValueInner {390 cores: vec![ObjCore::new(v)],391 assertions_ran: Cell::new(false),392 value_cache: RefCell::new(FxHashMap::new()),393 }))394 }395 pub fn new_empty() -> Self {396 Self::new(EmptyObject)397 }398 pub fn builder() -> ObjValueBuilder {457 pub fn builder() -> ObjValueBuilder {399 ObjValueBuilder::new()458 ObjValueBuilder::new()400 }459 }420 ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())479 ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())421 }480 }422481482 pub fn extend(&mut self) -> ObjValueBuilder {483 let mut out = ObjValueBuilder::new();484 out.with_super(self.clone());485 out486 }487423 #[must_use]488 #[must_use]424 pub fn extend_from(&self, sup: Self) -> Self {489 pub fn extend_from(&self, sup: Self) -> Self {425 let mut cores = sup.0.cores.clone();490 let mut cores = sup.0.cores.clone();442 .filter(|(_, (visible, _))| *visible)507 .filter(|(_, (visible, _))| *visible)443 .count()508 .count()444 }509 }445 pub fn is_empty(&self) -> bool {446 self.len() == 0447 }448 /// For each field, calls callback.510 /// For each field, calls callback.449 /// If callback returns false - ends iteration prematurely.511 /// If callback returns false - ends iteration prematurely.450 ///512 ///451 /// Returns false if ended prematurely513 /// Returns false if ended prematurely452 pub fn enum_fields(&self, handler: &mut EnumFieldsHandler<'_>) -> bool {514 pub fn enum_fields(&self, handler: &mut EnumFieldsHandler<'_>) -> bool {453 let mut super_depth = SuperDepth::default();515 let mut super_depth = SuperDepth::default();454 self.enum_fields_internal(516 self.enum_fields_idx(455 &mut super_depth,517 &mut super_depth,456 handler,518 handler,457 CoreIdx {519 CoreIdx {458 idx: self.0.cores.len(),520 idx: self.0.cores.len(),459 },521 },460 )522 )461 }523 }462 fn enum_fields_internal(524 fn enum_fields_idx(463 &self,525 &self,464 super_depth: &mut SuperDepth,526 super_depth: &mut SuperDepth,465 handler: &mut EnumFieldsHandler<'_>,527 handler: &mut EnumFieldsHandler<'_>,483 )545 )484 }546 }485 fn has_field_include_hidden_idx(&self, name: IStr, core: CoreIdx) -> bool {547 fn has_field_include_hidden_idx(&self, name: IStr, core: CoreIdx) -> bool {486 self.0.cores[..core.idx]548 for ele in self.0.cores[..core.idx].iter().rev() {487 .iter()488 .rev()489 .any(|v| v.0.has_field_include_hidden(name.clone()))549 match ele.0.has_field_include_hidden_core(name.clone()) {550 HasFieldIncludeHidden::Exists => return true,551 HasFieldIncludeHidden::NotFound => {}552 HasFieldIncludeHidden::Omit => break,553 }554 }555 false490 }556 }491 pub fn has_field(&self, name: IStr) -> bool {557 pub fn has_field(&self, name: IStr) -> bool {492 match self.field_visibility(name) {558 match self.field_visibility(name) {544 sup: CoreIdx { idx: sup },610 sup: CoreIdx { idx: sup },545 this: self.clone(),611 this: self.clone(),546 };612 };547 if let Some((val, proc)) = core.0.get_for(key.clone(), sup_this)? {613 match core.0.get_for_core(key.clone(), sup_this)? {548 match proc {614 GetFor::Final(val) if add_stack.is_empty() => return Ok(Some(val)),549 ValueProcess::None if add_stack.is_empty() => return Ok(Some(val)),550 ValueProcess::None => {615 GetFor::Final(val) => {551 add_stack.push(val);552 break;553 }554 ValueProcess::SuperPlus => {555 add_stack.push(val);616 add_stack.push(val);556 }617 break;557 }618 }619 GetFor::SuperPlus(val) => {620 add_stack.push(val);621 }622 GetFor::Omit => {623 break;624 }625 GetFor::NotFound => {626 continue;627 }558 }628 }559 }629 }560 if add_stack.is_empty() {630 if add_stack.is_empty() {594 fn field_visibility_idx(&self, field: IStr, core: CoreIdx) -> Option<Visibility> {664 fn field_visibility_idx(&self, field: IStr, core: CoreIdx) -> Option<Visibility> {595 let mut exists = false;665 let mut exists = false;596 for ele in self.0.cores[..core.idx].iter().rev() {666 for ele in self.0.cores[..core.idx].iter().rev() {597 let vis = ele.0.field_visibility(field.clone());667 let vis = ele.0.field_visibility_core(field.clone());598 match vis {668 match vis {599 Some(Visibility::Unhide | Visibility::Hidden) => return vis,669 FieldVisibility::Found(vis @ (Visibility::Unhide | Visibility::Hidden)) => {670 return Some(vis)600 Some(Visibility::Normal) => exists = true,671 }672 FieldVisibility::Found(Visibility::Normal) => exists = true,601 None => {}673 FieldVisibility::NotFound => {}674 FieldVisibility::Omit => break,602 }675 }603 }676 }604 exists.then_some(Visibility::Normal)677 exists.then_some(Visibility::Normal)616 sup: CoreIdx { idx },689 sup: CoreIdx { idx },617 this: self.clone(),690 this: self.clone(),618 };691 };619 ele.0.run_assertions_raw(sup_this).inspect_err(|_e| {692 ele.0.run_assertions_core(sup_this).inspect_err(|_e| {620 finish_asserting(self);693 finish_asserting(self);621 })?;694 })?;622 }695 }664 self.enum_fields(&mut |depth, index, name, visibility| {737 self.enum_fields(&mut |depth, index, name, visibility| {665 let new_sort_key = FieldSortKey::new(depth, index);738 let new_sort_key = FieldSortKey::new(depth, index);666 let entry = out.entry(name);739 let entry = out.entry(name);740 if matches!(visibility, EnumFields::Omit) {741 if let Entry::Occupied(v) = entry {742 v.remove();743 }744 return ControlFlow::Continue(());745 }667 let (visible, _) = entry.or_insert((true, new_sort_key));746 let (visible, _) = entry.or_insert((true, new_sort_key));668 match visibility {747 match visibility {669 Visibility::Normal => {}748 EnumFields::Omit => unreachable!(),749 EnumFields::Normal(Visibility::Normal) => {}670 Visibility::Hidden => {750 EnumFields::Normal(Visibility::Hidden) => {671 *visible = false;751 *visible = false;672 }752 }673 Visibility::Unhide => {753 EnumFields::Normal(Visibility::Unhide) => {674 *visible = true;754 *visible = true;675 }755 }676 };756 };677 false757 return ControlFlow::Continue(());678 });758 });679 out759 out680 }760 }776856777impl OopObject {857impl OopObject {778 pub fn new(858 pub fn new(779 this_entries: Cc<FxHashMap<IStr, ObjMember>>,859 this_entries: FxHashMap<IStr, ObjMember>,780 assertions: Cc<Vec<CcObjectAssertion>>,860 assertions: Vec<CcObjectAssertion>,781 ) -> Self {861 ) -> Self {782 Self {862 Self {783 this_entries,863 this_entries,784 value_cache: RefCell::new(FxHashMap::new()),785 assertions,864 assertions,786 }865 }787 }866 }794 handler: &mut EnumFieldsHandler<'_>,873 handler: &mut EnumFieldsHandler<'_>,795 ) -> bool {874 ) -> bool {796 for (name, member) in self.this_entries.iter() {875 for (name, member) in self.this_entries.iter() {797 if handler(876 if matches!(877 handler(798 *super_depth,878 *super_depth,799 member.original_index,879 member.original_index,800 name.clone(),880 name.clone(),801 member.flags.visibility(),881 EnumFields::Normal(member.flags.visibility()),882 ),883 ControlFlow::Break(())802 ) {884 ) {803 return false;885 return false;804 }886 }805 }887 }806 true888 true807 }889 }808890809 fn has_field_include_hidden(&self, name: IStr) -> bool {891 fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden {810 self.this_entries.contains_key(&name)892 if self.this_entries.contains_key(&name) {893 HasFieldIncludeHidden::Exists894 } else {895 HasFieldIncludeHidden::NotFound896 }811 }897 }812898813 fn get_for(&self, key: IStr, sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>> {899 fn get_for_core(&self, key: IStr, sup_this: SupThis) -> Result<GetFor> {814 match self.this_entries.get(&key) {900 match self.this_entries.get(&key) {815 Some(k) => Ok(Some((901 Some(k) => {816 k.invoke.evaluate(sup_this)?,902 let v = k.invoke.evaluate(sup_this)?;817 if k.flags.add() {903 Ok(if k.flags.add() {818 ValueProcess::SuperPlus904 GetFor::SuperPlus(v)819 } else {905 } else {820 ValueProcess::None906 GetFor::Final(v)821 },907 })822 ))),908 }823 None => Ok(None),909 None => Ok(GetFor::NotFound),824 }910 }825 }911 }826 fn field_visibility(&self, name: IStr) -> Option<Visibility> {912 fn field_visibility_core(&self, name: IStr) -> FieldVisibility {827 Some(self.this_entries.get(&name)?.flags.visibility())913 match self.this_entries.get(&name) {914 Some(f) => FieldVisibility::Found(f.flags.visibility()),915 None => FieldVisibility::NotFound,916 }828 }917 }829918830 fn run_assertions_raw(&self, sup_this: SupThis) -> Result<()> {919 fn run_assertions_core(&self, sup_this: SupThis) -> Result<()> {831 if self.assertions.is_empty() {920 if self.assertions.is_empty() {832 return Ok(());921 return Ok(());833 }922 }840929841#[allow(clippy::module_name_repetitions)]930#[allow(clippy::module_name_repetitions)]842pub struct ObjValueBuilder {931pub struct ObjValueBuilder {843 sup: Option<ObjValue>,932 sup: Vec<CcObjectCore>,844 map: FxHashMap<IStr, ObjMember>,933845 assertions: Vec<CcObjectAssertion>,934 new: OopObject,846 next_field_index: FieldIndex,935 next_field_index: FieldIndex,847}936}848impl ObjValueBuilder {937impl ObjValueBuilder {851 }940 }852 pub fn with_capacity(capacity: usize) -> Self {941 pub fn with_capacity(capacity: usize) -> Self {853 Self {942 Self {854 sup: None,943 sup: vec![],855 map: FxHashMap::with_capacity(capacity),944 new: OopObject {945 assertions: vec![],946 this_entries: FxHashMap::with_capacity(capacity),856 assertions: Vec::new(),947 },857 next_field_index: FieldIndex::default(),948 next_field_index: FieldIndex::default(),858 }949 }859 }950 }951 pub fn reserve_cores(&mut self, capacity: usize) -> &mut Self {952 self.sup.reserve_exact(capacity);953 self954 }860 pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self {955 pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self {861 self.assertions.reserve_exact(capacity);956 self.new.assertions.reserve_exact(capacity);862 self957 self863 }958 }864 pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {959 pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {865 self.sup = Some(super_obj);960 self.sup = super_obj.0.cores.clone();866 self961 self867 }962 }868963869 pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {964 pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {870 self.assertions.push(CcObjectAssertion::new(assertion));965 self.new.assertions.push(CcObjectAssertion::new(assertion));871 self966 self872 }967 }873 pub fn field(&mut self, name: impl Into<IStr>) -> ObjMemberBuilder<ValueBuilder<'_>> {968 pub fn field(&mut self, name: impl Into<IStr>) -> ObjMemberBuilder<ValueBuilder<'_>> {892 Ok(self)987 Ok(self)893 }988 }894989895 pub fn build(self) -> ObjValue {990 pub fn extend_with_core(&mut self, core: impl ObjectCore) {896 if self.sup.is_none() && self.map.is_empty() && self.assertions.is_empty() {991 self.commit();992 self.sup.push(CcObjectCore::new(core));993 }994995 fn commit(&mut self) {996 if !self.new.is_empty() {897 return ObjValue::new_empty();997 self.sup.push(CcObjectCore::new(mem::take(&mut self.new)));898 }998 }899 let res = ObjValue::new(OopObject::new(Cc::new(self.map), Cc::new(self.assertions)));999 self.next_field_index = FieldIndex::default();1000 }10011002 pub fn with_fields_omitted(&mut self, omit: FxHashSet<IStr>) {1003 self.commit();1004 self.sup.push(CcObjectCore::new(OmitFieldsCore { omit }));1005 }10061007 pub fn build(mut self) -> ObjValue {1008 self.commit();900 self.sup.map(|sup| res.extend_from(sup)).unwrap_or(res)1009 if self.sup.is_empty() {1010 return ObjValue::empty();1011 }1012 ObjValue(Cc::new(ObjValueInner {1013 cores: self.sup,1014 assertions_ran: Cell::new(false),1015 value_cache: Default::default(),1016 }))901 }1017 }902}1018}903impl Default for ObjValueBuilder {1019impl Default for ObjValueBuilder {968 pub fn value(self, value: impl Into<Val>) {1084 pub fn value(self, value: impl Into<Val>) {969 let (receiver, name, member) =1085 let (receiver, name, member) =970 self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value.into())));1086 self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value.into())));971 let entry = receiver.0.map.entry(name);1087 let entry = receiver.0.new.this_entries.entry(name);972 entry.insert_entry(member);1088 entry.insert_entry(member);973 }1089 }9741090985 pub fn binding(self, binding: MaybeUnbound) -> Result<()> {1101 pub fn binding(self, binding: MaybeUnbound) -> Result<()> {986 let (receiver, name, member) = self.build_member(binding);1102 let (receiver, name, member) = self.build_member(binding);987 let location = member.location.clone();1103 let location = member.location.clone();988 let old = receiver.0.map.insert(name.clone(), member);1104 let old = receiver.0.new.this_entries.insert(name.clone(), member);989 if old.is_some() {1105 if old.is_some() {990 in_frame(1106 in_frame(991 CallLocation(location.as_ref()),1107 CallLocation(location.as_ref()),crates/jrsonnet-stdlib/src/manifest/xml.rsdiffbeforeafterboth62 if let Val::Obj(attrs) = maybe_attrs {62 if let Val::Obj(attrs) = maybe_attrs {63 (true, attrs)63 (true, attrs)64 } else {64 } else {65 (false, ObjValue::new_empty())65 (false, ObjValue::empty())66 }66 }67 } else {67 } else {68 (false, ObjValue::new_empty())68 (false, ObjValue::empty())69 };69 };70 Ok(Self::Tag {70 Ok(Self::Tag {71 tag,71 tag,crates/jrsonnet-stdlib/src/misc.rsdiffbeforeafterboth172 let Some(patch) = patch.as_obj() else {172 let Some(patch) = patch.as_obj() else {173 return Ok(patch);173 return Ok(patch);174 };174 };175 let target = target.as_obj().unwrap_or_else(|| ObjValue::new_empty());175 let target = target.as_obj().unwrap_or_else(|| ObjValue::empty());176 let target_fields = target176 let target_fields = target177 .fields(177 .fields(178 // FIXME: Makes no sense to preserve order for BTreeSet, it would be better to use IndexSet here?178 // FIXME: Makes no sense to preserve order for BTreeSet, it would be better to use IndexSet here?crates/jrsonnet-stdlib/src/objects.rsdiffbeforeafterboth1use jrsonnet_evaluator::{1use jrsonnet_evaluator::{2 function::builtin,2 function::builtin,3 gc::WithCapacityExt,3 rustc_hash::FxHashSet,4 rustc_hash::FxHashSet,4 val::{ArrValue, Val},5 val::{ArrValue, Val},5 IStr, MaybeUnbound, ObjValue, ObjValueBuilder, Thunk,6 IStr, ObjValue, ObjValueBuilder,6};7};788#[builtin]9#[builtin]158#[builtin]159#[builtin]159pub fn builtin_object_remove_key(160pub fn builtin_object_remove_key(obj: ObjValue, key: IStr) -> ObjValue {160 obj: ObjValue,161 key: IStr,162163 // Standard implementation uses std.objectFields without such argument, we can't164 // assume order preservation should always be enabled/disabled165 #[default(false)]166 #[cfg(feature = "exp-preserve-order")]167 preserve_order: bool,168) -> ObjValue {169 let mut new_obj = ObjValueBuilder::with_capacity(obj.len() - 1);161 let mut omit = FxHashSet::with_capacity(1);170 let all_fields = obj.fields_ex(162 omit.insert(key);171 true,163172 #[cfg(feature = "exp-preserve-order")]173 preserve_order,174 );175 let visible_fields = obj164 let mut out = ObjValueBuilder::new();176 .fields_ex(177 false,178 #[cfg(feature = "exp-preserve-order")]179 preserve_order,180 )181 .into_iter()182 .collect::<FxHashSet<_>>();183184 for field in &all_fields {185 if *field == key {186 continue;187 }188 let mut b = new_obj.field(field.clone());189 if !visible_fields.contains(&field) {165 out.with_super(obj).with_fields_omitted(omit);190 b = b.hide();191 }192 let _ = b.binding(MaybeUnbound::Bound(Thunk::result(193 obj.get(field.clone()).transpose().expect("field exists"),194 )));195 }196197 new_obj.build()166 out.build()198}167}199168tests/tests/as_native.rsdiffbeforeafterboth1use jrsonnet_evaluator::{trace::PathResolver, FileImportResolver, Result, State};1use jrsonnet_evaluator::{FileImportResolver, Result, State, trace::PathResolver};2use jrsonnet_stdlib::ContextInitializer;2use jrsonnet_stdlib::ContextInitializer;334mod common;4mod common;tests/tests/builtin.rsdiffbeforeafterboth1mod common;1mod common;223use jrsonnet_evaluator::{3use jrsonnet_evaluator::{4 ContextBuilder, ContextInitializer, FileImportResolver, Result, State, Thunk, Val,4 function::{builtin, builtin::Builtin, CallLocation, FuncVal},5 function::{CallLocation, FuncVal, builtin, builtin::Builtin},5 parser::Source,6 parser::Source,6 trace::PathResolver,7 trace::PathResolver,7 typed::Typed,8 typed::Typed,8 ContextBuilder, ContextInitializer, FileImportResolver, Result, State, Thunk, Val,9};9};10use jrsonnet_gcmodule::Trace;10use jrsonnet_gcmodule::Trace;11use jrsonnet_stdlib::ContextInitializer as StdContextInitializer;11use jrsonnet_stdlib::ContextInitializer as StdContextInitializer;tests/tests/common.rsdiffbeforeafterboth1use jrsonnet_evaluator::{1use jrsonnet_evaluator::{2 bail,3 function::{builtin, FuncVal},4 parser::Source,5 ContextBuilder, ContextInitializer as ContextInitializerT, ObjValueBuilder, Result, Thunk, Val,2 ContextBuilder, ContextInitializer as ContextInitializerT, ObjValueBuilder, Result, Thunk, Val,3 bail,4 function::{FuncVal, builtin},5 parser::Source,6};6};7use jrsonnet_gcmodule::Trace;7use jrsonnet_gcmodule::Trace;88tests/tests/golden.rsdiffbeforeafterboth4};4};556use jrsonnet_evaluator::{6use jrsonnet_evaluator::{7 FileImportResolver, State,7 manifest::JsonFormat,8 manifest::JsonFormat,8 trace::{CompactFormat, PathResolver, TraceFormat},9 trace::{CompactFormat, PathResolver, TraceFormat},9 FileImportResolver, State,10};10};11use jrsonnet_stdlib::ContextInitializer;11use jrsonnet_stdlib::ContextInitializer;12mod common;12mod common;tests/tests/sanity.rsdiffbeforeafterboth1use jrsonnet_evaluator::{1use jrsonnet_evaluator::{2 bail,2 FileImportResolver, Result, State, Val, bail,3 trace::{CompactFormat, PathResolver, TraceFormat},3 trace::{CompactFormat, PathResolver, TraceFormat},4 FileImportResolver, Result, State, Val,5};4};6use jrsonnet_stdlib::ContextInitializer;5use jrsonnet_stdlib::ContextInitializer;76tests/tests/std_native.rsdiffbeforeafterboth1use jrsonnet_evaluator::{function::builtin, trace::PathResolver, State};1use jrsonnet_evaluator::{State, function::builtin, trace::PathResolver};2use jrsonnet_stdlib::ContextInitializer;2use jrsonnet_stdlib::ContextInitializer;334#[builtin]4#[builtin]tests/tests/suite.rsdiffbeforeafterboth4};4};556use jrsonnet_evaluator::{6use jrsonnet_evaluator::{7 FileImportResolver, State, Val,7 trace::{CompactFormat, PathResolver, TraceFormat},8 trace::{CompactFormat, PathResolver, TraceFormat},8 FileImportResolver, State, Val,9};9};10use jrsonnet_stdlib::ContextInitializer;10use jrsonnet_stdlib::ContextInitializer;1111tests/tests/typed_obj.rsdiffbeforeafterboth223use std::fmt::Debug;3use std::fmt::Debug;445use jrsonnet_evaluator::{trace::PathResolver, typed::Typed, Result, State};5use jrsonnet_evaluator::{Result, State, trace::PathResolver, typed::Typed};6use jrsonnet_stdlib::ContextInitializer;6use jrsonnet_stdlib::ContextInitializer;778#[derive(Clone, Typed, PartialEq, Debug)]8#[derive(Clone, Typed, PartialEq, Debug)]