1use std::cell::Cell;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_parser::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.clone())?;109 }110 Ok(())111 }112}113114#[allow(clippy::module_name_repetitions)]115pub struct ObjValueBuilder {116 sup: Vec<CcObjectCore>,117118 new: OopObject,119 next_field_index: FieldIndex,120}121impl ObjValueBuilder {122 pub fn new() -> Self {123 Self::with_capacity(0)124 }125 pub fn with_capacity(capacity: usize) -> Self {126 Self {127 sup: vec![],128 new: OopObject::new(FxHashMap::with_capacity(capacity), None),129 next_field_index: FieldIndex::default(),130 }131 }132 pub fn reserve_cores(&mut self, capacity: usize) -> &mut Self {133 self.sup.reserve_exact(capacity);134 self135 }136 pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {137 self.sup.clone_from(&super_obj.0.cores);138 self139 }140141 pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {142 assert!(143 self.new.assertion.is_none(),144 "one OopObject can only have one assertion"145 );146 self.new.assertion = Some(CcObjectAssertion::new(assertion));147 self148 }149 pub fn field(&mut self, name: impl Into<IStr>) -> ObjMemberBuilder<ValueBuilder<'_>> {150 let field_index = self.next_field_index;151 self.next_field_index = self.next_field_index.next();152 ObjMemberBuilder::new(ValueBuilder(self), name.into(), field_index)153 }154 155 156 157 158 pub fn method(&mut self, name: impl Into<IStr>, value: impl Into<FuncVal>) -> &mut Self {159 self.field(name).hide().value(Val::Func(value.into()));160 self161 }162 pub fn try_method(163 &mut self,164 name: impl Into<IStr>,165 value: impl Into<FuncVal>,166 ) -> Result<&mut Self> {167 self.field(name).hide().try_value(Val::Func(value.into()))?;168 Ok(self)169 }170171 pub fn extend_with_core(&mut self, core: impl ObjectCore) {172 self.commit();173 self.sup.push(CcObjectCore::new(core));174 }175176 fn commit(&mut self) {177 if !self.new.is_empty() {178 self.sup.push(CcObjectCore::new(mem::take(&mut self.new)));179 }180 self.next_field_index = FieldIndex::default();181 }182183 pub fn with_fields_omitted(&mut self, omit: FxHashSet<IStr>) {184 self.commit();185 self.sup.push(CcObjectCore::new(OmitFieldsCore {186 omit,187 prev_layers: self.sup.len(),188 }));189 }190191 pub fn build(mut self) -> ObjValue {192 self.commit();193 if self.sup.is_empty() {194 return ObjValue::empty();195 }196 ObjValue(Cc::new(ObjValueInner {197 cores: self.sup,198 assertions_ran: Cell::new(false),199 value_cache: Default::default(),200 }))201 }202}203impl Default for ObjValueBuilder {204 fn default() -> Self {205 Self::with_capacity(0)206 }207}208209pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);210impl ObjMemberBuilder<ValueBuilder<'_>> {211 212 pub fn value(self, value: impl Into<Val>) {213 let (receiver, name, member) =214 self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value.into())));215 let entry = receiver.0.new.this_entries.entry(name);216 entry.insert_entry(member);217 }218 219 pub fn thunk(self, value: impl Into<Thunk<Val>>) {220 let (receiver, name, member) = self.build_member(MaybeUnbound::Bound(value.into()));221 let entry = receiver.0.new.this_entries.entry(name);222 entry.insert_entry(member);223 }224225 226 pub fn try_value(self, value: impl Into<Val>) -> Result<()> {227 self.try_thunk(Thunk::evaluated(value.into()))228 }229 pub fn try_thunk(self, value: impl Into<Thunk<Val>>) -> Result<()> {230 self.binding(MaybeUnbound::Bound(value.into()))231 }232 pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {233 self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)))234 }235 pub fn binding(self, binding: MaybeUnbound) -> Result<()> {236 let (receiver, name, member) = self.build_member(binding);237 let location = member.location.clone();238 let old = receiver.0.new.this_entries.insert(name.clone(), member);239 if old.is_some() {240 in_frame(241 CallLocation(location.as_ref()),242 || format!("field <{}> initializtion", name.clone()),243 || bail!(DuplicateFieldName(name.clone())),244 )?;245 }246 Ok(())247 }248}