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 163 164 165 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 223 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 230 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 237 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}