git.delta.rocks / jrsonnet / refs/commits / 7da68eaa8a4d

difftreelog

source

crates/jrsonnet-evaluator/src/obj/oop.rs6.8 KiBsourcehistory
1use std::cell::{Cell, RefCell};2use std::ops::ControlFlow;3use std::{fmt, mem};45use crate::function::{CallLocation, FuncVal};6use crate::gc::WithCapacityExt as _;7use crate::{8	bail, error::ErrorKind::*, in_frame, CcUnbound, MaybeUnbound, Result, Thunk, Unbound, Val,9};10use jrsonnet_gcmodule::{Cc, Trace};11use jrsonnet_ir::IStr;12use rustc_hash::{FxHashMap, FxHashSet};1314use super::ordering::{FieldIndex, SuperDepth};15use super::{16	CcObjectAssertion, CcObjectCore, EnumFields, EnumFieldsHandler, FieldVisibility, GetFor,17	HasFieldIncludeHidden, ObjMember, ObjMemberBuilder, ObjValue, ObjValueInner, ObjectAssertion,18	ObjectCore, OmitFieldsCore, SupThis,19};2021#[allow(clippy::module_name_repetitions)]22#[derive(Trace, Default)]23#[trace(tracking(force))]24pub struct OopObject {25	assertion: Option<CcObjectAssertion>,26	this_entries: FxHashMap<IStr, ObjMember>,27}28impl fmt::Debug for OopObject {29	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {30		f.debug_struct("OopObject")31			.field("this_entries", &self.this_entries)32			.finish_non_exhaustive()33	}34}35impl OopObject {36	fn is_empty(&self) -> bool {37		self.assertion.is_none() && self.this_entries.is_empty()38	}39}40impl OopObject {41	pub fn new(42		this_entries: FxHashMap<IStr, ObjMember>,43		assertion: Option<CcObjectAssertion>,44	) -> Self {45		Self {46			assertion,47			this_entries,48		}49	}50}5152impl ObjectCore for OopObject {53	fn enum_fields_core(54		&self,55		super_depth: &mut SuperDepth,56		handler: &mut EnumFieldsHandler<'_>,57	) -> bool {58		for (name, member) in &self.this_entries {59			if matches!(60				handler(61					*super_depth,62					member.original_index,63					name.clone(),64					EnumFields::Normal(member.flags.visibility()),65				),66				ControlFlow::Break(())67			) {68				return false;69			}70		}71		true72	}7374	fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden {75		if self.this_entries.contains_key(&name) {76			HasFieldIncludeHidden::Exists77		} else {78			HasFieldIncludeHidden::NotFound79		}80	}8182	fn get_for_core(&self, key: IStr, sup_this: SupThis, omit_only: bool) -> Result<GetFor> {83		if omit_only {84			return Ok(GetFor::NotFound);85		}86		match self.this_entries.get(&key) {87			Some(k) => {88				let v = k.invoke.evaluate(sup_this)?;89				Ok(if k.flags.add() {90					GetFor::SuperPlus(v)91				} else {92					GetFor::Final(v)93				})94			}95			None => Ok(GetFor::NotFound),96		}97	}98	fn field_visibility_core(&self, name: IStr) -> FieldVisibility {99		self.this_entries100			.get(&name)101			.map_or(FieldVisibility::NotFound, |f| {102				FieldVisibility::Found(f.flags.visibility())103			})104	}105106	fn run_assertions_core(&self, sup_this: SupThis) -> Result<()> {107		if let Some(assertion) = &self.assertion {108			assertion.0.run(sup_this)?;109		}110		Ok(())111	}112}113114#[allow(clippy::module_name_repetitions)]115pub struct ObjValueBuilder {116	sup: Vec<CcObjectCore>,117	has_assertions: bool,118119	new: OopObject,120	next_field_index: FieldIndex,121}122impl ObjValueBuilder {123	pub fn new() -> Self {124		Self::with_capacity(0)125	}126	pub fn with_capacity(capacity: usize) -> Self {127		Self {128			sup: vec![],129			has_assertions: false,130			new: OopObject::new(FxHashMap::with_capacity(capacity), None),131			next_field_index: FieldIndex::default(),132		}133	}134	pub fn reserve_cores(&mut self, capacity: usize) -> &mut Self {135		self.sup.reserve_exact(capacity);136		self137	}138	pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {139		self.has_assertions |= super_obj.0.has_assertions;140		self.sup.clone_from(&super_obj.0.cores);141		self142	}143144	pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {145		assert!(146			self.new.assertion.is_none(),147			"one OopObject can only have one assertion"148		);149		self.has_assertions = true;150		self.new.assertion = Some(CcObjectAssertion::new(assertion));151		self152	}153	pub fn field(&mut self, name: impl Into<IStr>) -> ObjMemberBuilder<ValueBuilder<'_>> {154		let field_index = self.next_field_index;155		self.next_field_index = self.next_field_index.next();156		ObjMemberBuilder::new(ValueBuilder(self), name.into(), field_index)157	}158	/// Preset for common method definiton pattern:159	/// Create a hidden field with the function value.160	///161	/// `.field(name).hide().value(Val::function(value))`162	pub fn method(&mut self, name: impl Into<IStr>, value: impl Into<FuncVal>) -> &mut Self {163		self.field(name).hide().value(Val::Func(value.into()));164		self165	}166	pub fn try_method(167		&mut self,168		name: impl Into<IStr>,169		value: impl Into<FuncVal>,170	) -> Result<&mut Self> {171		self.field(name).hide().try_value(Val::Func(value.into()))?;172		Ok(self)173	}174175	pub fn extend_with_core(&mut self, core: impl ObjectCore) {176		self.commit();177		self.sup.push(CcObjectCore::new(core));178	}179180	fn commit(&mut self) {181		if !self.new.is_empty() {182			self.sup.push(CcObjectCore::new(mem::take(&mut self.new)));183		}184		self.next_field_index = FieldIndex::default();185	}186187	pub fn with_fields_omitted(&mut self, omit: FxHashSet<IStr>) {188		self.commit();189		self.sup.push(CcObjectCore::new(OmitFieldsCore {190			omit,191			prev_layers: self.sup.len(),192		}));193	}194195	pub fn build(mut self) -> ObjValue {196		self.commit();197		if self.sup.is_empty() {198			return ObjValue::empty();199		}200		let has_assertions = self.has_assertions;201		ObjValue(Cc::new(ObjValueInner {202			cores: self.sup,203			assertions_ran: Cell::new(!has_assertions),204			has_assertions,205			value_cache: RefCell::default(),206		}))207	}208}209impl Default for ObjValueBuilder {210	fn default() -> Self {211		Self::with_capacity(0)212	}213}214215pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);216impl ObjMemberBuilder<ValueBuilder<'_>> {217	/// Inserts value, replacing if it is already defined218	pub fn value(self, value: impl Into<Val>) {219		let (receiver, name, member) =220			self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value.into())));221		let entry = receiver.0.new.this_entries.entry(name);222		entry.insert_entry(member);223	}224	/// Inserts thunk, replacing if it is already defined225	pub fn thunk(self, value: impl Into<Thunk<Val>>) {226		let (receiver, name, member) = self.build_member(MaybeUnbound::Bound(value.into()));227		let entry = receiver.0.new.this_entries.entry(name);228		entry.insert_entry(member);229	}230231	/// Tries to insert value, returns an error if it was already defined232	pub fn try_value(self, value: impl Into<Val>) -> Result<()> {233		self.try_thunk(Thunk::evaluated(value.into()))234	}235	pub fn try_thunk(self, value: impl Into<Thunk<Val>>) -> Result<()> {236		self.binding(MaybeUnbound::Bound(value.into()))237	}238	pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {239		self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)))240	}241	pub fn binding(self, binding: MaybeUnbound) -> Result<()> {242		let (receiver, name, member) = self.build_member(binding);243		let location = member.location.clone();244		let old = receiver.0.new.this_entries.insert(name.clone(), member);245		if old.is_some() {246			in_frame(247				CallLocation(location.as_ref()),248				|| format!("field <{}> initializtion", name.clone()),249				|| bail!(DuplicateFieldName(name.clone())),250			)?;251		}252		Ok(())253	}254}