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

difftreelog

source

crates/jrsonnet-evaluator/src/obj/oop.rs6.8 KiBsourcehistory
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}