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

difftreelog

source

crates/jrsonnet-evaluator/src/obj/oop.rs6.9 KiBsourcehistory
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, FieldIndex)>,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, FieldIndex)>,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, idx)) in &self.this_entries {63			if matches!(64				handler(65					*super_depth,66					*idx,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	}116117	fn has_assertion(&self) -> bool {118		self.assertion.is_some()119	}120}121122#[allow(clippy::module_name_repetitions)]123pub struct ObjValueBuilder {124	sup: Vec<CcObjectCore>,125	has_assertions: bool,126127	new: OopObject,128	next_field_index: FieldIndex,129}130impl ObjValueBuilder {131	pub fn new() -> Self {132		Self::with_capacity(0)133	}134	pub fn with_capacity(capacity: usize) -> Self {135		Self {136			sup: Vec::new(),137			has_assertions: false,138			new: OopObject::new(FxHashMap::with_capacity(capacity), None),139			next_field_index: FieldIndex::default(),140		}141	}142	pub fn reserve_fields(&mut self, capacity: usize) {143		self.new.this_entries.reserve(capacity);144	}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.has_assertions |= core.has_assertion();185		self.sup.push(CcObjectCore::new(core));186	}187188	fn commit(&mut self) {189		if !self.new.is_empty() {190			self.sup.push(CcObjectCore::new(mem::take(&mut self.new)));191		}192		self.next_field_index = FieldIndex::default();193	}194195	pub fn with_fields_omitted(&mut self, omit: FxHashSet<IStr>) {196		self.commit();197		self.sup.push(CcObjectCore::new(OmitFieldsCore {198			omit,199			prev_layers: self.sup.len(),200		}));201	}202203	pub fn build(mut self) -> ObjValue {204		self.commit();205		if self.sup.is_empty() {206			return ObjValue::empty();207		}208		let has_assertions = self.has_assertions;209		ObjValue(Cc::new(ObjValueInner {210			cores: self.sup,211			assertions_ran: Cell::new(!has_assertions),212			has_assertions,213			value_cache: RefCell::default(),214		}))215	}216}217impl Default for ObjValueBuilder {218	fn default() -> Self {219		Self::with_capacity(0)220	}221}222223pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);224impl ObjMemberBuilder<ValueBuilder<'_>> {225	/// Inserts value, replacing if it is already defined226	pub fn value(self, value: impl Into<Val>) {227		let (receiver, name, idx, member) = self.build_member(MaybeUnbound::Const(value.into()));228		let entry = receiver.0.new.this_entries.entry(name);229		entry.insert_entry((member, idx));230	}231	/// Inserts thunk, replacing if it is already defined232	pub fn thunk(self, value: impl Into<Thunk<Val>>) {233		let (receiver, name, idx, member) = self.build_member(MaybeUnbound::Bound(value.into()));234		let entry = receiver.0.new.this_entries.entry(name);235		entry.insert_entry((member, idx));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.binding(MaybeUnbound::Const(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, idx, member) = self.build_member(binding);250		let location = member.location.clone();251		let old = receiver252			.0253			.new254			.this_entries255			.insert(name.clone(), (member, idx));256		if old.is_some() {257			in_frame(258				CallLocation(location.as_ref()),259				|| format!("field <{}> initializtion", name.clone()),260				|| bail!(DuplicateFieldName(name.clone())),261			)?;262		}263		Ok(())264	}265}