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}
after · 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	num::Saturating,9	ops::ControlFlow,10};1112use educe::Educe;13use jrsonnet_gcmodule::{cc_dyn, Acyclic, Cc, Trace, Weak};14use jrsonnet_interner::IStr;15use jrsonnet_parser::{Span, Visibility};16use rustc_hash::{FxHashMap, FxHashSet};1718use crate::{19	arr::{PickObjectKeyValues, PickObjectValues},20	bail,21	error::{suggest_object_fields, ErrorKind::*},22	function::{CallLocation, FuncVal},23	gc::WithCapacityExt as _,24	identity_hash, in_frame,25	operator::evaluate_add_op,26	val::ArrValue,27	CcUnbound, MaybeUnbound, Result, Thunk, Unbound, Val,28};2930#[cfg(not(feature = "exp-preserve-order"))]31mod ordering {32	#![allow(33		// This module works as stub for preserve-order feature34		clippy::unused_self,35	)]3637	use jrsonnet_gcmodule::Trace;3839	#[derive(Clone, Copy, Default, Debug, Trace)]40	pub struct FieldIndex(());41	impl FieldIndex {42		pub const fn next(self) -> Self {43			Self(())44		}45	}4647	#[derive(Clone, Copy, Default, Debug, Trace)]48	pub struct SuperDepth(());49	impl SuperDepth {50		pub(super) fn deepen(self) {}51	}52}5354#[cfg(feature = "exp-preserve-order")]55mod ordering {56	use std::cmp::Reverse;5758	use jrsonnet_gcmodule::Trace;5960	#[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]61	pub struct FieldIndex(u32);62	impl FieldIndex {63		pub fn next(self) -> Self {64			Self(self.0 + 1)65		}66	}6768	#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Debug)]69	pub struct SuperDepth(u32);70	impl SuperDepth {71		pub(super) fn deepen(&mut self) {72			self.0 += 173		}74	}7576	#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]77	pub struct FieldSortKey(Reverse<SuperDepth>, FieldIndex);78	impl FieldSortKey {79		pub fn new(depth: SuperDepth, index: FieldIndex) -> Self {80			Self(Reverse(depth), index)81		}82	}83}8485#[cfg(feature = "exp-preserve-order")]86use ordering::FieldSortKey;87use ordering::{FieldIndex, SuperDepth};8889// 0 - add90//  12 - visibility91#[derive(Clone, Copy)]92pub struct ObjFieldFlags(u8);93impl ObjFieldFlags {94	fn new(add: bool, visibility: Visibility) -> Self {95		let mut v = 0;96		if add {97			v |= 1;98		}99		v |= match visibility {100			Visibility::Normal => 0b000,101			Visibility::Hidden => 0b010,102			Visibility::Unhide => 0b100,103		};104		Self(v)105	}106	pub fn add(&self) -> bool {107		self.0 & 1 != 0108	}109	pub fn visibility(&self) -> Visibility {110		match (self.0 & 0b110) >> 1 {111			0b00 => Visibility::Normal,112			0b01 => Visibility::Hidden,113			0b10 => Visibility::Unhide,114			_ => unreachable!(),115		}116	}117}118impl Debug for ObjFieldFlags {119	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {120		f.debug_struct("ObjFieldFlags")121			.field("add", &self.add())122			.field("visibility", &self.visibility())123			.finish()124	}125}126127#[allow(clippy::module_name_repetitions)]128#[derive(Debug, Trace)]129pub struct ObjMember {130	#[trace(skip)]131	flags: ObjFieldFlags,132	original_index: FieldIndex,133	pub invoke: MaybeUnbound,134	pub location: Option<Span>,135}136137cc_dyn!(CcObjectAssertion, ObjectAssertion);138pub trait ObjectAssertion: Trace {139	fn run(&self, sup_this: SupThis) -> Result<()>;140}141142// Field => This143144#[derive(Trace, Debug)]145enum CacheValue {146	Cached(Result<Option<Val>>),147	Pending,148}149150#[allow(clippy::module_name_repetitions)]151#[derive(Trace, Default)]152#[trace(tracking(force))]153pub struct OopObject {154	assertions: Vec<CcObjectAssertion>,155	this_entries: FxHashMap<IStr, ObjMember>,156}157impl Debug for OopObject {158	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {159		f.debug_struct("OopObject")160			.field("this_entries", &self.this_entries)161			.finish_non_exhaustive()162	}163}164impl OopObject {165	fn is_empty(&self) -> bool {166		self.assertions.is_empty() && self.this_entries.is_empty()167	}168}169170type EnumFieldsHandler<'a> =171	dyn FnMut(SuperDepth, FieldIndex, IStr, EnumFields) -> ControlFlow<()> + 'a;172173pub enum EnumFields {174	Normal(Visibility),175	Omit(Skip),176}177178#[derive(Trace, Clone)]179pub enum GetFor {180	// Return value181	Final(Val),182	// Continue iterating over cores, add current value to sum stack183	SuperPlus(Val),184	// Ignore the field value, stop at this layer instead185	Omit(#[trace(skip)] Skip),186	NotFound,187}188189#[derive(Acyclic, Clone)]190pub enum FieldVisibility {191	Found(Visibility),192	Omit(Skip),193	NotFound,194}195196#[derive(Acyclic, Clone)]197pub enum HasFieldIncludeHidden {198	Exists,199	NotFound,200	Omit(Skip),201}202203type Skip = Saturating<usize>;204205pub trait ObjectCore: Trace + Any + Debug {206	// If callback returns false, iteration stops, and this call returns false.207	fn enum_fields_core(208		&self,209		super_depth: &mut SuperDepth,210		handler: &mut EnumFieldsHandler<'_>,211	) -> bool;212213	fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden;214215	fn get_for_core(&self, key: IStr, sup_this: SupThis, omit_only: bool) -> Result<GetFor>;216	fn field_visibility_core(&self, field: IStr) -> FieldVisibility;217218	fn run_assertions_core(&self, sup_this: SupThis) -> Result<()>;219}220221#[derive(Clone, Trace)]222pub struct WeakObjValue(#[trace(skip)] Weak<ObjValueInner>);223impl Debug for WeakObjValue {224	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {225		f.debug_tuple("WeakObjValue").finish()226	}227}228229impl PartialEq for WeakObjValue {230	fn eq(&self, other: &Self) -> bool {231		Weak::ptr_eq(&self.0, &other.0)232	}233}234235impl Eq for WeakObjValue {}236impl Hash for WeakObjValue {237	fn hash<H: Hasher>(&self, hasher: &mut H) {238		// Safety: usize is POD239		let addr = unsafe { *std::ptr::addr_of!(self.0).cast() };240		hasher.write_usize(addr);241	}242}243244cc_dyn!(245	#[derive(Clone, Debug)]246	CcObjectCore, ObjectCore,247	pub fn new() {...}248);249#[derive(Trace, Educe)]250#[educe(Debug)]251struct ObjValueInner {252	cores: Vec<CcObjectCore>,253	assertions_ran: Cell<bool>,254	value_cache: RefCell<FxHashMap<(IStr, CoreIdx), CacheValue>>,255}256257thread_local! {258	static RUNNING_ASSERTIONS: RefCell<FxHashSet<ObjValue>> = RefCell::default();259}260fn is_asserting(obj: &ObjValue) -> bool {261	RUNNING_ASSERTIONS.with_borrow(|v| v.contains(obj))262}263/// Returns false if already asserting264fn start_asserting(obj: &ObjValue) -> bool {265	RUNNING_ASSERTIONS.with_borrow_mut(|v| v.insert(obj.clone()))266}267fn finish_asserting(obj: &ObjValue) {268	RUNNING_ASSERTIONS.with_borrow_mut(|v| {269		let r = v.remove(obj);270		debug_assert!(271			r,272			"finish_asserting was called before start_asserting or twice"273		);274	});275}276277thread_local! {278	static EMPTY_OBJ: ObjValue = ObjValue(Cc::new(ObjValueInner {279		cores: vec![],280		assertions_ran: Cell::new(true),281		value_cache: Default::default(),282	}))283}284285#[allow(clippy::module_name_repetitions)]286#[derive(Clone, Trace, Debug, Educe)]287#[educe(PartialEq, Hash, Eq)]288pub struct ObjValue(289	#[educe(PartialEq(method(Cc::ptr_eq)), Hash(method(identity_hash)))] Cc<ObjValueInner>,290);291292impl ObjValue {293	pub fn empty() -> Self {294		EMPTY_OBJ.with(|v| v.clone())295	}296	pub fn is_empty(&self) -> bool {297		self.0.cores.is_empty() || self.len() == 0298	}299}300301#[derive(Trace, Debug)]302struct StandaloneSuperCore {303	sup: CoreIdx,304	this: ObjValue,305}306impl ObjectCore for StandaloneSuperCore {307	fn enum_fields_core(308		&self,309		super_depth: &mut SuperDepth,310		handler: &mut EnumFieldsHandler<'_>,311	) -> bool {312		self.this.enum_fields_idx(super_depth, handler, self.sup)313	}314315	fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden {316		if self.this.has_field_include_hidden_idx(name, self.sup) {317			HasFieldIncludeHidden::Exists318		} else {319			HasFieldIncludeHidden::NotFound320		}321	}322323	fn get_for_core(&self, key: IStr, _sup_this: SupThis, omit_only: bool) -> Result<GetFor> {324		if omit_only {325			return Ok(GetFor::NotFound);326		}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	prev_layers: usize,347}348impl ObjectCore for OmitFieldsCore {349	fn enum_fields_core(350		&self,351		super_depth: &mut SuperDepth,352		handler: &mut EnumFieldsHandler<'_>,353	) -> bool {354		let mut fi = FieldIndex::default();355		for f in &self.omit {356			if let ControlFlow::Break(()) = handler(357				*super_depth,358				fi,359				f.clone(),360				EnumFields::Omit(Saturating(self.prev_layers)),361			) {362				return false;363			}364			fi = fi.next();365		}366		true367	}368369	fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden {370		if self.omit.contains(&name) {371			return HasFieldIncludeHidden::Omit(Saturating(self.prev_layers));372		}373		HasFieldIncludeHidden::NotFound374	}375376	fn get_for_core(&self, key: IStr, _sup_this: SupThis, _omit_only: bool) -> Result<GetFor> {377		if self.omit.contains(&key) {378			return Ok(GetFor::Omit(Saturating(self.prev_layers)));379		}380		Ok(GetFor::NotFound)381	}382383	fn field_visibility_core(&self, field: IStr) -> FieldVisibility {384		if self.omit.contains(&field) {385			return FieldVisibility::Omit(Saturating(self.prev_layers));386		}387		FieldVisibility::NotFound388	}389390	fn run_assertions_core(&self, _sup_this: SupThis) -> Result<()> {391		Ok(())392	}393}394395#[derive(Hash, PartialEq, Eq, Trace, Clone, Copy, Debug)]396struct CoreIdx {397	idx: usize,398}399impl CoreIdx {400	fn super_exists(self) -> bool {401		self.idx != 0402	}403}404#[derive(Trace, Clone, PartialEq, Eq, Hash, Debug)]405pub struct SupThis {406	sup: CoreIdx,407	this: ObjValue,408}409impl SupThis {410	pub fn has_super(&self) -> bool {411		self.sup.super_exists()412	}413	/// Implementation of `"field" in super` operation,414	/// works faster than standalone super path.415	///416	/// In case of no `super` existence, returns false.417	pub fn field_in_super(&self, field: IStr) -> bool {418		self.this.has_field_include_hidden_idx(field, self.sup)419	}420	/// Implementation of `super.field` operation,421	/// works faster than standalone super path.422	///423	/// In case of no `super` existence, returns `NoSuperFound`424	pub fn get_super(&self, field: IStr) -> Result<Option<Val>> {425		if !self.sup.super_exists() {426			bail!(NoSuperFound);427		}428		self.this.get_idx(field, self.sup)429	}430	/// `super` with `self` overriden for top-level lookups.431	/// Exists when super appears outside of `super.field`/`"field" in super` expressions432	/// Exclusive to jrsonnet.433	///434	/// Might return `NoSuperFound` error.435	pub fn standalone_super(&self) -> Result<ObjValue> {436		if !self.sup.super_exists() {437			bail!(NoSuperFound)438		}439		let mut out = ObjValue::builder();440		out.reserve_cores(1).extend_with_core(StandaloneSuperCore {441			sup: self.sup,442			this: self.this.clone(),443		});444		Ok(out.build())445	}446	pub fn this(&self) -> &ObjValue {447		&self.this448	}449	pub fn downgrade(self) -> WeakSupThis {450		WeakSupThis {451			sup: self.sup,452			this: self.this.downgrade(),453		}454	}455}456#[derive(Trace, PartialEq, Eq, Hash, Debug)]457pub struct WeakSupThis {458	sup: CoreIdx,459	this: WeakObjValue,460}461462impl ObjValue {463	pub fn builder() -> ObjValueBuilder {464		ObjValueBuilder::new()465	}466	pub fn builder_with_capacity(capacity: usize) -> ObjValueBuilder {467		ObjValueBuilder::with_capacity(capacity)468	}469	pub(crate) fn extend_with_raw_member(self, key: IStr, value: ObjMember) -> Self {470		let mut out = ObjValueBuilder::with_capacity(1);471		out.with_super(self);472		let mut member = out.field(key);473		if value.flags.add() {474			member = member.add();475		}476		if let Some(loc) = value.location {477			member = member.with_location(loc);478		}479		let _ = member480			.with_visibility(value.flags.visibility())481			.binding(value.invoke);482		out.build()483	}484	pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder<'_>> {485		ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())486	}487488	pub fn extend(&mut self) -> ObjValueBuilder {489		let mut out = ObjValueBuilder::new();490		out.with_super(self.clone());491		out492	}493494	#[must_use]495	pub fn extend_from(&self, sup: Self) -> Self {496		let mut cores = sup.0.cores.clone();497		cores.extend(self.0.cores.iter().cloned());498		ObjValue(Cc::new(ObjValueInner {499			cores,500			value_cache: RefCell::default(),501			assertions_ran: Cell::new(false),502		}))503	}504	// #[must_use]505	// pub fn with_this(&self, this: Self) -> Self {506	// 	self.0.with_this(self.clone(), this)507	// }508	/// Returns amount of visible object fields509	/// If object only contains hidden fields - may return zero.510	pub fn len(&self) -> usize {511		self.fields_visibility()512			.values()513			.filter(|d| d.visible())514			.count()515	}516	/// For each field, calls callback.517	/// If callback returns false - ends iteration prematurely.518	///519	/// Returns false if ended prematurely520	pub fn enum_fields(&self, handler: &mut EnumFieldsHandler<'_>) -> bool {521		let mut super_depth = SuperDepth::default();522		self.enum_fields_idx(523			&mut super_depth,524			handler,525			CoreIdx {526				idx: self.0.cores.len(),527			},528		)529	}530	fn enum_fields_idx(531		&self,532		super_depth: &mut SuperDepth,533		handler: &mut EnumFieldsHandler<'_>,534		idx: CoreIdx,535	) -> bool {536		for core in self.0.cores[..idx.idx].iter().rev() {537			if !core.0.enum_fields_core(super_depth, handler) {538				return false;539			}540			super_depth.deepen();541		}542		true543	}544545	pub fn has_field_include_hidden(&self, name: IStr) -> bool {546		self.has_field_include_hidden_idx(547			name,548			CoreIdx {549				idx: self.0.cores.len(),550			},551		)552	}553	fn has_field_include_hidden_idx(&self, name: IStr, core: CoreIdx) -> bool {554		let mut skip = Saturating(0usize);555		for ele in self.0.cores[..core.idx].iter().rev() {556			match ele.0.has_field_include_hidden_core(name.clone()) {557				HasFieldIncludeHidden::Exists => {558					if skip.0 == 0 {559						return true;560					}561				}562				HasFieldIncludeHidden::Omit(new_skip) => {563					// +1 including this core564					skip = skip.max(new_skip + Saturating(1));565				}566				HasFieldIncludeHidden::NotFound => {}567			}568			skip -= 1;569		}570		false571	}572	pub fn has_field(&self, name: IStr) -> bool {573		match self.field_visibility(name) {574			Some(Visibility::Unhide | Visibility::Normal) => true,575			Some(Visibility::Hidden) | None => false,576		}577	}578	pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {579		if include_hidden {580			self.has_field_include_hidden(name)581		} else {582			self.has_field(name)583		}584	}585	pub fn get(&self, key: IStr) -> Result<Option<Val>> {586		self.get_idx(587			key,588			CoreIdx {589				idx: self.0.cores.len(),590			},591		)592	}593594	fn get_idx(&self, key: IStr, core: CoreIdx) -> Result<Option<Val>> {595		let cache_key = (key.clone(), core);596		{597			let mut cache = self.0.value_cache.borrow_mut();598			// entry_ref candidate?599			match cache.entry(cache_key.clone()) {600				Entry::Occupied(v) => match v.get() {601					CacheValue::Cached(v) => return v.clone(),602					CacheValue::Pending => {603						if !is_asserting(self) {604							bail!(InfiniteRecursionDetected);605						}606					}607				},608				Entry::Vacant(v) => {609					v.insert(CacheValue::Pending);610				}611			};612		}613		let result = self.get_idx_uncached(key, core);614		{615			let mut cache = self.0.value_cache.borrow_mut();616			cache.insert(cache_key, CacheValue::Cached(result.clone()));617		}618		result619	}620	fn get_idx_uncached(&self, key: IStr, core: CoreIdx) -> Result<Option<Val>> {621		self.run_assertions()?;622		let mut add_stack = Vec::with_capacity(2);623		let mut skip = Saturating(0);624		for (sup, core) in self.0.cores[..core.idx].iter().enumerate().rev() {625			let sup_this = SupThis {626				sup: CoreIdx { idx: sup },627				this: self.clone(),628			};629			match core.0.get_for_core(key.clone(), sup_this, skip.0 != 0)? {630				GetFor::Final(val) if add_stack.is_empty() => {631					if skip.0 == 0 {632						return Ok(Some(val));633					}634				}635				GetFor::Final(val) => {636					if skip.0 == 0 {637						add_stack.push(val);638						break;639					}640				}641				GetFor::SuperPlus(val) => {642					if skip.0 == 0 {643						add_stack.push(val);644					}645				}646				GetFor::Omit(new_skip) => {647					// +1 including this core648					skip = skip.max(new_skip + Saturating(1));649				}650				GetFor::NotFound => {}651			}652			skip -= 1;653		}654		if add_stack.is_empty() {655			// None of layers had this field656			return Ok(None);657		} else if add_stack.len() == 1 {658			// A layer had this field, but it wanted this field to be added with super.659			// However, no super had this field, fail-safe660			return Ok(Some(add_stack.pop().expect("single element on stack")));661		}662		let mut values = add_stack.into_iter().rev();663		let init = values.next().expect("at least 2 elements");664665		values666			.try_fold(init, |a, b| evaluate_add_op(&a, &b))667			.map(Some)668669		// self.0.get_raw(key, this)670	}671672	pub fn get_or_bail(&self, key: IStr) -> Result<Val> {673		let Some(value) = self.get(key.clone())? else {674			let suggestions = suggest_object_fields(self, key.clone());675			bail!(NoSuchField(key, suggestions))676		};677		Ok(value)678	}679680	fn field_visibility(&self, field: IStr) -> Option<Visibility> {681		self.field_visibility_idx(682			field,683			CoreIdx {684				idx: self.0.cores.len(),685			},686		)687	}688	fn field_visibility_idx(&self, field: IStr, core: CoreIdx) -> Option<Visibility> {689		let mut exists = false;690		let mut skip = Saturating(0usize);691		for ele in self.0.cores[..core.idx].iter().rev() {692			let vis = ele.0.field_visibility_core(field.clone());693			match vis {694				FieldVisibility::Found(vis @ (Visibility::Unhide | Visibility::Hidden)) => {695					if skip.0 == 0 {696						return Some(vis);697					}698				}699				FieldVisibility::Found(Visibility::Normal) => {700					if skip.0 == 0 {701						exists = true702					}703				}704				FieldVisibility::NotFound => {}705				FieldVisibility::Omit(new_skip) => {706					// +1 including this core707					skip = skip.max(new_skip + Saturating(1));708				}709			}710			skip -= 1;711		}712		exists.then_some(Visibility::Normal)713	}714715	pub fn run_assertions(&self) -> Result<()> {716		if self.0.assertions_ran.get() {717			return Ok(());718		}719		if !start_asserting(self) {720			return Ok(());721		}722		for (idx, ele) in self.0.cores.iter().enumerate() {723			let sup_this = SupThis {724				sup: CoreIdx { idx },725				this: self.clone(),726			};727			ele.0.run_assertions_core(sup_this).inspect_err(|_e| {728				finish_asserting(self);729			})?;730		}731		finish_asserting(self);732		self.0.assertions_ran.set(true);733		Ok(())734	}735736	pub fn iter(737		&self,738		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,739	) -> impl Iterator<Item = (IStr, Result<Val>)> + '_ {740		let fields = self.fields(741			#[cfg(feature = "exp-preserve-order")]742			preserve_order,743		);744		fields.into_iter().map(|field| {745			(746				field.clone(),747				self.get(field)748					.map(|opt| opt.expect("iterating over keys, field exists")),749			)750		})751	}752	pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {753		if !self.has_field_ex(key.clone(), true) {754			return None;755		}756		let obj = self.clone();757758		Some(Thunk!(move || Ok(obj.get(key)?.expect("field exists"))))759	}760	pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {761		let obj = self.clone();762		Thunk!(move || obj.get_or_bail(key))763	}764	pub fn ptr_eq(a: &Self, b: &Self) -> bool {765		Cc::ptr_eq(&a.0, &b.0)766	}767	pub fn downgrade(self) -> WeakObjValue {768		WeakObjValue(self.0.downgrade())769	}770}771772#[derive(Debug)]773struct FieldVisibilityData {774	omitted_until: Saturating<usize>,775	exists_visible: Option<Visibility>,776	#[cfg(feature = "exp-preserve-order")]777	key: FieldSortKey,778}779impl FieldVisibilityData {780	fn visible(&self) -> bool {781		self.exists_visible782			.expect("non-existing fields shall be dropped at the end of fn fields_visibility()")783			.is_visible()784	}785	#[cfg(feature = "exp-preserve-order")]786	fn sort_key(&self) -> FieldSortKey {787		self.key788	}789}790791impl ObjValue {792	fn fields_visibility(&self) -> FxHashMap<IStr, FieldVisibilityData> {793		let mut out = FxHashMap::default();794795		let mut super_depth = SuperDepth::default();796		let mut omit_index = Saturating(0);797		for core in self.0.cores.iter().rev() {798			core.0799				.enum_fields_core(&mut super_depth, &mut |_depth, _index, name, visibility| {800					let entry = out.entry(name);801					let data = entry.or_insert(FieldVisibilityData {802						exists_visible: None,803						#[cfg(feature = "exp-preserve-order")]804						key: FieldSortKey::new(_depth, _index),805						omitted_until: omit_index,806					});807					match visibility {808						EnumFields::Omit(new_skip) => {809							// +1 including this core810							data.omitted_until = data811								.omitted_until812								.max(omit_index + new_skip + Saturating(1));813						}814						EnumFields::Normal(Visibility::Normal) => {815							if data.omitted_until <= omit_index {816								if data.exists_visible.is_none() {817									data.exists_visible = Some(Visibility::Normal);818								}819							}820						}821						EnumFields::Normal(Visibility::Hidden) => {822							if data.omitted_until <= omit_index {823								data.exists_visible = Some(match data.exists_visible {824									// We're iterating in reverse, later unhide is preserved825									Some(Visibility::Unhide) => Visibility::Unhide,826									_ => Visibility::Hidden,827								});828							}829						}830						EnumFields::Normal(Visibility::Unhide) => {831							if data.omitted_until <= omit_index {832								data.exists_visible = Some(match data.exists_visible {833									// We're iterating in reverse, later hide is preserved834									Some(Visibility::Hidden) => Visibility::Hidden,835									_ => Visibility::Unhide,836								});837							}838						}839					};840					return ControlFlow::Continue(());841				});842843			super_depth.deepen();844			omit_index += 1;845		}846847		out.retain(|_, v| v.exists_visible.is_some());848849		out850	}851	pub fn fields_ex(852		&self,853		include_hidden: bool,854		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,855	) -> Vec<IStr> {856		#[cfg(feature = "exp-preserve-order")]857		if preserve_order {858			let (mut fields, mut keys): (Vec<_>, Vec<_>) = self859				.fields_visibility()860				.into_iter()861				.filter(|(_, d)| include_hidden || d.visible())862				.enumerate()863				.map(|(idx, (k, d))| (k, (d.sort_key(), idx)))864				.unzip();865			keys.sort_unstable_by_key(|v| v.0);866			// Reorder in-place by resulting indexes867			for i in 0..fields.len() {868				let x = fields[i].clone();869				let mut j = i;870				loop {871					let k = keys[j].1;872					keys[j].1 = j;873					if k == i {874						break;875					}876					fields[j] = fields[k].clone();877					j = k;878				}879				fields[j] = x;880			}881			return fields;882		}883884		let mut fields: Vec<_> = self885			.fields_visibility()886			.into_iter()887			.filter(|(_, d)| include_hidden || d.visible())888			.map(|(k, _)| k)889			.collect();890		fields.sort_unstable();891		fields892	}893	pub fn fields(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Vec<IStr> {894		self.fields_ex(895			false,896			#[cfg(feature = "exp-preserve-order")]897			preserve_order,898		)899	}900	pub fn values_ex(901		&self,902		include_hidden: bool,903		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,904	) -> ArrValue {905		ArrValue::new(PickObjectValues::new(906			self.clone(),907			self.fields_ex(908				include_hidden,909				#[cfg(feature = "exp-preserve-order")]910				preserve_order,911			),912		))913	}914	pub fn values(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> ArrValue {915		self.values_ex(916			false,917			#[cfg(feature = "exp-preserve-order")]918			preserve_order,919		)920	}921	pub fn key_values_ex(922		&self,923		include_hidden: bool,924		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,925	) -> ArrValue {926		ArrValue::new(PickObjectKeyValues::new(927			self.clone(),928			self.fields_ex(929				include_hidden,930				#[cfg(feature = "exp-preserve-order")]931				preserve_order,932			),933		))934	}935	pub fn key_values(936		&self,937		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,938	) -> ArrValue {939		self.key_values_ex(940			false,941			#[cfg(feature = "exp-preserve-order")]942			preserve_order,943		)944	}945}946947impl OopObject {948	pub fn new(949		this_entries: FxHashMap<IStr, ObjMember>,950		assertions: Vec<CcObjectAssertion>,951	) -> Self {952		Self {953			this_entries,954			assertions,955		}956	}957}958959impl ObjectCore for OopObject {960	fn enum_fields_core(961		&self,962		super_depth: &mut SuperDepth,963		handler: &mut EnumFieldsHandler<'_>,964	) -> bool {965		for (name, member) in self.this_entries.iter() {966			if matches!(967				handler(968					*super_depth,969					member.original_index,970					name.clone(),971					EnumFields::Normal(member.flags.visibility()),972				),973				ControlFlow::Break(())974			) {975				return false;976			}977		}978		true979	}980981	fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden {982		if self.this_entries.contains_key(&name) {983			HasFieldIncludeHidden::Exists984		} else {985			HasFieldIncludeHidden::NotFound986		}987	}988989	fn get_for_core(&self, key: IStr, sup_this: SupThis, omit_only: bool) -> Result<GetFor> {990		if omit_only {991			return Ok(GetFor::NotFound);992		}993		match self.this_entries.get(&key) {994			Some(k) => {995				let v = k.invoke.evaluate(sup_this)?;996				Ok(if k.flags.add() {997					GetFor::SuperPlus(v)998				} else {999					GetFor::Final(v)1000				})1001			}1002			None => Ok(GetFor::NotFound),1003		}1004	}1005	fn field_visibility_core(&self, name: IStr) -> FieldVisibility {1006		match self.this_entries.get(&name) {1007			Some(f) => FieldVisibility::Found(f.flags.visibility()),1008			None => FieldVisibility::NotFound,1009		}1010	}10111012	fn run_assertions_core(&self, sup_this: SupThis) -> Result<()> {1013		if self.assertions.is_empty() {1014			return Ok(());1015		}1016		for assertion in self.assertions.iter() {1017			assertion.0.run(sup_this.clone())?;1018		}1019		Ok(())1020	}1021}10221023#[allow(clippy::module_name_repetitions)]1024pub struct ObjValueBuilder {1025	sup: Vec<CcObjectCore>,10261027	new: OopObject,1028	next_field_index: FieldIndex,1029}1030impl ObjValueBuilder {1031	pub fn new() -> Self {1032		Self::with_capacity(0)1033	}1034	pub fn with_capacity(capacity: usize) -> Self {1035		Self {1036			sup: vec![],1037			new: OopObject {1038				assertions: vec![],1039				this_entries: FxHashMap::with_capacity(capacity),1040			},1041			next_field_index: FieldIndex::default(),1042		}1043	}1044	pub fn reserve_cores(&mut self, capacity: usize) -> &mut Self {1045		self.sup.reserve_exact(capacity);1046		self1047	}1048	pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self {1049		self.new.assertions.reserve_exact(capacity);1050		self1051	}1052	pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {1053		self.sup = super_obj.0.cores.clone();1054		self1055	}10561057	pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {1058		self.new.assertions.push(CcObjectAssertion::new(assertion));1059		self1060	}1061	pub fn field(&mut self, name: impl Into<IStr>) -> ObjMemberBuilder<ValueBuilder<'_>> {1062		let field_index = self.next_field_index;1063		self.next_field_index = self.next_field_index.next();1064		ObjMemberBuilder::new(ValueBuilder(self), name.into(), field_index)1065	}1066	/// Preset for common method definiton pattern:1067	/// Create a hidden field with the function value.1068	///1069	/// `.field(name).hide().value(Val::function(value))`1070	pub fn method(&mut self, name: impl Into<IStr>, value: impl Into<FuncVal>) -> &mut Self {1071		self.field(name).hide().value(Val::Func(value.into()));1072		self1073	}1074	pub fn try_method(1075		&mut self,1076		name: impl Into<IStr>,1077		value: impl Into<FuncVal>,1078	) -> Result<&mut Self> {1079		self.field(name).hide().try_value(Val::Func(value.into()))?;1080		Ok(self)1081	}10821083	pub fn extend_with_core(&mut self, core: impl ObjectCore) {1084		self.commit();1085		self.sup.push(CcObjectCore::new(core));1086	}10871088	fn commit(&mut self) {1089		if !self.new.is_empty() {1090			self.sup.push(CcObjectCore::new(mem::take(&mut self.new)));1091		}1092		self.next_field_index = FieldIndex::default();1093	}10941095	pub fn with_fields_omitted(&mut self, omit: FxHashSet<IStr>) {1096		self.commit();1097		self.sup.push(CcObjectCore::new(OmitFieldsCore {1098			omit,1099			prev_layers: self.sup.len(),1100		}));1101	}11021103	pub fn build(mut self) -> ObjValue {1104		self.commit();1105		if self.sup.is_empty() {1106			return ObjValue::empty();1107		}1108		ObjValue(Cc::new(ObjValueInner {1109			cores: self.sup,1110			assertions_ran: Cell::new(false),1111			value_cache: Default::default(),1112		}))1113	}1114}1115impl Default for ObjValueBuilder {1116	fn default() -> Self {1117		Self::with_capacity(0)1118	}1119}11201121#[allow(clippy::module_name_repetitions)]1122#[must_use = "value not added unless binding() was called"]1123pub struct ObjMemberBuilder<Kind> {1124	kind: Kind,1125	name: IStr,1126	add: bool,1127	visibility: Visibility,1128	original_index: FieldIndex,1129	location: Option<Span>,1130}11311132#[allow(clippy::missing_const_for_fn)]1133impl<Kind> ObjMemberBuilder<Kind> {1134	pub(crate) fn new(kind: Kind, name: IStr, original_index: FieldIndex) -> Self {1135		Self {1136			kind,1137			name,1138			original_index,1139			add: false,1140			visibility: Visibility::Normal,1141			location: None,1142		}1143	}11441145	pub const fn with_add(mut self, add: bool) -> Self {1146		self.add = add;1147		self1148	}1149	pub fn add(self) -> Self {1150		self.with_add(true)1151	}1152	pub fn with_visibility(mut self, visibility: Visibility) -> Self {1153		self.visibility = visibility;1154		self1155	}1156	pub fn hide(self) -> Self {1157		self.with_visibility(Visibility::Hidden)1158	}1159	pub fn with_location(mut self, location: Span) -> Self {1160		self.location = Some(location);1161		self1162	}1163	fn build_member(self, binding: MaybeUnbound) -> (Kind, IStr, ObjMember) {1164		(1165			self.kind,1166			self.name,1167			ObjMember {1168				flags: ObjFieldFlags::new(self.add, self.visibility),1169				original_index: self.original_index,1170				invoke: binding,1171				location: self.location,1172			},1173		)1174	}1175}11761177pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);1178impl ObjMemberBuilder<ValueBuilder<'_>> {1179	/// Inserts value, replacing if it is already defined1180	pub fn value(self, value: impl Into<Val>) {1181		let (receiver, name, member) =1182			self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value.into())));1183		let entry = receiver.0.new.this_entries.entry(name);1184		entry.insert_entry(member);1185	}11861187	/// Tries to insert value, returns an error if it was already defined1188	pub fn try_value(self, value: impl Into<Val>) -> Result<()> {1189		self.try_thunk(Thunk::evaluated(value.into()))1190	}1191	pub fn try_thunk(self, value: impl Into<Thunk<Val>>) -> Result<()> {1192		self.binding(MaybeUnbound::Bound(value.into()))1193	}1194	pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {1195		self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)))1196	}1197	pub fn binding(self, binding: MaybeUnbound) -> Result<()> {1198		let (receiver, name, member) = self.build_member(binding);1199		let location = member.location.clone();1200		let old = receiver.0.new.this_entries.insert(name.clone(), member);1201		if old.is_some() {1202			in_frame(1203				CallLocation(location.as_ref()),1204				|| format!("field <{}> initializtion", name.clone()),1205				|| bail!(DuplicateFieldName(name.clone())),1206			)?;1207		}1208		Ok(())1209	}1210}12111212pub struct ExtendBuilder<'v>(&'v mut ObjValue);1213impl ObjMemberBuilder<ExtendBuilder<'_>> {1214	pub fn value(self, value: impl Into<Val>) {1215		self.binding(MaybeUnbound::Bound(Thunk::evaluated(value.into())));1216	}1217	pub fn bindable(self, bindable: impl Unbound<Bound = Val>) {1218		self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)));1219	}1220	pub fn binding(self, binding: MaybeUnbound) {1221		let (receiver, name, member) = self.build_member(binding);1222		let new = receiver.0.clone();1223		*receiver.0 = new.extend_with_raw_member(name, member);1224	}1225}