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

difftreelog

refactor object builder

Yaroslav Bolyukin2021-07-04parent: #f38d264.patch.diff
in: master

3 files changed

modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -2,14 +2,14 @@
 	error::Error::*,
 	evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},
 	push, throw, with_state, ArrValue, Bindable, Context, ContextCreator, FuncDesc, FuncVal,
-	FutureWrapper, LazyBinding, LazyVal, LazyValValue, ObjMember, ObjValue, ObjectAssertion,
+	FutureWrapper, LazyBinding, LazyVal, LazyValValue, ObjValue, ObjValueBuilder, ObjectAssertion,
 	Result, Val,
 };
 use jrsonnet_gc::{Gc, Trace};
 use jrsonnet_interner::IStr;
 use jrsonnet_parser::{
 	ArgsDesc, AssertStmt, BindSpec, CompSpec, Expr, ExprLocation, FieldMember, ForSpecData,
-	IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, Visibility,
+	IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc,
 };
 use jrsonnet_types::ValType;
 use rustc_hash::{FxHashMap, FxHasher};
@@ -254,8 +254,7 @@
 		new_bindings.fill(bindings);
 	}
 
-	let mut new_members = FxHashMap::default();
-	let mut assertions: Vec<Box<dyn ObjectAssertion>> = Vec::new();
+	let mut builder = ObjValueBuilder::new();
 	for member in members.iter() {
 		match member {
 			Member::Field(FieldMember {
@@ -291,19 +290,16 @@
 						)?))
 					}
 				}
-				new_members.insert(
-					name.clone(),
-					ObjMember {
-						add: *plus,
-						visibility: *visibility,
-						invoke: LazyBinding::Bindable(Gc::new(Box::new(ObjMemberBinding {
-							context_creator: context_creator.clone(),
-							value: value.clone(),
-							name,
-						}))),
-						location: value.1.clone(),
-					},
-				);
+				builder
+					.member(name.clone())
+					.with_add(*plus)
+					.with_visibility(*visibility)
+					.with_location(value.1.clone())
+					.bindable(Box::new(ObjMemberBinding {
+						context_creator: context_creator.clone(),
+						value: value.clone(),
+						name,
+					}));
 			}
 			Member::Field(FieldMember {
 				name,
@@ -338,20 +334,16 @@
 						)))
 					}
 				}
-				new_members.insert(
-					name.clone(),
-					ObjMember {
-						add: false,
-						visibility: Visibility::Hidden,
-						invoke: LazyBinding::Bindable(Gc::new(Box::new(ObjMemberBinding {
-							context_creator: context_creator.clone(),
-							value: value.clone(),
-							params: params.clone(),
-							name,
-						}))),
-						location: value.1.clone(),
-					},
-				);
+				builder
+					.member(name.clone())
+					.hide()
+					.with_location(value.1.clone())
+					.binding(LazyBinding::Bindable(Gc::new(Box::new(ObjMemberBinding {
+						context_creator: context_creator.clone(),
+						value: value.clone(),
+						params: params.clone(),
+						name,
+					}))));
 			}
 			Member::BindStmt(_) => {}
 			Member::AssertStmt(stmt) => {
@@ -371,14 +363,14 @@
 						evaluate_assert(ctx, &self.assert)
 					}
 				}
-				assertions.push(Box::new(ObjectAssert {
+				builder.assert(Box::new(ObjectAssert {
 					context_creator: context_creator.clone(),
 					assert: stmt.clone(),
 				}));
 			}
 		}
 	}
-	let this = ObjValue::new(None, Gc::new(new_members), Gc::new(assertions));
+	let this = builder.build();
 	future_this.fill(this.clone());
 	Ok(this)
 }
@@ -388,7 +380,7 @@
 		ObjBody::MemberList(members) => evaluate_member_list_object(context, members)?,
 		ObjBody::ObjComp(obj) => {
 			let future_this = FutureWrapper::new();
-			let mut new_members = FxHashMap::default();
+			let mut builder = ObjValueBuilder::new();
 			evaluate_comp(context.clone(), &obj.compspecs, &mut |ctx| {
 				let new_bindings = FutureWrapper::new();
 				let context_creator = ContextCreator(context.clone(), new_bindings.clone());
@@ -435,18 +427,13 @@
 								)?))
 							}
 						}
