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

difftreelog

source

crates/jrsonnet-evaluator/src/obj.rs30.0 KiBsourcehistory
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}