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

difftreelog

source

crates/jrsonnet-evaluator/src/obj.rs23.9 KiBsourcehistory
1use std::{2	any::Any,3	cell::RefCell,4	fmt::Debug,5	hash::{Hash, Hasher},6	ptr::addr_of,7};89use jrsonnet_gcmodule::{Cc, Trace, Weak};10use jrsonnet_interner::IStr;11use jrsonnet_parser::{Span, Visibility};12use rustc_hash::FxHashMap;1314use crate::{15	arr::{PickObjectKeyValues, PickObjectValues},16	bail,17	error::{suggest_object_fields, Error, ErrorKind::*},18	function::{CallLocation, FuncVal},19	gc::{GcHashMap, GcHashSet, TraceBox},20	in_frame,21	operator::evaluate_add_op,22	tb,23	val::ArrValue,24	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 const fn deeper(self) -> Self {48			Self(())49		}50	}5152	#[derive(Clone, Copy)]53	pub struct FieldSortKey(());54	impl FieldSortKey {55		pub const fn new(_: SuperDepth, _: FieldIndex) -> Self {56			Self(())57		}58	}59}6061#[cfg(feature = "exp-preserve-order")]62mod ordering {63	use std::cmp::Reverse;6465	use jrsonnet_gcmodule::Trace;6667	#[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]68	pub struct FieldIndex(u32);69	impl FieldIndex {70		pub fn next(self) -> Self {71			Self(self.0 + 1)72		}73	}7475	#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Debug)]76	pub struct SuperDepth(u32);77	impl SuperDepth {78		pub fn deeper(self) -> Self {79			Self(self.0 + 1)80		}81	}8283	#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]84	pub struct FieldSortKey(Reverse<SuperDepth>, FieldIndex);85	impl FieldSortKey {86		pub fn new(depth: SuperDepth, index: FieldIndex) -> Self {87			Self(Reverse(depth), index)88		}89	}90}9192use ordering::{FieldIndex, FieldSortKey, SuperDepth};9394// 0 - add95//  12 - visibility96#[derive(Clone, Copy)]97pub struct ObjFieldFlags(u8);98impl ObjFieldFlags {99	fn new(add: bool, visibility: Visibility) -> Self {100		let mut v = 0;101		if add {102			v |= 1;103		}104		v |= match visibility {105			Visibility::Normal => 0b000,106			Visibility::Hidden => 0b010,107			Visibility::Unhide => 0b100,108		};109		Self(v)110	}111	pub fn add(&self) -> bool {112		self.0 & 1 != 0113	}114	pub fn visibility(&self) -> Visibility {115		match (self.0 & 0b110) >> 1 {116			0b00 => Visibility::Normal,117			0b01 => Visibility::Hidden,118			0b10 => Visibility::Unhide,119			_ => unreachable!(),120		}121	}122}123impl Debug for ObjFieldFlags {124	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {125		f.debug_struct("ObjFieldFlags")126			.field("add", &self.add())127			.field("visibility", &self.visibility())128			.finish()129	}130}131132#[allow(clippy::module_name_repetitions)]133#[derive(Debug, Trace)]134pub struct ObjMember {135	#[trace(skip)]136	flags: ObjFieldFlags,137	original_index: FieldIndex,138	pub invoke: MaybeUnbound,139	pub location: Option<Span>,140}141142pub trait ObjectAssertion: Trace {143	fn run(&self, super_obj: Option<ObjValue>, this: Option<ObjValue>) -> Result<()>;144}145146// Field => This147148#[derive(Trace)]149enum CacheValue {150	Cached(Val),151	NotFound,152	Pending,153	Errored(Error),154}155156#[allow(clippy::module_name_repetitions)]157#[derive(Trace)]158#[trace(tracking(force))]159pub struct OopObject {160	sup: Option<ObjValue>,161	// this: Option<ObjValue>,162	assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,163	assertions_ran: RefCell<GcHashSet<ObjValue>>,164	this_entries: Cc<GcHashMap<IStr, ObjMember>>,165	value_cache: RefCell<GcHashMap<(IStr, Option<WeakObjValue>), CacheValue>>,166}167impl Debug for OopObject {168	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {169		f.debug_struct("OopObject")170			.field("sup", &self.sup)171			// .field("assertions", &self.assertions)172			// .field("assertions_ran", &self.assertions_ran)173			.field("this_entries", &self.this_entries)174			// .field("value_cache", &self.value_cache)175			.finish_non_exhaustive()176	}177}178179type EnumFieldsHandler<'a> = dyn FnMut(SuperDepth, FieldIndex, IStr, Visibility) -> bool + 'a;180181pub trait ObjectLike: Trace + Any + Debug {182	fn extend_from(&self, sup: ObjValue) -> ObjValue;183	/// When using standalone super in object, `this.super_obj.with_this(this)` is executed184	fn with_this(&self, me: ObjValue, this: ObjValue) -> ObjValue {185		ObjValue::new(ThisOverride { inner: me, this })186	}187	fn this(&self) -> Option<ObjValue> {188		None189	}190	fn len(&self) -> usize;191	fn is_empty(&self) -> bool;192	// If callback returns false, iteration stops193	fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool;194195	fn has_field_include_hidden(&self, name: IStr) -> bool;196	fn has_field(&self, name: IStr) -> bool;197198	fn get_for(&self, key: IStr, this: ObjValue) -> Result<Option<Val>>;199	fn get_for_uncached(&self, key: IStr, this: ObjValue) -> Result<Option<Val>>;200	fn field_visibility(&self, field: IStr) -> Option<Visibility>;201202	fn run_assertions_raw(&self, this: ObjValue) -> Result<()>;203}204205#[derive(Clone, Trace)]206pub struct WeakObjValue(#[trace(skip)] pub(crate) Weak<TraceBox<dyn ObjectLike>>);207208impl PartialEq for WeakObjValue {209	fn eq(&self, other: &Self) -> bool {210		Weak::ptr_eq(&self.0, &other.0)211	}212}213214impl Eq for WeakObjValue {}215impl Hash for WeakObjValue {216	fn hash<H: Hasher>(&self, hasher: &mut H) {217		// Safety: usize is POD218		let addr = unsafe { *std::ptr::addr_of!(self.0).cast() };219		hasher.write_usize(addr);220	}221}222223#[allow(clippy::module_name_repetitions)]224#[derive(Clone, Trace, Debug)]225pub struct ObjValue(pub(crate) Cc<TraceBox<dyn ObjectLike>>);226227#[derive(Debug, Trace)]228struct EmptyObject;229impl ObjectLike for EmptyObject {230	fn extend_from(&self, sup: ObjValue) -> ObjValue {231		// obj + {} == obj232		sup233	}234235	fn this(&self) -> Option<ObjValue> {236		None237	}238239	fn len(&self) -> usize {240		0241	}242243	fn is_empty(&self) -> bool {244		true245	}246247	fn enum_fields(&self, _depth: SuperDepth, _handler: &mut EnumFieldsHandler<'_>) -> bool {248		false249	}250251	fn has_field_include_hidden(&self, _name: IStr) -> bool {252		false253	}254255	fn has_field(&self, _name: IStr) -> bool {256		false257	}258259	fn get_for(&self, _key: IStr, _this: ObjValue) -> Result<Option<Val>> {260		Ok(None)261	}262	fn get_for_uncached(&self, _key: IStr, _this: ObjValue) -> Result<Option<Val>> {263		Ok(None)264	}265266	fn run_assertions_raw(&self, _this: ObjValue) -> Result<()> {267		Ok(())268	}269270	fn field_visibility(&self, _field: IStr) -> Option<Visibility> {271		None272	}273}274275#[derive(Trace, Debug)]276struct ThisOverride {277	inner: ObjValue,278	this: ObjValue,279}280impl ObjectLike for ThisOverride {281	fn with_this(&self, _me: ObjValue, this: ObjValue) -> ObjValue {282		ObjValue::new(Self {283			inner: self.inner.clone(),284			this,285		})286	}287288	fn extend_from(&self, sup: ObjValue) -> ObjValue {289		self.inner.extend_from(sup).with_this(self.this.clone())290	}291292	fn this(&self) -> Option<ObjValue> {293		Some(self.this.clone())294	}295296	fn len(&self) -> usize {297		self.inner.len()298	}299300	fn is_empty(&self) -> bool {301		self.inner.is_empty()302	}303304	fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool {305		self.inner.enum_fields(depth, handler)306	}307308	fn has_field_include_hidden(&self, name: IStr) -> bool {309		self.inner.has_field_include_hidden(name)310	}311312	fn has_field(&self, name: IStr) -> bool {313		self.inner.has_field(name)314	}315316	fn get_for(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {317		self.inner.get_for(key, this)318	}319320	fn get_for_uncached(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {321		self.inner.get_raw(key, this)322	}323324	fn field_visibility(&self, field: IStr) -> Option<Visibility> {325		self.inner.field_visibility(field)326	}327328	fn run_assertions_raw(&self, this: ObjValue) -> Result<()> {329		self.inner.run_assertions_raw(this)330	}331}332333impl ObjValue {334	pub fn new(v: impl ObjectLike) -> Self {335		Self(Cc::new(tb!(v)))336	}337	pub fn new_empty() -> Self {338		Self::new(EmptyObject)339	}340	pub fn builder() -> ObjValueBuilder {341		ObjValueBuilder::new()342	}343	pub fn builder_with_capacity(capacity: usize) -> ObjValueBuilder {344		ObjValueBuilder::with_capacity(capacity)345	}346	pub(crate) fn extend_with_raw_member(self, key: IStr, value: ObjMember) -> Self {347		let mut out = ObjValueBuilder::with_capacity(1);348		out.with_super(self);349		let mut member = out.field(key);350		if value.flags.add() {351			member = member.add();352		}353		if let Some(loc) = value.location {354			member = member.with_location(loc);355		}356		let _ = member357			.with_visibility(value.flags.visibility())358			.binding(value.invoke);359		out.build()360	}361	pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder<'_>> {362		ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())363	}364365	#[must_use]366	pub fn extend_from(&self, sup: Self) -> Self {367		self.0.extend_from(sup)368	}369	#[must_use]370	pub fn with_this(&self, this: Self) -> Self {371		self.0.with_this(self.clone(), this)372	}373	pub fn len(&self) -> usize {374		self.0.len()375	}376	pub fn is_empty(&self) -> bool {377		self.0.is_empty()378	}379	pub fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool {380		self.0.enum_fields(depth, handler)381	}382383	pub fn has_field_include_hidden(&self, name: IStr) -> bool {384		self.0.has_field_include_hidden(name)385	}386	pub fn has_field(&self, name: IStr) -> bool {387		self.0.has_field(name)388	}389	pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {390		if include_hidden {391			self.has_field_include_hidden(name)392		} else {393			self.has_field(name)394		}395	}396397	pub fn get(&self, key: IStr) -> Result<Option<Val>> {398		self.run_assertions()?;399		self.get_for(key, self.0.this().unwrap_or_else(|| self.clone()))400	}401402	pub fn get_for(&self, key: IStr, this: Self) -> Result<Option<Val>> {403		self.0.get_for(key, this)404	}405406	pub fn get_or_bail(&self, key: IStr) -> Result<Val> {407		let Some(value) = self.get(key.clone())? else {408			let suggestions = suggest_object_fields(self, key.clone());409			bail!(NoSuchField(key, suggestions))410		};411		Ok(value)412	}413414	fn get_raw(&self, key: IStr, this: Self) -> Result<Option<Val>> {415		self.0.get_for_uncached(key, this)416	}417418	fn field_visibility(&self, field: IStr) -> Option<Visibility> {419		self.0.field_visibility(field)420	}421422	pub fn run_assertions(&self) -> Result<()> {423		// FIXME: Should it use `self.0.this()` in case of standalone super?424		self.run_assertions_raw(self.clone())425	}426	fn run_assertions_raw(&self, this: Self) -> Result<()> {427		self.0.run_assertions_raw(this)428	}429430	pub fn iter(431		&self,432		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,433	) -> impl Iterator<Item = (IStr, Result<Val>)> + '_ {434		let fields = self.fields(435			#[cfg(feature = "exp-preserve-order")]436			preserve_order,437		);438		fields.into_iter().map(|field| {439			(440				field.clone(),441				self.get(field)442					.map(|opt| opt.expect("iterating over keys, field exists")),443			)444		})445	}446	pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {447		if !self.has_field_ex(key.clone(), true) {448			return None;449		}450		let obj = self.clone();451452		Some(Thunk!(move || Ok(obj.get(key)?.expect("field exists"))))453	}454	pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {455		let obj = self.clone();456		Thunk!(move || obj.get_or_bail(key))457	}458	pub fn ptr_eq(a: &Self, b: &Self) -> bool {459		Cc::ptr_eq(&a.0, &b.0)460	}461	pub fn downgrade(self) -> WeakObjValue {462		WeakObjValue(self.0.downgrade())463	}464	fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {465		let mut out = FxHashMap::default();466		self.enum_fields(467			SuperDepth::default(),468			&mut |depth, index, name, visibility| {469				let new_sort_key = FieldSortKey::new(depth, index);470				let entry = out.entry(name);471				let (visible, _) = entry.or_insert((true, new_sort_key));472				match visibility {473					Visibility::Normal => {}474					Visibility::Hidden => {475						*visible = false;476					}477					Visibility::Unhide => {478						*visible = true;479					}480				};481				false482			},483		);484		out485	}486	pub fn fields_ex(487		&self,488		include_hidden: bool,489		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,490	) -> Vec<IStr> {491		#[cfg(feature = "exp-preserve-order")]492		if preserve_order {493			let (mut fields, mut keys): (Vec<_>, Vec<_>) = self494				.fields_visibility()495				.into_iter()496				.filter(|(_, (visible, _))| include_hidden || *visible)497				.enumerate()498				.map(|(idx, (k, (_, sk)))| (k, (sk, idx)))499				.unzip();500			keys.sort_unstable_by_key(|v| v.0);501			// Reorder in-place by resulting indexes502			for i in 0..fields.len() {503				let x = fields[i].clone();504				let mut j = i;505				loop {506					let k = keys[j].1;507					keys[j].1 = j;508					if k == i {509						break;510					}511					fields[j] = fields[k].clone();512					j = k;513				}514				fields[j] = x;515			}516			return fields;517		}518519		let mut fields: Vec<_> = self520			.fields_visibility()521			.into_iter()522			.filter(|(_, (visible, _))| include_hidden || *visible)523			.map(|(k, _)| k)524			.collect();525		fields.sort_unstable();526		fields527	}528	pub fn fields(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Vec<IStr> {529		self.fields_ex(530			false,531			#[cfg(feature = "exp-preserve-order")]532			preserve_order,533		)534	}535	pub fn values_ex(536		&self,537		include_hidden: bool,538		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,539	) -> ArrValue {540		ArrValue::new(PickObjectValues::new(541			self.clone(),542			self.fields_ex(543				include_hidden,544				#[cfg(feature = "exp-preserve-order")]545				preserve_order,546			),547		))548	}549	pub fn values(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> ArrValue {550		self.values_ex(551			false,552			#[cfg(feature = "exp-preserve-order")]553			preserve_order,554		)555	}556	pub fn key_values_ex(557		&self,558		include_hidden: bool,559		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,560	) -> ArrValue {561		ArrValue::new(PickObjectKeyValues::new(562			self.clone(),563			self.fields_ex(564				include_hidden,565				#[cfg(feature = "exp-preserve-order")]566				preserve_order,567			),568		))569	}570	pub fn key_values(571		&self,572		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,573	) -> ArrValue {574		self.key_values_ex(575			false,576			#[cfg(feature = "exp-preserve-order")]577			preserve_order,578		)579	}580}581582impl OopObject {583	pub fn new(584		sup: Option<ObjValue>,585		this_entries: Cc<GcHashMap<IStr, ObjMember>>,586		assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,587	) -> Self {588		Self {589			sup,590			// this: None,591			assertions,592			assertions_ran: RefCell::new(GcHashSet::new()),593			this_entries,594			value_cache: RefCell::new(GcHashMap::new()),595		}596	}597598	fn evaluate_this(&self, v: &ObjMember, real_this: ObjValue) -> Result<Val> {599		v.invoke.evaluate(self.sup.clone(), Some(real_this))600	}601602	// FIXME: Duplication between ObjValue and OopObject603	fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {604		let mut out = FxHashMap::default();605		self.enum_fields(606			SuperDepth::default(),607			&mut |depth, index, name, visibility| {608				let new_sort_key = FieldSortKey::new(depth, index);609				let entry = out.entry(name);610				let (visible, _) = entry.or_insert((true, new_sort_key));611				match visibility {612					Visibility::Normal => {}613					Visibility::Hidden => {614						*visible = false;615					}616					Visibility::Unhide => {617						*visible = true;618					}619				};620				false621			},622		);623		out624	}625}626627impl ObjectLike for OopObject {628	fn extend_from(&self, sup: ObjValue) -> ObjValue {629		ObjValue::new(match &self.sup {630			None => Self::new(631				Some(sup),632				self.this_entries.clone(),633				self.assertions.clone(),634			),635			Some(v) => Self::new(636				Some(v.extend_from(sup)),637				self.this_entries.clone(),638				self.assertions.clone(),639			),640		})641	}642643	fn len(&self) -> usize {644		// Maybe it will be better to not compute sort key here?645		self.fields_visibility()646			.into_iter()647			.filter(|(_, (visible, _))| *visible)648			.count()649	}650651	/// Returns false only if there is any visible entry.652	///653	/// Note that object with hidden fields `{a:: 1}` will be reported as empty here.654	fn is_empty(&self) -> bool {655		self.len() == 0656	}657658	/// Run callback for every field found in object659	///660	/// Returns true if ended prematurely661	fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool {662		if let Some(s) = &self.sup {663			if s.enum_fields(depth.deeper(), handler) {664				return true;665			}666		}667		for (name, member) in self.this_entries.iter() {668			if handler(669				depth,670				member.original_index,671				name.clone(),672				member.flags.visibility(),673			) {674				return true;675			}676		}677		false678	}679680	fn has_field_include_hidden(&self, name: IStr) -> bool {681		if self.this_entries.contains_key(&name) {682			true683		} else if let Some(super_obj) = &self.sup {684			super_obj.has_field_include_hidden(name)685		} else {686			false687		}688	}689	fn has_field(&self, name: IStr) -> bool {690		self.field_visibility(name)691			.map_or(false, |v| v.is_visible())692	}693694	fn get_for(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {695		let cache_key = (key.clone(), Some(this.clone().downgrade()));696		if let Some(v) = self.value_cache.borrow().get(&cache_key) {697			return Ok(match v {698				CacheValue::Cached(v) => Some(v.clone()),699				CacheValue::NotFound => None,700				CacheValue::Pending => bail!(InfiniteRecursionDetected),701				CacheValue::Errored(e) => return Err(e.clone()),702			});703		}704		self.value_cache705			.borrow_mut()706			.insert(cache_key.clone(), CacheValue::Pending);707		let value = self.get_for_uncached(key, this).inspect_err(|e| {708			self.value_cache709				.borrow_mut()710				.insert(cache_key.clone(), CacheValue::Errored(e.clone()));711		})?;712		self.value_cache.borrow_mut().insert(713			cache_key,714			value715				.as_ref()716				.map_or(CacheValue::NotFound, |v| CacheValue::Cached(v.clone())),717		);718		Ok(value)719	}720	fn get_for_uncached(&self, key: IStr, real_this: ObjValue) -> Result<Option<Val>> {721		match (self.this_entries.get(&key), &self.sup) {722			(Some(k), None) => Ok(Some(self.evaluate_this(k, real_this)?)),723			(Some(k), Some(super_obj)) => {724				let our = self.evaluate_this(k, real_this.clone())?;725				if k.flags.add() {726					super_obj727						.get_raw(key, real_this)?728						.map_or(Ok(Some(our.clone())), |v| {729							Ok(Some(evaluate_add_op(&v, &our)?))730						})731				} else {732					Ok(Some(our))733				}734			}735			(None, Some(super_obj)) => super_obj.get_raw(key, real_this),736			(None, None) => Ok(None),737		}738	}739	fn field_visibility(&self, name: IStr) -> Option<Visibility> {740		if let Some(m) = self.this_entries.get(&name) {741			Some(match &m.flags.visibility() {742				Visibility::Normal => self743					.sup744					.as_ref()745					.and_then(|super_obj| super_obj.field_visibility(name))746					.unwrap_or(Visibility::Normal),747				v => *v,748			})749		} else if let Some(super_obj) = &self.sup {750			super_obj.field_visibility(name)751		} else {752			None753		}754	}755756	fn run_assertions_raw(&self, real_this: ObjValue) -> Result<()> {757		if self.assertions.is_empty() {758			if let Some(super_obj) = &self.sup {759				super_obj.run_assertions_raw(real_this)?;760			}761			return Ok(());762		}763		if self.assertions_ran.borrow_mut().insert(real_this.clone()) {764			for assertion in self.assertions.iter() {765				if let Err(e) = assertion.run(self.sup.clone(), Some(real_this.clone())) {766					self.assertions_ran.borrow_mut().remove(&real_this);767					return Err(e);768				}769			}770			if let Some(super_obj) = &self.sup {771				super_obj.run_assertions_raw(real_this)?;772			}773		}774		Ok(())775	}776}777778impl PartialEq for ObjValue {779	fn eq(&self, other: &Self) -> bool {780		Cc::ptr_eq(&self.0, &other.0)781	}782}783784impl Eq for ObjValue {}785impl Hash for ObjValue {786	fn hash<H: Hasher>(&self, hasher: &mut H) {787		hasher.write_usize(addr_of!(*self.0) as usize);788	}789}790791#[allow(clippy::module_name_repetitions)]792pub struct ObjValueBuilder {793	sup: Option<ObjValue>,794	map: GcHashMap<IStr, ObjMember>,795	assertions: Vec<TraceBox<dyn ObjectAssertion>>,796	next_field_index: FieldIndex,797}798impl ObjValueBuilder {799	pub fn new() -> Self {800		Self::with_capacity(0)801	}802	pub fn with_capacity(capacity: usize) -> Self {803		Self {804			sup: None,805			map: GcHashMap::with_capacity(capacity),806			assertions: Vec::new(),807			next_field_index: FieldIndex::default(),808		}809	}810	pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self {811		self.assertions.reserve_exact(capacity);812		self813	}814	pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {815		self.sup = Some(super_obj);816		self817	}818819	pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {820		self.assertions.push(tb!(assertion));821		self822	}823	pub fn field(&mut self, name: impl Into<IStr>) -> ObjMemberBuilder<ValueBuilder<'_>> {824		let field_index = self.next_field_index;825		self.next_field_index = self.next_field_index.next();826		ObjMemberBuilder::new(ValueBuilder(self), name.into(), field_index)827	}828	/// Preset for common method definiton pattern:829	/// Create a hidden field with the function value.830	///831	/// `.field(name).hide().value(Val::function(value))`832	pub fn method(&mut self, name: impl Into<IStr>, value: impl Into<FuncVal>) -> &mut Self {833		self.field(name).hide().value(Val::Func(value.into()));834		self835	}836	pub fn try_method(837		&mut self,838		name: impl Into<IStr>,839		value: impl Into<FuncVal>,840	) -> Result<&mut Self> {841		self.field(name).hide().try_value(Val::Func(value.into()))?;842		Ok(self)843	}844845	pub fn build(self) -> ObjValue {846		if self.sup.is_none() && self.map.is_empty() && self.assertions.is_empty() {847			return ObjValue::new_empty();848		}849		ObjValue::new(OopObject::new(850			self.sup,851			Cc::new(self.map),852			Cc::new(self.assertions),853		))854	}855}856impl Default for ObjValueBuilder {857	fn default() -> Self {858		Self::with_capacity(0)859	}860}861862#[allow(clippy::module_name_repetitions)]863#[must_use = "value not added unless binding() was called"]864pub struct ObjMemberBuilder<Kind> {865	kind: Kind,866	name: IStr,867	add: bool,868	visibility: Visibility,869	original_index: FieldIndex,870	location: Option<Span>,871}872873#[allow(clippy::missing_const_for_fn)]874impl<Kind> ObjMemberBuilder<Kind> {875	pub(crate) fn new(kind: Kind, name: IStr, original_index: FieldIndex) -> Self {876		Self {877			kind,878			name,879			original_index,880			add: false,881			visibility: Visibility::Normal,882			location: None,883		}884	}885886	pub const fn with_add(mut self, add: bool) -> Self {887		self.add = add;888		self889	}890	pub fn add(self) -> Self {891		self.with_add(true)892	}893	pub fn with_visibility(mut self, visibility: Visibility) -> Self {894		self.visibility = visibility;895		self896	}897	pub fn hide(self) -> Self {898		self.with_visibility(Visibility::Hidden)899	}900	pub fn with_location(mut self, location: Span) -> Self {901		self.location = Some(location);902		self903	}904	fn build_member(self, binding: MaybeUnbound) -> (Kind, IStr, ObjMember) {905		(906			self.kind,907			self.name,908			ObjMember {909				flags: ObjFieldFlags::new(self.add, self.visibility),910				original_index: self.original_index,911				invoke: binding,912				location: self.location,913			},914		)915	}916}917918pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);919impl ObjMemberBuilder<ValueBuilder<'_>> {920	/// Inserts value, replacing if it is already defined921	pub fn value(self, value: impl Into<Val>) {922		let (receiver, name, member) =923			self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value.into())));924		let entry = receiver.0.map.entry(name);925		entry.insert(member);926	}927928	/// Tries to insert value, returns an error if it was already defined929	pub fn try_value(self, value: impl Into<Val>) -> Result<()> {930		self.try_thunk(Thunk::evaluated(value.into()))931	}932	pub fn try_thunk(self, value: impl Into<Thunk<Val>>) -> Result<()> {933		self.binding(MaybeUnbound::Bound(value.into()))934	}935	pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {936		self.binding(MaybeUnbound::Unbound(Cc::new(tb!(bindable))))937	}938	pub fn binding(self, binding: MaybeUnbound) -> Result<()> {939		let (receiver, name, member) = self.build_member(binding);940		let location = member.location.clone();941		let old = receiver.0.map.insert(name.clone(), member);942		if old.is_some() {943			in_frame(944				CallLocation(location.as_ref()),945				|| format!("field <{}> initializtion", name.clone()),946				|| bail!(DuplicateFieldName(name.clone())),947			)?;948		}949		Ok(())950	}951}952953pub struct ExtendBuilder<'v>(&'v mut ObjValue);954impl ObjMemberBuilder<ExtendBuilder<'_>> {955	pub fn value(self, value: impl Into<Val>) {956		self.binding(MaybeUnbound::Bound(Thunk::evaluated(value.into())));957	}958	pub fn bindable(self, bindable: TraceBox<dyn Unbound<Bound = Val>>) {959		self.binding(MaybeUnbound::Unbound(Cc::new(bindable)));960	}961	pub fn binding(self, binding: MaybeUnbound) {962		let (receiver, name, member) = self.build_member(binding);963		let new = receiver.0.clone();964		*receiver.0 = new.extend_with_raw_member(name, member);965	}966}