git.delta.rocks / jrsonnet / refs/commits / 74ea504236fd

difftreelog

source

crates/jrsonnet-evaluator/src/obj.rs24.3 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, ThunkValue},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		#[derive(Trace)]448		struct ThunkGet {449			obj: ObjValue,450			key: IStr,451		}452		impl ThunkValue for ThunkGet {453			type Output = Val;454455			fn get(self: Box<Self>) -> Result<Self::Output> {456				Ok(self.obj.get(self.key)?.expect("field exists"))457			}458		}459460		if !self.has_field_ex(key.clone(), true) {461			return None;462		}463		Some(Thunk::new(ThunkGet {464			obj: self.clone(),465			key,466		}))467	}468	pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {469		#[derive(Trace)]470		struct ThunkGet {471			obj: ObjValue,472			key: IStr,473		}474		impl ThunkValue for ThunkGet {475			type Output = Val;476477			fn get(self: Box<Self>) -> Result<Self::Output> {478				self.obj.get_or_bail(self.key)479			}480		}481482		Thunk::new(ThunkGet {483			obj: self.clone(),484			key,485		})486	}487	pub fn ptr_eq(a: &Self, b: &Self) -> bool {488		Cc::ptr_eq(&a.0, &b.0)489	}490	pub fn downgrade(self) -> WeakObjValue {491		WeakObjValue(self.0.downgrade())492	}493	fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {494		let mut out = FxHashMap::default();495		self.enum_fields(496			SuperDepth::default(),497			&mut |depth, index, name, visibility| {498				let new_sort_key = FieldSortKey::new(depth, index);499				let entry = out.entry(name);500				let (visible, _) = entry.or_insert((true, new_sort_key));501				match visibility {502					Visibility::Normal => {}503					Visibility::Hidden => {504						*visible = false;505					}506					Visibility::Unhide => {507						*visible = true;508					}509				};510				false511			},512		);513		out514	}515	pub fn fields_ex(516		&self,517		include_hidden: bool,518		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,519	) -> Vec<IStr> {520		#[cfg(feature = "exp-preserve-order")]521		if preserve_order {522			let (mut fields, mut keys): (Vec<_>, Vec<_>) = self523				.fields_visibility()524				.into_iter()525				.filter(|(_, (visible, _))| include_hidden || *visible)526				.enumerate()527				.map(|(idx, (k, (_, sk)))| (k, (sk, idx)))528				.unzip();529			keys.sort_unstable_by_key(|v| v.0);530			// Reorder in-place by resulting indexes531			for i in 0..fields.len() {532				let x = fields[i].clone();533				let mut j = i;534				loop {535					let k = keys[j].1;536					keys[j].1 = j;537					if k == i {538						break;539					}540					fields[j] = fields[k].clone();541					j = k;542				}543				fields[j] = x;544			}545			return fields;546		}547548		let mut fields: Vec<_> = self549			.fields_visibility()550			.into_iter()551			.filter(|(_, (visible, _))| include_hidden || *visible)552			.map(|(k, _)| k)553			.collect();554		fields.sort_unstable();555		fields556	}557	pub fn fields(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Vec<IStr> {558		self.fields_ex(559			false,560			#[cfg(feature = "exp-preserve-order")]561			preserve_order,562		)563	}564	pub fn values_ex(565		&self,566		include_hidden: bool,567		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,568	) -> ArrValue {569		ArrValue::new(PickObjectValues::new(570			self.clone(),571			self.fields_ex(572				include_hidden,573				#[cfg(feature = "exp-preserve-order")]574				preserve_order,575			),576		))577	}578	pub fn values(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> ArrValue {579		self.values_ex(580			false,581			#[cfg(feature = "exp-preserve-order")]582			preserve_order,583		)584	}585	pub fn key_values_ex(586		&self,587		include_hidden: bool,588		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,589	) -> ArrValue {590		ArrValue::new(PickObjectKeyValues::new(591			self.clone(),592			self.fields_ex(593				include_hidden,594				#[cfg(feature = "exp-preserve-order")]595				preserve_order,596			),597		))598	}599	pub fn key_values(600		&self,601		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,602	) -> ArrValue {603		self.key_values_ex(604			false,605			#[cfg(feature = "exp-preserve-order")]606			preserve_order,607		)608	}609}610611impl OopObject {612	pub fn new(613		sup: Option<ObjValue>,614		this_entries: Cc<GcHashMap<IStr, ObjMember>>,615		assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,616	) -> Self {617		Self {618			sup,619			// this: None,620			assertions,621			assertions_ran: RefCell::new(GcHashSet::new()),622			this_entries,623			value_cache: RefCell::new(GcHashMap::new()),624		}625	}626627	fn evaluate_this(&self, v: &ObjMember, real_this: ObjValue) -> Result<Val> {628		v.invoke.evaluate(self.sup.clone(), Some(real_this))629	}630631	// FIXME: Duplication between ObjValue and OopObject632	fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {633		let mut out = FxHashMap::default();634		self.enum_fields(635			SuperDepth::default(),636			&mut |depth, index, name, visibility| {637				let new_sort_key = FieldSortKey::new(depth, index);638				let entry = out.entry(name);639				let (visible, _) = entry.or_insert((true, new_sort_key));640				match visibility {641					Visibility::Normal => {}642					Visibility::Hidden => {643						*visible = false;644					}645					Visibility::Unhide => {646						*visible = true;647					}648				};649				false650			},651		);652		out653	}654}655656impl ObjectLike for OopObject {657	fn extend_from(&self, sup: ObjValue) -> ObjValue {658		ObjValue::new(match &self.sup {659			None => Self::new(660				Some(sup),661				self.this_entries.clone(),662				self.assertions.clone(),663			),664			Some(v) => Self::new(665				Some(v.extend_from(sup)),666				self.this_entries.clone(),667				self.assertions.clone(),668			),669		})670	}671672	fn len(&self) -> usize {673		// Maybe it will be better to not compute sort key here?674		self.fields_visibility()675			.into_iter()676			.filter(|(_, (visible, _))| *visible)677			.count()678	}679680	/// Returns false only if there is any visible entry.681	///682	/// Note that object with hidden fields `{a:: 1}` will be reported as empty here.683	fn is_empty(&self) -> bool {684		self.len() == 0685	}686687	/// Run callback for every field found in object688	///689	/// Returns true if ended prematurely690	fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool {691		if let Some(s) = &self.sup {692			if s.enum_fields(depth.deeper(), handler) {693				return true;694			}695		}696		for (name, member) in self.this_entries.iter() {697			if handler(698				depth,699				member.original_index,700				name.clone(),701				member.flags.visibility(),702			) {703				return true;704			}705		}706		false707	}708709	fn has_field_include_hidden(&self, name: IStr) -> bool {710		if self.this_entries.contains_key(&name) {711			true712		} else if let Some(super_obj) = &self.sup {713			super_obj.has_field_include_hidden(name)714		} else {715			false716		}717	}718	fn has_field(&self, name: IStr) -> bool {719		self.field_visibility(name)720			.map_or(false, |v| v.is_visible())721	}722723	fn get_for(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {724		let cache_key = (key.clone(), Some(this.clone().downgrade()));725		if let Some(v) = self.value_cache.borrow().get(&cache_key) {726			return Ok(match v {727				CacheValue::Cached(v) => Some(v.clone()),728				CacheValue::NotFound => None,729				CacheValue::Pending => bail!(InfiniteRecursionDetected),730				CacheValue::Errored(e) => return Err(e.clone()),731			});732		}733		self.value_cache734			.borrow_mut()735			.insert(cache_key.clone(), CacheValue::Pending);736		let value = self.get_for_uncached(key, this).map_err(|e| {737			self.value_cache738				.borrow_mut()739				.insert(cache_key.clone(), CacheValue::Errored(e.clone()));740			e741		})?;742		self.value_cache.borrow_mut().insert(743			cache_key,744			value745				.as_ref()746				.map_or(CacheValue::NotFound, |v| CacheValue::Cached(v.clone())),747		);748		Ok(value)749	}750	fn get_for_uncached(&self, key: IStr, real_this: ObjValue) -> Result<Option<Val>> {751		match (self.this_entries.get(&key), &self.sup) {752			(Some(k), None) => Ok(Some(self.evaluate_this(k, real_this)?)),753			(Some(k), Some(super_obj)) => {754				let our = self.evaluate_this(k, real_this.clone())?;755				if k.flags.add() {756					super_obj757						.get_raw(key, real_this)?758						.map_or(Ok(Some(our.clone())), |v| {759							Ok(Some(evaluate_add_op(&v, &our)?))760						})761				} else {762					Ok(Some(our))763				}764			}765			(None, Some(super_obj)) => super_obj.get_raw(key, real_this),766			(None, None) => Ok(None),767		}768	}769	fn field_visibility(&self, name: IStr) -> Option<Visibility> {770		if let Some(m) = self.this_entries.get(&name) {771			Some(match &m.flags.visibility() {772				Visibility::Normal => self773					.sup774					.as_ref()775					.and_then(|super_obj| super_obj.field_visibility(name))776					.unwrap_or(Visibility::Normal),777				v => *v,778			})779		} else if let Some(super_obj) = &self.sup {780			super_obj.field_visibility(name)781		} else {782			None783		}784	}785786	fn run_assertions_raw(&self, real_this: ObjValue) -> Result<()> {787		if self.assertions.is_empty() {788			if let Some(super_obj) = &self.sup {789				super_obj.run_assertions_raw(real_this)?;790			}791			return Ok(());792		}793		if self.assertions_ran.borrow_mut().insert(real_this.clone()) {794			for assertion in self.assertions.iter() {795				if let Err(e) = assertion.run(self.sup.clone(), Some(real_this.clone())) {796					self.assertions_ran.borrow_mut().remove(&real_this);797					return Err(e);798				}799			}800			if let Some(super_obj) = &self.sup {801				super_obj.run_assertions_raw(real_this)?;802			}803		}804		Ok(())805	}806}807808impl PartialEq for ObjValue {809	fn eq(&self, other: &Self) -> bool {810		Cc::ptr_eq(&self.0, &other.0)811	}812}813814impl Eq for ObjValue {}815impl Hash for ObjValue {816	fn hash<H: Hasher>(&self, hasher: &mut H) {817		hasher.write_usize(addr_of!(*self.0) as usize);818	}819}820821#[allow(clippy::module_name_repetitions)]822pub struct ObjValueBuilder {823	sup: Option<ObjValue>,824	map: GcHashMap<IStr, ObjMember>,825	assertions: Vec<TraceBox<dyn ObjectAssertion>>,826	next_field_index: FieldIndex,827}828impl ObjValueBuilder {829	pub fn new() -> Self {830		Self::with_capacity(0)831	}832	pub fn with_capacity(capacity: usize) -> Self {833		Self {834			sup: None,835			map: GcHashMap::with_capacity(capacity),836			assertions: Vec::new(),837			next_field_index: FieldIndex::default(),838		}839	}840	pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self {841		self.assertions.reserve_exact(capacity);842		self843	}844	pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {845		self.sup = Some(super_obj);846		self847	}848849	pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {850		self.assertions.push(tb!(assertion));851		self852	}853	pub fn field(&mut self, name: impl Into<IStr>) -> ObjMemberBuilder<ValueBuilder<'_>> {854		let field_index = self.next_field_index;855		self.next_field_index = self.next_field_index.next();856		ObjMemberBuilder::new(ValueBuilder(self), name.into(), field_index)857	}858	/// Preset for common method definiton pattern:859	/// Create a hidden field with the function value.860	///861	/// `.field(name).hide().value(Val::function(value))`862	pub fn method(&mut self, name: impl Into<IStr>, value: impl Into<FuncVal>) -> &mut Self {863		self.field(name).hide().value(Val::Func(value.into()));864		self865	}866	pub fn try_method(867		&mut self,868		name: impl Into<IStr>,869		value: impl Into<FuncVal>,870	) -> Result<&mut Self> {871		self.field(name).hide().try_value(Val::Func(value.into()))?;872		Ok(self)873	}874875	pub fn build(self) -> ObjValue {876		if self.sup.is_none() && self.map.is_empty() && self.assertions.is_empty() {877			return ObjValue::new_empty();878		}879		ObjValue::new(OopObject::new(880			self.sup,881			Cc::new(self.map),882			Cc::new(self.assertions),883		))884	}885}886impl Default for ObjValueBuilder {887	fn default() -> Self {888		Self::with_capacity(0)889	}890}891892#[allow(clippy::module_name_repetitions)]893#[must_use = "value not added unless binding() was called"]894pub struct ObjMemberBuilder<Kind> {895	kind: Kind,896	name: IStr,897	add: bool,898	visibility: Visibility,899	original_index: FieldIndex,900	location: Option<Span>,901}902903#[allow(clippy::missing_const_for_fn)]904impl<Kind> ObjMemberBuilder<Kind> {905	pub(crate) fn new(kind: Kind, name: IStr, original_index: FieldIndex) -> Self {906		Self {907			kind,908			name,909			original_index,910			add: false,911			visibility: Visibility::Normal,912			location: None,913		}914	}915916	pub const fn with_add(mut self, add: bool) -> Self {917		self.add = add;918		self919	}920	pub fn add(self) -> Self {921		self.with_add(true)922	}923	pub fn with_visibility(mut self, visibility: Visibility) -> Self {924		self.visibility = visibility;925		self926	}927	pub fn hide(self) -> Self {928		self.with_visibility(Visibility::Hidden)929	}930	pub fn with_location(mut self, location: Span) -> Self {931		self.location = Some(location);932		self933	}934	fn build_member(self, binding: MaybeUnbound) -> (Kind, IStr, ObjMember) {935		(936			self.kind,937			self.name,938			ObjMember {939				flags: ObjFieldFlags::new(self.add, self.visibility),940				original_index: self.original_index,941				invoke: binding,942				location: self.location,943			},944		)945	}946}947948pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);949impl ObjMemberBuilder<ValueBuilder<'_>> {950	/// Inserts value, replacing if it is already defined951	pub fn value(self, value: impl Into<Val>) {952		let (receiver, name, member) =953			self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value.into())));954		let entry = receiver.0.map.entry(name);955		entry.insert(member);956	}957958	/// Tries to insert value, returns an error if it was already defined959	pub fn try_value(self, value: impl Into<Val>) -> Result<()> {960		self.thunk(Thunk::evaluated(value.into()))961	}962	pub fn thunk(self, value: impl Into<Thunk<Val>>) -> Result<()> {963		self.binding(MaybeUnbound::Bound(value.into()))964	}965	pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {966		self.binding(MaybeUnbound::Unbound(Cc::new(tb!(bindable))))967	}968	pub fn binding(self, binding: MaybeUnbound) -> Result<()> {969		let (receiver, name, member) = self.build_member(binding);970		let location = member.location.clone();971		let old = receiver.0.map.insert(name.clone(), member);972		if old.is_some() {973			in_frame(974				CallLocation(location.as_ref()),975				|| format!("field <{}> initializtion", name.clone()),976				|| bail!(DuplicateFieldName(name.clone())),977			)?;978		}979		Ok(())980	}981}982983pub struct ExtendBuilder<'v>(&'v mut ObjValue);984impl ObjMemberBuilder<ExtendBuilder<'_>> {985	pub fn value(self, value: impl Into<Val>) {986		self.binding(MaybeUnbound::Bound(Thunk::evaluated(value.into())));987	}988	pub fn bindable(self, bindable: TraceBox<dyn Unbound<Bound = Val>>) {989		self.binding(MaybeUnbound::Unbound(Cc::new(bindable)));990	}991	pub fn binding(self, binding: MaybeUnbound) {992		let (receiver, name, member) = self.build_member(binding);993		let new = receiver.0.clone();994		*receiver.0 = new.extend_with_raw_member(name, member);995	}996}