git.delta.rocks / jrsonnet / refs/commits / eea91c38b18b

difftreelog

fix objectRemoveKey only skips the current stack

qsrrwpnwYaroslav Bolyukin2026-02-08parent: #3c9d13d.patch.diff
in: master

1 file changed

modifiedcrates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/obj.rs
1use std::{2	any::Any,3	cell::{Cell, RefCell},4	collections::hash_map::Entry,5	fmt::{self, Debug},6	hash::{Hash, Hasher},7	mem,8	ops::ControlFlow,9};1011use educe::Educe;12use jrsonnet_gcmodule::{cc_dyn, Acyclic, Cc, Trace, Weak};13use jrsonnet_interner::IStr;14use jrsonnet_parser::{Span, Visibility};15use rustc_hash::{FxHashMap, FxHashSet};1617use crate::{18	arr::{PickObjectKeyValues, PickObjectValues},19	bail,20	error::{suggest_object_fields, ErrorKind::*},21	function::{CallLocation, FuncVal},22	gc::WithCapacityExt as _,23	identity_hash, in_frame,24	operator::evaluate_add_op,25	val::ArrValue,26	CcUnbound, MaybeUnbound, Result, Thunk, Unbound, Val,27};2829#[cfg(not(feature = "exp-preserve-order"))]30mod ordering {31	#![allow(32		// This module works as stub for preserve-order feature33		clippy::unused_self,34	)]3536	use jrsonnet_gcmodule::Trace;3738	#[derive(Clone, Copy, Default, Debug, Trace)]39	pub struct FieldIndex(());40	impl FieldIndex {41		pub const fn next(self) -> Self {42			Self(())43		}44	}4546	#[derive(Clone, Copy, Default, Debug, Trace)]47	pub struct SuperDepth(());48	impl SuperDepth {49		pub(super) fn deepen(self) {}50	}5152	#[derive(Clone, Copy, Debug)]53	pub struct FieldSortKey(());54	impl FieldSortKey {55		pub const fn new(_: SuperDepth, _: FieldIndex) -> Self {56			Self(())57		}58	}59}6061#[cfg(feature = "exp-preserve-order")]62mod ordering {63	use std::cmp::Reverse;6465	use jrsonnet_gcmodule::Trace;6667	#[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]68	pub struct FieldIndex(u32);69	impl FieldIndex {70		pub fn next(self) -> Self {71			Self(self.0 + 1)72		}73	}7475	#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Debug)]76	pub struct SuperDepth(u32);77	impl SuperDepth {78		pub(super) fn deepen(&mut self) {79			self.0 += 180		}81	}8283	#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]84	pub struct FieldSortKey(Reverse<SuperDepth>, FieldIndex);85	impl FieldSortKey {86		pub fn new(depth: SuperDepth, index: FieldIndex) -> Self {87			Self(Reverse(depth), index)88		}89	}90}9192use ordering::{FieldIndex, FieldSortKey, SuperDepth};9394// 0 - add95//  12 - visibility96#[derive(Clone, Copy)]97pub struct ObjFieldFlags(u8);98impl ObjFieldFlags {99	fn new(add: bool, visibility: Visibility) -> Self {100		let mut v = 0;101		if add {102			v |= 1;103		}104		v |= match visibility {105			Visibility::Normal => 0b000,106			Visibility::Hidden => 0b010,107			Visibility::Unhide => 0b100,108		};109		Self(v)110	}111	pub fn add(&self) -> bool {112		self.0 & 1 != 0113	}114	pub fn visibility(&self) -> Visibility {115		match (self.0 & 0b110) >> 1 {116			0b00 => Visibility::Normal,117			0b01 => Visibility::Hidden,118			0b10 => Visibility::Unhide,119			_ => unreachable!(),120		}121	}122}123impl Debug for ObjFieldFlags {124	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {125		f.debug_struct("ObjFieldFlags")126			.field("add", &self.add())127			.field("visibility", &self.visibility())128			.finish()129	}130}131132#[allow(clippy::module_name_repetitions)]133#[derive(Debug, Trace)]134pub struct ObjMember {135	#[trace(skip)]136	flags: ObjFieldFlags,137	original_index: FieldIndex,138	pub invoke: MaybeUnbound,139	pub location: Option<Span>,140}141142cc_dyn!(CcObjectAssertion, ObjectAssertion);143pub trait ObjectAssertion: Trace {144	fn run(&self, sup_this: SupThis) -> Result<()>;145}146147// Field => This148149#[derive(Trace, Debug)]150enum CacheValue {151	Cached(Result<Option<Val>>),152	Pending,153}154155#[allow(clippy::module_name_repetitions)]156#[derive(Trace, Default)]157#[trace(tracking(force))]158pub struct OopObject {159	assertions: Vec<CcObjectAssertion>,160	this_entries: FxHashMap<IStr, ObjMember>,161}162impl Debug for OopObject {163	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {164		f.debug_struct("OopObject")165			.field("this_entries", &self.this_entries)166			.finish_non_exhaustive()167	}168}169impl OopObject {170	fn is_empty(&self) -> bool {171		self.assertions.is_empty() && self.this_entries.is_empty()172	}173}174175type EnumFieldsHandler<'a> =176	dyn FnMut(SuperDepth, FieldIndex, IStr, EnumFields) -> ControlFlow<()> + 'a;177178pub enum EnumFields {179	Normal(Visibility),180	Omit,181}182183#[derive(Trace, Clone)]184pub enum GetFor {185	// Return value186	Final(Val),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,192}193194#[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}207208pub trait ObjectCore: Trace + Any + Debug {209	// If callback returns false, iteration stops, and this call returns false.210	fn enum_fields_core(211		&self,212		super_depth: &mut SuperDepth,213		handler: &mut EnumFieldsHandler<'_>,214	) -> bool;215216	fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden;217218	fn get_for_core(&self, key: IStr, sup_this: SupThis) -> Result<GetFor>;219	fn field_visibility_core(&self, field: IStr) -> FieldVisibility;220221	fn run_assertions_core(&self, sup_this: SupThis) -> Result<()>;222}223224#[derive(Clone, Trace)]225pub struct WeakObjValue(#[trace(skip)] Weak<ObjValueInner>);226impl Debug for WeakObjValue {227	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {228		f.debug_tuple("WeakObjValue").finish()229	}230}231232impl PartialEq for WeakObjValue {233	fn eq(&self, other: &Self) -> bool {234		Weak::ptr_eq(&self.0, &other.0)235	}236}237238impl Eq for WeakObjValue {}239impl Hash for WeakObjValue {240	fn hash<H: Hasher>(&self, hasher: &mut H) {241		// Safety: usize is POD242		let addr = unsafe { *std::ptr::addr_of!(self.0).cast() };243		hasher.write_usize(addr);244	}245}246247cc_dyn!(248	#[derive(Clone, Debug)]249	CcObjectCore, ObjectCore,250	pub fn new() {...}251);252#[derive(Trace, Educe)]253#[educe(Debug)]254struct ObjValueInner {255	cores: Vec<CcObjectCore>,256	assertions_ran: Cell<bool>,257	value_cache: RefCell<FxHashMap<(IStr, CoreIdx), CacheValue>>,258}259260thread_local! {261	static RUNNING_ASSERTIONS: RefCell<FxHashSet<ObjValue>> = RefCell::default();262}263fn is_asserting(obj: &ObjValue) -> bool {264	RUNNING_ASSERTIONS.with_borrow(|v| v.contains(obj))265}266/// Returns false if already asserting267fn start_asserting(obj: &ObjValue) -> bool {268	RUNNING_ASSERTIONS.with_borrow_mut(|v| v.insert(obj.clone()))269}270fn finish_asserting(obj: &ObjValue) {271	RUNNING_ASSERTIONS.with_borrow_mut(|v| {272		let r = v.remove(obj);273		debug_assert!(274			r,275			"finish_asserting was called before start_asserting or twice"276		);277	});278}279280thread_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}287288#[allow(clippy::module_name_repetitions)]289#[derive(Clone, Trace, Debug, Educe)]290#[educe(PartialEq, Hash, Eq)]291pub struct ObjValue(292	#[educe(PartialEq(method(Cc::ptr_eq)), Hash(method(identity_hash)))] Cc<ObjValueInner>,293);294295impl 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}303304#[derive(Trace, Debug)]305struct StandaloneSuperCore {306	sup: CoreIdx,307	this: ObjValue,308}309impl ObjectCore for StandaloneSuperCore {310	fn enum_fields_core(311		&self,312		super_depth: &mut SuperDepth,313		handler: &mut EnumFieldsHandler<'_>,314	) -> bool {315		self.this.enum_fields_idx(super_depth, handler, self.sup)316	}317318	fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden {319		if self.this.has_field_include_hidden_idx(name, self.sup) {320			HasFieldIncludeHidden::Exists321		} else {322			HasFieldIncludeHidden::NotFound323		}324	}325326	fn get_for_core(&self, key: IStr, _sup_this: SupThis) -> Result<GetFor> {327		let v = self.this.get_idx(key, self.sup)?;328		Ok(v.map_or(GetFor::NotFound, |v| GetFor::Final(v)))329	}330331	fn field_visibility_core(&self, field: IStr) -> FieldVisibility {332		match self.this.field_visibility_idx(field, self.sup) {333			Some(c) => FieldVisibility::Found(c),334			None => FieldVisibility::NotFound,335		}336	}337338	fn run_assertions_core(&self, _sup_this: SupThis) -> Result<()> {339		self.this.run_assertions()340	}341}342343#[derive(Debug, Acyclic)]344struct OmitFieldsCore {345	omit: FxHashSet<IStr>,346}347impl ObjectCore for OmitFieldsCore {348	fn enum_fields_core(349		&self,350		super_depth: &mut SuperDepth,351		handler: &mut EnumFieldsHandler<'_>,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		}360		true361	}362363	fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden {364		if self.omit.contains(&name) {365			return HasFieldIncludeHidden::Omit;366		}367		HasFieldIncludeHidden::NotFound368	}369370	fn get_for_core(&self, key: IStr, _sup_this: SupThis) -> Result<GetFor> {371		if self.omit.contains(&key) {372			return Ok(GetFor::Omit);373		}374		Ok(GetFor::NotFound)375	}376377	fn field_visibility_core(&self, field: IStr) -> FieldVisibility {378		if self.omit.contains(&field) {379			return FieldVisibility::Omit;380		}381		FieldVisibility::NotFound382	}383384	fn run_assertions_core(&self, _sup_this: SupThis) -> Result<()> {385		Ok(())386	}387}388389#[derive(Hash, PartialEq, Eq, Trace, Clone, Copy, Debug)]390struct CoreIdx {391	idx: usize,392}393impl CoreIdx {394	fn super_exists(self) -> bool {395		self.idx != 0396	}397}398#[derive(Trace, Clone, PartialEq, Eq, Hash, Debug)]399pub struct SupThis {400	sup: CoreIdx,401	this: ObjValue,402}403impl SupThis {404	pub fn has_super(&self) -> bool {405		self.sup.super_exists()406	}407	/// Implementation of `"field" in super` operation,408	/// works faster than standalone super path.409	///410	/// In case of no `super` existence, returns false.411	pub fn field_in_super(&self, field: IStr) -> bool {412		self.this.has_field_include_hidden_idx(field, self.sup)413	}414	/// Implementation of `super.field` operation,415	/// works faster than standalone super path.416	///417	/// In case of no `super` existence, returns `NoSuperFound`418	pub fn get_super(&self, field: IStr) -> Result<Option<Val>> {419		if !self.sup.super_exists() {420			bail!(NoSuperFound);421		}422		self.this.get_idx(field, self.sup)423	}424	/// `super` with `self` overriden for top-level lookups.425	/// Exists when super appears outside of `super.field`/`"field" in super` expressions426	/// Exclusive to jrsonnet.427	///428	/// Might return `NoSuperFound` error.429	pub fn standalone_super(&self) -> Result<ObjValue> {430		if !self.sup.super_exists() {431			bail!(NoSuperFound)432		}433		let mut out = ObjValue::builder();434		out.reserve_cores(1).extend_with_core(StandaloneSuperCore {435			sup: self.sup,436			this: self.this.clone(),437		});438		Ok(out.build())439	}440	pub fn this(&self) -> &ObjValue {441		&self.this442	}443	pub fn downgrade(self) -> WeakSupThis {444		WeakSupThis {445			sup: self.sup,446			this: self.this.downgrade(),447		}448	}449}450#[derive(Trace, PartialEq, Eq, Hash, Debug)]451pub struct WeakSupThis {452	sup: CoreIdx,453	this: WeakObjValue,454}455456impl ObjValue {457	pub fn builder() -> ObjValueBuilder {458		ObjValueBuilder::new()459	}460	pub fn builder_with_capacity(capacity: usize) -> ObjValueBuilder {461		ObjValueBuilder::with_capacity(capacity)462	}463	pub(crate) fn extend_with_raw_member(self, key: IStr, value: ObjMember) -> Self {464		let mut out = ObjValueBuilder::with_capacity(1);465		out.with_super(self);466		let mut member = out.field(key);467		if value.flags.add() {468			member = member.add();469		}470		if let Some(loc) = value.location {471			member = member.with_location(loc);472		}473		let _ = member474			.with_visibility(value.flags.visibility())475			.binding(value.invoke);476		out.build()477	}478	pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder<'_>> {479		ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())480	}481482	pub fn extend(&mut self) -> ObjValueBuilder {483		let mut out = ObjValueBuilder::new();484		out.with_super(self.clone());485		out486	}487488	#[must_use]489	pub fn extend_from(&self, sup: Self) -> Self {490		let mut cores = sup.0.cores.clone();491		cores.extend(self.0.cores.iter().cloned());492		ObjValue(Cc::new(ObjValueInner {493			cores,494			value_cache: RefCell::default(),495			assertions_ran: Cell::new(false),496		}))497	}498	// #[must_use]499	// pub fn with_this(&self, this: Self) -> Self {500	// 	self.0.with_this(self.clone(), this)501	// }502	/// Returns amount of visible object fields503	/// If object only contains hidden fields - may return zero.504	pub fn len(&self) -> usize {505		self.fields_visibility()506			.iter()507			.filter(|(_, (visible, _))| *visible)508			.count()509	}510	/// For each field, calls callback.511	/// If callback returns false - ends iteration prematurely.512	///513	/// Returns false if ended prematurely514	pub fn enum_fields(&self, handler: &mut EnumFieldsHandler<'_>) -> bool {515		let mut super_depth = SuperDepth::default();516		self.enum_fields_idx(517			&mut super_depth,518			handler,519			CoreIdx {520				idx: self.0.cores.len(),521			},522		)523	}524	fn enum_fields_idx(525		&self,526		super_depth: &mut SuperDepth,527		handler: &mut EnumFieldsHandler<'_>,528		idx: CoreIdx,529	) -> bool {530		for core in self.0.cores[..idx.idx].iter() {531			if !core.0.enum_fields_core(super_depth, handler) {532				return false;533			}534			super_depth.deepen();535		}536		true537	}538539	pub fn has_field_include_hidden(&self, name: IStr) -> bool {540		self.has_field_include_hidden_idx(541			name,542			CoreIdx {543				idx: self.0.cores.len(),544			},545		)546	}547	fn has_field_include_hidden_idx(&self, name: IStr, core: CoreIdx) -> bool {548		for ele in self.0.cores[..core.idx].iter().rev() {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		false556	}557	pub fn has_field(&self, name: IStr) -> bool {558		match self.field_visibility(name) {559			Some(Visibility::Unhide | Visibility::Normal) => true,560			Some(Visibility::Hidden) | None => false,561		}562	}563	pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {564		if include_hidden {565			self.has_field_include_hidden(name)566		} else {567			self.has_field(name)568		}569	}570	pub fn get(&self, key: IStr) -> Result<Option<Val>> {571		self.get_idx(572			key,573			CoreIdx {574				idx: self.0.cores.len(),575			},576		)577	}578579	fn get_idx(&self, key: IStr, core: CoreIdx) -> Result<Option<Val>> {580		let cache_key = (key.clone(), core);581		{582			let mut cache = self.0.value_cache.borrow_mut();583			// entry_ref candidate?584			match cache.entry(cache_key.clone()) {585				Entry::Occupied(v) => match v.get() {586					CacheValue::Cached(v) => return v.clone(),587					CacheValue::Pending => {588						if !is_asserting(self) {589							bail!(InfiniteRecursionDetected);590						}591					}592				},593				Entry::Vacant(v) => {594					v.insert(CacheValue::Pending);595				}596			};597		}598		let result = self.get_idx_uncached(key, core);599		{600			let mut cache = self.0.value_cache.borrow_mut();601			cache.insert(cache_key, CacheValue::Cached(result.clone()));602		}603		result604	}605	fn get_idx_uncached(&self, key: IStr, core: CoreIdx) -> Result<Option<Val>> {606		self.run_assertions()?;607		let mut add_stack = Vec::with_capacity(2);608		for (sup, core) in self.0.cores[..core.idx].iter().enumerate().rev() {609			let sup_this = SupThis {610				sup: CoreIdx { idx: sup },611				this: self.clone(),612			};613			match core.0.get_for_core(key.clone(), sup_this)? {614				GetFor::Final(val) if add_stack.is_empty() => return Ok(Some(val)),615				GetFor::Final(val) => {616					add_stack.push(val);617					break;618				}619				GetFor::SuperPlus(val) => {620					add_stack.push(val);621				}622				GetFor::Omit => {623					break;624				}625				GetFor::NotFound => {626					continue;627				}628			}629		}630		if add_stack.is_empty() {631			// None of layers had this field632			return Ok(None);633		} else if add_stack.len() == 1 {634			// A layer had this field, but it wanted this field to be added with super.635			// However, no super had this field, fail-safe636			return Ok(Some(add_stack.pop().expect("single element on stack")));637		}638		let mut values = add_stack.into_iter().rev();639		let init = values.next().expect("at least 2 elements");640641		values642			.try_fold(init, |a, b| evaluate_add_op(&a, &b))643			.map(Some)644645		// self.0.get_raw(key, this)646	}647648	pub fn get_or_bail(&self, key: IStr) -> Result<Val> {649		let Some(value) = self.get(key.clone())? else {650			let suggestions = suggest_object_fields(self, key.clone());651			bail!(NoSuchField(key, suggestions))652		};653		Ok(value)654	}655656	fn field_visibility(&self, field: IStr) -> Option<Visibility> {657		self.field_visibility_idx(658			field,659			CoreIdx {660				idx: self.0.cores.len(),661			},662		)663	}664	fn field_visibility_idx(&self, field: IStr, core: CoreIdx) -> Option<Visibility> {665		let mut exists = false;666		for ele in self.0.cores[..core.idx].iter().rev() {667			let vis = ele.0.field_visibility_core(field.clone());668			match vis {669				FieldVisibility::Found(vis @ (Visibility::Unhide | Visibility::Hidden)) => {670					return Some(vis)671				}672				FieldVisibility::Found(Visibility::Normal) => exists = true,673				FieldVisibility::NotFound => {}674				FieldVisibility::Omit => break,675			}676		}677		exists.then_some(Visibility::Normal)678	}679680	pub fn run_assertions(&self) -> Result<()> {681		if self.0.assertions_ran.get() {682			return Ok(());683		}684		if !start_asserting(self) {685			return Ok(());686		}687		for (idx, ele) in self.0.cores.iter().enumerate() {688			let sup_this = SupThis {689				sup: CoreIdx { idx },690				this: self.clone(),691			};692			ele.0.run_assertions_core(sup_this).inspect_err(|_e| {693				finish_asserting(self);694			})?;695		}696		finish_asserting(self);697		self.0.assertions_ran.set(true);698		Ok(())699	}700701	pub fn iter(702		&self,703		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,704	) -> impl Iterator<Item = (IStr, Result<Val>)> + '_ {705		let fields = self.fields(706			#[cfg(feature = "exp-preserve-order")]707			preserve_order,708		);709		fields.into_iter().map(|field| {710			(711				field.clone(),712				self.get(field)713					.map(|opt| opt.expect("iterating over keys, field exists")),714			)715		})716	}717	pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {718		if !self.has_field_ex(key.clone(), true) {719			return None;720		}721		let obj = self.clone();722723		Some(Thunk!(move || Ok(obj.get(key)?.expect("field exists"))))724	}725	pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {726		let obj = self.clone();727		Thunk!(move || obj.get_or_bail(key))728	}729	pub fn ptr_eq(a: &Self, b: &Self) -> bool {730		Cc::ptr_eq(&a.0, &b.0)731	}732	pub fn downgrade(self) -> WeakObjValue {733		WeakObjValue(self.0.downgrade())734	}735	fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {736		let mut out = FxHashMap::default();737		self.enum_fields(&mut |depth, index, name, visibility| {738			let new_sort_key = FieldSortKey::new(depth, index);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			}746			let (visible, _) = entry.or_insert((true, new_sort_key));747			match visibility {748				EnumFields::Omit => unreachable!(),749				EnumFields::Normal(Visibility::Normal) => {}750				EnumFields::Normal(Visibility::Hidden) => {751					*visible = false;752				}753				EnumFields::Normal(Visibility::Unhide) => {754					*visible = true;755				}756			};757			return ControlFlow::Continue(());758		});759		out760	}761	pub fn fields_ex(762		&self,763		include_hidden: bool,764		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,765	) -> Vec<IStr> {766		#[cfg(feature = "exp-preserve-order")]767		if preserve_order {768			let (mut fields, mut keys): (Vec<_>, Vec<_>) = self769				.fields_visibility()770				.into_iter()771				.filter(|(_, (visible, _))| include_hidden || *visible)772				.enumerate()773				.map(|(idx, (k, (_, sk)))| (k, (sk, idx)))774				.unzip();775			keys.sort_unstable_by_key(|v| v.0);776			// Reorder in-place by resulting indexes777			for i in 0..fields.len() {778				let x = fields[i].clone();779				let mut j = i;780				loop {781					let k = keys[j].1;782					keys[j].1 = j;783					if k == i {784						break;785					}786					fields[j] = fields[k].clone();787					j = k;788				}789				fields[j] = x;790			}791			return fields;792		}793794		let mut fields: Vec<_> = self795			.fields_visibility()796			.into_iter()797			.filter(|(_, (visible, _))| include_hidden || *visible)798			.map(|(k, _)| k)799			.collect();800		fields.sort_unstable();801		fields802	}803	pub fn fields(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Vec<IStr> {804		self.fields_ex(805			false,806			#[cfg(feature = "exp-preserve-order")]807			preserve_order,808		)809	}810	pub fn values_ex(811		&self,812		include_hidden: bool,813		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,814	) -> ArrValue {815		ArrValue::new(PickObjectValues::new(816			self.clone(),817			self.fields_ex(818				include_hidden,819				#[cfg(feature = "exp-preserve-order")]820				preserve_order,821			),822		))823	}824	pub fn values(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> ArrValue {825		self.values_ex(826			false,827			#[cfg(feature = "exp-preserve-order")]828			preserve_order,829		)830	}831	pub fn key_values_ex(832		&self,833		include_hidden: bool,834		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,835	) -> ArrValue {836		ArrValue::new(PickObjectKeyValues::new(837			self.clone(),838			self.fields_ex(839				include_hidden,840				#[cfg(feature = "exp-preserve-order")]841				preserve_order,842			),843		))844	}845	pub fn key_values(846		&self,847		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,848	) -> ArrValue {849		self.key_values_ex(850			false,851			#[cfg(feature = "exp-preserve-order")]852			preserve_order,853		)854	}855}856857impl OopObject {858	pub fn new(859		this_entries: FxHashMap<IStr, ObjMember>,860		assertions: Vec<CcObjectAssertion>,861	) -> Self {862		Self {863			this_entries,864			assertions,865		}866	}867}868869impl ObjectCore for OopObject {870	fn enum_fields_core(871		&self,872		super_depth: &mut SuperDepth,873		handler: &mut EnumFieldsHandler<'_>,874	) -> bool {875		for (name, member) in self.this_entries.iter() {876			if matches!(877				handler(878					*super_depth,879					member.original_index,880					name.clone(),881					EnumFields::Normal(member.flags.visibility()),882				),883				ControlFlow::Break(())884			) {885				return false;886			}887		}888		true889	}890891	fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden {892		if self.this_entries.contains_key(&name) {893			HasFieldIncludeHidden::Exists894		} else {895			HasFieldIncludeHidden::NotFound896		}897	}898899	fn get_for_core(&self, key: IStr, sup_this: SupThis) -> Result<GetFor> {900		match self.this_entries.get(&key) {901			Some(k) => {902				let v = k.invoke.evaluate(sup_this)?;903				Ok(if k.flags.add() {904					GetFor::SuperPlus(v)905				} else {906					GetFor::Final(v)907				})908			}909			None => Ok(GetFor::NotFound),910		}911	}912	fn field_visibility_core(&self, name: IStr) -> FieldVisibility {913		match self.this_entries.get(&name) {914			Some(f) => FieldVisibility::Found(f.flags.visibility()),915			None => FieldVisibility::NotFound,916		}917	}918919	fn run_assertions_core(&self, sup_this: SupThis) -> Result<()> {920		if self.assertions.is_empty() {921			return Ok(());922		}923		for assertion in self.assertions.iter() {924			assertion.0.run(sup_this.clone())?;925		}926		Ok(())927	}928}929930#[allow(clippy::module_name_repetitions)]931pub struct ObjValueBuilder {932	sup: Vec<CcObjectCore>,933934	new: OopObject,935	next_field_index: FieldIndex,936}937impl ObjValueBuilder {938	pub fn new() -> Self {939		Self::with_capacity(0)940	}941	pub fn with_capacity(capacity: usize) -> Self {942		Self {943			sup: vec![],944			new: OopObject {945				assertions: vec![],946				this_entries: FxHashMap::with_capacity(capacity),947			},948			next_field_index: FieldIndex::default(),949		}950	}951	pub fn reserve_cores(&mut self, capacity: usize) -> &mut Self {952		self.sup.reserve_exact(capacity);953		self954	}955	pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self {956		self.new.assertions.reserve_exact(capacity);957		self958	}959	pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {960		self.sup = super_obj.0.cores.clone();961		self962	}963964	pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {965		self.new.assertions.push(CcObjectAssertion::new(assertion));966		self967	}968	pub fn field(&mut self, name: impl Into<IStr>) -> ObjMemberBuilder<ValueBuilder<'_>> {969		let field_index = self.next_field_index;970		self.next_field_index = self.next_field_index.next();971		ObjMemberBuilder::new(ValueBuilder(self), name.into(), field_index)972	}973	/// Preset for common method definiton pattern:974	/// Create a hidden field with the function value.975	///976	/// `.field(name).hide().value(Val::function(value))`977	pub fn method(&mut self, name: impl Into<IStr>, value: impl Into<FuncVal>) -> &mut Self {978		self.field(name).hide().value(Val::Func(value.into()));979		self980	}981	pub fn try_method(982		&mut self,983		name: impl Into<IStr>,984		value: impl Into<FuncVal>,985	) -> Result<&mut Self> {986		self.field(name).hide().try_value(Val::Func(value.into()))?;987		Ok(self)988	}989990	pub fn extend_with_core(&mut self, core: impl ObjectCore) {991		self.commit();992		self.sup.push(CcObjectCore::new(core));993	}994995	fn commit(&mut self) {996		if !self.new.is_empty() {997			self.sup.push(CcObjectCore::new(mem::take(&mut self.new)));998		}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();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		}))1017	}1018}1019impl Default for ObjValueBuilder {1020	fn default() -> Self {1021		Self::with_capacity(0)1022	}1023}10241025#[allow(clippy::module_name_repetitions)]1026#[must_use = "value not added unless binding() was called"]1027pub struct ObjMemberBuilder<Kind> {1028	kind: Kind,1029	name: IStr,1030	add: bool,1031	visibility: Visibility,1032	original_index: FieldIndex,1033	location: Option<Span>,1034}10351036#[allow(clippy::missing_const_for_fn)]1037impl<Kind> ObjMemberBuilder<Kind> {1038	pub(crate) fn new(kind: Kind, name: IStr, original_index: FieldIndex) -> Self {1039		Self {1040			kind,1041			name,1042			original_index,1043			add: false,1044			visibility: Visibility::Normal,1045			location: None,1046		}1047	}10481049	pub const fn with_add(mut self, add: bool) -> Self {1050		self.add = add;1051		self1052	}1053	pub fn add(self) -> Self {1054		self.with_add(true)1055	}1056	pub fn with_visibility(mut self, visibility: Visibility) -> Self {1057		self.visibility = visibility;1058		self1059	}1060	pub fn hide(self) -> Self {1061		self.with_visibility(Visibility::Hidden)1062	}1063	pub fn with_location(mut self, location: Span) -> Self {1064		self.location = Some(location);1065		self1066	}1067	fn build_member(self, binding: MaybeUnbound) -> (Kind, IStr, ObjMember) {1068		(1069			self.kind,1070			self.name,1071			ObjMember {1072				flags: ObjFieldFlags::new(self.add, self.visibility),1073				original_index: self.original_index,1074				invoke: binding,1075				location: self.location,1076			},1077		)1078	}1079}10801081pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);1082impl ObjMemberBuilder<ValueBuilder<'_>> {1083	/// Inserts value, replacing if it is already defined1084	pub fn value(self, value: impl Into<Val>) {1085		let (receiver, name, member) =1086			self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value.into())));1087		let entry = receiver.0.new.this_entries.entry(name);1088		entry.insert_entry(member);1089	}10901091	/// Tries to insert value, returns an error if it was already defined1092	pub fn try_value(self, value: impl Into<Val>) -> Result<()> {1093		self.try_thunk(Thunk::evaluated(value.into()))1094	}1095	pub fn try_thunk(self, value: impl Into<Thunk<Val>>) -> Result<()> {1096		self.binding(MaybeUnbound::Bound(value.into()))1097	}1098	pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {1099		self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)))1100	}1101	pub fn binding(self, binding: MaybeUnbound) -> Result<()> {1102		let (receiver, name, member) = self.build_member(binding);1103		let location = member.location.clone();1104		let old = receiver.0.new.this_entries.insert(name.clone(), member);1105		if old.is_some() {1106			in_frame(1107				CallLocation(location.as_ref()),1108				|| format!("field <{}> initializtion", name.clone()),1109				|| bail!(DuplicateFieldName(name.clone())),1110			)?;1111		}1112		Ok(())1113	}1114}11151116pub struct ExtendBuilder<'v>(&'v mut ObjValue);1117impl ObjMemberBuilder<ExtendBuilder<'_>> {1118	pub fn value(self, value: impl Into<Val>) {1119		self.binding(MaybeUnbound::Bound(Thunk::evaluated(value.into())));1120	}1121	pub fn bindable(self, bindable: impl Unbound<Bound = Val>) {1122		self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)));1123	}1124	pub fn binding(self, binding: MaybeUnbound) {1125		let (receiver, name, member) = self.build_member(binding);1126		let new = receiver.0.clone();1127		*receiver.0 = new.extend_with_raw_member(name, member);1128	}1129}