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

difftreelog

feat immutable obj core list

mnxzylovYaroslav Bolyukin2026-04-25parent: #5fc1275.patch.diff
in: master

7 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -163,6 +163,15 @@
 checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
 
 [[package]]
+name = "bitmaps"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2"
+dependencies = [
+ "typenum",
+]
+
+[[package]]
 name = "block-buffer"
 version = "0.10.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -682,6 +691,20 @@
 checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
 
 [[package]]
+name = "im-rc"
+version = "15.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af1955a75fa080c677d3972822ec4bad316169ab1cfc6c257a942c2265dbe5fe"
+dependencies = [
+ "bitmaps",
+ "rand_core 0.6.4",
+ "rand_xoshiro",
+ "sized-chunks",
+ "typenum",
+ "version_check",
+]
+
+[[package]]
 name = "indexmap"
 version = "2.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -793,6 +816,7 @@
  "anyhow",
  "educe",
  "hi-doc",
+ "im-rc",
  "jrsonnet-gcmodule",
  "jrsonnet-interner",
  "jrsonnet-ir",
@@ -837,18 +861,19 @@
 
 [[package]]
 name = "jrsonnet-gcmodule"
-version = "0.4.2"
+version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f95b976a79e4000bb9e07ff0709dca0ea27bcf1952d4c17d91fb7364d6145683"
+checksum = "8a6a63a6e55ba82764e483d7f8a181f25db95a8f25da8ae6520e95a5fe39c6a6"
 dependencies = [
+ "im-rc",
  "jrsonnet-gcmodule-derive",
 ]
 
 [[package]]
 name = "jrsonnet-gcmodule-derive"
-version = "0.4.2"
+version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51d928626220a310ff0cec815e80cf7fe104697184352ca21c40534e0b0d72d9"
+checksum = "095fe3c4c0acf32de80205a8a479ef63c216b9efb0024dec9eb7fe1c5ef1f1a1"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1319,7 +1344,7 @@
 checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
 dependencies = [
  "rand_chacha",
- "rand_core",
+ "rand_core 0.9.5",
 ]
 
 [[package]]
@@ -1329,11 +1354,17 @@
 checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
 dependencies = [
  "ppv-lite86",
- "rand_core",
+ "rand_core 0.9.5",
 ]
 
 [[package]]
 name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+
+[[package]]
+name = "rand_core"
 version = "0.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
@@ -1342,6 +1373,15 @@
 ]
 
 [[package]]
+name = "rand_xoshiro"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
+dependencies = [
+ "rand_core 0.6.4",
+]
+
+[[package]]
 name = "random_color"
 version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1587,6 +1627,16 @@
 checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
 
 [[package]]
+name = "sized-chunks"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e"
+dependencies = [
+ "bitmaps",
+ "typenum",
+]
+
+[[package]]
 name = "smallvec"
 version = "1.15.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
modifiedCargo.tomldiffbeforeafterboth
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,7 +22,7 @@
 jrsonnet-cli = { path = "./crates/jrsonnet-cli", version = "0.5.0-pre98" }
 jrsonnet-types = { path = "./crates/jrsonnet-types", version = "0.5.0-pre98" }
 jrsonnet-formatter = { path = "./crates/jrsonnet-formatter", version = "0.5.0-pre98" }
-jrsonnet-gcmodule = { version = "0.4.2" }
+jrsonnet-gcmodule = { version = "0.4.3", features = ["im-rc"] }
 # Diagnostics.
 # hi-doc is my library, which handles text formatting very well, but isn't polished enough yet
 # Previous implementation was based on annotate-snippets, which I don't like for many reasons.
modifiedcrates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/Cargo.toml
+++ b/crates/jrsonnet-evaluator/Cargo.toml
@@ -76,6 +76,7 @@
   "Hash",
   "PartialEq",
 ] }
+im-rc = "15.1.0"
 
 [build-dependencies]
 rustversion = "1.0.22"
modifiedcrates/jrsonnet-evaluator/src/evaluate/destructure.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/destructure.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/destructure.rs
@@ -144,9 +144,7 @@
 						Thunk!(move || {
 							let full = full.evaluate()?;
 							let mut builder = ObjValueBuilder::new();
-							builder
-								.reserve_cores(1)
-								.extend_with_core(full.as_standalone());
+							builder.extend_with_core(full.as_standalone());
 							builder.with_fields_omitted(captured_fields);
 							Ok(Val::Obj(builder.build()))
 						}),
modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -410,7 +410,7 @@
 			let locals = obj.locals.clone();
 			evaluate_comp(ctx, &obj.compspecs, 0, &mut |ctx, reserve| {
 				let uctx = evaluate_object_locals(ctx.clone(), locals.clone());
-				builder.reserve_cores(reserve);
+				builder.reserve_fields(reserve);
 
 				evaluate_field_member(&mut builder, ctx, uctx, &obj.field)
 			})?;
modifiedcrates/jrsonnet-evaluator/src/obj/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/obj/mod.rs
+++ b/crates/jrsonnet-evaluator/src/obj/mod.rs
@@ -11,6 +11,7 @@
 };
 
 use educe::Educe;
+use im_rc::{Vector, vector};
 use jrsonnet_gcmodule::{Acyclic, Cc, Trace, Weak, cc_dyn};
 use jrsonnet_interner::IStr;
 use jrsonnet_ir::Span;
@@ -235,10 +236,11 @@
 	CcObjectCore, ObjectCore,
 	pub fn new() {...}
 );
+
 #[derive(Trace, Educe)]
 #[educe(Debug)]
 struct ObjValueInner {
-	cores: Vec<CcObjectCore>,
+	cores: Vector<CcObjectCore>,
 	assertions_ran: Cell<bool>,
 	has_assertions: bool,
 	value_cache: RefCell<FxHashMap<(IStr, CoreIdx), CacheValue>>,
@@ -266,7 +268,7 @@
 
 thread_local! {
 	static EMPTY_OBJ: ObjValue = ObjValue(Cc::new(ObjValueInner {
-		cores: vec![],
+		cores: vector![],
 		assertions_ran: Cell::new(true),
 		has_assertions: false,
 		value_cache: RefCell::default(),
@@ -428,7 +430,7 @@
 			bail!(NoSuperFound)
 		}
 		let mut out = ObjValue::builder();
-		out.reserve_cores(1).extend_with_core(StandaloneSuperCore {
+		out.extend_with_core(StandaloneSuperCore {
 			sup: self.sup,
 			this: self.this.clone(),
 		});
@@ -484,9 +486,7 @@
 
 	#[must_use]
 	pub fn extend_from(&self, sup: Self) -> Self {
-		let mut cores = Vec::with_capacity(sup.0.cores.len() + self.0.cores.len());
-		cores.extend(sup.0.cores.iter().cloned());
-		cores.extend(self.0.cores.iter().cloned());
+		let cores = sup.0.cores.clone() + self.0.cores.clone();
 		let has_assertions = sup.0.has_assertions || self.0.has_assertions;
 		ObjValue(Cc::new(ObjValueInner {
 			cores,
@@ -521,13 +521,27 @@
 			},
 		)
 	}
+
+	fn iter_cores(&self, idx: CoreIdx) -> impl Iterator<Item = &CcObjectCore> {
+		self.0.cores.iter().take(idx.idx).rev()
+	}
+	fn iter_cores_enumerate(&self, idx: CoreIdx) -> impl Iterator<Item = (CoreIdx, &CcObjectCore)> {
+		self.0
+			.cores
+			.iter()
+			.take(idx.idx)
+			.enumerate()
+			.rev()
+			.map(|(idx, o)| (CoreIdx { idx }, o))
+	}
+
 	fn enum_fields_idx(
 		&self,
 		super_depth: &mut SuperDepth,
 		handler: &mut EnumFieldsHandler<'_>,
 		idx: CoreIdx,
 	) -> bool {
-		for core in self.0.cores[..idx.idx].iter().rev() {
+		for core in self.iter_cores(idx) {
 			if !core.0.enum_fields_core(super_depth, handler) {
 				return false;
 			}
@@ -546,7 +560,7 @@
 	}
 	fn has_field_include_hidden_idx(&self, name: IStr, core: CoreIdx) -> bool {
 		let mut skip = Saturating(0usize);
-		for ele in self.0.cores[..core.idx].iter().rev() {
+		for ele in self.iter_cores(core) {
 			match ele.0.has_field_include_hidden_core(name.clone()) {
 				HasFieldIncludeHidden::Exists => {
 					if skip.0 == 0 {
@@ -616,9 +630,9 @@
 		let mut first_add = None;
 		let mut add_stack: Vec<Val> = Vec::new();
 		let mut skip = Saturating(0);
-		for (sup, core) in self.0.cores[..core.idx].iter().enumerate().rev() {
+		for (sup, core) in self.iter_cores_enumerate(core) {
 			let sup_this = SupThis {
-				sup: CoreIdx { idx: sup },
+				sup,
 				this: self.clone(),
 			};
 			match core.0.get_for_core(key.clone(), sup_this, skip.0 != 0)? {
@@ -686,7 +700,7 @@
 	fn field_visibility_idx(&self, field: IStr, core: CoreIdx) -> Option<Visibility> {
 		let mut exists = false;
 		let mut skip = Saturating(0usize);
-		for ele in self.0.cores[..core.idx].iter().rev() {
+		for ele in self.iter_cores(core) {
 			let vis = ele.0.field_visibility_core(field.clone());
 			match vis {
 				FieldVisibility::Found(vis @ (Visibility::Unhide | Visibility::Hidden)) => {
modifiedcrates/jrsonnet-evaluator/src/obj/oop.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/obj/oop.rs
1use std::{2	cell::{Cell, RefCell},3	fmt, mem,4	ops::ControlFlow,5};67use jrsonnet_gcmodule::{Cc, Trace};8use jrsonnet_ir::IStr;9use rustc_hash::{FxHashMap, FxHashSet};1011use super::{12	CcObjectAssertion, CcObjectCore, EnumFields, EnumFieldsHandler, FieldVisibility, GetFor,13	HasFieldIncludeHidden, ObjMember, ObjMemberBuilder, ObjValue, ObjValueInner, ObjectAssertion,14	ObjectCore, OmitFieldsCore, SupThis,15	ordering::{FieldIndex, SuperDepth},16};17use crate::{18	CcUnbound, MaybeUnbound, Result, Thunk, Unbound, Val, bail,19	error::ErrorKind::*,20	function::{CallLocation, FuncVal},21	gc::WithCapacityExt as _,22	in_frame,23};2425#[allow(clippy::module_name_repetitions)]26#[derive(Trace, Default)]27#[trace(tracking(force))]28pub struct OopObject {29	assertion: Option<CcObjectAssertion>,30	this_entries: FxHashMap<IStr, ObjMember>,31}32impl fmt::Debug for OopObject {33	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {34		f.debug_struct("OopObject")35			.field("this_entries", &self.this_entries)36			.finish_non_exhaustive()37	}38}39impl OopObject {40	fn is_empty(&self) -> bool {41		self.assertion.is_none() && self.this_entries.is_empty()42	}43}44impl OopObject {45	pub fn new(46		this_entries: FxHashMap<IStr, ObjMember>,47		assertion: Option<CcObjectAssertion>,48	) -> Self {49		Self {50			assertion,51			this_entries,52		}53	}54}5556impl ObjectCore for OopObject {57	fn enum_fields_core(58		&self,59		super_depth: &mut SuperDepth,60		handler: &mut EnumFieldsHandler<'_>,61	) -> bool {62		for (name, member) in &self.this_entries {63			if matches!(64				handler(65					*super_depth,66					member.original_index,67					name.clone(),68					EnumFields::Normal(member.flags.visibility()),69				),70				ControlFlow::Break(())71			) {72				return false;73			}74		}75		true76	}7778	fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden {79		if self.this_entries.contains_key(&name) {80			HasFieldIncludeHidden::Exists81		} else {82			HasFieldIncludeHidden::NotFound83		}84	}8586	fn get_for_core(&self, key: IStr, sup_this: SupThis, omit_only: bool) -> Result<GetFor> {87		if omit_only {88			return Ok(GetFor::NotFound);89		}90		match self.this_entries.get(&key) {91			Some(k) => {92				let v = k.invoke.evaluate(sup_this)?;93				Ok(if k.flags.add() {94					GetFor::SuperPlus(v)95				} else {96					GetFor::Final(v)97				})98			}99			None => Ok(GetFor::NotFound),100		}101	}102	fn field_visibility_core(&self, name: IStr) -> FieldVisibility {103		self.this_entries104			.get(&name)105			.map_or(FieldVisibility::NotFound, |f| {106				FieldVisibility::Found(f.flags.visibility())107			})108	}109110	fn run_assertions_core(&self, sup_this: SupThis) -> Result<()> {111		if let Some(assertion) = &self.assertion {112			assertion.0.run(sup_this)?;113		}114		Ok(())115	}116}117118#[allow(clippy::module_name_repetitions)]119pub struct ObjValueBuilder {120	sup: Vec<CcObjectCore>,121	has_assertions: bool,122123	new: OopObject,124	next_field_index: FieldIndex,125}126impl ObjValueBuilder {127	pub fn new() -> Self {128		Self::with_capacity(0)129	}130	pub fn with_capacity(capacity: usize) -> Self {131		Self {132			sup: vec![],133			has_assertions: false,134			new: OopObject::new(FxHashMap::with_capacity(capacity), None),135			next_field_index: FieldIndex::default(),136		}137	}138	pub fn reserve_fields(&mut self, capacity: usize) {139		self.new.this_entries.reserve(capacity);140	}141	pub fn reserve_cores(&mut self, capacity: usize) -> &mut Self {142		self.sup.reserve_exact(capacity);143		self144	}145	pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {146		self.has_assertions |= super_obj.0.has_assertions;147		self.sup.clone_from(&super_obj.0.cores);148		self149	}150151	pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {152		assert!(153			self.new.assertion.is_none(),154			"one OopObject can only have one assertion"155		);156		self.has_assertions = true;157		self.new.assertion = Some(CcObjectAssertion::new(assertion));158		self159	}160	pub fn field(&mut self, name: impl Into<IStr>) -> ObjMemberBuilder<ValueBuilder<'_>> {161		let field_index = self.next_field_index;162		self.next_field_index = self.next_field_index.next();163		ObjMemberBuilder::new(ValueBuilder(self), name.into(), field_index)164	}165	/// Preset for common method definiton pattern:166	/// Create a hidden field with the function value.167	///168	/// `.field(name).hide().value(Val::function(value))`169	pub fn method(&mut self, name: impl Into<IStr>, value: impl Into<FuncVal>) -> &mut Self {170		self.field(name).hide().value(Val::Func(value.into()));171		self172	}173	pub fn try_method(174		&mut self,175		name: impl Into<IStr>,176		value: impl Into<FuncVal>,177	) -> Result<&mut Self> {178		self.field(name).hide().try_value(Val::Func(value.into()))?;179		Ok(self)180	}181182	pub fn extend_with_core(&mut self, core: impl ObjectCore) {183		self.commit();184		self.sup.push(CcObjectCore::new(core));185	}186187	fn commit(&mut self) {188		if !self.new.is_empty() {189			self.sup.push(CcObjectCore::new(mem::take(&mut self.new)));190		}191		self.next_field_index = FieldIndex::default();192	}193194	pub fn with_fields_omitted(&mut self, omit: FxHashSet<IStr>) {195		self.commit();196		self.sup.push(CcObjectCore::new(OmitFieldsCore {197			omit,198			prev_layers: self.sup.len(),199		}));200	}201202	pub fn build(mut self) -> ObjValue {203		self.commit();204		if self.sup.is_empty() {205			return ObjValue::empty();206		}207		let has_assertions = self.has_assertions;208		ObjValue(Cc::new(ObjValueInner {209			cores: self.sup,210			assertions_ran: Cell::new(!has_assertions),211			has_assertions,212			value_cache: RefCell::default(),213		}))214	}215}216impl Default for ObjValueBuilder {217	fn default() -> Self {218		Self::with_capacity(0)219	}220}221222pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);223impl ObjMemberBuilder<ValueBuilder<'_>> {224	/// Inserts value, replacing if it is already defined225	pub fn value(self, value: impl Into<Val>) {226		let (receiver, name, member) =227			self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value.into())));228		let entry = receiver.0.new.this_entries.entry(name);229		entry.insert_entry(member);230	}231	/// Inserts thunk, replacing if it is already defined232	pub fn thunk(self, value: impl Into<Thunk<Val>>) {233		let (receiver, name, member) = self.build_member(MaybeUnbound::Bound(value.into()));234		let entry = receiver.0.new.this_entries.entry(name);235		entry.insert_entry(member);236	}237238	/// Tries to insert value, returns an error if it was already defined239	pub fn try_value(self, value: impl Into<Val>) -> Result<()> {240		self.try_thunk(Thunk::evaluated(value.into()))241	}242	pub fn try_thunk(self, value: impl Into<Thunk<Val>>) -> Result<()> {243		self.binding(MaybeUnbound::Bound(value.into()))244	}245	pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {246		self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)))247	}248	pub fn binding(self, binding: MaybeUnbound) -> Result<()> {249		let (receiver, name, member) = self.build_member(binding);250		let location = member.location.clone();251		let old = receiver.0.new.this_entries.insert(name.clone(), member);252		if old.is_some() {253			in_frame(254				CallLocation(location.as_ref()),255				|| format!("field <{}> initializtion", name.clone()),256				|| bail!(DuplicateFieldName(name.clone())),257			)?;258		}259		Ok(())260	}261}
after · crates/jrsonnet-evaluator/src/obj/oop.rs
1use std::{2	cell::{Cell, RefCell},3	fmt, mem,4	ops::ControlFlow,5};67use im_rc::Vector;8use jrsonnet_gcmodule::{Cc, Trace};9use jrsonnet_ir::IStr;10use rustc_hash::{FxHashMap, FxHashSet};1112use super::{13	CcObjectAssertion, CcObjectCore, EnumFields, EnumFieldsHandler, FieldVisibility, GetFor,14	HasFieldIncludeHidden, ObjMember, ObjMemberBuilder, ObjValue, ObjValueInner, ObjectAssertion,15	ObjectCore, OmitFieldsCore, SupThis,16	ordering::{FieldIndex, SuperDepth},17};18use crate::{19	CcUnbound, MaybeUnbound, Result, Thunk, Unbound, Val, bail,20	error::ErrorKind::*,21	function::{CallLocation, FuncVal},22	gc::WithCapacityExt as _,23	in_frame,24};2526#[allow(clippy::module_name_repetitions)]27#[derive(Trace, Default)]28#[trace(tracking(force))]29pub struct OopObject {30	assertion: Option<CcObjectAssertion>,31	this_entries: FxHashMap<IStr, ObjMember>,32}33impl fmt::Debug for OopObject {34	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {35		f.debug_struct("OopObject")36			.field("this_entries", &self.this_entries)37			.finish_non_exhaustive()38	}39}40impl OopObject {41	fn is_empty(&self) -> bool {42		self.assertion.is_none() && self.this_entries.is_empty()43	}44}45impl OopObject {46	pub fn new(47		this_entries: FxHashMap<IStr, ObjMember>,48		assertion: Option<CcObjectAssertion>,49	) -> Self {50		Self {51			assertion,52			this_entries,53		}54	}55}5657impl ObjectCore for OopObject {58	fn enum_fields_core(59		&self,60		super_depth: &mut SuperDepth,61		handler: &mut EnumFieldsHandler<'_>,62	) -> bool {63		for (name, member) in &self.this_entries {64			if matches!(65				handler(66					*super_depth,67					member.original_index,68					name.clone(),69					EnumFields::Normal(member.flags.visibility()),70				),71				ControlFlow::Break(())72			) {73				return false;74			}75		}76		true77	}7879	fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden {80		if self.this_entries.contains_key(&name) {81			HasFieldIncludeHidden::Exists82		} else {83			HasFieldIncludeHidden::NotFound84		}85	}8687	fn get_for_core(&self, key: IStr, sup_this: SupThis, omit_only: bool) -> Result<GetFor> {88		if omit_only {89			return Ok(GetFor::NotFound);90		}91		match self.this_entries.get(&key) {92			Some(k) => {93				let v = k.invoke.evaluate(sup_this)?;94				Ok(if k.flags.add() {95					GetFor::SuperPlus(v)96				} else {97					GetFor::Final(v)98				})99			}100			None => Ok(GetFor::NotFound),101		}102	}103	fn field_visibility_core(&self, name: IStr) -> FieldVisibility {104		self.this_entries105			.get(&name)106			.map_or(FieldVisibility::NotFound, |f| {107				FieldVisibility::Found(f.flags.visibility())108			})109	}110111	fn run_assertions_core(&self, sup_this: SupThis) -> Result<()> {112		if let Some(assertion) = &self.assertion {113			assertion.0.run(sup_this)?;114		}115		Ok(())116	}117}118119#[allow(clippy::module_name_repetitions)]120pub struct ObjValueBuilder {121	sup: Vector<CcObjectCore>,122	has_assertions: bool,123124	new: OopObject,125	next_field_index: FieldIndex,126}127impl ObjValueBuilder {128	pub fn new() -> Self {129		Self::with_capacity(0)130	}131	pub fn with_capacity(capacity: usize) -> Self {132		Self {133			sup: Vector::new(),134			has_assertions: false,135			new: OopObject::new(FxHashMap::with_capacity(capacity), None),136			next_field_index: FieldIndex::default(),137		}138	}139	pub fn reserve_fields(&mut self, capacity: usize) {140		self.new.this_entries.reserve(capacity);141	}142	pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {143		self.has_assertions |= super_obj.0.has_assertions;144		self.sup = super_obj.0.cores.clone();145		self146	}147148	pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {149		assert!(150			self.new.assertion.is_none(),151			"one OopObject can only have one assertion"152		);153		self.has_assertions = true;154		self.new.assertion = Some(CcObjectAssertion::new(assertion));155		self156	}157	pub fn field(&mut self, name: impl Into<IStr>) -> ObjMemberBuilder<ValueBuilder<'_>> {158		let field_index = self.next_field_index;159		self.next_field_index = self.next_field_index.next();160		ObjMemberBuilder::new(ValueBuilder(self), name.into(), field_index)161	}162	/// Preset for common method definiton pattern:163	/// Create a hidden field with the function value.164	///165	/// `.field(name).hide().value(Val::function(value))`166	pub fn method(&mut self, name: impl Into<IStr>, value: impl Into<FuncVal>) -> &mut Self {167		self.field(name).hide().value(Val::Func(value.into()));168		self169	}170	pub fn try_method(171		&mut self,172		name: impl Into<IStr>,173		value: impl Into<FuncVal>,174	) -> Result<&mut Self> {175		self.field(name).hide().try_value(Val::Func(value.into()))?;176		Ok(self)177	}178179	pub fn extend_with_core(&mut self, core: impl ObjectCore) {180		self.commit();181		self.sup.push_back(CcObjectCore::new(core));182	}183184	fn commit(&mut self) {185		if !self.new.is_empty() {186			self.sup187				.push_back(CcObjectCore::new(mem::take(&mut self.new)));188		}189		self.next_field_index = FieldIndex::default();190	}191192	pub fn with_fields_omitted(&mut self, omit: FxHashSet<IStr>) {193		self.commit();194		self.sup.push_back(CcObjectCore::new(OmitFieldsCore {195			omit,196			prev_layers: self.sup.len(),197		}));198	}199200	pub fn build(mut self) -> ObjValue {201		self.commit();202		if self.sup.is_empty() {203			return ObjValue::empty();204		}205		let has_assertions = self.has_assertions;206		ObjValue(Cc::new(ObjValueInner {207			cores: self.sup,208			assertions_ran: Cell::new(!has_assertions),209			has_assertions,210			value_cache: RefCell::default(),211		}))212	}213}214impl Default for ObjValueBuilder {215	fn default() -> Self {216		Self::with_capacity(0)217	}218}219220pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);221impl ObjMemberBuilder<ValueBuilder<'_>> {222	/// Inserts value, replacing if it is already defined223	pub fn value(self, value: impl Into<Val>) {224		let (receiver, name, member) =225			self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value.into())));226		let entry = receiver.0.new.this_entries.entry(name);227		entry.insert_entry(member);228	}229	/// Inserts thunk, replacing if it is already defined230	pub fn thunk(self, value: impl Into<Thunk<Val>>) {231		let (receiver, name, member) = self.build_member(MaybeUnbound::Bound(value.into()));232		let entry = receiver.0.new.this_entries.entry(name);233		entry.insert_entry(member);234	}235236	/// Tries to insert value, returns an error if it was already defined237	pub fn try_value(self, value: impl Into<Val>) -> Result<()> {238		self.try_thunk(Thunk::evaluated(value.into()))239	}240	pub fn try_thunk(self, value: impl Into<Thunk<Val>>) -> Result<()> {241		self.binding(MaybeUnbound::Bound(value.into()))242	}243	pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {244		self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)))245	}246	pub fn binding(self, binding: MaybeUnbound) -> Result<()> {247		let (receiver, name, member) = self.build_member(binding);248		let location = member.location.clone();249		let old = receiver.0.new.this_entries.insert(name.clone(), member);250		if old.is_some() {251			in_frame(252				CallLocation(location.as_ref()),253				|| format!("field <{}> initializtion", name.clone()),254				|| bail!(DuplicateFieldName(name.clone())),255			)?;256		}257		Ok(())258	}259}