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

difftreelog

source

crates/jrsonnet-evaluator/src/obj.rs25.2 KiBsourcehistory
1use std::{2	any::Any, cell::{Cell, RefCell}, collections::hash_map::Entry, fmt::{self, Debug}, hash::{Hash, Hasher}3};45use jrsonnet_gcmodule::{cc_dyn, Cc, Trace, Weak};6use educe::Educe;7use jrsonnet_interner::IStr;8use jrsonnet_parser::{Span, Visibility};9use rustc_hash::{FxHashMap, FxHashSet};1011use crate::{12	arr::{PickObjectKeyValues, PickObjectValues},13	bail,14	error::{suggest_object_fields, ErrorKind::*},15	function::{CallLocation, FuncVal},16	gc::WithCapacityExt as _,17	in_frame,18	identity_hash, 19	operator::evaluate_add_op,20	val::ArrValue,21	CcUnbound, MaybeUnbound, Result, Thunk, Unbound, Val,22};2324#[cfg(not(feature = "exp-preserve-order"))]25mod ordering {26	#![allow(27		// This module works as stub for preserve-order feature28		clippy::unused_self,29	)]3031	use jrsonnet_gcmodule::Trace;3233	#[derive(Clone, Copy, Default, Debug, Trace)]34	pub struct FieldIndex(());35	impl FieldIndex {36		pub const fn next(self) -> Self {37			Self(())38		}39	}4041	#[derive(Clone, Copy, Default, Debug, Trace)]42	pub struct SuperDepth(());43	impl SuperDepth {44		pub(super) fn deepen(self) {}45	}4647	#[derive(Clone, Copy, Debug)]48	pub struct FieldSortKey(());49	impl FieldSortKey {50		pub const fn new(_: SuperDepth, _: FieldIndex) -> Self {51			Self(())52		}53	}54}5556#[cfg(feature = "exp-preserve-order")]57mod ordering {58	use std::cmp::Reverse;5960	use jrsonnet_gcmodule::Trace;6162	#[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]63	pub struct FieldIndex(u32);64	impl FieldIndex {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 += 175		}76	}7778	#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]79	pub struct FieldSortKey(Reverse<SuperDepth>, FieldIndex);80	impl FieldSortKey {81		pub fn new(depth: SuperDepth, index: FieldIndex) -> Self {82			Self(Reverse(depth), index)83		}84	}85}8687use ordering::{FieldIndex, FieldSortKey, 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)]152#[trace(tracking(force))]153pub struct OopObject {154	// this: Option<ObjValue>,155	assertions: Cc<Vec<CcObjectAssertion>>,156	this_entries: Cc<FxHashMap<IStr, ObjMember>>,157	value_cache: RefCell<FxHashMap<(IStr, Option<WeakObjValue>), CacheValue>>,158}159impl Debug for OopObject {160	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {161		f.debug_struct("OopObject")162			// .field("assertions", &self.assertions)163			// .field("assertions_ran", &self.assertions_ran)164			.field("this_entries", &self.this_entries)165			// .field("value_cache", &self.value_cache)166			.finish_non_exhaustive()167	}168}169170type EnumFieldsHandler<'a> = dyn FnMut(SuperDepth, FieldIndex, IStr, Visibility) -> bool + 'a;171172#[derive(Trace, Clone)]173pub enum ValueProcess {174	None,175	SuperPlus,176}177178pub trait ObjectCore: Trace + Any + Debug {179	// If callback returns false, iteration stops, and this call returns false.180	fn enum_fields_core(181		&self,182		super_depth: &mut SuperDepth,183		handler: &mut EnumFieldsHandler<'_>,184	) -> bool;185186	fn has_field_include_hidden(&self, name: IStr) -> bool;187188	fn get_for(&self, key: IStr, sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>>;189	// fn get_for_uncached(&self, key: IStr, this: ObjValue) -> Result<Option<(Val, ValueProcess)>>;190	fn field_visibility(&self, field: IStr) -> Option<Visibility>;191192	fn run_assertions_raw(&self, sup_this: SupThis) -> Result<()>;193}194195#[derive(Clone, Trace)]196pub struct WeakObjValue(#[trace(skip)] Weak<ObjValueInner>);197impl Debug for WeakObjValue {198	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {199		f.debug_tuple("WeakObjValue").finish()200	}201}202203impl PartialEq for WeakObjValue {204	fn eq(&self, other: &Self) -> bool {205		Weak::ptr_eq(&self.0, &other.0)206	}207}208209impl Eq for WeakObjValue {}210impl Hash for WeakObjValue {211	fn hash<H: Hasher>(&self, hasher: &mut H) {212		// Safety: usize is POD213		let addr = unsafe { *std::ptr::addr_of!(self.0).cast() };214		hasher.write_usize(addr);215	}216}217218cc_dyn!(219	#[derive(Clone, Debug)]220	ObjCore, ObjectCore,221	pub fn new() {...}222);223#[derive(Trace, Educe)]224#[educe(Debug)]225struct ObjValueInner {226	cores: Vec<ObjCore>,227	assertions_ran: Cell<bool>,228	value_cache: RefCell<FxHashMap<(IStr, CoreIdx), CacheValue>>,229}230231thread_local! {232	static RUNNING_ASSERTIONS: RefCell<FxHashSet<ObjValue>> = RefCell::default();233}234fn is_asserting(obj: &ObjValue) -> bool {235	RUNNING_ASSERTIONS.with_borrow(|v| v.contains(obj))236}237/// Returns false if already asserting238fn start_asserting(obj: &ObjValue) -> bool {239	RUNNING_ASSERTIONS.with_borrow_mut(|v| v.insert(obj.clone()))240}241fn finish_asserting(obj: &ObjValue) {242	RUNNING_ASSERTIONS.with_borrow_mut(|v| {243		let r = v.remove(obj);244		debug_assert!(245			r,246			"finish_asserting was called before start_asserting or twice"247		);248	});249}250251#[allow(clippy::module_name_repetitions)]252#[derive(Clone, Trace, Debug, Educe)]253#[educe(PartialEq, Hash, Eq)]254pub struct ObjValue(255	#[educe(PartialEq(method(Cc::ptr_eq)), Hash(method(identity_hash)))] Cc<ObjValueInner>,256);257258#[derive(Trace, Debug)]259struct StandaloneSuperCore {260	sup: CoreIdx,261	this: ObjValue,262}263impl ObjectCore for StandaloneSuperCore {264	fn enum_fields_core(265		&self,266		super_depth: &mut SuperDepth,267		handler: &mut EnumFieldsHandler<'_>,268	) -> bool {269		self.this270			.enum_fields_internal(super_depth, handler, self.sup)271	}272273	fn has_field_include_hidden(&self, name: IStr) -> bool {274		self.this.has_field_include_hidden_idx(name, self.sup)275	}276277	fn get_for(&self, key: IStr, _sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>> {278		let v = self.this.get_idx(key, self.sup)?;279		Ok(v.map(|v| (v, ValueProcess::None)))280	}281282	fn field_visibility(&self, field: IStr) -> Option<Visibility> {283		self.this.field_visibility_idx(field, self.sup)284	}285286	fn run_assertions_raw(&self, _sup_this: SupThis) -> Result<()> {287		self.this.run_assertions()288	}289}290291#[derive(Debug, Trace)]292struct EmptyObject;293impl ObjectCore for EmptyObject {294	fn enum_fields_core(295		&self,296		_super_depth: &mut SuperDepth,297		_handler: &mut EnumFieldsHandler<'_>,298	) -> bool {299		true300	}301302	fn has_field_include_hidden(&self, _name: IStr) -> bool {303		false304	}305306	fn get_for(&self, _key: IStr, _sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>> {307		Ok(None)308	}309310	fn run_assertions_raw(&self, _sup_this: SupThis) -> Result<()> {311		Ok(())312	}313314	fn field_visibility(&self, _field: IStr) -> Option<Visibility> {315		None316	}317}318319#[derive(Hash, PartialEq, Eq, Trace, Clone, Copy, Debug)]320struct CoreIdx {321	idx: usize,322}323impl CoreIdx {324	fn super_exists(self) -> bool {325		self.idx != 0326	}327}328#[derive(Trace, Clone, PartialEq, Eq, Hash, Debug)]329pub struct SupThis {330	sup: CoreIdx,331	this: ObjValue,332}333impl SupThis {334	pub fn has_super(&self) -> bool {335		self.sup.super_exists()336	}337	/// Implementation of `"field" in super` operation,338	/// works faster than standalone super path.339	///340	/// In case of no `super` existence, returns false.341	pub fn field_in_super(&self, field: IStr) -> bool {342		self.this.has_field_include_hidden_idx(field, self.sup)343	}344	/// Implementation of `super.field` operation,345	/// works faster than standalone super path.346	///347	/// In case of no `super` existence, returns `NoSuperFound`348	pub fn get_super(&self, field: IStr) -> Result<Option<Val>> {349		if !self.sup.super_exists() {350			bail!(NoSuperFound);351		}352		self.this.get_idx(field, self.sup)353	}354	/// `super` with `self` overriden for top-level lookups.355	/// Exists when super appears outside of `super.field`/`"field" in super` expressions356	/// Exclusive to jrsonnet.357	///358	/// Might return `NoSuperFound` error.359	pub fn standalone_super(&self) -> Result<ObjValue> {360		if !self.sup.super_exists() {361			bail!(NoSuperFound)362		}363		Ok(ObjValue::new(StandaloneSuperCore {364			sup: self.sup,365			this: self.this.clone(),366		}))367	}368	pub fn this(&self) -> &ObjValue {369		&self.this370	}371	pub fn downgrade(self) -> WeakSupThis {372		WeakSupThis {373			sup: self.sup,374			this: self.this.downgrade(),375		}376	}377}378#[derive(Trace, PartialEq, Eq, Hash, Debug)]379pub struct WeakSupThis {380	sup: CoreIdx,381	this: WeakObjValue,382}383384impl ObjValue {385	pub fn new(v: impl ObjectCore) -> Self {386		Self(Cc::new(ObjValueInner {387			cores: vec![ObjCore::new(v)],388			assertions_ran: Cell::new(false),389			value_cache: RefCell::new(FxHashMap::new()),390		}))391	}392	pub fn new_empty() -> Self {393		Self::new(EmptyObject)394	}395	pub fn builder() -> ObjValueBuilder {396		ObjValueBuilder::new()397	}398	pub fn builder_with_capacity(capacity: usize) -> ObjValueBuilder {399		ObjValueBuilder::with_capacity(capacity)400	}401	pub(crate) fn extend_with_raw_member(self, key: IStr, value: ObjMember) -> Self {402		let mut out = ObjValueBuilder::with_capacity(1);403		out.with_super(self);404		let mut member = out.field(key);405		if value.flags.add() {406			member = member.add();407		}408		if let Some(loc) = value.location {409			member = member.with_location(loc);410		}411		let _ = member412			.with_visibility(value.flags.visibility())413			.binding(value.invoke);414		out.build()415	}416	pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder<'_>> {417		ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())418	}419420	#[must_use]421	pub fn extend_from(&self, sup: Self) -> Self {422		let mut cores = sup.0.cores.clone();423		cores.extend(self.0.cores.iter().cloned());424		ObjValue(Cc::new(ObjValueInner {425			cores,426			value_cache: RefCell::default(),427			assertions_ran: Cell::new(false),428		}))429	}430	// #[must_use]431	// pub fn with_this(&self, this: Self) -> Self {432	// 	self.0.with_this(self.clone(), this)433	// }434	/// Returns amount of visible object fields435	/// If object only contains hidden fields - may return zero.436	pub fn len(&self) -> usize {437		self.fields_visibility()438			.iter()439			.filter(|(_, (visible, _))| *visible)440			.count()441	}442	pub fn is_empty(&self) -> bool {443		self.len() == 0444	}445	/// For each field, calls callback.446	/// If callback returns false - ends iteration prematurely.447	///448	/// Returns false if ended prematurely449	pub fn enum_fields(&self, handler: &mut EnumFieldsHandler<'_>) -> bool {450		let mut super_depth = SuperDepth::default();451		self.enum_fields_internal(452			&mut super_depth,453			handler,454			CoreIdx {455				idx: self.0.cores.len(),456			},457		)458	}459	fn enum_fields_internal(460		&self,461		super_depth: &mut SuperDepth,462		handler: &mut EnumFieldsHandler<'_>,463		idx: CoreIdx,464	) -> bool {465		for core in self.0.cores[..idx.idx].iter() {466			if !core.0.enum_fields_core(super_depth, handler) {467				return false;468			}469			super_depth.deepen();470		}471		true472	}473474	pub fn has_field_include_hidden(&self, name: IStr) -> bool {475		self.has_field_include_hidden_idx(476			name,477			CoreIdx {478				idx: self.0.cores.len(),479			},480		)481	}482	fn has_field_include_hidden_idx(&self, name: IStr, core: CoreIdx) -> bool {483		self.0.cores[..core.idx]484			.iter()485			.rev()486			.any(|v| v.0.has_field_include_hidden(name.clone()))487	}488	pub fn has_field(&self, name: IStr) -> bool {489		match self.field_visibility(name) {490			Some(Visibility::Unhide | Visibility::Normal) => true,491			Some(Visibility::Hidden) | None => false,492		}493	}494	pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {495		if include_hidden {496			self.has_field_include_hidden(name)497		} else {498			self.has_field(name)499		}500	}501	pub fn get(&self, key: IStr) -> Result<Option<Val>> {502		self.get_idx(503			key,504			CoreIdx {505				idx: self.0.cores.len(),506			},507		)508	}509510	fn get_idx(&self, key: IStr, core: CoreIdx) -> Result<Option<Val>> {511		let cache_key = (key.clone(), core);512		{513			let mut cache = self.0.value_cache.borrow_mut();514			// entry_ref candidate?515			match cache.entry(cache_key.clone()) {516				Entry::Occupied(v) => match v.get() {517					CacheValue::Cached(v) => return v.clone(),518					CacheValue::Pending => {519						if !is_asserting(self) {520							bail!(InfiniteRecursionDetected);521						}522					}523				},524				Entry::Vacant(v) => {525					v.insert(CacheValue::Pending);526				}527			};528		}529		let result = self.get_idx_uncached(key, core);530		{531			let mut cache = self.0.value_cache.borrow_mut();532			cache.insert(cache_key, CacheValue::Cached(result.clone()));533		}534		result535	}536	fn get_idx_uncached(&self, key: IStr, core: CoreIdx) -> Result<Option<Val>> {537		self.run_assertions()?;538		let mut add_stack = Vec::with_capacity(2);539		for (sup, core) in self.0.cores[..core.idx].iter().enumerate().rev() {540			let sup_this = SupThis {541				sup: CoreIdx { idx: sup },542				this: self.clone(),543			};544			if let Some((val, proc)) = core.0.get_for(key.clone(), sup_this)? {545				match proc {546					ValueProcess::None if add_stack.is_empty() => return Ok(Some(val)),547					ValueProcess::None => {548						add_stack.push(val);549						break;550					}551					ValueProcess::SuperPlus => {552						add_stack.push(val);553					}554				}555			}556		}557		if add_stack.is_empty() {558			// None of layers had this field559			return Ok(None);560		} else if add_stack.len() == 1 {561			// A layer had this field, but it wanted this field to be added with super.562			// However, no super had this field, fail-safe563			return Ok(Some(add_stack.pop().expect("single element on stack")));564		}565		let mut values = add_stack.into_iter().rev();566		let init = values.next().expect("at least 2 elements");567568		values569			.try_fold(init, |a, b| evaluate_add_op(&a, &b))570			.map(Some)571572		// self.0.get_raw(key, this)573	}574575	pub fn get_or_bail(&self, key: IStr) -> Result<Val> {576		let Some(value) = self.get(key.clone())? else {577			let suggestions = suggest_object_fields(self, key.clone());578			bail!(NoSuchField(key, suggestions))579		};580		Ok(value)581	}582583	fn field_visibility(&self, field: IStr) -> Option<Visibility> {584		self.field_visibility_idx(585			field,586			CoreIdx {587				idx: self.0.cores.len(),588			},589		)590	}591	fn field_visibility_idx(&self, field: IStr, core: CoreIdx) -> Option<Visibility> {592		let mut exists = false;593		for ele in self.0.cores[..core.idx].iter().rev() {594			let vis = ele.0.field_visibility(field.clone());595			match vis {596				Some(Visibility::Unhide | Visibility::Hidden) => return vis,597				Some(Visibility::Normal) => exists = true,598				None => {}599			}600		}601		exists.then_some(Visibility::Normal)602	}603604	pub fn run_assertions(&self) -> Result<()> {605		if self.0.assertions_ran.get() {606			return Ok(());607		}608		if !start_asserting(self) {609			return Ok(());610		}611		for (idx, ele) in self.0.cores.iter().enumerate() {612			let sup_this = SupThis {613				sup: CoreIdx { idx },614				this: self.clone(),615			};616			ele.0.run_assertions_raw(sup_this).inspect_err(|_e| {617				finish_asserting(self);618			})?;619		}620		finish_asserting(self);621		self.0.assertions_ran.set(true);622		Ok(())623	}624625	pub fn iter(626		&self,627		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,628	) -> impl Iterator<Item = (IStr, Result<Val>)> + '_ {629		let fields = self.fields(630			#[cfg(feature = "exp-preserve-order")]631			preserve_order,632		);633		fields.into_iter().map(|field| {634			(635				field.clone(),636				self.get(field)637					.map(|opt| opt.expect("iterating over keys, field exists")),638			)639		})640	}641	pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {642		if !self.has_field_ex(key.clone(), true) {643			return None;644		}645		let obj = self.clone();646647		Some(Thunk!(move || Ok(obj.get(key)?.expect("field exists"))))648	}649	pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {650		let obj = self.clone();651		Thunk!(move || obj.get_or_bail(key))652	}653	pub fn ptr_eq(a: &Self, b: &Self) -> bool {654		Cc::ptr_eq(&a.0, &b.0)655	}656	pub fn downgrade(self) -> WeakObjValue {657		WeakObjValue(self.0.downgrade())658	}659	fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {660		let mut out = FxHashMap::default();661		self.enum_fields(&mut |depth, index, name, visibility| {662			dbg!(&name, visibility);663			let new_sort_key = FieldSortKey::new(depth, index);664			let entry = out.entry(name);665			let (visible, _) = entry.or_insert((true, new_sort_key));666			match visibility {667				Visibility::Normal => {}668				Visibility::Hidden => {669					*visible = false;670				}671				Visibility::Unhide => {672					*visible = true;673				}674			};675			false676		});677		out678	}679	pub fn fields_ex(680		&self,681		include_hidden: bool,682		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,683	) -> Vec<IStr> {684		#[cfg(feature = "exp-preserve-order")]685		if preserve_order {686			let (mut fields, mut keys): (Vec<_>, Vec<_>) = self687				.fields_visibility()688				.into_iter()689				.filter(|(_, (visible, _))| include_hidden || *visible)690				.enumerate()691				.map(|(idx, (k, (_, sk)))| (k, (sk, idx)))692				.unzip();693			keys.sort_unstable_by_key(|v| v.0);694			// Reorder in-place by resulting indexes695			for i in 0..fields.len() {696				let x = fields[i].clone();697				let mut j = i;698				loop {699					let k = keys[j].1;700					keys[j].1 = j;701					if k == i {702						break;703					}704					fields[j] = fields[k].clone();705					j = k;706				}707				fields[j] = x;708			}709			return fields;710		}711712		let mut fields: Vec<_> = dbg!(self713			.fields_visibility())714			.into_iter()715			.filter(|(_, (visible, _))| include_hidden || *visible)716			.map(|(k, _)| k)717			.collect();718		fields.sort_unstable();719		fields720	}721	pub fn fields(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Vec<IStr> {722		self.fields_ex(723			false,724			#[cfg(feature = "exp-preserve-order")]725			preserve_order,726		)727	}728	pub fn values_ex(729		&self,730		include_hidden: bool,731		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,732	) -> ArrValue {733		ArrValue::new(PickObjectValues::new(734			self.clone(),735			self.fields_ex(736				include_hidden,737				#[cfg(feature = "exp-preserve-order")]738				preserve_order,739			),740		))741	}742	pub fn values(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> ArrValue {743		self.values_ex(744			false,745			#[cfg(feature = "exp-preserve-order")]746			preserve_order,747		)748	}749	pub fn key_values_ex(750		&self,751		include_hidden: bool,752		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,753	) -> ArrValue {754		ArrValue::new(PickObjectKeyValues::new(755			self.clone(),756			self.fields_ex(757				include_hidden,758				#[cfg(feature = "exp-preserve-order")]759				preserve_order,760			),761		))762	}763	pub fn key_values(764		&self,765		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,766	) -> ArrValue {767		self.key_values_ex(768			false,769			#[cfg(feature = "exp-preserve-order")]770			preserve_order,771		)772	}773}774775impl OopObject {776	pub fn new(777		this_entries: Cc<FxHashMap<IStr, ObjMember>>,778		assertions: Cc<Vec<CcObjectAssertion>>,779	) -> Self {780		Self {781			this_entries,782			value_cache: RefCell::new(FxHashMap::new()),783			assertions,784		}785	}786}787788impl ObjectCore for OopObject {789	fn enum_fields_core(790		&self,791		super_depth: &mut SuperDepth,792		handler: &mut EnumFieldsHandler<'_>,793	) -> bool {794		for (name, member) in self.this_entries.iter() {795			if handler(796				*super_depth,797				member.original_index,798				name.clone(),799				member.flags.visibility(),800			) {801				return false;802			}803		}804		true805	}806807	fn has_field_include_hidden(&self, name: IStr) -> bool {808		self.this_entries.contains_key(&name)809	}810811	fn get_for(&self, key: IStr, sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>> {812		match self.this_entries.get(&key) {813			Some(k) => Ok(Some((814				k.invoke.evaluate(sup_this)?,815				if k.flags.add() {816					ValueProcess::SuperPlus817				} else {818					ValueProcess::None819				},820			))),821			None => Ok(None),822		}823	}824	fn field_visibility(&self, name: IStr) -> Option<Visibility> {825		Some(self.this_entries.get(&name)?.flags.visibility())826	}827828	fn run_assertions_raw(&self, sup_this: SupThis) -> Result<()> {829		if self.assertions.is_empty() {830			return Ok(());831		}832		for assertion in self.assertions.iter() {833			assertion.0.run(sup_this.clone())?;834		}835		Ok(())836	}837}838839#[allow(clippy::module_name_repetitions)]840pub struct ObjValueBuilder {841	sup: Option<ObjValue>,842	map: FxHashMap<IStr, ObjMember>,843	assertions: Vec<CcObjectAssertion>,844	next_field_index: FieldIndex,845}846impl ObjValueBuilder {847	pub fn new() -> Self {848		Self::with_capacity(0)849	}850	pub fn with_capacity(capacity: usize) -> Self {851		Self {852			sup: None,853			map: FxHashMap::with_capacity(capacity),854			assertions: Vec::new(),855			next_field_index: FieldIndex::default(),856		}857	}858	pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self {859		self.assertions.reserve_exact(capacity);860		self861	}862	pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {863		self.sup = Some(super_obj);864		self865	}866867	pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {868		self.assertions.push(CcObjectAssertion::new(assertion));869		self870	}871	pub fn field(&mut self, name: impl Into<IStr>) -> ObjMemberBuilder<ValueBuilder<'_>> {872		let field_index = self.next_field_index;873		self.next_field_index = self.next_field_index.next();874		ObjMemberBuilder::new(ValueBuilder(self), name.into(), field_index)875	}876	/// Preset for common method definiton pattern:877	/// Create a hidden field with the function value.878	///879	/// `.field(name).hide().value(Val::function(value))`880	pub fn method(&mut self, name: impl Into<IStr>, value: impl Into<FuncVal>) -> &mut Self {881		self.field(name).hide().value(Val::Func(value.into()));882		self883	}884	pub fn try_method(885		&mut self,886		name: impl Into<IStr>,887		value: impl Into<FuncVal>,888	) -> Result<&mut Self> {889		self.field(name).hide().try_value(Val::Func(value.into()))?;890		Ok(self)891	}892893	pub fn build(self) -> ObjValue {894		if self.sup.is_none() && self.map.is_empty() && self.assertions.is_empty() {895			return ObjValue::new_empty();896		}897		let res = ObjValue::new(OopObject::new(Cc::new(self.map), Cc::new(self.assertions)));898		self.sup.map(|sup| res.extend_from(sup)).unwrap_or(res)899	}900}901impl Default for ObjValueBuilder {902	fn default() -> Self {903		Self::with_capacity(0)904	}905}906907#[allow(clippy::module_name_repetitions)]908#[must_use = "value not added unless binding() was called"]909pub struct ObjMemberBuilder<Kind> {910	kind: Kind,911	name: IStr,912	add: bool,913	visibility: Visibility,914	original_index: FieldIndex,915	location: Option<Span>,916}917918#[allow(clippy::missing_const_for_fn)]919impl<Kind> ObjMemberBuilder<Kind> {920	pub(crate) fn new(kind: Kind, name: IStr, original_index: FieldIndex) -> Self {921		Self {922			kind,923			name,924			original_index,925			add: false,926			visibility: Visibility::Normal,927			location: None,928		}929	}930931	pub const fn with_add(mut self, add: bool) -> Self {932		self.add = add;933		self934	}935	pub fn add(self) -> Self {936		self.with_add(true)937	}938	pub fn with_visibility(mut self, visibility: Visibility) -> Self {939		self.visibility = visibility;940		self941	}942	pub fn hide(self) -> Self {943		self.with_visibility(Visibility::Hidden)944	}945	pub fn with_location(mut self, location: Span) -> Self {946		self.location = Some(location);947		self948	}949	fn build_member(self, binding: MaybeUnbound) -> (Kind, IStr, ObjMember) {950		(951			self.kind,952			self.name,953			ObjMember {954				flags: ObjFieldFlags::new(self.add, self.visibility),955				original_index: self.original_index,956				invoke: binding,957				location: self.location,958			},959		)960	}961}962963pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);964impl ObjMemberBuilder<ValueBuilder<'_>> {965	/// Inserts value, replacing if it is already defined966	pub fn value(self, value: impl Into<Val>) {967		let (receiver, name, member) =968			self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value.into())));969		let entry = receiver.0.map.entry(name);970		entry.insert_entry(member);971	}972973	/// Tries to insert value, returns an error if it was already defined974	pub fn try_value(self, value: impl Into<Val>) -> Result<()> {975		self.try_thunk(Thunk::evaluated(value.into()))976	}977	pub fn try_thunk(self, value: impl Into<Thunk<Val>>) -> Result<()> {978		self.binding(MaybeUnbound::Bound(value.into()))979	}980	pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {981		self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)))982	}983	pub fn binding(self, binding: MaybeUnbound) -> Result<()> {984		let (receiver, name, member) = self.build_member(binding);985		let location = member.location.clone();986		let old = receiver.0.map.insert(name.clone(), member);987		if old.is_some() {988			in_frame(989				CallLocation(location.as_ref()),990				|| format!("field <{}> initializtion", name.clone()),991				|| bail!(DuplicateFieldName(name.clone())),992			)?;993		}994		Ok(())995	}996}997998pub struct ExtendBuilder<'v>(&'v mut ObjValue);999impl ObjMemberBuilder<ExtendBuilder<'_>> {1000	pub fn value(self, value: impl Into<Val>) {1001		self.binding(MaybeUnbound::Bound(Thunk::evaluated(value.into())));1002	}1003	pub fn bindable(self, bindable: impl Unbound<Bound = Val>) {1004		self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)));1005	}1006	pub fn binding(self, binding: MaybeUnbound) {1007		let (receiver, name, member) = self.build_member(binding);1008		let new = receiver.0.clone();1009		*receiver.0 = new.extend_with_raw_member(name, member);1010	}1011}