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 159 160 161 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 218 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 225 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 232 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}