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

difftreelog

source

crates/jrsonnet-evaluator/src/obj.rs15.3 KiBsourcehistory
1use std::{2	cell::RefCell,3	fmt::Debug,4	hash::{Hash, Hasher},5	ptr::addr_of,6};78use jrsonnet_gcmodule::{Cc, Trace, Weak};9use jrsonnet_interner::IStr;10use jrsonnet_parser::{ExprLocation, Visibility};11use rustc_hash::FxHashMap;1213use crate::{14	error::{Error::*, LocError},15	function::CallLocation,16	gc::{GcHashMap, GcHashSet, TraceBox},17	operator::evaluate_add_op,18	throw, LazyBinding, Result, State, Thunk, Unbound, Val,19};2021#[cfg(not(feature = "exp-preserve-order"))]22mod ordering {23	#![allow(24		// This module works as stub for preserve-order feature25		clippy::unused_self,26	)]2728	use jrsonnet_gcmodule::Trace;2930	#[derive(Clone, Copy, Default, Debug, Trace)]31	pub struct FieldIndex;32	impl FieldIndex {33		pub const fn next(self) -> Self {34			Self35		}36	}3738	#[derive(Clone, Copy, Default, Debug, Trace)]39	pub struct SuperDepth;40	impl SuperDepth {41		pub const fn deeper(self) -> Self {42			Self43		}44	}4546	#[derive(Clone, Copy)]47	pub struct FieldSortKey;48	impl FieldSortKey {49		pub const fn new(_: SuperDepth, _: FieldIndex) -> Self {50			Self51		}52	}53}5455#[cfg(feature = "exp-preserve-order")]56mod ordering {57	use std::cmp::Reverse;5859	use jrsonnet_gcmodule::Trace;6061	#[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]62	pub struct FieldIndex(u32);63	impl FieldIndex {64		pub fn next(self) -> Self {65			Self(self.0 + 1)66		}67	}6869	#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Debug)]70	pub struct SuperDepth(u32);71	impl SuperDepth {72		pub fn deeper(self) -> Self {73			Self(self.0 + 1)74		}75	}7677	#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]78	pub struct FieldSortKey(Reverse<SuperDepth>, FieldIndex);79	impl FieldSortKey {80		pub fn new(depth: SuperDepth, index: FieldIndex) -> Self {81			Self(Reverse(depth), index)82		}83		pub fn collide(self, other: Self) -> Self {84			if self.0 .0 > other.0 .0 {85				self86			} else if self.0 .0 < other.0 .0 {87				other88			} else {89				unreachable!("object can't have two fields with same name")90			}91		}92	}93}9495use ordering::*;9697#[allow(clippy::module_name_repetitions)]98#[derive(Debug, Trace)]99pub struct ObjMember {100	pub add: bool,101	pub visibility: Visibility,102	original_index: FieldIndex,103	pub invoke: LazyBinding,104	pub location: Option<ExprLocation>,105}106107pub trait ObjectAssertion: Trace {108	fn run(&self, s: State, super_obj: Option<ObjValue>, this: Option<ObjValue>) -> Result<()>;109}110111// Field => This112type CacheKey = (IStr, WeakObjValue);113114#[derive(Trace)]115enum CacheValue {116	Cached(Val),117	NotFound,118	Pending,119	Errored(LocError),120}121122#[allow(clippy::module_name_repetitions)]123#[derive(Trace)]124#[trace(tracking(force))]125pub struct ObjValueInternals {126	sup: Option<ObjValue>,127	this: Option<ObjValue>,128129	assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,130	assertions_ran: RefCell<GcHashSet<ObjValue>>,131	this_entries: Cc<GcHashMap<IStr, ObjMember>>,132	value_cache: RefCell<GcHashMap<CacheKey, CacheValue>>,133}134135#[derive(Clone, Trace)]136pub struct WeakObjValue(#[trace(skip)] pub(crate) Weak<ObjValueInternals>);137138impl PartialEq for WeakObjValue {139	fn eq(&self, other: &Self) -> bool {140		Weak::ptr_eq(&self.0, &other.0)141	}142}143144impl Eq for WeakObjValue {}145impl Hash for WeakObjValue {146	fn hash<H: Hasher>(&self, hasher: &mut H) {147		// Safety: usize is POD148		let addr = unsafe { *std::ptr::addr_of!(self.0).cast() };149		hasher.write_usize(addr);150	}151}152153#[allow(clippy::module_name_repetitions)]154#[derive(Clone, Trace)]155pub struct ObjValue(pub(crate) Cc<ObjValueInternals>);156impl Debug for ObjValue {157	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {158		if let Some(super_obj) = self.0.sup.as_ref() {159			if f.alternate() {160				write!(f, "{:#?}", super_obj)?;161			} else {162				write!(f, "{:?}", super_obj)?;163			}164			write!(f, " + ")?;165		}166		let mut debug = f.debug_struct("ObjValue");167		for (name, member) in self.0.this_entries.iter() {168			debug.field(name, member);169		}170		debug.finish_non_exhaustive()171	}172}173174impl ObjValue {175	pub fn new(176		sup: Option<Self>,177		this_entries: Cc<GcHashMap<IStr, ObjMember>>,178		assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,179	) -> Self {180		Self(Cc::new(ObjValueInternals {181			sup,182			this: None,183			assertions,184			assertions_ran: RefCell::new(GcHashSet::new()),185			this_entries,186			value_cache: RefCell::new(GcHashMap::new()),187		}))188	}189	pub fn new_empty() -> Self {190		Self::new(None, Cc::new(GcHashMap::new()), Cc::new(Vec::new()))191	}192	#[must_use]193	pub fn extend_from(&self, sup: Self) -> Self {194		match &self.0.sup {195			None => Self::new(196				Some(sup),197				self.0.this_entries.clone(),198				self.0.assertions.clone(),199			),200			Some(v) => Self::new(201				Some(v.extend_from(sup)),202				self.0.this_entries.clone(),203				self.0.assertions.clone(),204			),205		}206	}207	pub(crate) fn extend_with_raw_member(self, key: IStr, value: ObjMember) -> Self {208		let mut new = GcHashMap::with_capacity(1);209		new.insert(key, value);210		Self::new(Some(self), Cc::new(new), Cc::new(Vec::new()))211	}212	pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder> {213		ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())214	}215216	#[must_use]217	pub fn with_this(&self, this: Self) -> Self {218		Self(Cc::new(ObjValueInternals {219			sup: self.0.sup.clone(),220			assertions: self.0.assertions.clone(),221			assertions_ran: RefCell::new(GcHashSet::new()),222			this: Some(this),223			this_entries: self.0.this_entries.clone(),224			value_cache: RefCell::new(GcHashMap::new()),225		}))226	}227228	pub fn len(&self) -> usize {229		self.fields_visibility()230			.into_iter()231			.filter(|(_, (visible, _))| *visible)232			.count()233	}234235	pub fn is_empty(&self) -> bool {236		if !self.0.this_entries.is_empty() {237			return false;238		}239		self.0.sup.as_ref().map_or(true, Self::is_empty)240	}241242	/// Run callback for every field found in object243	pub(crate) fn enum_fields(244		&self,245		depth: SuperDepth,246		handler: &mut impl FnMut(SuperDepth, &IStr, &ObjMember) -> bool,247	) -> bool {248		if let Some(s) = &self.0.sup {249			if s.enum_fields(depth.deeper(), handler) {250				return true;251			}252		}253		for (name, member) in self.0.this_entries.iter() {254			if handler(depth, name, member) {255				return true;256			}257		}258		false259	}260261	pub fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {262		let mut out = FxHashMap::default();263		self.enum_fields(SuperDepth::default(), &mut |depth, name, member| {264			let new_sort_key = FieldSortKey::new(depth, member.original_index);265			let entry = out.entry(name.clone());266			let (visible, _) = entry.or_insert((true, new_sort_key));267			match member.visibility {268				Visibility::Normal => {}269				Visibility::Hidden => {270					*visible = false;271				}272				Visibility::Unhide => {273					*visible = true;274				}275			};276			false277		});278		out279	}280	pub fn fields_ex(281		&self,282		include_hidden: bool,283		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,284	) -> Vec<IStr> {285		#[cfg(feature = "exp-preserve-order")]286		if preserve_order {287			let (mut fields, mut keys): (Vec<_>, Vec<_>) = self288				.fields_visibility()289				.into_iter()290				.filter(|(_, (visible, _))| include_hidden || *visible)291				.enumerate()292				.map(|(idx, (k, (_, sk)))| (k, (sk, idx)))293				.unzip();294			keys.sort_unstable_by_key(|v| v.0);295			// Reorder in-place by resulting indexes296			for i in 0..fields.len() {297				let x = fields[i].clone();298				let mut j = i;299				loop {300					let k = keys[j].1;301					keys[j].1 = j;302					if k == i {303						break;304					}305					fields[j] = fields[k].clone();306					j = k307				}308				fields[j] = x;309			}310			return fields;311		}312313		let mut fields: Vec<_> = self314			.fields_visibility()315			.into_iter()316			.filter(|(_, (visible, _))| include_hidden || *visible)317			.map(|(k, _)| k)318			.collect();319		fields.sort_unstable();320		fields321	}322	pub fn fields(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Vec<IStr> {323		self.fields_ex(324			false,325			#[cfg(feature = "exp-preserve-order")]326			preserve_order,327		)328	}329330	pub fn field_visibility(&self, name: IStr) -> Option<Visibility> {331		if let Some(m) = self.0.this_entries.get(&name) {332			Some(match &m.visibility {333				Visibility::Normal => self334					.0335					.sup336					.as_ref()337					.and_then(|super_obj| super_obj.field_visibility(name))338					.unwrap_or(Visibility::Normal),339				v => *v,340			})341		} else if let Some(super_obj) = &self.0.sup {342			super_obj.field_visibility(name)343		} else {344			None345		}346	}347348	fn has_field_include_hidden(&self, name: IStr) -> bool {349		if self.0.this_entries.contains_key(&name) {350			true351		} else if let Some(super_obj) = &self.0.sup {352			super_obj.has_field_include_hidden(name)353		} else {354			false355		}356	}357358	pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {359		if include_hidden {360			self.has_field_include_hidden(name)361		} else {362			self.has_field(name)363		}364	}365	pub fn has_field(&self, name: IStr) -> bool {366		self.field_visibility(name)367			.map_or(false, |v| v.is_visible())368	}369370	pub fn get(&self, s: State, key: IStr) -> Result<Option<Val>> {371		self.run_assertions(s.clone())?;372		self.get_raw(s, key, self.0.this.clone().unwrap_or_else(|| self.clone()))373	}374375	// pub fn extend_with(self, key: )376377	fn get_raw(&self, s: State, key: IStr, real_this: Self) -> Result<Option<Val>> {378		let cache_key = (key.clone(), WeakObjValue(real_this.0.downgrade()));379380		if let Some(v) = self.0.value_cache.borrow().get(&cache_key) {381			return Ok(match v {382				CacheValue::Cached(v) => Some(v.clone()),383				CacheValue::NotFound => None,384				CacheValue::Pending => throw!(InfiniteRecursionDetected),385				CacheValue::Errored(e) => return Err(e.clone()),386			});387		}388		self.0389			.value_cache390			.borrow_mut()391			.insert(cache_key.clone(), CacheValue::Pending);392		let fill_error = |e: LocError| {393			self.0394				.value_cache395				.borrow_mut()396				.insert(cache_key.clone(), CacheValue::Errored(e.clone()));397			e398		};399		let value = match (self.0.this_entries.get(&key), &self.0.sup) {400			(Some(k), None) => Ok(Some(401				self.evaluate_this(s, k, real_this).map_err(fill_error)?,402			)),403			(Some(k), Some(super_obj)) => {404				let our = self405					.evaluate_this(s.clone(), k, real_this.clone())406					.map_err(fill_error)?;407				if k.add {408					super_obj409						.get_raw(s.clone(), key, real_this)410						.map_err(fill_error)?411						.map_or(Ok(Some(our.clone())), |v| {412							Ok(Some(evaluate_add_op(s.clone(), &v, &our)?))413						})414				} else {415					Ok(Some(our))416				}417			}418			(None, Some(super_obj)) => super_obj.get_raw(s, key, real_this),419			(None, None) => Ok(None),420		}421		.map_err(fill_error)?;422		self.0.value_cache.borrow_mut().insert(423			cache_key,424			match &value {425				Some(v) => CacheValue::Cached(v.clone()),426				None => CacheValue::NotFound,427			},428		);429		Ok(value)430	}431	fn evaluate_this(&self, s: State, v: &ObjMember, real_this: Self) -> Result<Val> {432		v.invoke433			.evaluate(s.clone(), self.0.sup.clone(), Some(real_this))?434			.evaluate(s)435	}436437	fn run_assertions_raw(&self, s: State, real_this: &Self) -> Result<()> {438		if self.0.assertions_ran.borrow_mut().insert(real_this.clone()) {439			for assertion in self.0.assertions.iter() {440				if let Err(e) =441					assertion.run(s.clone(), self.0.sup.clone(), Some(real_this.clone()))442				{443					self.0.assertions_ran.borrow_mut().remove(real_this);444					return Err(e);445				}446			}447			if let Some(super_obj) = &self.0.sup {448				super_obj.run_assertions_raw(s, real_this)?;449			}450		}451		Ok(())452	}453	pub fn run_assertions(&self, s: State) -> Result<()> {454		self.run_assertions_raw(s, self)455	}456457	pub fn ptr_eq(a: &Self, b: &Self) -> bool {458		Cc::ptr_eq(&a.0, &b.0)459	}460	pub fn downgrade(self) -> WeakObjValue {461		WeakObjValue(self.0.downgrade())462	}463}464465impl PartialEq for ObjValue {466	fn eq(&self, other: &Self) -> bool {467		Cc::ptr_eq(&self.0, &other.0)468	}469}470471impl Eq for ObjValue {}472impl Hash for ObjValue {473	fn hash<H: Hasher>(&self, hasher: &mut H) {474		hasher.write_usize(addr_of!(*self.0) as usize);475	}476}477478#[allow(clippy::module_name_repetitions)]479pub struct ObjValueBuilder {480	sup: Option<ObjValue>,481	map: GcHashMap<IStr, ObjMember>,482	assertions: Vec<TraceBox<dyn ObjectAssertion>>,483	next_field_index: FieldIndex,484}485impl ObjValueBuilder {486	pub fn new() -> Self {487		Self::with_capacity(0)488	}489	pub fn with_capacity(capacity: usize) -> Self {490		Self {491			sup: None,492			map: GcHashMap::with_capacity(capacity),493			assertions: Vec::new(),494			next_field_index: FieldIndex::default(),495		}496	}497	pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self {498		self.assertions.reserve_exact(capacity);499		self500	}501	pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {502		self.sup = Some(super_obj);503		self504	}505506	pub fn assert(&mut self, assertion: TraceBox<dyn ObjectAssertion>) -> &mut Self {507		self.assertions.push(assertion);508		self509	}510	pub fn member(&mut self, name: IStr) -> ObjMemberBuilder<ValueBuilder> {511		let field_index = self.next_field_index;512		self.next_field_index = self.next_field_index.next();513		ObjMemberBuilder::new(ValueBuilder(self), name, field_index)514	}515516	pub fn build(self) -> ObjValue {517		ObjValue::new(self.sup, Cc::new(self.map), Cc::new(self.assertions))518	}519}520impl Default for ObjValueBuilder {521	fn default() -> Self {522		Self::with_capacity(0)523	}524}525526#[allow(clippy::module_name_repetitions)]527#[must_use = "value not added unless binding() was called"]528pub struct ObjMemberBuilder<Kind> {529	kind: Kind,530	name: IStr,531	add: bool,532	visibility: Visibility,533	original_index: FieldIndex,534	location: Option<ExprLocation>,535}536537#[allow(clippy::missing_const_for_fn)]538impl<Kind> ObjMemberBuilder<Kind> {539	pub(crate) fn new(kind: Kind, name: IStr, original_index: FieldIndex) -> Self {540		Self {541			kind,542			name,543			original_index,544			add: false,545			visibility: Visibility::Normal,546			location: None,547		}548	}549550	pub const fn with_add(mut self, add: bool) -> Self {551		self.add = add;552		self553	}554	pub fn add(self) -> Self {555		self.with_add(true)556	}557	pub fn with_visibility(mut self, visibility: Visibility) -> Self {558		self.visibility = visibility;559		self560	}561	pub fn hide(self) -> Self {562		self.with_visibility(Visibility::Hidden)563	}564	pub fn with_location(mut self, location: ExprLocation) -> Self {565		self.location = Some(location);566		self567	}568	fn build_member(self, binding: LazyBinding) -> (Kind, IStr, ObjMember) {569		(570			self.kind,571			self.name,572			ObjMember {573				add: self.add,574				visibility: self.visibility,575				original_index: self.original_index,576				invoke: binding,577				location: self.location,578			},579		)580	}581}582583pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);584impl<'v> ObjMemberBuilder<ValueBuilder<'v>> {585	pub fn value(self, s: State, value: Val) -> Result<()> {586		self.binding(s, LazyBinding::Bound(Thunk::evaluated(value)))587	}588	pub fn bindable(589		self,590		s: State,591		bindable: TraceBox<dyn Unbound<Bound = Thunk<Val>>>,592	) -> Result<()> {593		self.binding(s, LazyBinding::Bindable(Cc::new(bindable)))594	}595	pub fn binding(self, s: State, binding: LazyBinding) -> Result<()> {596		let (receiver, name, member) = self.build_member(binding);597		let location = member.location.clone();598		let old = receiver.0.map.insert(name.clone(), member);599		if old.is_some() {600			s.push(601				CallLocation(location.as_ref()),602				|| format!("field <{}> initializtion", name.clone()),603				|| throw!(DuplicateFieldName(name.clone())),604			)?;605		}606		Ok(())607	}608}609610pub struct ExtendBuilder<'v>(&'v mut ObjValue);611impl<'v> ObjMemberBuilder<ExtendBuilder<'v>> {612	pub fn value(self, value: Val) {613		self.binding(LazyBinding::Bound(Thunk::evaluated(value)));614	}615	pub fn bindable(self, bindable: TraceBox<dyn Unbound<Bound = Thunk<Val>>>) {616		self.binding(LazyBinding::Bindable(Cc::new(bindable)));617	}618	pub fn binding(self, binding: LazyBinding) {619		let (receiver, name, member) = self.build_member(binding);620		let new = receiver.0.clone();621		*receiver.0 = new.extend_with_raw_member(name, member);622	}623}