git.delta.rocks / jrsonnet / refs/commits / 5ac33cfd81f9

difftreelog

source

crates/jrsonnet-evaluator/src/obj.rs25.1 KiBsourcehistory
1use std::{2	any::Any,3	cell::{Cell, RefCell},4	collections::hash_map::Entry,5	fmt::{self, Debug},6	hash::{Hash, Hasher},7};89use educe::Educe;10use jrsonnet_gcmodule::{cc_dyn, Cc, Trace, Weak};11use jrsonnet_interner::IStr;12use jrsonnet_parser::{Span, Visibility};13use rustc_hash::{FxHashMap, FxHashSet};1415use crate::{16	arr::{PickObjectKeyValues, PickObjectValues},17	bail,18	error::{suggest_object_fields, ErrorKind::*},19	function::{CallLocation, FuncVal},20	gc::WithCapacityExt as _,21	identity_hash, in_frame,22	operator::evaluate_add_op,23	val::ArrValue,24	CcUnbound, MaybeUnbound, Result, Thunk, Unbound, Val,25};2627#[cfg(not(feature = "exp-preserve-order"))]28mod ordering {29	#![allow(30		// This module works as stub for preserve-order feature31		clippy::unused_self,32	)]3334	use jrsonnet_gcmodule::Trace;3536	#[derive(Clone, Copy, Default, Debug, Trace)]37	pub struct FieldIndex(());38	impl FieldIndex {39		pub const fn next(self) -> Self {40			Self(())41		}42	}4344	#[derive(Clone, Copy, Default, Debug, Trace)]45	pub struct SuperDepth(());46	impl SuperDepth {47		pub(super) fn deepen(self) {}48	}4950	#[derive(Clone, Copy, Debug)]51	pub struct FieldSortKey(());52	impl FieldSortKey {53		pub const fn new(_: SuperDepth, _: FieldIndex) -> Self {54			Self(())55		}56	}57}5859#[cfg(feature = "exp-preserve-order")]60mod ordering {61	use std::cmp::Reverse;6263	use jrsonnet_gcmodule::Trace;6465	#[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]66	pub struct FieldIndex(u32);67	impl FieldIndex {68		pub fn next(self) -> Self {69			Self(self.0 + 1)70		}71	}7273	#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Debug)]74	pub struct SuperDepth(u32);75	impl SuperDepth {76		pub(super) fn deepen(&mut self) {77			*self.0 += 178		}79	}8081	#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]82	pub struct FieldSortKey(Reverse<SuperDepth>, FieldIndex);83	impl FieldSortKey {84		pub fn new(depth: SuperDepth, index: FieldIndex) -> Self {85			Self(Reverse(depth), index)86		}87	}88}8990use ordering::{FieldIndex, FieldSortKey, SuperDepth};9192// 0 - add93//  12 - visibility94#[derive(Clone, Copy)]95pub struct ObjFieldFlags(u8);96impl ObjFieldFlags {97	fn new(add: bool, visibility: Visibility) -> Self {98		let mut v = 0;99		if add {100			v |= 1;101		}102		v |= match visibility {103			Visibility::Normal => 0b000,104			Visibility::Hidden => 0b010,105			Visibility::Unhide => 0b100,106		};107		Self(v)108	}109	pub fn add(&self) -> bool {110		self.0 & 1 != 0111	}112	pub fn visibility(&self) -> Visibility {113		match (self.0 & 0b110) >> 1 {114			0b00 => Visibility::Normal,115			0b01 => Visibility::Hidden,116			0b10 => Visibility::Unhide,117			_ => unreachable!(),118		}119	}120}121impl Debug for ObjFieldFlags {122	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {123		f.debug_struct("ObjFieldFlags")124			.field("add", &self.add())125			.field("visibility", &self.visibility())126			.finish()127	}128}129130#[allow(clippy::module_name_repetitions)]131#[derive(Debug, Trace)]132pub struct ObjMember {133	#[trace(skip)]134	flags: ObjFieldFlags,135	original_index: FieldIndex,136	pub invoke: MaybeUnbound,137	pub location: Option<Span>,138}139140cc_dyn!(CcObjectAssertion, ObjectAssertion);141pub trait ObjectAssertion: Trace {142	fn run(&self, sup_this: SupThis) -> Result<()>;143}144145// Field => This146147#[derive(Trace, Debug)]148enum CacheValue {149	Cached(Result<Option<Val>>),150	Pending,151}152153#[allow(clippy::module_name_repetitions)]154#[derive(Trace)]155#[trace(tracking(force))]156pub struct OopObject {157	// this: Option<ObjValue>,158	assertions: Cc<Vec<CcObjectAssertion>>,159	this_entries: Cc<FxHashMap<IStr, ObjMember>>,160	value_cache: RefCell<FxHashMap<(IStr, Option<WeakObjValue>), CacheValue>>,161}162impl Debug for OopObject {163	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {164		f.debug_struct("OopObject")165			// .field("assertions", &self.assertions)166			// .field("assertions_ran", &self.assertions_ran)167			.field("this_entries", &self.this_entries)168			// .field("value_cache", &self.value_cache)169			.finish_non_exhaustive()170	}171}172173type EnumFieldsHandler<'a> = dyn FnMut(SuperDepth, FieldIndex, IStr, Visibility) -> bool + 'a;174175#[derive(Trace, Clone)]176pub enum ValueProcess {177	None,178	SuperPlus,179}180181pub trait ObjectCore: Trace + Any + Debug {182	// If callback returns false, iteration stops, and this call returns false.183	fn enum_fields_core(184		&self,185		super_depth: &mut SuperDepth,186		handler: &mut EnumFieldsHandler<'_>,187	) -> bool;188189	fn has_field_include_hidden(&self, name: IStr) -> bool;190191	fn get_for(&self, key: IStr, sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>>;192	// fn get_for_uncached(&self, key: IStr, this: ObjValue) -> Result<Option<(Val, ValueProcess)>>;193	fn field_visibility(&self, field: IStr) -> Option<Visibility>;194195	fn run_assertions_raw(&self, sup_this: SupThis) -> Result<()>;196}197198#[derive(Clone, Trace)]199pub struct WeakObjValue(#[trace(skip)] Weak<ObjValueInner>);200impl Debug for WeakObjValue {201	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {202		f.debug_tuple("WeakObjValue").finish()203	}204}205206impl PartialEq for WeakObjValue {207	fn eq(&self, other: &Self) -> bool {208		Weak::ptr_eq(&self.0, &other.0)209	}210}211212impl Eq for WeakObjValue {}213impl Hash for WeakObjValue {214	fn hash<H: Hasher>(&self, hasher: &mut H) {215		// Safety: usize is POD216		let addr = unsafe { *std::ptr::addr_of!(self.0).cast() };217		hasher.write_usize(addr);218	}219}220221cc_dyn!(222	#[derive(Clone, Debug)]223	ObjCore, ObjectCore,224	pub fn new() {...}225);226#[derive(Trace, Educe)]227#[educe(Debug)]228struct ObjValueInner {229	cores: Vec<ObjCore>,230	assertions_ran: Cell<bool>,231	value_cache: RefCell<FxHashMap<(IStr, CoreIdx), CacheValue>>,232}233234thread_local! {235	static RUNNING_ASSERTIONS: RefCell<FxHashSet<ObjValue>> = RefCell::default();236}237fn is_asserting(obj: &ObjValue) -> bool {238	RUNNING_ASSERTIONS.with_borrow(|v| v.contains(obj))239}240/// Returns false if already asserting241fn start_asserting(obj: &ObjValue) -> bool {242	RUNNING_ASSERTIONS.with_borrow_mut(|v| v.insert(obj.clone()))243}244fn finish_asserting(obj: &ObjValue) {245	RUNNING_ASSERTIONS.with_borrow_mut(|v| {246		let r = v.remove(obj);247		debug_assert!(248			r,249			"finish_asserting was called before start_asserting or twice"250		);251	});252}253254#[allow(clippy::module_name_repetitions)]255#[derive(Clone, Trace, Debug, Educe)]256#[educe(PartialEq, Hash, Eq)]257pub struct ObjValue(258	#[educe(PartialEq(method(Cc::ptr_eq)), Hash(method(identity_hash)))] Cc<ObjValueInner>,259);260261#[derive(Trace, Debug)]262struct StandaloneSuperCore {263	sup: CoreIdx,264	this: ObjValue,265}266impl ObjectCore for StandaloneSuperCore {267	fn enum_fields_core(268		&self,269		super_depth: &mut SuperDepth,270		handler: &mut EnumFieldsHandler<'_>,271	) -> bool {272		self.this273			.enum_fields_internal(super_depth, handler, self.sup)274	}275276	fn has_field_include_hidden(&self, name: IStr) -> bool {277		self.this.has_field_include_hidden_idx(name, self.sup)278	}279280	fn get_for(&self, key: IStr, _sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>> {281		let v = self.this.get_idx(key, self.sup)?;282		Ok(v.map(|v| (v, ValueProcess::None)))283	}284285	fn field_visibility(&self, field: IStr) -> Option<Visibility> {286		self.this.field_visibility_idx(field, self.sup)287	}288289	fn run_assertions_raw(&self, _sup_this: SupThis) -> Result<()> {290		self.this.run_assertions()291	}292}293294#[derive(Debug, Trace)]295struct EmptyObject;296impl ObjectCore for EmptyObject {297	fn enum_fields_core(298		&self,299		_super_depth: &mut SuperDepth,300		_handler: &mut EnumFieldsHandler<'_>,301	) -> bool {302		true303	}304305	fn has_field_include_hidden(&self, _name: IStr) -> bool {306		false307	}308309	fn get_for(&self, _key: IStr, _sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>> {310		Ok(None)311	}312313	fn run_assertions_raw(&self, _sup_this: SupThis) -> Result<()> {314		Ok(())315	}316317	fn field_visibility(&self, _field: IStr) -> Option<Visibility> {318		None319	}320}321322#[derive(Hash, PartialEq, Eq, Trace, Clone, Copy, Debug)]323struct CoreIdx {324	idx: usize,325}326impl CoreIdx {327	fn super_exists(self) -> bool {328		self.idx != 0329	}330}331#[derive(Trace, Clone, PartialEq, Eq, Hash, Debug)]332pub struct SupThis {333	sup: CoreIdx,334	this: ObjValue,335}336impl SupThis {337	pub fn has_super(&self) -> bool {338		self.sup.super_exists()339	}340	/// Implementation of `"field" in super` operation,341	/// works faster than standalone super path.342	///343	/// In case of no `super` existence, returns false.344	pub fn field_in_super(&self, field: IStr) -> bool {345		self.this.has_field_include_hidden_idx(field, self.sup)346	}347	/// Implementation of `super.field` operation,348	/// works faster than standalone super path.349	///350	/// In case of no `super` existence, returns `NoSuperFound`351	pub fn get_super(&self, field: IStr) -> Result<Option<Val>> {352		if !self.sup.super_exists() {353			bail!(NoSuperFound);354		}355		self.this.get_idx(field, self.sup)356	}357	/// `super` with `self` overriden for top-level lookups.358	/// Exists when super appears outside of `super.field`/`"field" in super` expressions359	/// Exclusive to jrsonnet.360	///361	/// Might return `NoSuperFound` error.362	pub fn standalone_super(&self) -> Result<ObjValue> {363		if !self.sup.super_exists() {364			bail!(NoSuperFound)365		}366		Ok(ObjValue::new(StandaloneSuperCore {367			sup: self.sup,368			this: self.this.clone(),369		}))370	}371	pub fn this(&self) -> &ObjValue {372		&self.this373	}374	pub fn downgrade(self) -> WeakSupThis {375		WeakSupThis {376			sup: self.sup,377			this: self.this.downgrade(),378		}379	}380}381#[derive(Trace, PartialEq, Eq, Hash, Debug)]382pub struct WeakSupThis {383	sup: CoreIdx,384	this: WeakObjValue,385}386387impl ObjValue {388	pub fn new(v: impl ObjectCore) -> Self {389		Self(Cc::new(ObjValueInner {390			cores: vec![ObjCore::new(v)],391			assertions_ran: Cell::new(false),392			value_cache: RefCell::new(FxHashMap::new()),393		}))394	}395	pub fn new_empty() -> Self {396		Self::new(EmptyObject)397	}398	pub fn builder() -> ObjValueBuilder {399		ObjValueBuilder::new()400	}401	pub fn builder_with_capacity(capacity: usize) -> ObjValueBuilder {402		ObjValueBuilder::with_capacity(capacity)403	}404	pub(crate) fn extend_with_raw_member(self, key: IStr, value: ObjMember) -> Self {405		let mut out = ObjValueBuilder::with_capacity(1);406		out.with_super(self);407		let mut member = out.field(key);408		if value.flags.add() {409			member = member.add();410		}411		if let Some(loc) = value.location {412			member = member.with_location(loc);413		}414		let _ = member415			.with_visibility(value.flags.visibility())416			.binding(value.invoke);417		out.build()418	}419	pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder<'_>> {420		ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())421	}422423	#[must_use]424	pub fn extend_from(&self, sup: Self) -> Self {425		let mut cores = sup.0.cores.clone();426		cores.extend(self.0.cores.iter().cloned());427		ObjValue(Cc::new(ObjValueInner {428			cores,429			value_cache: RefCell::default(),430			assertions_ran: Cell::new(false),431		}))432	}433	// #[must_use]434	// pub fn with_this(&self, this: Self) -> Self {435	// 	self.0.with_this(self.clone(), this)436	// }437	/// Returns amount of visible object fields438	/// If object only contains hidden fields - may return zero.439	pub fn len(&self) -> usize {440		self.fields_visibility()441			.iter()442			.filter(|(_, (visible, _))| *visible)443			.count()444	}445	pub fn is_empty(&self) -> bool {446		self.len() == 0447	}448	/// For each field, calls callback.449	/// If callback returns false - ends iteration prematurely.450	///451	/// Returns false if ended prematurely452	pub fn enum_fields(&self, handler: &mut EnumFieldsHandler<'_>) -> bool {453		let mut super_depth = SuperDepth::default();454		self.enum_fields_internal(455			&mut super_depth,456			handler,457			CoreIdx {458				idx: self.0.cores.len(),459			},460		)461	}462	fn enum_fields_internal(463		&self,464		super_depth: &mut SuperDepth,465		handler: &mut EnumFieldsHandler<'_>,466		idx: CoreIdx,467	) -> bool {468		for core in self.0.cores[..idx.idx].iter() {469			if !core.0.enum_fields_core(super_depth, handler) {470				return false;471			}472			super_depth.deepen();473		}474		true475	}476477	pub fn has_field_include_hidden(&self, name: IStr) -> bool {478		self.has_field_include_hidden_idx(479			name,480			CoreIdx {481				idx: self.0.cores.len(),482			},483		)484	}485	fn has_field_include_hidden_idx(&self, name: IStr, core: CoreIdx) -> bool {486		self.0.cores[..core.idx]487			.iter()488			.rev()489			.any(|v| v.0.has_field_include_hidden(name.clone()))490	}491	pub fn has_field(&self, name: IStr) -> bool {492		match self.field_visibility(name) {493			Some(Visibility::Unhide | Visibility::Normal) => true,494			Some(Visibility::Hidden) | None => false,495		}496	}497	pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {498		if include_hidden {499			self.has_field_include_hidden(name)500		} else {501			self.has_field(name)502		}503	}504	pub fn get(&self, key: IStr) -> Result<Option<Val>> {505		self.get_idx(506			key,507			CoreIdx {508				idx: self.0.cores.len(),509			},510		)511	}512513	fn get_idx(&self, key: IStr, core: CoreIdx) -> Result<Option<Val>> {514		let cache_key = (key.clone(), core);515		{516			let mut cache = self.0.value_cache.borrow_mut();517			// entry_ref candidate?518			match cache.entry(cache_key.clone()) {519				Entry::Occupied(v) => match v.get() {520					CacheValue::Cached(v) => return v.clone(),521					CacheValue::Pending => {522						if !is_asserting(self) {523							bail!(InfiniteRecursionDetected);524						}525					}526				},527				Entry::Vacant(v) => {528					v.insert(CacheValue::Pending);529				}530			};531		}532		let result = self.get_idx_uncached(key, core);533		{534			let mut cache = self.0.value_cache.borrow_mut();535			cache.insert(cache_key, CacheValue::Cached(result.clone()));536		}537		result538	}539	fn get_idx_uncached(&self, key: IStr, core: CoreIdx) -> Result<Option<Val>> {540		self.run_assertions()?;541		let mut add_stack = Vec::with_capacity(2);542		for (sup, core) in self.0.cores[..core.idx].iter().enumerate().rev() {543			let sup_this = SupThis {544				sup: CoreIdx { idx: sup },545				this: self.clone(),546			};547			if let Some((val, proc)) = core.0.get_for(key.clone(), sup_this)? {548				match proc {549					ValueProcess::None if add_stack.is_empty() => return Ok(Some(val)),550					ValueProcess::None => {551						add_stack.push(val);552						break;553					}554					ValueProcess::SuperPlus => {555						add_stack.push(val);556					}557				}558			}559		}560		if add_stack.is_empty() {561			// None of layers had this field562			return Ok(None);563		} else if add_stack.len() == 1 {564			// A layer had this field, but it wanted this field to be added with super.565			// However, no super had this field, fail-safe566			return Ok(Some(add_stack.pop().expect("single element on stack")));567		}568		let mut values = add_stack.into_iter().rev();569		let init = values.next().expect("at least 2 elements");570571		values572			.try_fold(init, |a, b| evaluate_add_op(&a, &b))573			.map(Some)574575		// self.0.get_raw(key, this)576	}577578	pub fn get_or_bail(&self, key: IStr) -> Result<Val> {579		let Some(value) = self.get(key.clone())? else {580			let suggestions = suggest_object_fields(self, key.clone());581			bail!(NoSuchField(key, suggestions))582		};583		Ok(value)584	}585586	fn field_visibility(&self, field: IStr) -> Option<Visibility> {587		self.field_visibility_idx(588			field,589			CoreIdx {590				idx: self.0.cores.len(),591			},592		)593	}594	fn field_visibility_idx(&self, field: IStr, core: CoreIdx) -> Option<Visibility> {595		let mut exists = false;596		for ele in self.0.cores[..core.idx].iter().rev() {597			let vis = ele.0.field_visibility(field.clone());598			match vis {599				Some(Visibility::Unhide | Visibility::Hidden) => return vis,600				Some(Visibility::Normal) => exists = true,601				None => {}602			}603		}604		exists.then_some(Visibility::Normal)605	}606607	pub fn run_assertions(&self) -> Result<()> {608		if self.0.assertions_ran.get() {609			return Ok(());610		}611		if !start_asserting(self) {612			return Ok(());613		}614		for (idx, ele) in self.0.cores.iter().enumerate() {615			let sup_this = SupThis {616				sup: CoreIdx { idx },617				this: self.clone(),618			};619			ele.0.run_assertions_raw(sup_this).inspect_err(|_e| {620				finish_asserting(self);621			})?;622		}623		finish_asserting(self);624		self.0.assertions_ran.set(true);625		Ok(())626	}627628	pub fn iter(629		&self,630		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,631	) -> impl Iterator<Item = (IStr, Result<Val>)> + '_ {632		let fields = self.fields(633			#[cfg(feature = "exp-preserve-order")]634			preserve_order,635		);636		fields.into_iter().map(|field| {637			(638				field.clone(),639				self.get(field)640					.map(|opt| opt.expect("iterating over keys, field exists")),641			)642		})643	}644	pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {645		if !self.has_field_ex(key.clone(), true) {646			return None;647		}648		let obj = self.clone();649650		Some(Thunk!(move || Ok(obj.get(key)?.expect("field exists"))))651	}652	pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {653		let obj = self.clone();654		Thunk!(move || obj.get_or_bail(key))655	}656	pub fn ptr_eq(a: &Self, b: &Self) -> bool {657		Cc::ptr_eq(&a.0, &b.0)658	}659	pub fn downgrade(self) -> WeakObjValue {660		WeakObjValue(self.0.downgrade())661	}662	fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {663		let mut out = FxHashMap::default();664		self.enum_fields(&mut |depth, index, name, visibility| {665			let new_sort_key = FieldSortKey::new(depth, index);666			let entry = out.entry(name);667			let (visible, _) = entry.or_insert((true, new_sort_key));668			match visibility {669				Visibility::Normal => {}670				Visibility::Hidden => {671					*visible = false;672				}673				Visibility::Unhide => {674					*visible = true;675				}676			};677			false678		});679		out680	}681	pub fn fields_ex(682		&self,683		include_hidden: bool,684		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,685	) -> Vec<IStr> {686		#[cfg(feature = "exp-preserve-order")]687		if preserve_order {688			let (mut fields, mut keys): (Vec<_>, Vec<_>) = self689				.fields_visibility()690				.into_iter()691				.filter(|(_, (visible, _))| include_hidden || *visible)692				.enumerate()693				.map(|(idx, (k, (_, sk)))| (k, (sk, idx)))694				.unzip();695			keys.sort_unstable_by_key(|v| v.0);696			// Reorder in-place by resulting indexes697			for i in 0..fields.len() {698				let x = fields[i].clone();699				let mut j = i;700				loop {701					let k = keys[j].1;702					keys[j].1 = j;703					if k == i {704						break;705					}706					fields[j] = fields[k].clone();707					j = k;708				}709				fields[j] = x;710			}711			return fields;712		}713714		let mut fields: Vec<_> = self715			.fields_visibility()716			.into_iter()717			.filter(|(_, (visible, _))| include_hidden || *visible)718			.map(|(k, _)| k)719			.collect();720		fields.sort_unstable();721		fields722	}723	pub fn fields(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Vec<IStr> {724		self.fields_ex(725			false,726			#[cfg(feature = "exp-preserve-order")]727			preserve_order,728		)729	}730	pub fn values_ex(731		&self,732		include_hidden: bool,733		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,734	) -> ArrValue {735		ArrValue::new(PickObjectValues::new(736			self.clone(),737			self.fields_ex(738				include_hidden,739				#[cfg(feature = "exp-preserve-order")]740				preserve_order,741			),742		))743	}744	pub fn values(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> ArrValue {745		self.values_ex(746			false,747			#[cfg(feature = "exp-preserve-order")]748			preserve_order,749		)750	}751	pub fn key_values_ex(752		&self,753		include_hidden: bool,754		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,755	) -> ArrValue {756		ArrValue::new(PickObjectKeyValues::new(757			self.clone(),758			self.fields_ex(759				include_hidden,760				#[cfg(feature = "exp-preserve-order")]761				preserve_order,762			),763		))764	}765	pub fn key_values(766		&self,767		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,768	) -> ArrValue {769		self.key_values_ex(770			false,771			#[cfg(feature = "exp-preserve-order")]772			preserve_order,773		)774	}775}776777impl OopObject {778	pub fn new(779		this_entries: Cc<FxHashMap<IStr, ObjMember>>,780		assertions: Cc<Vec<CcObjectAssertion>>,781	) -> Self {782		Self {783			this_entries,784			value_cache: RefCell::new(FxHashMap::new()),785			assertions,786		}787	}788}789790impl ObjectCore for OopObject {791	fn enum_fields_core(792		&self,793		super_depth: &mut SuperDepth,794		handler: &mut EnumFieldsHandler<'_>,795	) -> bool {796		for (name, member) in self.this_entries.iter() {797			if handler(798				*super_depth,799				member.original_index,800				name.clone(),801				member.flags.visibility(),802			) {803				return false;804			}805		}806		true807	}808809	fn has_field_include_hidden(&self, name: IStr) -> bool {810		self.this_entries.contains_key(&name)811	}812813	fn get_for(&self, key: IStr, sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>> {814		match self.this_entries.get(&key) {815			Some(k) => Ok(Some((816				k.invoke.evaluate(sup_this)?,817				if k.flags.add() {818					ValueProcess::SuperPlus819				} else {820					ValueProcess::None821				},822			))),823			None => Ok(None),824		}825	}826	fn field_visibility(&self, name: IStr) -> Option<Visibility> {827		Some(self.this_entries.get(&name)?.flags.visibility())828	}829830	fn run_assertions_raw(&self, sup_this: SupThis) -> Result<()> {831		if self.assertions.is_empty() {832			return Ok(());833		}834		for assertion in self.assertions.iter() {835			assertion.0.run(sup_this.clone())?;836		}837		Ok(())838	}839}840841#[allow(clippy::module_name_repetitions)]842pub struct ObjValueBuilder {843	sup: Option<ObjValue>,844	map: FxHashMap<IStr, ObjMember>,845	assertions: Vec<CcObjectAssertion>,846	next_field_index: FieldIndex,847}848impl ObjValueBuilder {849	pub fn new() -> Self {850		Self::with_capacity(0)851	}852	pub fn with_capacity(capacity: usize) -> Self {853		Self {854			sup: None,855			map: FxHashMap::with_capacity(capacity),856			assertions: Vec::new(),857			next_field_index: FieldIndex::default(),858		}859	}860	pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self {861		self.assertions.reserve_exact(capacity);862		self863	}864	pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {865		self.sup = Some(super_obj);866		self867	}868869	pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {870		self.assertions.push(CcObjectAssertion::new(assertion));871		self872	}873	pub fn field(&mut self, name: impl Into<IStr>) -> ObjMemberBuilder<ValueBuilder<'_>> {874		let field_index = self.next_field_index;875		self.next_field_index = self.next_field_index.next();876		ObjMemberBuilder::new(ValueBuilder(self), name.into(), field_index)877	}878	/// Preset for common method definiton pattern:879	/// Create a hidden field with the function value.880	///881	/// `.field(name).hide().value(Val::function(value))`882	pub fn method(&mut self, name: impl Into<IStr>, value: impl Into<FuncVal>) -> &mut Self {883		self.field(name).hide().value(Val::Func(value.into()));884		self885	}886	pub fn try_method(887		&mut self,888		name: impl Into<IStr>,889		value: impl Into<FuncVal>,890	) -> Result<&mut Self> {891		self.field(name).hide().try_value(Val::Func(value.into()))?;892		Ok(self)893	}894895	pub fn build(self) -> ObjValue {896		if self.sup.is_none() && self.map.is_empty() && self.assertions.is_empty() {897			return ObjValue::new_empty();898		}899		let res = ObjValue::new(OopObject::new(Cc::new(self.map), Cc::new(self.assertions)));900		self.sup.map(|sup| res.extend_from(sup)).unwrap_or(res)901	}902}903impl Default for ObjValueBuilder {904	fn default() -> Self {905		Self::with_capacity(0)906	}907}908909#[allow(clippy::module_name_repetitions)]910#[must_use = "value not added unless binding() was called"]911pub struct ObjMemberBuilder<Kind> {912	kind: Kind,913	name: IStr,914	add: bool,915	visibility: Visibility,916	original_index: FieldIndex,917	location: Option<Span>,918}919920#[allow(clippy::missing_const_for_fn)]921impl<Kind> ObjMemberBuilder<Kind> {922	pub(crate) fn new(kind: Kind, name: IStr, original_index: FieldIndex) -> Self {923		Self {924			kind,925			name,926			original_index,927			add: false,928			visibility: Visibility::Normal,929			location: None,930		}931	}932933	pub const fn with_add(mut self, add: bool) -> Self {934		self.add = add;935		self936	}937	pub fn add(self) -> Self {938		self.with_add(true)939	}940	pub fn with_visibility(mut self, visibility: Visibility) -> Self {941		self.visibility = visibility;942		self943	}944	pub fn hide(self) -> Self {945		self.with_visibility(Visibility::Hidden)946	}947	pub fn with_location(mut self, location: Span) -> Self {948		self.location = Some(location);949		self950	}951	fn build_member(self, binding: MaybeUnbound) -> (Kind, IStr, ObjMember) {952		(953			self.kind,954			self.name,955			ObjMember {956				flags: ObjFieldFlags::new(self.add, self.visibility),957				original_index: self.original_index,958				invoke: binding,959				location: self.location,960			},961		)962	}963}964965pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);966impl ObjMemberBuilder<ValueBuilder<'_>> {967	/// Inserts value, replacing if it is already defined968	pub fn value(self, value: impl Into<Val>) {969		let (receiver, name, member) =970			self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value.into())));971		let entry = receiver.0.map.entry(name);972		entry.insert_entry(member);973	}974975	/// Tries to insert value, returns an error if it was already defined976	pub fn try_value(self, value: impl Into<Val>) -> Result<()> {977		self.try_thunk(Thunk::evaluated(value.into()))978	}979	pub fn try_thunk(self, value: impl Into<Thunk<Val>>) -> Result<()> {980		self.binding(MaybeUnbound::Bound(value.into()))981	}982	pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {983		self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)))984	}985	pub fn binding(self, binding: MaybeUnbound) -> Result<()> {986		let (receiver, name, member) = self.build_member(binding);987		let location = member.location.clone();988		let old = receiver.0.map.insert(name.clone(), member);989		if old.is_some() {990			in_frame(991				CallLocation(location.as_ref()),992				|| format!("field <{}> initializtion", name.clone()),993				|| bail!(DuplicateFieldName(name.clone())),994			)?;995		}996		Ok(())997	}998}9991000pub struct ExtendBuilder<'v>(&'v mut ObjValue);1001impl ObjMemberBuilder<ExtendBuilder<'_>> {1002	pub fn value(self, value: impl Into<Val>) {1003		self.binding(MaybeUnbound::Bound(Thunk::evaluated(value.into())));1004	}1005	pub fn bindable(self, bindable: impl Unbound<Bound = Val>) {1006		self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)));1007	}1008	pub fn binding(self, binding: MaybeUnbound) {1009		let (receiver, name, member) = self.build_member(binding);1010		let new = receiver.0.clone();1011		*receiver.0 = new.extend_with_raw_member(name, member);1012	}1013}