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

difftreelog

source

crates/jrsonnet-evaluator/src/obj.rs30.7 KiBsourcehistory
1use std::{2	any::Any,3	cell::{Cell, RefCell},4	collections::hash_map::Entry,5	fmt::{self, Debug},6	hash::{Hash, Hasher},7	mem,8	num::Saturating,9	ops::ControlFlow,10};1112use educe::Educe;13use jrsonnet_gcmodule::{cc_dyn, Acyclic, Cc, Trace, Weak};14use jrsonnet_interner::IStr;15use jrsonnet_parser::{Span, Visibility};16use rustc_hash::{FxHashMap, FxHashSet};1718use crate::{19	arr::{PickObjectKeyValues, PickObjectValues},20	bail,21	error::{suggest_object_fields, ErrorKind::*},22	function::{CallLocation, FuncVal},23	gc::WithCapacityExt as _,24	identity_hash, in_frame,25	operator::evaluate_add_op,26	val::{ArrValue, ThunkValue},27	CcUnbound, MaybeUnbound, Result, Thunk, Unbound, Val,28};2930#[cfg(not(feature = "exp-preserve-order"))]31mod ordering {32	#![allow(33		// This module works as stub for preserve-order feature34		clippy::unused_self,35	)]3637	use jrsonnet_gcmodule::Trace;3839	#[derive(Clone, Copy, Default, Debug, Trace)]40	pub struct FieldIndex(());41	impl FieldIndex {42		pub const fn next(self) -> Self {43			Self(())44		}45	}4647	#[derive(Clone, Copy, Default, Debug, Trace)]48	pub struct SuperDepth(());49	impl SuperDepth {50		pub(super) fn deepen(self) {}51	}52}5354#[cfg(feature = "exp-preserve-order")]55mod ordering {56	use std::cmp::Reverse;5758	use jrsonnet_gcmodule::Trace;5960	#[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]61	pub struct FieldIndex(u32);62	impl FieldIndex {63		pub fn next(self) -> Self {64			Self(self.0 + 1)65		}66	}6768	#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Debug)]69	pub struct SuperDepth(u32);70	impl SuperDepth {71		pub(super) fn deepen(&mut self) {72			self.0 += 173		}74	}7576	#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]77	pub struct FieldSortKey(Reverse<SuperDepth>, FieldIndex);78	impl FieldSortKey {79		pub fn new(depth: SuperDepth, index: FieldIndex) -> Self {80			Self(Reverse(depth), index)81		}82	}83}8485#[cfg(feature = "exp-preserve-order")]86use ordering::FieldSortKey;87use ordering::{FieldIndex, SuperDepth};8889// 0 - add90//  12 - visibility91#[derive(Clone, Copy)]92pub struct ObjFieldFlags(u8);93impl ObjFieldFlags {94	fn new(add: bool, visibility: Visibility) -> Self {95		let mut v = 0;96		if add {97			v |= 1;98		}99		v |= match visibility {100			Visibility::Normal => 0b000,101			Visibility::Hidden => 0b010,102			Visibility::Unhide => 0b100,103		};104		Self(v)105	}106	pub fn add(&self) -> bool {107		self.0 & 1 != 0108	}109	pub fn visibility(&self) -> Visibility {110		match (self.0 & 0b110) >> 1 {111			0b00 => Visibility::Normal,112			0b01 => Visibility::Hidden,113			0b10 => Visibility::Unhide,114			_ => unreachable!(),115		}116	}117}118impl Debug for ObjFieldFlags {119	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {120		f.debug_struct("ObjFieldFlags")121			.field("add", &self.add())122			.field("visibility", &self.visibility())123			.finish()124	}125}126127#[allow(clippy::module_name_repetitions)]128#[derive(Debug, Trace)]129pub struct ObjMember {130	#[trace(skip)]131	flags: ObjFieldFlags,132	original_index: FieldIndex,133	pub invoke: MaybeUnbound,134	pub location: Option<Span>,135}136137cc_dyn!(CcObjectAssertion, ObjectAssertion);138pub trait ObjectAssertion: Trace {139	fn run(&self, sup_this: SupThis) -> Result<()>;140}141142// Field => This143144#[derive(Trace, Debug)]145enum CacheValue {146	Cached(Result<Option<Val>>),147	Pending,148}149150#[allow(clippy::module_name_repetitions)]151#[derive(Trace, Default)]152#[trace(tracking(force))]153pub struct OopObject {154	assertion: Option<CcObjectAssertion>,155	this_entries: FxHashMap<IStr, ObjMember>,156}157impl Debug for OopObject {158	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {159		f.debug_struct("OopObject")160			.field("this_entries", &self.this_entries)161			.finish_non_exhaustive()162	}163}164impl OopObject {165	fn is_empty(&self) -> bool {166		self.assertion.is_none() && self.this_entries.is_empty()167	}168}169170type EnumFieldsHandler<'a> =171	dyn FnMut(SuperDepth, FieldIndex, IStr, EnumFields) -> ControlFlow<()> + 'a;172173pub enum EnumFields {174	Normal(Visibility),175	Omit(Skip),176}177178#[derive(Trace, Clone)]179pub enum GetFor {180	// Return value181	Final(Val),182	// Continue iterating over cores, add current value to sum stack183	SuperPlus(Val),184	// Ignore the field value, stop at this layer instead185	Omit(#[trace(skip)] Skip),186	NotFound,187}188189#[derive(Acyclic, Clone)]190pub enum FieldVisibility {191	Found(Visibility),192	Omit(Skip),193	NotFound,194}195196#[derive(Acyclic, Clone)]197pub enum HasFieldIncludeHidden {198	Exists,199	NotFound,200	Omit(Skip),201}202203type Skip = Saturating<usize>;204205pub trait ObjectCore: Trace + Any + Debug {206	// If callback returns false, iteration stops, and this call returns false.207	fn enum_fields_core(208		&self,209		super_depth: &mut SuperDepth,210		handler: &mut EnumFieldsHandler<'_>,211	) -> bool;212213	fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden;214215	fn get_for_core(&self, key: IStr, sup_this: SupThis, omit_only: bool) -> Result<GetFor>;216	fn field_visibility_core(&self, field: IStr) -> FieldVisibility;217218	fn run_assertions_core(&self, sup_this: SupThis) -> Result<()>;219}220221#[derive(Clone, Trace)]222pub struct WeakObjValue(#[trace(skip)] Weak<ObjValueInner>);223impl Debug for WeakObjValue {224	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {225		f.debug_tuple("WeakObjValue").finish()226	}227}228229impl PartialEq for WeakObjValue {230	fn eq(&self, other: &Self) -> bool {231		Weak::ptr_eq(&self.0, &other.0)232	}233}234235impl Eq for WeakObjValue {}236impl Hash for WeakObjValue {237	fn hash<H: Hasher>(&self, hasher: &mut H) {238		// Safety: usize is POD239		let addr = unsafe { *std::ptr::addr_of!(self.0).cast() };240		hasher.write_usize(addr);241	}242}243244cc_dyn!(245	#[derive(Clone, Debug)]246	CcObjectCore, ObjectCore,247	pub fn new() {...}248);249#[derive(Trace, Educe)]250#[educe(Debug)]251struct ObjValueInner {252	cores: Vec<CcObjectCore>,253	assertions_ran: Cell<bool>,254	value_cache: RefCell<FxHashMap<(IStr, CoreIdx), CacheValue>>,255}256257thread_local! {258	static RUNNING_ASSERTIONS: RefCell<FxHashSet<ObjValue>> = RefCell::default();259}260fn is_asserting(obj: &ObjValue) -> bool {261	RUNNING_ASSERTIONS.with_borrow(|v| v.contains(obj))262}263/// Returns false if already asserting264fn start_asserting(obj: &ObjValue) -> bool {265	RUNNING_ASSERTIONS.with_borrow_mut(|v| v.insert(obj.clone()))266}267fn finish_asserting(obj: &ObjValue) {268	RUNNING_ASSERTIONS.with_borrow_mut(|v| {269		let r = v.remove(obj);270		debug_assert!(271			r,272			"finish_asserting was called before start_asserting or twice"273		);274	});275}276277thread_local! {278	static EMPTY_OBJ: ObjValue = ObjValue(Cc::new(ObjValueInner {279		cores: vec![],280		assertions_ran: Cell::new(true),281		value_cache: Default::default(),282	}))283}284285#[allow(clippy::module_name_repetitions)]286#[derive(Clone, Trace, Debug, Educe)]287#[educe(PartialEq, Hash, Eq)]288pub struct ObjValue(289	#[educe(PartialEq(method(Cc::ptr_eq)), Hash(method(identity_hash)))] Cc<ObjValueInner>,290);291292impl ObjValue {293	pub fn empty() -> Self {294		EMPTY_OBJ.with(|v| v.clone())295	}296	pub fn is_empty(&self) -> bool {297		self.0.cores.is_empty() || self.len() == 0298	}299}300301#[derive(Trace, Debug)]302struct StandaloneSuperCore {303	sup: CoreIdx,304	this: ObjValue,305}306impl ObjectCore for StandaloneSuperCore {307	fn enum_fields_core(308		&self,309		super_depth: &mut SuperDepth,310		handler: &mut EnumFieldsHandler<'_>,311	) -> bool {312		self.this.enum_fields_idx(super_depth, handler, self.sup)313	}314315	fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden {316		if self.this.has_field_include_hidden_idx(name, self.sup) {317			HasFieldIncludeHidden::Exists318		} else {319			HasFieldIncludeHidden::NotFound320		}321	}322323	fn get_for_core(&self, key: IStr, _sup_this: SupThis, omit_only: bool) -> Result<GetFor> {324		if omit_only {325			return Ok(GetFor::NotFound);326		}327		let v = self.this.get_idx(key, self.sup)?;328		Ok(v.map_or(GetFor::NotFound, |v| GetFor::Final(v)))329	}330331	fn field_visibility_core(&self, field: IStr) -> FieldVisibility {332		match self.this.field_visibility_idx(field, self.sup) {333			Some(c) => FieldVisibility::Found(c),334			None => FieldVisibility::NotFound,335		}336	}337338	fn run_assertions_core(&self, _sup_this: SupThis) -> Result<()> {339		self.this.run_assertions()340	}341}342343#[derive(Debug, Acyclic)]344struct OmitFieldsCore {345	omit: FxHashSet<IStr>,346	prev_layers: usize,347}348impl ObjectCore for OmitFieldsCore {349	fn enum_fields_core(350		&self,351		super_depth: &mut SuperDepth,352		handler: &mut EnumFieldsHandler<'_>,353	) -> bool {354		let mut fi = FieldIndex::default();355		for f in &self.omit {356			if let ControlFlow::Break(()) = handler(357				*super_depth,358				fi,359				f.clone(),360				EnumFields::Omit(Saturating(self.prev_layers)),361			) {362				return false;363			}364			fi = fi.next();365		}366		true367	}368369	fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden {370		if self.omit.contains(&name) {371			return HasFieldIncludeHidden::Omit(Saturating(self.prev_layers));372		}373		HasFieldIncludeHidden::NotFound374	}375376	fn get_for_core(&self, key: IStr, _sup_this: SupThis, _omit_only: bool) -> Result<GetFor> {377		if self.omit.contains(&key) {378			return Ok(GetFor::Omit(Saturating(self.prev_layers)));379		}380		Ok(GetFor::NotFound)381	}382383	fn field_visibility_core(&self, field: IStr) -> FieldVisibility {384		if self.omit.contains(&field) {385			return FieldVisibility::Omit(Saturating(self.prev_layers));386		}387		FieldVisibility::NotFound388	}389390	fn run_assertions_core(&self, _sup_this: SupThis) -> Result<()> {391		Ok(())392	}393}394395#[derive(Hash, PartialEq, Eq, Trace, Clone, Copy, Debug)]396struct CoreIdx {397	idx: usize,398}399impl CoreIdx {400	fn super_exists(self) -> bool {401		self.idx != 0402	}403}404#[derive(Trace, Clone, PartialEq, Eq, Hash, Debug)]405pub struct SupThis {406	sup: CoreIdx,407	this: ObjValue,408}409impl SupThis {410	pub fn has_super(&self) -> bool {411		self.sup.super_exists()412	}413	/// Implementation of `"field" in super` operation,414	/// works faster than standalone super path.415	///416	/// In case of no `super` existence, returns false.417	pub fn field_in_super(&self, field: IStr) -> bool {418		self.this.has_field_include_hidden_idx(field, self.sup)419	}420	/// Implementation of `super.field` operation,421	/// works faster than standalone super path.422	///423	/// In case of no `super` existence, returns `NoSuperFound`424	pub fn get_super(&self, field: IStr) -> Result<Option<Val>> {425		if !self.sup.super_exists() {426			bail!(NoSuperFound);427		}428		self.this.get_idx(field, self.sup)429	}430	/// `super` with `self` overriden for top-level lookups.431	/// Exists when super appears outside of `super.field`/`"field" in super` expressions432	/// Exclusive to jrsonnet.433	///434	/// Might return `NoSuperFound` error.435	pub fn standalone_super(&self) -> Result<ObjValue> {436		if !self.sup.super_exists() {437			bail!(NoSuperFound)438		}439		let mut out = ObjValue::builder();440		out.reserve_cores(1).extend_with_core(StandaloneSuperCore {441			sup: self.sup,442			this: self.this.clone(),443		});444		Ok(out.build())445	}446	pub fn this(&self) -> &ObjValue {447		&self.this448	}449	pub fn downgrade(self) -> WeakSupThis {450		WeakSupThis {451			sup: self.sup,452			this: self.this.downgrade(),453		}454	}455}456#[derive(Trace, PartialEq, Eq, Hash, Debug)]457pub struct WeakSupThis {458	sup: CoreIdx,459	this: WeakObjValue,460}461462impl ObjValue {463	pub fn builder() -> ObjValueBuilder {464		ObjValueBuilder::new()465	}466	pub fn builder_with_capacity(capacity: usize) -> ObjValueBuilder {467		ObjValueBuilder::with_capacity(capacity)468	}469	pub(crate) fn extend_with_raw_member(self, key: IStr, value: ObjMember) -> Self {470		let mut out = ObjValueBuilder::with_capacity(1);471		out.with_super(self);472		let mut member = out.field(key);473		if value.flags.add() {474			member = member.add();475		}476		if let Some(loc) = value.location {477			member = member.with_location(loc);478		}479		let _ = member480			.with_visibility(value.flags.visibility())481			.binding(value.invoke);482		out.build()483	}484	pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder<'_>> {485		ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())486	}487488	pub fn extend(&mut self) -> ObjValueBuilder {489		let mut out = ObjValueBuilder::new();490		out.with_super(self.clone());491		out492	}493494	#[must_use]495	pub fn extend_from(&self, sup: Self) -> Self {496		let mut cores = sup.0.cores.clone();497		cores.extend(self.0.cores.iter().cloned());498		ObjValue(Cc::new(ObjValueInner {499			cores,500			value_cache: RefCell::default(),501			assertions_ran: Cell::new(false),502		}))503	}504	// #[must_use]505	// pub fn with_this(&self, this: Self) -> Self {506	// 	self.0.with_this(self.clone(), this)507	// }508	/// Returns amount of visible object fields509	/// If object only contains hidden fields - may return zero.510	pub fn len(&self) -> usize {511		self.fields_visibility()512			.values()513			.filter(|d| d.visible())514			.count()515	}516	/// For each field, calls callback.517	/// If callback returns false - ends iteration prematurely.518	///519	/// Returns false if ended prematurely520	pub fn enum_fields(&self, handler: &mut EnumFieldsHandler<'_>) -> bool {521		let mut super_depth = SuperDepth::default();522		self.enum_fields_idx(523			&mut super_depth,524			handler,525			CoreIdx {526				idx: self.0.cores.len(),527			},528		)529	}530	fn enum_fields_idx(531		&self,532		super_depth: &mut SuperDepth,533		handler: &mut EnumFieldsHandler<'_>,534		idx: CoreIdx,535	) -> bool {536		for core in self.0.cores[..idx.idx].iter().rev() {537			if !core.0.enum_fields_core(super_depth, handler) {538				return false;539			}540			super_depth.deepen();541		}542		true543	}544545	pub fn has_field_include_hidden(&self, name: IStr) -> bool {546		self.has_field_include_hidden_idx(547			name,548			CoreIdx {549				idx: self.0.cores.len(),550			},551		)552	}553	fn has_field_include_hidden_idx(&self, name: IStr, core: CoreIdx) -> bool {554		let mut skip = Saturating(0usize);555		for ele in self.0.cores[..core.idx].iter().rev() {556			match ele.0.has_field_include_hidden_core(name.clone()) {557				HasFieldIncludeHidden::Exists => {558					if skip.0 == 0 {559						return true;560					}561				}562				HasFieldIncludeHidden::Omit(new_skip) => {563					// +1 including this core564					skip = skip.max(new_skip + Saturating(1));565				}566				HasFieldIncludeHidden::NotFound => {}567			}568			skip -= 1;569		}570		false571	}572	pub fn has_field(&self, name: IStr) -> bool {573		match self.field_visibility(name) {574			Some(Visibility::Unhide | Visibility::Normal) => true,575			Some(Visibility::Hidden) | None => false,576		}577	}578	pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {579		if include_hidden {580			self.has_field_include_hidden(name)581		} else {582			self.has_field(name)583		}584	}585	pub fn get(&self, key: IStr) -> Result<Option<Val>> {586		self.get_idx(587			key,588			CoreIdx {589				idx: self.0.cores.len(),590			},591		)592	}593594	fn get_idx(&self, key: IStr, core: CoreIdx) -> Result<Option<Val>> {595		let cache_key = (key.clone(), core);596		{597			let mut cache = self.0.value_cache.borrow_mut();598			// entry_ref candidate?599			match cache.entry(cache_key.clone()) {600				Entry::Occupied(v) => match v.get() {601					CacheValue::Cached(v) => return v.clone(),602					CacheValue::Pending => {603						if !is_asserting(self) {604							bail!(InfiniteRecursionDetected);605						}606					}607				},608				Entry::Vacant(v) => {609					v.insert(CacheValue::Pending);610				}611			};612		}613		let result = self.get_idx_uncached(key, core);614		{615			let mut cache = self.0.value_cache.borrow_mut();616			cache.insert(cache_key, CacheValue::Cached(result.clone()));617		}618		result619	}620	fn get_idx_uncached(&self, key: IStr, core: CoreIdx) -> Result<Option<Val>> {621		self.run_assertions()?;622		let mut add_stack = Vec::with_capacity(2);623		let mut skip = Saturating(0);624		for (sup, core) in self.0.cores[..core.idx].iter().enumerate().rev() {625			let sup_this = SupThis {626				sup: CoreIdx { idx: sup },627				this: self.clone(),628			};629			match core.0.get_for_core(key.clone(), sup_this, skip.0 != 0)? {630				GetFor::Final(val) if add_stack.is_empty() => {631					if skip.0 == 0 {632						return Ok(Some(val));633					}634				}635				GetFor::Final(val) => {636					if skip.0 == 0 {637						add_stack.push(val);638						break;639					}640				}641				GetFor::SuperPlus(val) => {642					if skip.0 == 0 {643						add_stack.push(val);644					}645				}646				GetFor::Omit(new_skip) => {647					// +1 including this core648					skip = skip.max(new_skip + Saturating(1));649				}650				GetFor::NotFound => {}651			}652			skip -= 1;653		}654		if add_stack.is_empty() {655			// None of layers had this field656			return Ok(None);657		} else if add_stack.len() == 1 {658			// A layer had this field, but it wanted this field to be added with super.659			// However, no super had this field, fail-safe660			return Ok(Some(add_stack.pop().expect("single element on stack")));661		}662		let mut values = add_stack.into_iter().rev();663		let init = values.next().expect("at least 2 elements");664665		values666			.try_fold(init, |a, b| evaluate_add_op(&a, &b))667			.map(Some)668669		// self.0.get_raw(key, this)670	}671672	pub fn get_or_bail(&self, key: IStr) -> Result<Val> {673		let Some(value) = self.get(key.clone())? else {674			let suggestions = suggest_object_fields(self, key.clone());675			bail!(NoSuchField(key, suggestions))676		};677		Ok(value)678	}679680	fn field_visibility(&self, field: IStr) -> Option<Visibility> {681		self.field_visibility_idx(682			field,683			CoreIdx {684				idx: self.0.cores.len(),685			},686		)687	}688	fn field_visibility_idx(&self, field: IStr, core: CoreIdx) -> Option<Visibility> {689		let mut exists = false;690		let mut skip = Saturating(0usize);691		for ele in self.0.cores[..core.idx].iter().rev() {692			let vis = ele.0.field_visibility_core(field.clone());693			match vis {694				FieldVisibility::Found(vis @ (Visibility::Unhide | Visibility::Hidden)) => {695					if skip.0 == 0 {696						return Some(vis);697					}698				}699				FieldVisibility::Found(Visibility::Normal) => {700					if skip.0 == 0 {701						exists = true702					}703				}704				FieldVisibility::NotFound => {}705				FieldVisibility::Omit(new_skip) => {706					// +1 including this core707					skip = skip.max(new_skip + Saturating(1));708				}709			}710			skip -= 1;711		}712		exists.then_some(Visibility::Normal)713	}714715	pub fn run_assertions(&self) -> Result<()> {716		if self.0.assertions_ran.get() {717			return Ok(());718		}719		if !start_asserting(self) {720			return Ok(());721		}722		for (idx, ele) in self.0.cores.iter().enumerate() {723			let sup_this = SupThis {724				sup: CoreIdx { idx },725				this: self.clone(),726			};727			ele.0.run_assertions_core(sup_this).inspect_err(|_e| {728				finish_asserting(self);729			})?;730		}731		finish_asserting(self);732		self.0.assertions_ran.set(true);733		Ok(())734	}735736	pub fn iter(737		&self,738		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,739	) -> impl Iterator<Item = (IStr, Result<Val>)> + '_ {740		let fields = self.fields(741			#[cfg(feature = "exp-preserve-order")]742			preserve_order,743		);744		fields.into_iter().map(|field| {745			(746				field.clone(),747				self.get(field)748					.map(|opt| opt.expect("iterating over keys, field exists")),749			)750		})751	}752	pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {753		if !self.has_field_ex(key.clone(), true) {754			return None;755		}756		#[derive(Trace)]757		struct ObjFieldThunk {758			obj: ObjValue,759			key: IStr,760		}761		impl ThunkValue for ObjFieldThunk {762			type Output = Val;763764			fn get(&self) -> Result<Self::Output> {765				self.obj766					.get(self.key.clone())767					.transpose()768					.expect("field existence checked")769			}770		}771772		Some(Thunk::new(ObjFieldThunk {773			obj: self.clone(),774			key,775		}))776	}777	pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {778		#[derive(Trace)]779		struct ObjFieldThunk {780			obj: ObjValue,781			key: IStr,782		}783		impl ThunkValue for ObjFieldThunk {784			type Output = Val;785786			fn get(&self) -> Result<Self::Output> {787				self.obj.get_or_bail(self.key.clone())788			}789		}790791		Thunk::new(ObjFieldThunk {792			obj: self.clone(),793			key,794		})795	}796	pub fn ptr_eq(a: &Self, b: &Self) -> bool {797		Cc::ptr_eq(&a.0, &b.0)798	}799	pub fn downgrade(self) -> WeakObjValue {800		WeakObjValue(self.0.downgrade())801	}802}803804#[derive(Debug)]805struct FieldVisibilityData {806	omitted_until: Saturating<usize>,807	exists_visible: Option<Visibility>,808	#[cfg(feature = "exp-preserve-order")]809	key: FieldSortKey,810}811impl FieldVisibilityData {812	fn visible(&self) -> bool {813		self.exists_visible814			.expect("non-existing fields shall be dropped at the end of fn fields_visibility()")815			.is_visible()816	}817	#[cfg(feature = "exp-preserve-order")]818	fn sort_key(&self) -> FieldSortKey {819		self.key820	}821}822823impl ObjValue {824	fn fields_visibility(&self) -> FxHashMap<IStr, FieldVisibilityData> {825		let mut out = FxHashMap::default();826827		let mut super_depth = SuperDepth::default();828		let mut omit_index = Saturating(0);829		for core in self.0.cores.iter().rev() {830			core.0831				.enum_fields_core(&mut super_depth, &mut |_depth, _index, name, visibility| {832					let entry = out.entry(name);833					let data = entry.or_insert(FieldVisibilityData {834						exists_visible: None,835						#[cfg(feature = "exp-preserve-order")]836						key: FieldSortKey::new(_depth, _index),837						omitted_until: omit_index,838					});839					match visibility {840						EnumFields::Omit(new_skip) => {841							// +1 including this core842							data.omitted_until = data843								.omitted_until844								.max(omit_index + new_skip + Saturating(1));845						}846						EnumFields::Normal(Visibility::Normal) => {847							if data.omitted_until <= omit_index {848								if data.exists_visible.is_none() {849									data.exists_visible = Some(Visibility::Normal);850								}851							}852						}853						EnumFields::Normal(Visibility::Hidden) => {854							if data.omitted_until <= omit_index {855								data.exists_visible = Some(match data.exists_visible {856									// We're iterating in reverse, later unhide is preserved857									Some(Visibility::Unhide) => Visibility::Unhide,858									_ => Visibility::Hidden,859								});860							}861						}862						EnumFields::Normal(Visibility::Unhide) => {863							if data.omitted_until <= omit_index {864								data.exists_visible = Some(match data.exists_visible {865									// We're iterating in reverse, later hide is preserved866									Some(Visibility::Hidden) => Visibility::Hidden,867									_ => Visibility::Unhide,868								});869							}870						}871					};872					return ControlFlow::Continue(());873				});874875			super_depth.deepen();876			omit_index += 1;877		}878879		out.retain(|_, v| v.exists_visible.is_some());880881		out882	}883	pub fn fields_ex(884		&self,885		include_hidden: bool,886		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,887	) -> Vec<IStr> {888		#[cfg(feature = "exp-preserve-order")]889		if preserve_order {890			let (mut fields, mut keys): (Vec<_>, Vec<_>) = self891				.fields_visibility()892				.into_iter()893				.filter(|(_, d)| include_hidden || d.visible())894				.enumerate()895				.map(|(idx, (k, d))| (k, (d.sort_key(), idx)))896				.unzip();897			keys.sort_unstable_by_key(|v| v.0);898			// Reorder in-place by resulting indexes899			for i in 0..fields.len() {900				let x = fields[i].clone();901				let mut j = i;902				loop {903					let k = keys[j].1;904					keys[j].1 = j;905					if k == i {906						break;907					}908					fields[j] = fields[k].clone();909					j = k;910				}911				fields[j] = x;912			}913			return fields;914		}915916		let mut fields: Vec<_> = self917			.fields_visibility()918			.into_iter()919			.filter(|(_, d)| include_hidden || d.visible())920			.map(|(k, _)| k)921			.collect();922		fields.sort_unstable();923		fields924	}925	pub fn fields(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Vec<IStr> {926		self.fields_ex(927			false,928			#[cfg(feature = "exp-preserve-order")]929			preserve_order,930		)931	}932	pub fn values_ex(933		&self,934		include_hidden: bool,935		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,936	) -> ArrValue {937		ArrValue::new(PickObjectValues::new(938			self.clone(),939			self.fields_ex(940				include_hidden,941				#[cfg(feature = "exp-preserve-order")]942				preserve_order,943			),944		))945	}946	pub fn values(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> ArrValue {947		self.values_ex(948			false,949			#[cfg(feature = "exp-preserve-order")]950			preserve_order,951		)952	}953	pub fn key_values_ex(954		&self,955		include_hidden: bool,956		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,957	) -> ArrValue {958		ArrValue::new(PickObjectKeyValues::new(959			self.clone(),960			self.fields_ex(961				include_hidden,962				#[cfg(feature = "exp-preserve-order")]963				preserve_order,964			),965		))966	}967	pub fn key_values(968		&self,969		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,970	) -> ArrValue {971		self.key_values_ex(972			false,973			#[cfg(feature = "exp-preserve-order")]974			preserve_order,975		)976	}977}978979impl OopObject {980	pub fn new(981		this_entries: FxHashMap<IStr, ObjMember>,982		assertion: Option<CcObjectAssertion>,983	) -> Self {984		Self {985			this_entries,986			assertion,987		}988	}989}990991impl ObjectCore for OopObject {992	fn enum_fields_core(993		&self,994		super_depth: &mut SuperDepth,995		handler: &mut EnumFieldsHandler<'_>,996	) -> bool {997		for (name, member) in self.this_entries.iter() {998			if matches!(999				handler(1000					*super_depth,1001					member.original_index,1002					name.clone(),1003					EnumFields::Normal(member.flags.visibility()),1004				),1005				ControlFlow::Break(())1006			) {1007				return false;1008			}1009		}1010		true1011	}10121013	fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden {1014		if self.this_entries.contains_key(&name) {1015			HasFieldIncludeHidden::Exists1016		} else {1017			HasFieldIncludeHidden::NotFound1018		}1019	}10201021	fn get_for_core(&self, key: IStr, sup_this: SupThis, omit_only: bool) -> Result<GetFor> {1022		if omit_only {1023			return Ok(GetFor::NotFound);1024		}1025		match self.this_entries.get(&key) {1026			Some(k) => {1027				let v = k.invoke.evaluate(sup_this)?;1028				Ok(if k.flags.add() {1029					GetFor::SuperPlus(v)1030				} else {1031					GetFor::Final(v)1032				})1033			}1034			None => Ok(GetFor::NotFound),1035		}1036	}1037	fn field_visibility_core(&self, name: IStr) -> FieldVisibility {1038		match self.this_entries.get(&name) {1039			Some(f) => FieldVisibility::Found(f.flags.visibility()),1040			None => FieldVisibility::NotFound,1041		}1042	}10431044	fn run_assertions_core(&self, sup_this: SupThis) -> Result<()> {1045		if let Some(assertion) = &self.assertion {1046			assertion.0.run(sup_this.clone())?;1047		}1048		Ok(())1049	}1050}10511052#[allow(clippy::module_name_repetitions)]1053pub struct ObjValueBuilder {1054	sup: Vec<CcObjectCore>,10551056	new: OopObject,1057	next_field_index: FieldIndex,1058}1059impl ObjValueBuilder {1060	pub fn new() -> Self {1061		Self::with_capacity(0)1062	}1063	pub fn with_capacity(capacity: usize) -> Self {1064		Self {1065			sup: vec![],1066			new: OopObject {1067				assertion: None,1068				this_entries: FxHashMap::with_capacity(capacity),1069			},1070			next_field_index: FieldIndex::default(),1071		}1072	}1073	pub fn reserve_cores(&mut self, capacity: usize) -> &mut Self {1074		self.sup.reserve_exact(capacity);1075		self1076	}1077	pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {1078		self.sup = super_obj.0.cores.clone();1079		self1080	}10811082	pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {1083		assert!(1084			self.new.assertion.is_none(),1085			"one OopObject can only have one assertion"1086		);1087		self.new.assertion = Some(CcObjectAssertion::new(assertion));1088		self1089	}1090	pub fn field(&mut self, name: impl Into<IStr>) -> ObjMemberBuilder<ValueBuilder<'_>> {1091		let field_index = self.next_field_index;1092		self.next_field_index = self.next_field_index.next();1093		ObjMemberBuilder::new(ValueBuilder(self), name.into(), field_index)1094	}1095	/// Preset for common method definiton pattern:1096	/// Create a hidden field with the function value.1097	///1098	/// `.field(name).hide().value(Val::function(value))`1099	pub fn method(&mut self, name: impl Into<IStr>, value: impl Into<FuncVal>) -> &mut Self {1100		self.field(name).hide().value(Val::Func(value.into()));1101		self1102	}1103	pub fn try_method(1104		&mut self,1105		name: impl Into<IStr>,1106		value: impl Into<FuncVal>,1107	) -> Result<&mut Self> {1108		self.field(name).hide().try_value(Val::Func(value.into()))?;1109		Ok(self)1110	}11111112	pub fn extend_with_core(&mut self, core: impl ObjectCore) {1113		self.commit();1114		self.sup.push(CcObjectCore::new(core));1115	}11161117	fn commit(&mut self) {1118		if !self.new.is_empty() {1119			self.sup.push(CcObjectCore::new(mem::take(&mut self.new)));1120		}1121		self.next_field_index = FieldIndex::default();1122	}11231124	pub fn with_fields_omitted(&mut self, omit: FxHashSet<IStr>) {1125		self.commit();1126		self.sup.push(CcObjectCore::new(OmitFieldsCore {1127			omit,1128			prev_layers: self.sup.len(),1129		}));1130	}11311132	pub fn build(mut self) -> ObjValue {1133		self.commit();1134		if self.sup.is_empty() {1135			return ObjValue::empty();1136		}1137		ObjValue(Cc::new(ObjValueInner {1138			cores: self.sup,1139			assertions_ran: Cell::new(false),1140			value_cache: Default::default(),1141		}))1142	}1143}1144impl Default for ObjValueBuilder {1145	fn default() -> Self {1146		Self::with_capacity(0)1147	}1148}11491150#[allow(clippy::module_name_repetitions)]1151#[must_use = "value not added unless binding() was called"]1152pub struct ObjMemberBuilder<Kind> {1153	kind: Kind,1154	name: IStr,1155	add: bool,1156	visibility: Visibility,1157	original_index: FieldIndex,1158	location: Option<Span>,1159}11601161#[allow(clippy::missing_const_for_fn)]1162impl<Kind> ObjMemberBuilder<Kind> {1163	pub(crate) fn new(kind: Kind, name: IStr, original_index: FieldIndex) -> Self {1164		Self {1165			kind,1166			name,1167			original_index,1168			add: false,1169			visibility: Visibility::Normal,1170			location: None,1171		}1172	}11731174	pub const fn with_add(mut self, add: bool) -> Self {1175		self.add = add;1176		self1177	}1178	pub fn add(self) -> Self {1179		self.with_add(true)1180	}1181	pub fn with_visibility(mut self, visibility: Visibility) -> Self {1182		self.visibility = visibility;1183		self1184	}1185	pub fn hide(self) -> Self {1186		self.with_visibility(Visibility::Hidden)1187	}1188	pub fn with_location(mut self, location: Span) -> Self {1189		self.location = Some(location);1190		self1191	}1192	fn build_member(self, binding: MaybeUnbound) -> (Kind, IStr, ObjMember) {1193		(1194			self.kind,1195			self.name,1196			ObjMember {1197				flags: ObjFieldFlags::new(self.add, self.visibility),1198				original_index: self.original_index,1199				invoke: binding,1200				location: self.location,1201			},1202		)1203	}1204}12051206pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);1207impl ObjMemberBuilder<ValueBuilder<'_>> {1208	/// Inserts value, replacing if it is already defined1209	pub fn value(self, value: impl Into<Val>) {1210		let (receiver, name, member) =1211			self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value.into())));1212		let entry = receiver.0.new.this_entries.entry(name);1213		entry.insert_entry(member);1214	}1215	/// Inserts thunk, replacing if it is already defined1216	pub fn thunk(self, value: impl Into<Thunk<Val>>) {1217		let (receiver, name, member) = self.build_member(MaybeUnbound::Bound(value.into()));1218		let entry = receiver.0.new.this_entries.entry(name);1219		entry.insert_entry(member);1220	}12211222	/// Tries to insert value, returns an error if it was already defined1223	pub fn try_value(self, value: impl Into<Val>) -> Result<()> {1224		self.try_thunk(Thunk::evaluated(value.into()))1225	}1226	pub fn try_thunk(self, value: impl Into<Thunk<Val>>) -> Result<()> {1227		self.binding(MaybeUnbound::Bound(value.into()))1228	}1229	pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {1230		self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)))1231	}1232	pub fn binding(self, binding: MaybeUnbound) -> Result<()> {1233		let (receiver, name, member) = self.build_member(binding);1234		let location = member.location.clone();1235		let old = receiver.0.new.this_entries.insert(name.clone(), member);1236		if old.is_some() {1237			in_frame(1238				CallLocation(location.as_ref()),1239				|| format!("field <{}> initializtion", name.clone()),1240				|| bail!(DuplicateFieldName(name.clone())),1241			)?;1242		}1243		Ok(())1244	}1245}12461247pub struct ExtendBuilder<'v>(&'v mut ObjValue);1248impl ObjMemberBuilder<ExtendBuilder<'_>> {1249	pub fn value(self, value: impl Into<Val>) {1250		self.binding(MaybeUnbound::Bound(Thunk::evaluated(value.into())));1251	}1252	pub fn bindable(self, bindable: impl Unbound<Bound = Val>) {1253		self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)));1254	}1255	pub fn binding(self, binding: MaybeUnbound) {1256		let (receiver, name, member) = self.build_member(binding);1257		let new = receiver.0.clone();1258		*receiver.0 = new.extend_with_raw_member(name, member);1259	}1260}