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

difftreelog

source

crates/jrsonnet-evaluator/src/obj/mod.rs24.8 KiBsourcehistory
1use std::{2	any::Any, cell::{Cell, RefCell}, clone::Clone, cmp::Reverse, collections::hash_map::Entry, fmt::{self, Debug}, hash::{Hash, Hasher}, num::Saturating, ops::ControlFlow3};45use educe::Educe;6use jrsonnet_gcmodule::{cc_dyn, Acyclic, Cc, Trace, Weak};7use jrsonnet_interner::IStr;8use jrsonnet_ir::Span;9use rustc_hash::{FxHashMap, FxHashSet};1011mod oop;1213pub use jrsonnet_ir::Visibility;14pub use oop::ObjValueBuilder;1516use crate::{17	arr::{PickObjectKeyValues, PickObjectValues},18	bail,19	error::{suggest_object_fields, ErrorKind::*},20	identity_hash,21	operator::evaluate_add_op,22	val::{ArrValue, ThunkValue},23	CcUnbound, MaybeUnbound, Result, Thunk, Unbound, Val,24};2526#[cfg(not(feature = "exp-preserve-order"))]27pub mod ordering {28	#![allow(29		// This module works as stub for preserve-order feature30		clippy::unused_self,31	)]3233	use jrsonnet_gcmodule::Trace;3435	#[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]36	pub struct FieldIndex(());37	impl FieldIndex {38		pub fn absolute(_v: u32) -> Self {39			Self(())40		}41		#[must_use]42		pub const fn next(self) -> Self {43			Self(())44		}45	}4647	#[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]48	pub struct SuperDepth(());49	impl SuperDepth {50		pub(super) fn deepen(self) {}51	}52}5354#[cfg(feature = "exp-preserve-order")]55pub mod ordering {56	use jrsonnet_gcmodule::Trace;5758	#[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]59	pub struct FieldIndex(u32);60	impl FieldIndex {61		pub fn absolute(v: u32) -> Self {62			Self(v)63		}64		#[must_use]65		pub fn next(self) -> Self {66			Self(self.0 + 1)67		}68	}6970	#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Debug)]71	pub struct SuperDepth(u32);72	impl SuperDepth {73		pub(super) fn deepen(&mut self) {74			self.0 += 1;75		}76	}77}7879use ordering::{FieldIndex, SuperDepth};8081#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]82pub struct FieldSortKey(Reverse<SuperDepth>, FieldIndex);83impl FieldSortKey {84	pub fn new(depth: SuperDepth, index: FieldIndex) -> Self {85		Self(Reverse(depth), index)86	}87}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}149150pub type EnumFieldsHandler<'a> =151	dyn FnMut(SuperDepth, FieldIndex, IStr, EnumFields) -> ControlFlow<()> + 'a;152153pub enum EnumFields {154	Normal(Visibility),155	Omit(Skip),156}157158#[derive(Trace, Clone)]159pub enum GetFor {160	// Return value161	Final(Val),162	// Continue iterating over cores, add current value to sum stack163	SuperPlus(Val),164	// Ignore the field value, stop at this layer instead165	Omit(#[trace(skip)] Skip),166	NotFound,167}168169#[derive(Acyclic, Clone)]170pub enum FieldVisibility {171	Found(Visibility),172	Omit(Skip),173	NotFound,174}175176#[derive(Acyclic, Clone)]177pub enum HasFieldIncludeHidden {178	Exists,179	NotFound,180	Omit(Skip),181}182183type Skip = Saturating<usize>;184185pub trait ObjectCore: Trace + Any + Debug {186	// If callback returns false, iteration stops, and this call returns false.187	fn enum_fields_core(188		&self,189		super_depth: &mut SuperDepth,190		handler: &mut EnumFieldsHandler<'_>,191	) -> bool;192193	fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden;194195	fn get_for_core(&self, key: IStr, sup_this: SupThis, omit_only: bool) -> Result<GetFor>;196	fn field_visibility_core(&self, field: IStr) -> FieldVisibility;197198	fn run_assertions_core(&self, sup_this: SupThis) -> Result<()>;199}200201#[derive(Clone, Trace)]202pub struct WeakObjValue(#[trace(skip)] Weak<ObjValueInner>);203impl Debug for WeakObjValue {204	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {205		f.debug_tuple("WeakObjValue").finish()206	}207}208209impl PartialEq for WeakObjValue {210	fn eq(&self, other: &Self) -> bool {211		Weak::ptr_eq(&self.0, &other.0)212	}213}214215impl Eq for WeakObjValue {}216impl Hash for WeakObjValue {217	fn hash<H: Hasher>(&self, hasher: &mut H) {218		// Safety: usize is POD219		let addr = unsafe { *std::ptr::addr_of!(self.0).cast() };220		hasher.write_usize(addr);221	}222}223224cc_dyn!(225	#[derive(Clone, Debug)]226	CcObjectCore, ObjectCore,227	pub fn new() {...}228);229#[derive(Trace, Educe)]230#[educe(Debug)]231struct ObjValueInner {232	cores: Vec<CcObjectCore>,233	assertions_ran: Cell<bool>,234	value_cache: RefCell<FxHashMap<(IStr, CoreIdx), CacheValue>>,235}236237thread_local! {238	static RUNNING_ASSERTIONS: RefCell<FxHashSet<ObjValue>> = RefCell::default();239}240fn is_asserting(obj: &ObjValue) -> bool {241	RUNNING_ASSERTIONS.with_borrow(|v| v.contains(obj))242}243/// Returns false if already asserting244fn start_asserting(obj: &ObjValue) -> bool {245	RUNNING_ASSERTIONS.with_borrow_mut(|v| v.insert(obj.clone()))246}247fn finish_asserting(obj: &ObjValue) {248	RUNNING_ASSERTIONS.with_borrow_mut(|v| {249		let r = v.remove(obj);250		debug_assert!(251			r,252			"finish_asserting was called before start_asserting or twice"253		);254	});255}256257thread_local! {258	static EMPTY_OBJ: ObjValue = ObjValue(Cc::new(ObjValueInner {259		cores: vec![],260		assertions_ran: Cell::new(true),261		value_cache: RefCell::default(),262	}))263}264265#[allow(clippy::module_name_repetitions)]266#[derive(Clone, Trace, Debug, Educe)]267#[educe(PartialEq, Hash, Eq)]268pub struct ObjValue(269	#[educe(PartialEq(method(Cc::ptr_eq)), Hash(method(identity_hash)))] Cc<ObjValueInner>,270);271272impl ObjValue {273	pub fn empty() -> Self {274		EMPTY_OBJ.with(Clone::clone)275	}276	pub fn is_empty(&self) -> bool {277		self.0.cores.is_empty() || self.len() == 0278	}279}280281#[derive(Trace, Debug)]282struct StandaloneSuperCore {283	sup: CoreIdx,284	this: ObjValue,285}286impl ObjectCore for StandaloneSuperCore {287	fn enum_fields_core(288		&self,289		super_depth: &mut SuperDepth,290		handler: &mut EnumFieldsHandler<'_>,291	) -> bool {292		self.this.enum_fields_idx(super_depth, handler, self.sup)293	}294295	fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden {296		if self.this.has_field_include_hidden_idx(name, self.sup) {297			HasFieldIncludeHidden::Exists298		} else {299			HasFieldIncludeHidden::NotFound300		}301	}302303	fn get_for_core(&self, key: IStr, _sup_this: SupThis, omit_only: bool) -> Result<GetFor> {304		if omit_only {305			return Ok(GetFor::NotFound);306		}307		let v = self.this.get_idx(key, self.sup)?;308		Ok(v.map_or(GetFor::NotFound, GetFor::Final))309	}310311	fn field_visibility_core(&self, field: IStr) -> FieldVisibility {312		self.this313			.field_visibility_idx(field, self.sup)314			.map_or(FieldVisibility::NotFound, FieldVisibility::Found)315	}316317	fn run_assertions_core(&self, _sup_this: SupThis) -> Result<()> {318		self.this.run_assertions()319	}320}321322#[derive(Debug, Acyclic)]323struct OmitFieldsCore {324	omit: FxHashSet<IStr>,325	prev_layers: usize,326}327impl ObjectCore for OmitFieldsCore {328	fn enum_fields_core(329		&self,330		super_depth: &mut SuperDepth,331		handler: &mut EnumFieldsHandler<'_>,332	) -> bool {333		let mut fi = FieldIndex::default();334		for f in &self.omit {335			if handler(336				*super_depth,337				fi,338				f.clone(),339				EnumFields::Omit(Saturating(self.prev_layers)),340			) == ControlFlow::Break(())341			{342				return false;343			}344			fi = fi.next();345		}346		true347	}348349	fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden {350		if self.omit.contains(&name) {351			return HasFieldIncludeHidden::Omit(Saturating(self.prev_layers));352		}353		HasFieldIncludeHidden::NotFound354	}355356	fn get_for_core(&self, key: IStr, _sup_this: SupThis, _omit_only: bool) -> Result<GetFor> {357		if self.omit.contains(&key) {358			return Ok(GetFor::Omit(Saturating(self.prev_layers)));359		}360		Ok(GetFor::NotFound)361	}362363	fn field_visibility_core(&self, field: IStr) -> FieldVisibility {364		if self.omit.contains(&field) {365			return FieldVisibility::Omit(Saturating(self.prev_layers));366		}367		FieldVisibility::NotFound368	}369370	fn run_assertions_core(&self, _sup_this: SupThis) -> Result<()> {371		Ok(())372	}373}374375#[derive(Hash, PartialEq, Eq, Trace, Clone, Copy, Debug)]376struct CoreIdx {377	idx: usize,378}379impl CoreIdx {380	fn super_exists(self) -> bool {381		self.idx != 0382	}383}384#[derive(Trace, Clone, PartialEq, Eq, Hash, Debug)]385pub struct SupThis {386	sup: CoreIdx,387	this: ObjValue,388}389impl SupThis {390	pub fn has_super(&self) -> bool {391		self.sup.super_exists()392	}393	/// Implementation of `"field" in super` operation,394	/// works faster than standalone super path.395	///396	/// In case of no `super` existence, returns false.397	pub fn field_in_super(&self, field: IStr) -> bool {398		self.this.has_field_include_hidden_idx(field, self.sup)399	}400	/// Implementation of `super.field` operation,401	/// works faster than standalone super path.402	///403	/// In case of no `super` existence, returns `NoSuperFound`404	pub fn get_super(&self, field: IStr) -> Result<Option<Val>> {405		if !self.sup.super_exists() {406			bail!(NoSuperFound);407		}408		self.this.get_idx(field, self.sup)409	}410	/// `super` with `self` overriden for top-level lookups.411	/// Exists when super appears outside of `super.field`/`"field" in super` expressions412	/// Exclusive to jrsonnet.413	///414	/// Might return `NoSuperFound` error.415	pub fn standalone_super(&self) -> Result<ObjValue> {416		if !self.sup.super_exists() {417			bail!(NoSuperFound)418		}419		let mut out = ObjValue::builder();420		out.reserve_cores(1).extend_with_core(StandaloneSuperCore {421			sup: self.sup,422			this: self.this.clone(),423		});424		Ok(out.build())425	}426	pub fn this(&self) -> &ObjValue {427		&self.this428	}429	pub fn downgrade(self) -> WeakSupThis {430		WeakSupThis {431			sup: self.sup,432			this: self.this.downgrade(),433		}434	}435}436#[derive(Trace, PartialEq, Eq, Hash, Debug)]437pub struct WeakSupThis {438	sup: CoreIdx,439	this: WeakObjValue,440}441442impl ObjValue {443	pub fn builder() -> ObjValueBuilder {444		ObjValueBuilder::new()445	}446	pub fn builder_with_capacity(capacity: usize) -> ObjValueBuilder {447		ObjValueBuilder::with_capacity(capacity)448	}449	pub(crate) fn extend_with_raw_member(self, key: IStr, value: ObjMember) -> Self {450		let mut out = ObjValueBuilder::with_capacity(1);451		out.with_super(self);452		let mut member = out.field(key);453		if value.flags.add() {454			member = member.add();455		}456		if let Some(loc) = value.location {457			member = member.with_location(loc);458		}459		let _ = member460			.with_visibility(value.flags.visibility())461			.binding(value.invoke);462		out.build()463	}464	pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder<'_>> {465		ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())466	}467468	pub fn extend(&mut self) -> ObjValueBuilder {469		let mut out = ObjValueBuilder::new();470		out.with_super(self.clone());471		out472	}473474	#[must_use]475	pub fn extend_from(&self, sup: Self) -> Self {476		let mut cores = sup.0.cores.clone();477		cores.extend(self.0.cores.iter().cloned());478		ObjValue(Cc::new(ObjValueInner {479			cores,480			value_cache: RefCell::default(),481			assertions_ran: Cell::new(false),482		}))483	}484	// #[must_use]485	// pub fn with_this(&self, this: Self) -> Self {486	// 	self.0.with_this(self.clone(), this)487	// }488	/// Returns amount of visible object fields489	/// If object only contains hidden fields - may return zero.490	pub fn len(&self) -> usize {491		self.fields_visibility()492			.values()493			.filter(|d| d.visible())494			.count()495	}496	/// For each field, calls callback.497	/// If callback returns false - ends iteration prematurely.498	///499	/// Returns false if ended prematurely500	pub fn enum_fields(&self, handler: &mut EnumFieldsHandler<'_>) -> bool {501		let mut super_depth = SuperDepth::default();502		self.enum_fields_idx(503			&mut super_depth,504			handler,505			CoreIdx {506				idx: self.0.cores.len(),507			},508		)509	}510	fn enum_fields_idx(511		&self,512		super_depth: &mut SuperDepth,513		handler: &mut EnumFieldsHandler<'_>,514		idx: CoreIdx,515	) -> bool {516		for core in self.0.cores[..idx.idx].iter().rev() {517			if !core.0.enum_fields_core(super_depth, handler) {518				return false;519			}520			super_depth.deepen();521		}522		true523	}524525	pub fn has_field_include_hidden(&self, name: IStr) -> bool {526		self.has_field_include_hidden_idx(527			name,528			CoreIdx {529				idx: self.0.cores.len(),530			},531		)532	}533	fn has_field_include_hidden_idx(&self, name: IStr, core: CoreIdx) -> bool {534		let mut skip = Saturating(0usize);535		for ele in self.0.cores[..core.idx].iter().rev() {536			match ele.0.has_field_include_hidden_core(name.clone()) {537				HasFieldIncludeHidden::Exists => {538					if skip.0 == 0 {539						return true;540					}541				}542				HasFieldIncludeHidden::Omit(new_skip) => {543					// +1 including this core544					skip = skip.max(new_skip + Saturating(1));545				}546				HasFieldIncludeHidden::NotFound => {}547			}548			skip -= 1;549		}550		false551	}552	pub fn has_field(&self, name: IStr) -> bool {553		match self.field_visibility(name) {554			Some(Visibility::Unhide | Visibility::Normal) => true,555			Some(Visibility::Hidden) | None => false,556		}557	}558	pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {559		if include_hidden {560			self.has_field_include_hidden(name)561		} else {562			self.has_field(name)563		}564	}565	pub fn get(&self, key: IStr) -> Result<Option<Val>> {566		self.get_idx(567			key,568			CoreIdx {569				idx: self.0.cores.len(),570			},571		)572	}573574	fn get_idx(&self, key: IStr, core: CoreIdx) -> Result<Option<Val>> {575		let cache_key = (key.clone(), core);576		{577			let mut cache = self.0.value_cache.borrow_mut();578			// entry_ref candidate?579			match cache.entry(cache_key.clone()) {580				Entry::Occupied(v) => match v.get() {581					CacheValue::Cached(v) => return v.clone(),582					CacheValue::Pending => {583						if !is_asserting(self) {584							bail!(InfiniteRecursionDetected);585						}586					}587				},588				Entry::Vacant(v) => {589					v.insert(CacheValue::Pending);590				}591			};592		}593		let result = self.get_idx_uncached(key, core);594		{595			let mut cache = self.0.value_cache.borrow_mut();596			cache.insert(cache_key, CacheValue::Cached(result.clone()));597		}598		result599	}600	fn get_idx_uncached(&self, key: IStr, core: CoreIdx) -> Result<Option<Val>> {601		self.run_assertions()?;602		let mut add_stack = Vec::with_capacity(2);603		let mut skip = Saturating(0);604		for (sup, core) in self.0.cores[..core.idx].iter().enumerate().rev() {605			let sup_this = SupThis {606				sup: CoreIdx { idx: sup },607				this: self.clone(),608			};609			match core.0.get_for_core(key.clone(), sup_this, skip.0 != 0)? {610				GetFor::Final(val) if add_stack.is_empty() => {611					if skip.0 == 0 {612						return Ok(Some(val));613					}614				}615				GetFor::Final(val) => {616					if skip.0 == 0 {617						add_stack.push(val);618						break;619					}620				}621				GetFor::SuperPlus(val) => {622					if skip.0 == 0 {623						add_stack.push(val);624					}625				}626				GetFor::Omit(new_skip) => {627					// +1 including this core628					skip = skip.max(new_skip + Saturating(1));629				}630				GetFor::NotFound => {}631			}632			skip -= 1;633		}634		if add_stack.is_empty() {635			// None of layers had this field636			return Ok(None);637		} else if add_stack.len() == 1 {638			// A layer had this field, but it wanted this field to be added with super.639			// However, no super had this field, fail-safe640			return Ok(Some(add_stack.pop().expect("single element on stack")));641		}642		let mut values = add_stack.into_iter().rev();643		let init = values.next().expect("at least 2 elements");644645		values646			.try_fold(init, |a, b| evaluate_add_op(&a, &b))647			.map(Some)648649		// self.0.get_raw(key, this)650	}651652	pub fn get_or_bail(&self, key: IStr) -> Result<Val> {653		let Some(value) = self.get(key.clone())? else {654			let suggestions = suggest_object_fields(self, key.clone());655			bail!(NoSuchField(key, suggestions))656		};657		Ok(value)658	}659660	fn field_visibility(&self, field: IStr) -> Option<Visibility> {661		self.field_visibility_idx(662			field,663			CoreIdx {664				idx: self.0.cores.len(),665			},666		)667	}668	fn field_visibility_idx(&self, field: IStr, core: CoreIdx) -> Option<Visibility> {669		let mut exists = false;670		let mut skip = Saturating(0usize);671		for ele in self.0.cores[..core.idx].iter().rev() {672			let vis = ele.0.field_visibility_core(field.clone());673			match vis {674				FieldVisibility::Found(vis @ (Visibility::Unhide | Visibility::Hidden)) => {675					if skip.0 == 0 {676						return Some(vis);677					}678				}679				FieldVisibility::Found(Visibility::Normal) => {680					if skip.0 == 0 {681						exists = true;682					}683				}684				FieldVisibility::NotFound => {}685				FieldVisibility::Omit(new_skip) => {686					// +1 including this core687					skip = skip.max(new_skip + Saturating(1));688				}689			}690			skip -= 1;691		}692		exists.then_some(Visibility::Normal)693	}694695	pub fn run_assertions(&self) -> Result<()> {696		if self.0.assertions_ran.get() {697			return Ok(());698		}699		if !start_asserting(self) {700			return Ok(());701		}702		for (idx, ele) in self.0.cores.iter().enumerate() {703			let sup_this = SupThis {704				sup: CoreIdx { idx },705				this: self.clone(),706			};707			ele.0.run_assertions_core(sup_this).inspect_err(|_e| {708				finish_asserting(self);709			})?;710		}711		finish_asserting(self);712		self.0.assertions_ran.set(true);713		Ok(())714	}715716	pub fn iter(717		&self,718		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,719	) -> impl Iterator<Item = (IStr, Result<Val>)> + '_ {720		let fields = self.fields(721			#[cfg(feature = "exp-preserve-order")]722			preserve_order,723		);724		fields.into_iter().map(|field| {725			(726				field.clone(),727				self.get(field)728					.map(|opt| opt.expect("iterating over keys, field exists")),729			)730		})731	}732	pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {733		#[derive(Trace)]734		struct ObjFieldThunk {735			obj: ObjValue,736			key: IStr,737		}738		impl ThunkValue for ObjFieldThunk {739			type Output = Val;740741			fn get(&self) -> Result<Self::Output> {742				self.obj743					.get(self.key.clone())744					.transpose()745					.expect("field existence checked")746			}747		}748749		if !self.has_field_ex(key.clone(), true) {750			return None;751		}752753		Some(Thunk::new(ObjFieldThunk {754			obj: self.clone(),755			key,756		}))757	}758	pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {759		#[derive(Trace)]760		struct ObjFieldThunk {761			obj: ObjValue,762			key: IStr,763		}764		impl ThunkValue for ObjFieldThunk {765			type Output = Val;766767			fn get(&self) -> Result<Self::Output> {768				self.obj.get_or_bail(self.key.clone())769			}770		}771772		Thunk::new(ObjFieldThunk {773			obj: self.clone(),774			key,775		})776	}777	pub fn ptr_eq(a: &Self, b: &Self) -> bool {778		Cc::ptr_eq(&a.0, &b.0)779	}780	pub fn downgrade(self) -> WeakObjValue {781		WeakObjValue(self.0.downgrade())782	}783}784785#[derive(Debug)]786struct FieldVisibilityData {787	omitted_until: Saturating<usize>,788	exists_visible: Option<Visibility>,789	#[allow(dead_code, reason = "used for exp-object-ordering, ZST otherwise")]790	key: FieldSortKey,791}792impl FieldVisibilityData {793	fn visible(&self) -> bool {794		self.exists_visible795			.expect("non-existing fields shall be dropped at the end of fn fields_visibility()")796			.is_visible()797	}798	#[allow(dead_code, reason = "used for exp-object-ordering, ZST otherwise")]799	fn sort_key(&self) -> FieldSortKey {800		self.key801	}802}803804impl ObjValue {805	fn fields_visibility(&self) -> FxHashMap<IStr, FieldVisibilityData> {806		let mut out = FxHashMap::default();807808		let mut super_depth = SuperDepth::default();809		let mut omit_index = Saturating(0);810		for core in self.0.cores.iter().rev() {811			core.0812				.enum_fields_core(&mut super_depth, &mut |depth, index, name, visibility| {813					let entry = out.entry(name);814					let data = entry.or_insert_with(|| FieldVisibilityData {815						exists_visible: None,816						key: FieldSortKey::new(depth, index),817						omitted_until: omit_index,818					});819					match visibility {820						EnumFields::Omit(new_skip) => {821							// +1 including this core822							data.omitted_until = data823								.omitted_until824								.max(omit_index + new_skip + Saturating(1));825						}826						EnumFields::Normal(Visibility::Normal) => {827							if data.omitted_until <= omit_index && data.exists_visible.is_none() {828								data.exists_visible = Some(Visibility::Normal);829							}830						}831						EnumFields::Normal(Visibility::Hidden) => {832							if data.omitted_until <= omit_index {833								data.exists_visible = Some(match data.exists_visible {834									// We're iterating in reverse, later unhide is preserved835									Some(Visibility::Unhide) => Visibility::Unhide,836									_ => Visibility::Hidden,837								});838							}839						}840						EnumFields::Normal(Visibility::Unhide) => {841							if data.omitted_until <= omit_index {842								data.exists_visible = Some(match data.exists_visible {843									// We're iterating in reverse, later hide is preserved844									Some(Visibility::Hidden) => Visibility::Hidden,845									_ => Visibility::Unhide,846								});847							}848						}849					}850					ControlFlow::Continue(())851				});852853			super_depth.deepen();854			omit_index += 1;855		}856857		out.retain(|_, v| v.exists_visible.is_some());858859		out860	}861	pub fn fields_ex(862		&self,863		include_hidden: bool,864		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,865	) -> Vec<IStr> {866		#[cfg(feature = "exp-preserve-order")]867		if preserve_order {868			let (mut fields, mut keys): (Vec<_>, Vec<_>) = self869				.fields_visibility()870				.into_iter()871				.filter(|(_, d)| include_hidden || d.visible())872				.enumerate()873				.map(|(idx, (k, d))| (k, (d.sort_key(), idx)))874				.unzip();875			keys.sort_unstable_by_key(|v| v.0);876			// Reorder in-place by resulting indexes877			for i in 0..fields.len() {878				let x = fields[i].clone();879				let mut j = i;880				loop {881					let k = keys[j].1;882					keys[j].1 = j;883					if k == i {884						break;885					}886					fields[j] = fields[k].clone();887					j = k;888				}889				fields[j] = x;890			}891			return fields;892		}893894		let mut fields: Vec<_> = self895			.fields_visibility()896			.into_iter()897			.filter(|(_, d)| include_hidden || d.visible())898			.map(|(k, _)| k)899			.collect();900		fields.sort_unstable();901		fields902	}903	pub fn fields(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Vec<IStr> {904		self.fields_ex(905			false,906			#[cfg(feature = "exp-preserve-order")]907			preserve_order,908		)909	}910	pub fn values_ex(911		&self,912		include_hidden: bool,913		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,914	) -> ArrValue {915		ArrValue::new(PickObjectValues::new(916			self.clone(),917			self.fields_ex(918				include_hidden,919				#[cfg(feature = "exp-preserve-order")]920				preserve_order,921			),922		))923	}924	pub fn values(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> ArrValue {925		self.values_ex(926			false,927			#[cfg(feature = "exp-preserve-order")]928			preserve_order,929		)930	}931	pub fn key_values_ex(932		&self,933		include_hidden: bool,934		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,935	) -> ArrValue {936		ArrValue::new(PickObjectKeyValues::new(937			self.clone(),938			self.fields_ex(939				include_hidden,940				#[cfg(feature = "exp-preserve-order")]941				preserve_order,942			),943		))944	}945	pub fn key_values(946		&self,947		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,948	) -> ArrValue {949		self.key_values_ex(950			false,951			#[cfg(feature = "exp-preserve-order")]952			preserve_order,953		)954	}955}956957#[allow(clippy::module_name_repetitions)]958#[must_use = "value not added unless binding() was called"]959pub struct ObjMemberBuilder<Kind> {960	kind: Kind,961	name: IStr,962	add: bool,963	visibility: Visibility,964	original_index: FieldIndex,965	location: Option<Span>,966}967968#[allow(clippy::missing_const_for_fn)]969impl<Kind> ObjMemberBuilder<Kind> {970	pub(crate) fn new(kind: Kind, name: IStr, original_index: FieldIndex) -> Self {971		Self {972			kind,973			name,974			original_index,975			add: false,976			visibility: Visibility::Normal,977			location: None,978		}979	}980981	pub const fn with_add(mut self, add: bool) -> Self {982		self.add = add;983		self984	}985	pub fn add(self) -> Self {986		self.with_add(true)987	}988	pub fn with_visibility(mut self, visibility: Visibility) -> Self {989		self.visibility = visibility;990		self991	}992	pub fn hide(self) -> Self {993		self.with_visibility(Visibility::Hidden)994	}995	pub fn with_location(mut self, location: Span) -> Self {996		self.location = Some(location);997		self998	}999	fn build_member(self, binding: MaybeUnbound) -> (Kind, IStr, ObjMember) {1000		(1001			self.kind,1002			self.name,1003			ObjMember {1004				flags: ObjFieldFlags::new(self.add, self.visibility),1005				original_index: self.original_index,1006				invoke: binding,1007				location: self.location,1008			},1009		)1010	}1011}10121013pub struct ExtendBuilder<'v>(&'v mut ObjValue);1014impl ObjMemberBuilder<ExtendBuilder<'_>> {1015	pub fn value(self, value: impl Into<Val>) {1016		self.binding(MaybeUnbound::Bound(Thunk::evaluated(value.into())));1017	}1018	pub fn bindable(self, bindable: impl Unbound<Bound = Val>) {1019		self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)));1020	}1021	pub fn binding(self, binding: MaybeUnbound) {1022		let (receiver, name, member) = self.build_member(binding);1023		let new = receiver.0.clone();1024		*receiver.0 = new.extend_with_raw_member(name, member);1025	}1026}