-						new_members.insert(
-							n,
-							ObjMember {
-								add: false,
-								visibility: Visibility::Normal,
-								invoke: LazyBinding::Bindable(Gc::new(Box::new(ObjCompBinding {
-									context: ctx,
-									value: obj.value.clone(),
-								}))),
-								location: obj.value.1.clone(),
-							},
-						);
+						builder
+							.member(n)
+							.with_location(obj.value.1.clone())
+							.bindable(Box::new(ObjCompBinding {
+								context: ctx,
+								value: obj.value.clone(),
+							}));
 					}
 					v => throw!(FieldMustBeStringGot(v.value_type())),
 				}
@@ -454,7 +441,7 @@
 				Ok(())
 			})?;
 
-			let this = ObjValue::new(None, Gc::new(new_members), Gc::new(Vec::new()));
+			let this = builder.build();
 			future_this.fill(this.clone());
 			this
 		}
modifiedcrates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/integrations/serde.rs
+++ b/crates/jrsonnet-evaluator/src/integrations/serde.rs
@@ -1,16 +1,9 @@
 use crate::{
 	error::{Error::*, LocError, Result},
-	throw, LazyBinding, LazyVal, ObjMember, ObjValue, Val,
+	throw, ObjValueBuilder, Val,
 };
-use jrsonnet_gc::Gc;
-use jrsonnet_parser::Visibility;
-use rustc_hash::FxHasher;
 use serde_json::{Map, Number, Value};
-use std::{
-	collections::HashMap,
-	convert::{TryFrom, TryInto},
-	hash::BuildHasherDefault,
-};
+use std::convert::{TryFrom, TryInto};
 
 impl TryFrom<&Val> for Value {
 	type Error = LocError;
@@ -54,29 +47,18 @@
 			Value::Number(n) => Self::Num(n.as_f64().expect("as f64")),
 			Value::String(s) => Self::Str((s as &str).into()),
 			Value::Array(a) => {
-				let mut out = Vec::with_capacity(a.len());
+				let mut out: Vec<Val> = Vec::with_capacity(a.len());
 				for v in a {
-					out.push(LazyVal::new_resolved(v.into()));
+					out.push(v.into());
 				}
 				Self::Arr(out.into())
 			}
 			Value::Object(o) => {
-				let mut entries = HashMap::with_capacity_and_hasher(
-					o.len(),
-					BuildHasherDefault::<FxHasher>::default(),
-				);
+				let mut builder = ObjValueBuilder::with_capacity(o.len());
 				for (k, v) in o {
-					entries.insert(
-						(k as &str).into(),
-						ObjMember {
-							add: false,
-							visibility: Visibility::Normal,
-							invoke: LazyBinding::Bound(LazyVal::new_resolved(v.into())),
-							location: None,
-						},
-					);
+					builder.member((k as &str).into()).value(v.into());
 				}
-				Self::Obj(ObjValue::new(None, Gc::new(entries), Gc::new(Vec::new())))
+				Val::Obj(builder.build())
 			}
 		}
 	}
modifiedcrates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/obj.rs
1use crate::operator::evaluate_add_op;2use crate::{LazyBinding, Result, Val};3use jrsonnet_gc::{Gc, GcCell, Trace};4use jrsonnet_interner::IStr;5use jrsonnet_parser::{ExprLocation, Visibility};6use rustc_hash::{FxHashMap, FxHashSet};7use std::hash::{Hash, Hasher};8use std::{fmt::Debug, hash::BuildHasherDefault};910#[derive(Debug, Trace)]11#[trivially_drop]12pub struct ObjMember {13	pub add: bool,14	pub visibility: Visibility,15	pub invoke: LazyBinding,16	pub location: Option<ExprLocation>,17}1819pub trait ObjectAssertion: Trace {20	fn run(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<()>;21}2223// Field => This24type CacheKey = (IStr, ObjValue);25#[derive(Trace)]26#[trivially_drop]27pub struct ObjValueInternals {28	super_obj: Option<ObjValue>,29	assertions: Gc<Vec<Box<dyn ObjectAssertion>>>,30	assertions_ran: GcCell<FxHashSet<ObjValue>>,31	this_obj: Option<ObjValue>,32	this_entries: Gc<FxHashMap<IStr, ObjMember>>,33	value_cache: GcCell<FxHashMap<CacheKey, Option<Val>>>,34}3536#[derive(Clone, Trace)]37#[trivially_drop]38pub struct ObjValue(pub(crate) Gc<ObjValueInternals>);39impl Debug for ObjValue {40	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {41		if let Some(super_obj) = self.0.super_obj.as_ref() {42			if f.alternate() {43				write!(f, "{:#?}", super_obj)?;44			} else {45				write!(f, "{:?}", super_obj)?;46			}47			write!(f, " + ")?;48		}49		let mut debug = f.debug_struct("ObjValue");50		for (name, member) in self.0.this_entries.iter() {51			debug.field(name, member);52		}53		#[cfg(feature = "unstable")]54		{55			debug.finish_non_exhaustive()56		}57		#[cfg(not(feature = "unstable"))]58		{59			debug.finish()60		}61	}62}6364impl ObjValue {65	pub fn new(66		super_obj: Option<Self>,67		this_entries: Gc<FxHashMap<IStr, ObjMember>>,68		assertions: Gc<Vec<Box<dyn ObjectAssertion>>>,69	) -> Self {70		Self(Gc::new(ObjValueInternals {71			super_obj,72			assertions,73			assertions_ran: GcCell::new(FxHashSet::default()),74			this_obj: None,75			this_entries,76			value_cache: GcCell::new(FxHashMap::default()),77		}))78	}79	pub fn new_empty() -> Self {80		Self::new(None, Gc::new(FxHashMap::default()), Gc::new(Vec::new()))81	}82	pub fn extend_from(&self, super_obj: Self) -> Self {83		match &self.0.super_obj {84			None => Self::new(85				Some(super_obj),86				self.0.this_entries.clone(),87				self.0.assertions.clone(),88			),89			Some(v) => Self::new(90				Some(v.extend_from(super_obj)),91				self.0.this_entries.clone(),92				self.0.assertions.clone(),93			),94		}95	}96	pub fn with_this(&self, this_obj: Self) -> Self {97		Self(Gc::new(ObjValueInternals {98			super_obj: self.0.super_obj.clone(),99			assertions: self.0.assertions.clone(),100			assertions_ran: GcCell::new(FxHashSet::default()),101			this_obj: Some(this_obj),102			this_entries: self.0.this_entries.clone(),103			value_cache: GcCell::new(FxHashMap::default()),104		}))105	}106107	/// Run callback for every field found in object108	pub(crate) fn enum_fields(&self, handler: &mut impl FnMut(&IStr, &Visibility) -> bool) -> bool {109		if let Some(s) = &self.0.super_obj {110			if s.enum_fields(handler) {111				return true;112			}113		}114		for (name, member) in self.0.this_entries.iter() {115			if handler(name, &member.visibility) {116				return true;117			}118		}119		false120	}121122	pub fn fields_visibility(&self) -> FxHashMap<IStr, bool> {123		let mut out = FxHashMap::default();124		self.enum_fields(&mut |name, visibility| {125			match visibility {126				Visibility::Normal => {127					let entry = out.entry(name.to_owned());128					entry.or_insert(true);129				}130				Visibility::Hidden => {131					out.insert(name.to_owned(), false);132				}133				Visibility::Unhide => {134					out.insert(name.to_owned(), true);135				}136			};137			false138		});139		out140	}141	pub fn fields_ex(&self, include_hidden: bool) -> Vec<IStr> {142		let mut fields: Vec<_> = self143			.fields_visibility()144			.into_iter()145			.filter(|(_k, v)| include_hidden || *v)146			.map(|(k, _)| k)147			.collect();148		fields.sort_unstable();149		fields150	}151	pub fn fields(&self) -> Vec<IStr> {152		self.fields_ex(false)153	}154155	pub fn field_visibility(&self, name: IStr) -> Option<Visibility> {156		if let Some(m) = self.0.this_entries.get(&name) {157			Some(match &m.visibility {158				Visibility::Normal => self159					.0160					.super_obj161					.as_ref()162					.and_then(|super_obj| super_obj.field_visibility(name))163					.unwrap_or(Visibility::Normal),164				v => *v,165			})166		} else if let Some(super_obj) = &self.0.super_obj {167			super_obj.field_visibility(name)168		} else {169			None170		}171	}172173	fn has_field_include_hidden(&self, name: IStr) -> bool {174		if self.0.this_entries.contains_key(&name) {175			true176		} else if let Some(super_obj) = &self.0.super_obj {177			super_obj.has_field_include_hidden(name)178		} else {179			false180		}181	}182183	pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {184		if include_hidden {185			self.has_field_include_hidden(name)186		} else {187			self.has_field(name)188		}189	}190	pub fn has_field(&self, name: IStr) -> bool {191		self.field_visibility(name)192			.map(|v| v.is_visible())193			.unwrap_or(false)194	}195196	pub fn get(&self, key: IStr) -> Result<Option<Val>> {197		self.run_assertions()?;198		self.get_raw(key, self.0.this_obj.as_ref())199	}200201	pub fn extend_with_field(self, key: IStr, value: ObjMember) -> Self {202		let mut new = FxHashMap::with_capacity_and_hasher(1, BuildHasherDefault::default());203		new.insert(key, value);204		Self::new(Some(self), Gc::new(new), Gc::new(Vec::new()))205	}206207	fn get_raw(&self, key: IStr, real_this: Option<&Self>) -> Result<Option<Val>> {208		let real_this = real_this.unwrap_or(self);209		let cache_key = (key.clone(), real_this.clone());210211		if let Some(v) = self.0.value_cache.borrow().get(&cache_key) {212			return Ok(v.clone());213		}214		let value = match (self.0.this_entries.get(&key), &self.0.super_obj) {215			(Some(k), None) => Ok(Some(self.evaluate_this(k, real_this)?)),216			(Some(k), Some(s)) => {217				let our = self.evaluate_this(k, real_this)?;218				if k.add {219					s.get_raw(key, Some(real_this))?220						.map_or(Ok(Some(our.clone())), |v| {221							Ok(Some(evaluate_add_op(&v, &our)?))222						})223				} else {224					Ok(Some(our))225				}226			}227			(None, Some(s)) => s.get_raw(key, Some(real_this)),228			(None, None) => Ok(None),229		}?;230		self.0231			.value_cache232			.borrow_mut()233			.insert(cache_key, value.clone());234		Ok(value)235	}236	fn evaluate_this(&self, v: &ObjMember, real_this: &Self) -> Result<Val> {237		v.invoke238			.evaluate(Some(real_this.clone()), self.0.super_obj.clone())?239			.evaluate()240	}241242	fn run_assertions_raw(&self, real_this: &Self) -> Result<()> {243		if self.0.assertions_ran.borrow_mut().insert(real_this.clone()) {244			for assertion in self.0.assertions.iter() {245				if let Err(e) = assertion.run(Some(real_this.clone()), self.0.super_obj.clone()) {246					self.0.assertions_ran.borrow_mut().remove(real_this);247					return Err(e);248				}249			}250			if let Some(super_obj) = &self.0.super_obj {251				super_obj.run_assertions_raw(real_this)?;252			}253		}254		Ok(())255	}256	pub fn run_assertions(&self) -> Result<()> {257		self.run_assertions_raw(self)258	}259260	pub fn ptr_eq(a: &Self, b: &Self) -> bool {261		Gc::ptr_eq(&a.0, &b.0)262	}263}264265impl PartialEq for ObjValue {266	fn eq(&self, other: &Self) -> bool {267		Gc::ptr_eq(&self.0, &other.0)268	}269}270271impl Eq for ObjValue {}272impl Hash for ObjValue {273	fn hash<H: Hasher>(&self, hasher: &mut H) {274		hasher.write_usize(&*self.0 as *const _ as usize)275	}276}
after · crates/jrsonnet-evaluator/src/obj.rs
1use crate::operator::evaluate_add_op;2use crate::{Bindable, LazyBinding, LazyVal, Result, Val};3use jrsonnet_gc::{Gc, GcCell, Trace};4use jrsonnet_interner::IStr;5use jrsonnet_parser::{ExprLocation, Visibility};6use rustc_hash::{FxHashMap, FxHashSet, FxHasher};7use std::collections::HashMap;8use std::hash::{Hash, Hasher};9use std::{fmt::Debug, hash::BuildHasherDefault};1011#[derive(Debug, Trace)]12#[trivially_drop]13pub struct ObjMember {14	pub add: bool,15	pub visibility: Visibility,16	pub invoke: LazyBinding,17	pub location: Option<ExprLocation>,18}1920pub trait ObjectAssertion: Trace {21	fn run(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<()>;22}2324// Field => This25type CacheKey = (IStr, ObjValue);26#[derive(Trace)]27#[trivially_drop]28pub struct ObjValueInternals {29	super_obj: Option<ObjValue>,30	assertions: Gc<Vec<Box<dyn ObjectAssertion>>>,31	assertions_ran: GcCell<FxHashSet<ObjValue>>,32	this_obj: Option<ObjValue>,33	this_entries: Gc<FxHashMap<IStr, ObjMember>>,34	value_cache: GcCell<FxHashMap<CacheKey, Option<Val>>>,35}3637#[derive(Clone, Trace)]38#[trivially_drop]39pub struct ObjValue(pub(crate) Gc<ObjValueInternals>);40impl Debug for ObjValue {41	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {42		if let Some(super_obj) = self.0.super_obj.as_ref() {43			if f.alternate() {44				write!(f, "{:#?}", super_obj)?;45			} else {46				write!(f, "{:?}", super_obj)?;47			}48			write!(f, " + ")?;49		}50		let mut debug = f.debug_struct("ObjValue");51		for (name, member) in self.0.this_entries.iter() {52			debug.field(name, member);53		}54		#[cfg(feature = "unstable")]55		{56			debug.finish_non_exhaustive()57		}58		#[cfg(not(feature = "unstable"))]59		{60			debug.finish()61		}62	}63}6465impl ObjValue {66	pub fn new(67		super_obj: Option<Self>,68		this_entries: Gc<FxHashMap<IStr, ObjMember>>,69		assertions: Gc<Vec<Box<dyn ObjectAssertion>>>,70	) -> Self {71		Self(Gc::new(ObjValueInternals {72			super_obj,73			assertions,74			assertions_ran: GcCell::new(FxHashSet::default()),75			this_obj: None,76			this_entries,77			value_cache: GcCell::new(FxHashMap::default()),78		}))79	}80	pub fn new_empty() -> Self {81		Self::new(None, Gc::new(FxHashMap::default()), Gc::new(Vec::new()))82	}83	pub fn extend_from(&self, super_obj: Self) -> Self {84		match &self.0.super_obj {85			None => Self::new(86				Some(super_obj),87				self.0.this_entries.clone(),88				self.0.assertions.clone(),89			),90			Some(v) => Self::new(91				Some(v.extend_from(super_obj)),92				self.0.this_entries.clone(),93				self.0.assertions.clone(),94			),95		}96	}97	pub fn with_this(&self, this_obj: Self) -> Self {98		Self(Gc::new(ObjValueInternals {99			super_obj: self.0.super_obj.clone(),100			assertions: self.0.assertions.clone(),101			assertions_ran: GcCell::new(FxHashSet::default()),102			this_obj: Some(this_obj),103			this_entries: self.0.this_entries.clone(),104			value_cache: GcCell::new(FxHashMap::default()),105		}))106	}107108	/// Run callback for every field found in object109	pub(crate) fn enum_fields(&self, handler: &mut impl FnMut(&IStr, &Visibility) -> bool) -> bool {110		if let Some(s) = &self.0.super_obj {111			if s.enum_fields(handler) {112				return true;113			}114		}115		for (name, member) in self.0.this_entries.iter() {116			if handler(name, &member.visibility) {117				return true;118			}119		}120		false121	}122123	pub fn fields_visibility(&self) -> FxHashMap<IStr, bool> {124		let mut out = FxHashMap::default();125		self.enum_fields(&mut |name, visibility| {126			match visibility {127				Visibility::Normal => {128					let entry = out.entry(name.to_owned());129					entry.or_insert(true);130				}131				Visibility::Hidden => {132					out.insert(name.to_owned(), false);133				}134				Visibility::Unhide => {135					out.insert(name.to_owned(), true);136				}137			};138			false139		});140		out141	}142	pub fn fields_ex(&self, include_hidden: bool) -> Vec<IStr> {143		let mut fields: Vec<_> = self144			.fields_visibility()145			.into_iter()146			.filter(|(_k, v)| include_hidden || *v)147			.map(|(k, _)| k)148			.collect();149		fields.sort_unstable();150		fields151	}152	pub fn fields(&self) -> Vec<IStr> {153		self.fields_ex(false)154	}155156	pub fn field_visibility(&self, name: IStr) -> Option<Visibility> {157		if let Some(m) = self.0.this_entries.get(&name) {158			Some(match &m.visibility {159				Visibility::Normal => self160					.0161					.super_obj162					.as_ref()163					.and_then(|super_obj| super_obj.field_visibility(name))164					.unwrap_or(Visibility::Normal),165				v => *v,166			})167		} else if let Some(super_obj) = &self.0.super_obj {168			super_obj.field_visibility(name)169		} else {170			None171		}172	}173174	fn has_field_include_hidden(&self, name: IStr) -> bool {175		if self.0.this_entries.contains_key(&name) {176			true177		} else if let Some(super_obj) = &self.0.super_obj {178			super_obj.has_field_include_hidden(name)179		} else {180			false181		}182	}183184	pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {185		if include_hidden {186			self.has_field_include_hidden(name)187		} else {188			self.has_field(name)189		}190	}191	pub fn has_field(&self, name: IStr) -> bool {192		self.field_visibility(name)193			.map(|v| v.is_visible())194			.unwrap_or(false)195	}196197	pub fn get(&self, key: IStr) -> Result<Option<Val>> {198		self.run_assertions()?;199		self.get_raw(key, self.0.this_obj.as_ref())200	}201202	pub fn extend_with_field(self, key: IStr, value: ObjMember) -> Self {203		let mut new = FxHashMap::with_capacity_and_hasher(1, BuildHasherDefault::default());204		new.insert(key, value);205		Self::new(Some(self), Gc::new(new), Gc::new(Vec::new()))206	}207208	fn get_raw(&self, key: IStr, real_this: Option<&Self>) -> Result<Option<Val>> {209		let real_this = real_this.unwrap_or(self);210		let cache_key = (key.clone(), real_this.clone());211212		if let Some(v) = self.0.value_cache.borrow().get(&cache_key) {213			return Ok(v.clone());214		}215		let value = match (self.0.this_entries.get(&key), &self.0.super_obj) {216			(Some(k), None) => Ok(Some(self.evaluate_this(k, real_this)?)),217			(Some(k), Some(s)) => {218				let our = self.evaluate_this(k, real_this)?;219				if k.add {220					s.get_raw(key, Some(real_this))?221						.map_or(Ok(Some(our.clone())), |v| {222							Ok(Some(evaluate_add_op(&v, &our)?))223						})224				} else {225					Ok(Some(our))226				}227			}228			(None, Some(s)) => s.get_raw(key, Some(real_this)),229			(None, None) => Ok(None),230		}?;231		self.0232			.value_cache233			.borrow_mut()234			.insert(cache_key, value.clone());235		Ok(value)236	}237	fn evaluate_this(&self, v: &ObjMember, real_this: &Self) -> Result<Val> {238		v.invoke239			.evaluate(Some(real_this.clone()), self.0.super_obj.clone())?240			.evaluate()241	}242243	fn run_assertions_raw(&self, real_this: &Self) -> Result<()> {244		if self.0.assertions_ran.borrow_mut().insert(real_this.clone()) {245			for assertion in self.0.assertions.iter() {246				if let Err(e) = assertion.run(Some(real_this.clone()), self.0.super_obj.clone()) {247					self.0.assertions_ran.borrow_mut().remove(real_this);248					return Err(e);249				}250			}251			if let Some(super_obj) = &self.0.super_obj {252				super_obj.run_assertions_raw(real_this)?;253			}254		}255		Ok(())256	}257	pub fn run_assertions(&self) -> Result<()> {258		self.run_assertions_raw(self)259	}260261	pub fn ptr_eq(a: &Self, b: &Self) -> bool {262		Gc::ptr_eq(&a.0, &b.0)263	}264}265266impl PartialEq for ObjValue {267	fn eq(&self, other: &Self) -> bool {268		Gc::ptr_eq(&self.0, &other.0)269	}270}271272impl Eq for ObjValue {}273impl Hash for ObjValue {274	fn hash<H: Hasher>(&self, hasher: &mut H) {275		hasher.write_usize(&*self.0 as *const _ as usize)276	}277}278279pub struct ObjValueBuilder {280	super_obj: Option<ObjValue>,281	map: FxHashMap<IStr, ObjMember>,282	assertions: Vec<Box<dyn ObjectAssertion>>,283}284impl ObjValueBuilder {285	pub fn new() -> Self {286		Self::with_capacity(0)287	}288	pub fn with_capacity(capacity: usize) -> Self {289		Self {290			super_obj: None,291			map: HashMap::with_capacity_and_hasher(292				capacity,293				BuildHasherDefault::<FxHasher>::default(),294			),295			assertions: Vec::new(),296		}297	}298	pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self {299		self.assertions.reserve_exact(capacity);300		self301	}302	pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {303		self.super_obj = Some(super_obj);304		self305	}306307	pub fn assert(&mut self, assertion: Box<dyn ObjectAssertion>) -> &mut Self {308		self.assertions.push(assertion);309		self310	}311	pub fn member(&mut self, name: IStr) -> ObjMemberBuilder {312		ObjMemberBuilder {313			value: self,314			name,315			add: false,316			visibility: Visibility::Normal,317			location: None,318		}319	}320321	pub fn build(self) -> ObjValue {322		ObjValue::new(self.super_obj, Gc::new(self.map), Gc::new(self.assertions))323	}324}325326#[must_use = "value not added unless binding() was called"]327pub struct ObjMemberBuilder<'v> {328	value: &'v mut ObjValueBuilder,329	name: IStr,330	add: bool,331	visibility: Visibility,332	location: Option<ExprLocation>,333}334335impl<'v> ObjMemberBuilder<'v> {336	pub fn with_add(mut self, add: bool) -> Self {337		self.add = add;338		self339	}340	pub fn add(self) -> Self {341		self.with_add(true)342	}343	pub fn with_visibility(mut self, visibility: Visibility) -> Self {344		self.visibility = visibility;345		self346	}347	pub fn hide(self) -> Self {348		self.with_visibility(Visibility::Hidden)349	}350	pub fn with_location(mut self, location: Option<ExprLocation>) -> Self {351		self.location = location;352		self353	}354	pub fn value(self, value: Val) -> &'v mut ObjValueBuilder {355		self.binding(LazyBinding::Bound(LazyVal::new_resolved(value)))356	}357	pub fn bindable(self, bindable: Box<dyn Bindable>) -> &'v mut ObjValueBuilder {358		self.binding(LazyBinding::Bindable(Gc::new(bindable)))359	}360	pub fn binding(self, binding: LazyBinding) -> &'v mut ObjValueBuilder {361		self.value.map.insert(362			self.name,363			ObjMember {364				add: self.add,365				visibility: self.visibility,366				invoke: binding,367				location: self.location,368			},369		);370		self.value371	}372}