1use std::{2 any::Any,3 cell::{Cell, RefCell},4 collections::hash_map::Entry,5 fmt::{self, Debug},6 hash::{Hash, Hasher},7};89use educe::Educe;10use jrsonnet_gcmodule::{cc_dyn, Cc, Trace, Weak};11use jrsonnet_interner::IStr;12use jrsonnet_parser::{Span, Visibility};13use rustc_hash::{FxHashMap, FxHashSet};1415use crate::{16 arr::{PickObjectKeyValues, PickObjectValues},17 bail,18 error::{suggest_object_fields, ErrorKind::*},19 function::{CallLocation, FuncVal},20 gc::WithCapacityExt as _,21 identity_hash, in_frame,22 operator::evaluate_add_op,23 val::ArrValue,24 CcUnbound, MaybeUnbound, Result, Thunk, Unbound, Val,25};2627#[cfg(not(feature = "exp-preserve-order"))]28mod ordering {29 #![allow(30 31 clippy::unused_self,32 )]3334 use jrsonnet_gcmodule::Trace;3536 #[derive(Clone, Copy, Default, Debug, Trace)]37 pub struct FieldIndex(());38 impl FieldIndex {39 pub const fn next(self) -> Self {40 Self(())41 }42 }4344 #[derive(Clone, Copy, Default, Debug, Trace)]45 pub struct SuperDepth(());46 impl SuperDepth {47 pub(super) fn deepen(self) {}48 }4950 #[derive(Clone, Copy, Debug)]51 pub struct FieldSortKey(());52 impl FieldSortKey {53 pub const fn new(_: SuperDepth, _: FieldIndex) -> Self {54 Self(())55 }56 }57}5859#[cfg(feature = "exp-preserve-order")]60mod ordering {61 use std::cmp::Reverse;6263 use jrsonnet_gcmodule::Trace;6465 #[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]66 pub struct FieldIndex(u32);67 impl FieldIndex {68 pub fn next(self) -> Self {69 Self(self.0 + 1)70 }71 }7273 #[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Debug)]74 pub struct SuperDepth(u32);75 impl SuperDepth {76 pub(super) fn deepen(&mut self) {77 *self.0 += 178 }79 }8081 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]82 pub struct FieldSortKey(Reverse<SuperDepth>, FieldIndex);83 impl FieldSortKey {84 pub fn new(depth: SuperDepth, index: FieldIndex) -> Self {85 Self(Reverse(depth), index)86 }87 }88}8990use ordering::{FieldIndex, FieldSortKey, SuperDepth};91929394#[derive(Clone, Copy)]95pub struct ObjFieldFlags(u8);96impl ObjFieldFlags {97 fn new(add: bool, visibility: Visibility) -> Self {98 let mut v = 0;99 if add {100 v |= 1;101 }102 v |= match visibility {103 Visibility::Normal => 0b000,104 Visibility::Hidden => 0b010,105 Visibility::Unhide => 0b100,106 };107 Self(v)108 }109 pub fn add(&self) -> bool {110 self.0 & 1 != 0111 }112 pub fn visibility(&self) -> Visibility {113 match (self.0 & 0b110) >> 1 {114 0b00 => Visibility::Normal,115 0b01 => Visibility::Hidden,116 0b10 => Visibility::Unhide,117 _ => unreachable!(),118 }119 }120}121impl Debug for ObjFieldFlags {122 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {123 f.debug_struct("ObjFieldFlags")124 .field("add", &self.add())125 .field("visibility", &self.visibility())126 .finish()127 }128}129130#[allow(clippy::module_name_repetitions)]131#[derive(Debug, Trace)]132pub struct ObjMember {133 #[trace(skip)]134 flags: ObjFieldFlags,135 original_index: FieldIndex,136 pub invoke: MaybeUnbound,137 pub location: Option<Span>,138}139140cc_dyn!(CcObjectAssertion, ObjectAssertion);141pub trait ObjectAssertion: Trace {142 fn run(&self, sup_this: SupThis) -> Result<()>;143}144145146147#[derive(Trace, Debug)]148enum CacheValue {149 Cached(Result<Option<Val>>),150 Pending,151}152153#[allow(clippy::module_name_repetitions)]154#[derive(Trace)]155#[trace(tracking(force))]156pub struct OopObject {157 158 assertions: Cc<Vec<CcObjectAssertion>>,159 this_entries: Cc<FxHashMap<IStr, ObjMember>>,160 value_cache: RefCell<FxHashMap<(IStr, Option<WeakObjValue>), CacheValue>>,161}162impl Debug for OopObject {163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {164 f.debug_struct("OopObject")165 166 167 .field("this_entries", &self.this_entries)168 169 .finish_non_exhaustive()170 }171}172173type EnumFieldsHandler<'a> = dyn FnMut(SuperDepth, FieldIndex, IStr, Visibility) -> bool + 'a;174175#[derive(Trace, Clone)]176pub enum ValueProcess {177 None,178 SuperPlus,179}180181pub trait ObjectCore: Trace + Any + Debug {182 183 fn enum_fields_core(184 &self,185 super_depth: &mut SuperDepth,186 handler: &mut EnumFieldsHandler<'_>,187 ) -> bool;188189 fn has_field_include_hidden(&self, name: IStr) -> bool;190191 fn get_for(&self, key: IStr, sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>>;192 193 fn field_visibility(&self, field: IStr) -> Option<Visibility>;194195 fn run_assertions_raw(&self, sup_this: SupThis) -> Result<()>;196}197198#[derive(Clone, Trace)]199pub struct WeakObjValue(#[trace(skip)] Weak<ObjValueInner>);200impl Debug for WeakObjValue {201 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {202 f.debug_tuple("WeakObjValue").finish()203 }204}205206impl PartialEq for WeakObjValue {207 fn eq(&self, other: &Self) -> bool {208 Weak::ptr_eq(&self.0, &other.0)209 }210}211212impl Eq for WeakObjValue {}213impl Hash for WeakObjValue {214 fn hash<H: Hasher>(&self, hasher: &mut H) {215 216 let addr = unsafe { *std::ptr::addr_of!(self.0).cast() };217 hasher.write_usize(addr);218 }219}220221cc_dyn!(222 #[derive(Clone, Debug)]223 ObjCore, ObjectCore,224 pub fn new() {...}225);226#[derive(Trace, Educe)]227#[educe(Debug)]228struct ObjValueInner {229 cores: Vec<ObjCore>,230 assertions_ran: Cell<bool>,231 value_cache: RefCell<FxHashMap<(IStr, CoreIdx), CacheValue>>,232}233234thread_local! {235 static RUNNING_ASSERTIONS: RefCell<FxHashSet<ObjValue>> = RefCell::default();236}237fn is_asserting(obj: &ObjValue) -> bool {238 RUNNING_ASSERTIONS.with_borrow(|v| v.contains(obj))239}240241fn start_asserting(obj: &ObjValue) -> bool {242 RUNNING_ASSERTIONS.with_borrow_mut(|v| v.insert(obj.clone()))243}244fn finish_asserting(obj: &ObjValue) {245 RUNNING_ASSERTIONS.with_borrow_mut(|v| {246 let r = v.remove(obj);247 debug_assert!(248 r,249 "finish_asserting was called before start_asserting or twice"250 );251 });252}253254#[allow(clippy::module_name_repetitions)]255#[derive(Clone, Trace, Debug, Educe)]256#[educe(PartialEq, Hash, Eq)]257pub struct ObjValue(258 #[educe(PartialEq(method(Cc::ptr_eq)), Hash(method(identity_hash)))] Cc<ObjValueInner>,259);260261#[derive(Trace, Debug)]262struct StandaloneSuperCore {263 sup: CoreIdx,264 this: ObjValue,265}266impl ObjectCore for StandaloneSuperCore {267 fn enum_fields_core(268 &self,269 super_depth: &mut SuperDepth,270 handler: &mut EnumFieldsHandler<'_>,271 ) -> bool {272 self.this273 .enum_fields_internal(super_depth, handler, self.sup)274 }275276 fn has_field_include_hidden(&self, name: IStr) -> bool {277 self.this.has_field_include_hidden_idx(name, self.sup)278 }279280 fn get_for(&self, key: IStr, _sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>> {281 let v = self.this.get_idx(key, self.sup)?;282 Ok(v.map(|v| (v, ValueProcess::None)))283 }284285 fn field_visibility(&self, field: IStr) -> Option<Visibility> {286 self.this.field_visibility_idx(field, self.sup)287 }288289 fn run_assertions_raw(&self, _sup_this: SupThis) -> Result<()> {290 self.this.run_assertions()291 }292}293294#[derive(Debug, Trace)]295struct EmptyObject;296impl ObjectCore for EmptyObject {297 fn enum_fields_core(298 &self,299 _super_depth: &mut SuperDepth,300 _handler: &mut EnumFieldsHandler<'_>,301 ) -> bool {302 true303 }304305 fn has_field_include_hidden(&self, _name: IStr) -> bool {306 false307 }308309 fn get_for(&self, _key: IStr, _sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>> {310 Ok(None)311 }312313 fn run_assertions_raw(&self, _sup_this: SupThis) -> Result<()> {314 Ok(())315 }316317 fn field_visibility(&self, _field: IStr) -> Option<Visibility> {318 None319 }320}321322#[derive(Hash, PartialEq, Eq, Trace, Clone, Copy, Debug)]323struct CoreIdx {324 idx: usize,325}326impl CoreIdx {327 fn super_exists(self) -> bool {328 self.idx != 0329 }330}331#[derive(Trace, Clone, PartialEq, Eq, Hash, Debug)]332pub struct SupThis {333 sup: CoreIdx,334 this: ObjValue,335}336impl SupThis {337 pub fn has_super(&self) -> bool {338 self.sup.super_exists()339 }340 341 342 343 344 pub fn field_in_super(&self, field: IStr) -> bool {345 self.this.has_field_include_hidden_idx(field, self.sup)346 }347 348 349 350 351 pub fn get_super(&self, field: IStr) -> Result<Option<Val>> {352 if !self.sup.super_exists() {353 bail!(NoSuperFound);354 }355 self.this.get_idx(field, self.sup)356 }357 358 359 360 361 362 pub fn standalone_super(&self) -> Result<ObjValue> {363 if !self.sup.super_exists() {364 bail!(NoSuperFound)365 }366 Ok(ObjValue::new(StandaloneSuperCore {367 sup: self.sup,368 this: self.this.clone(),369 }))370 }371 pub fn this(&self) -> &ObjValue {372 &self.this373 }374 pub fn downgrade(self) -> WeakSupThis {375 WeakSupThis {376 sup: self.sup,377 this: self.this.downgrade(),378 }379 }380}381#[derive(Trace, PartialEq, Eq, Hash, Debug)]382pub struct WeakSupThis {383 sup: CoreIdx,384 this: WeakObjValue,385}386387impl ObjValue {388 pub fn new(v: impl ObjectCore) -> Self {389 Self(Cc::new(ObjValueInner {390 cores: vec![ObjCore::new(v)],391 assertions_ran: Cell::new(false),392 value_cache: RefCell::new(FxHashMap::new()),393 }))394 }395 pub fn new_empty() -> Self {396 Self::new(EmptyObject)397 }398 pub fn builder() -> ObjValueBuilder {399 ObjValueBuilder::new()400 }401 pub fn builder_with_capacity(capacity: usize) -> ObjValueBuilder {402 ObjValueBuilder::with_capacity(capacity)403 }404 pub(crate) fn extend_with_raw_member(self, key: IStr, value: ObjMember) -> Self {405 let mut out = ObjValueBuilder::with_capacity(1);406 out.with_super(self);407 let mut member = out.field(key);408 if value.flags.add() {409 member = member.add();410 }411 if let Some(loc) = value.location {412 member = member.with_location(loc);413 }414 let _ = member415 .with_visibility(value.flags.visibility())416 .binding(value.invoke);417 out.build()418 }419 pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder<'_>> {420 ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())421 }422423 #[must_use]424 pub fn extend_from(&self, sup: Self) -> Self {425 let mut cores = sup.0.cores.clone();426 cores.extend(self.0.cores.iter().cloned());427 ObjValue(Cc::new(ObjValueInner {428 cores,429 value_cache: RefCell::default(),430 assertions_ran: Cell::new(false),431 }))432 }433 434 435 436 437 438 439 pub fn len(&self) -> usize {440 self.fields_visibility()441 .iter()442 .filter(|(_, (visible, _))| *visible)443 .count()444 }445 pub fn is_empty(&self) -> bool {446 self.len() == 0447 }448 449 450 451 452 pub fn enum_fields(&self, handler: &mut EnumFieldsHandler<'_>) -> bool {453 let mut super_depth = SuperDepth::default();454 self.enum_fields_internal(455 &mut super_depth,456 handler,457 CoreIdx {458 idx: self.0.cores.len(),459 },460 )461 }462 fn enum_fields_internal(463 &self,464 super_depth: &mut SuperDepth,465 handler: &mut EnumFieldsHandler<'_>,466 idx: CoreIdx,467 ) -> bool {468 for core in self.0.cores[..idx.idx].iter() {469 if !core.0.enum_fields_core(super_depth, handler) {470 return false;471 }472 super_depth.deepen();473 }474 true475 }476477 pub fn has_field_include_hidden(&self, name: IStr) -> bool {478 self.has_field_include_hidden_idx(479 name,480 CoreIdx {481 idx: self.0.cores.len(),482 },483 )484 }485 fn has_field_include_hidden_idx(&self, name: IStr, core: CoreIdx) -> bool {486 self.0.cores[..core.idx]487 .iter()488 .rev()489 .any(|v| v.0.has_field_include_hidden(name.clone()))490 }491 pub fn has_field(&self, name: IStr) -> bool {492 match self.field_visibility(name) {493 Some(Visibility::Unhide | Visibility::Normal) => true,494 Some(Visibility::Hidden) | None => false,495 }496 }497 pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {498 if include_hidden {499 self.has_field_include_hidden(name)500 } else {501 self.has_field(name)502 }503 }504 pub fn get(&self, key: IStr) -> Result<Option<Val>> {505 self.get_idx(506 key,507 CoreIdx {508 idx: self.0.cores.len(),509 },510 )511 }512513 fn get_idx(&self, key: IStr, core: CoreIdx) -> Result<Option<Val>> {514 let cache_key = (key.clone(), core);515 {516 let mut cache = self.0.value_cache.borrow_mut();517 518 match cache.entry(cache_key.clone()) {519 Entry::Occupied(v) => match v.get() {520 CacheValue::Cached(v) => return v.clone(),521 CacheValue::Pending => {522 if !is_asserting(self) {523 bail!(InfiniteRecursionDetected);524 }525 }526 },527 Entry::Vacant(v) => {528 v.insert(CacheValue::Pending);529 }530 };531 }532 let result = self.get_idx_uncached(key, core);533 {534 let mut cache = self.0.value_cache.borrow_mut();535 cache.insert(cache_key, CacheValue::Cached(result.clone()));536 }537 result538 }539 fn get_idx_uncached(&self, key: IStr, core: CoreIdx) -> Result<Option<Val>> {540 self.run_assertions()?;541 let mut add_stack = Vec::with_capacity(2);542 for (sup, core) in self.0.cores[..core.idx].iter().enumerate().rev() {543 let sup_this = SupThis {544 sup: CoreIdx { idx: sup },545 this: self.clone(),546 };547 if let Some((val, proc)) = core.0.get_for(key.clone(), sup_this)? {548 match proc {549 ValueProcess::None if add_stack.is_empty() => return Ok(Some(val)),550 ValueProcess::None => {551 add_stack.push(val);552 break;553 }554 ValueProcess::SuperPlus => {555 add_stack.push(val);556 }557 }558 }559 }560 if add_stack.is_empty() {561 562 return Ok(None);563 } else if add_stack.len() == 1 {564 565 566 return Ok(Some(add_stack.pop().expect("single element on stack")));567 }568 let mut values = add_stack.into_iter().rev();569 let init = values.next().expect("at least 2 elements");570571 values572 .try_fold(init, |a, b| evaluate_add_op(&a, &b))573 .map(Some)574575 576 }577578 pub fn get_or_bail(&self, key: IStr) -> Result<Val> {579 let Some(value) = self.get(key.clone())? else {580 let suggestions = suggest_object_fields(self, key.clone());581 bail!(NoSuchField(key, suggestions))582 };583 Ok(value)584 }585586 fn field_visibility(&self, field: IStr) -> Option<Visibility> {587 self.field_visibility_idx(588 field,589 CoreIdx {590 idx: self.0.cores.len(),591 },592 )593 }594 fn field_visibility_idx(&self, field: IStr, core: CoreIdx) -> Option<Visibility> {595 let mut exists = false;596 for ele in self.0.cores[..core.idx].iter().rev() {597 let vis = ele.0.field_visibility(field.clone());598 match vis {599 Some(Visibility::Unhide | Visibility::Hidden) => return vis,600 Some(Visibility::Normal) => exists = true,601 None => {}602 }603 }604 exists.then_some(Visibility::Normal)605 }606607 pub fn run_assertions(&self) -> Result<()> {608 if self.0.assertions_ran.get() {609 return Ok(());610 }611 if !start_asserting(self) {612 return Ok(());613 }614 for (idx, ele) in self.0.cores.iter().enumerate() {615 let sup_this = SupThis {616 sup: CoreIdx { idx },617 this: self.clone(),618 };619 ele.0.run_assertions_raw(sup_this).inspect_err(|_e| {620 finish_asserting(self);621 })?;622 }623 finish_asserting(self);624 self.0.assertions_ran.set(true);625 Ok(())626 }627628 pub fn iter(629 &self,630 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,631 ) -> impl Iterator<Item = (IStr, Result<Val>)> + '_ {632 let fields = self.fields(633 #[cfg(feature = "exp-preserve-order")]634 preserve_order,635 );636 fields.into_iter().map(|field| {637 (638 field.clone(),639 self.get(field)640 .map(|opt| opt.expect("iterating over keys, field exists")),641 )642 })643 }644 pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {645 if !self.has_field_ex(key.clone(), true) {646 return None;647 }648 let obj = self.clone();649650 Some(Thunk!(move || Ok(obj.get(key)?.expect("field exists"))))651 }652 pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {653 let obj = self.clone();654 Thunk!(move || obj.get_or_bail(key))655 }656 pub fn ptr_eq(a: &Self, b: &Self) -> bool {657 Cc::ptr_eq(&a.0, &b.0)658 }659 pub fn downgrade(self) -> WeakObjValue {660 WeakObjValue(self.0.downgrade())661 }662 fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {663 let mut out = FxHashMap::default();664 self.enum_fields(&mut |depth, index, name, visibility| {665 let new_sort_key = FieldSortKey::new(depth, index);666 let entry = out.entry(name);667 let (visible, _) = entry.or_insert((true, new_sort_key));668 match visibility {669 Visibility::Normal => {}670 Visibility::Hidden => {671 *visible = false;672 }673 Visibility::Unhide => {674 *visible = true;675 }676 };677 false678 });679 out680 }681 pub fn fields_ex(682 &self,683 include_hidden: bool,684 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,685 ) -> Vec<IStr> {686 #[cfg(feature = "exp-preserve-order")]687 if preserve_order {688 let (mut fields, mut keys): (Vec<_>, Vec<_>) = self689 .fields_visibility()690 .into_iter()691 .filter(|(_, (visible, _))| include_hidden || *visible)692 .enumerate()693 .map(|(idx, (k, (_, sk)))| (k, (sk, idx)))694 .unzip();695 keys.sort_unstable_by_key(|v| v.0);696 697 for i in 0..fields.len() {698 let x = fields[i].clone();699 let mut j = i;700 loop {701 let k = keys[j].1;702 keys[j].1 = j;703 if k == i {704 break;705 }706 fields[j] = fields[k].clone();707 j = k;708 }709 fields[j] = x;710 }711 return fields;712 }713714 let mut fields: Vec<_> = self715 .fields_visibility()716 .into_iter()717 .filter(|(_, (visible, _))| include_hidden || *visible)718 .map(|(k, _)| k)719 .collect();720 fields.sort_unstable();721 fields722 }723 pub fn fields(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Vec<IStr> {724 self.fields_ex(725 false,726 #[cfg(feature = "exp-preserve-order")]727 preserve_order,728 )729 }730 pub fn values_ex(731 &self,732 include_hidden: bool,733 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,734 ) -> ArrValue {735 ArrValue::new(PickObjectValues::new(736 self.clone(),737 self.fields_ex(738 include_hidden,739 #[cfg(feature = "exp-preserve-order")]740 preserve_order,741 ),742 ))743 }744 pub fn values(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> ArrValue {745 self.values_ex(746 false,747 #[cfg(feature = "exp-preserve-order")]748 preserve_order,749 )750 }751 pub fn key_values_ex(752 &self,753 include_hidden: bool,754 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,755 ) -> ArrValue {756 ArrValue::new(PickObjectKeyValues::new(757 self.clone(),758 self.fields_ex(759 include_hidden,760 #[cfg(feature = "exp-preserve-order")]761 preserve_order,762 ),763 ))764 }765 pub fn key_values(766 &self,767 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,768 ) -> ArrValue {769 self.key_values_ex(770 false,771 #[cfg(feature = "exp-preserve-order")]772 preserve_order,773 )774 }775}776777impl OopObject {778 pub fn new(779 this_entries: Cc<FxHashMap<IStr, ObjMember>>,780 assertions: Cc<Vec<CcObjectAssertion>>,781 ) -> Self {782 Self {783 this_entries,784 value_cache: RefCell::new(FxHashMap::new()),785 assertions,786 }787 }788}789790impl ObjectCore for OopObject {791 fn enum_fields_core(792 &self,793 super_depth: &mut SuperDepth,794 handler: &mut EnumFieldsHandler<'_>,795 ) -> bool {796 for (name, member) in self.this_entries.iter() {797 if handler(798 *super_depth,799 member.original_index,800 name.clone(),801 member.flags.visibility(),802 ) {803 return false;804 }805 }806 true807 }808809 fn has_field_include_hidden(&self, name: IStr) -> bool {810 self.this_entries.contains_key(&name)811 }812813 fn get_for(&self, key: IStr, sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>> {814 match self.this_entries.get(&key) {815 Some(k) => Ok(Some((816 k.invoke.evaluate(sup_this)?,817 if k.flags.add() {818 ValueProcess::SuperPlus819 } else {820 ValueProcess::None821 },822 ))),823 None => Ok(None),824 }825 }826 fn field_visibility(&self, name: IStr) -> Option<Visibility> {827 Some(self.this_entries.get(&name)?.flags.visibility())828 }829830 fn run_assertions_raw(&self, sup_this: SupThis) -> Result<()> {831 if self.assertions.is_empty() {832 return Ok(());833 }834 for assertion in self.assertions.iter() {835 assertion.0.run(sup_this.clone())?;836 }837 Ok(())838 }839}840841#[allow(clippy::module_name_repetitions)]842pub struct ObjValueBuilder {843 sup: Option<ObjValue>,844 map: FxHashMap<IStr, ObjMember>,845 assertions: Vec<CcObjectAssertion>,846 next_field_index: FieldIndex,847}848impl ObjValueBuilder {849 pub fn new() -> Self {850 Self::with_capacity(0)851 }852 pub fn with_capacity(capacity: usize) -> Self {853 Self {854 sup: None,855 map: FxHashMap::with_capacity(capacity),856 assertions: Vec::new(),857 next_field_index: FieldIndex::default(),858 }859 }860 pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self {861 self.assertions.reserve_exact(capacity);862 self863 }864 pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {865 self.sup = Some(super_obj);866 self867 }868869 pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {870 self.assertions.push(CcObjectAssertion::new(assertion));871 self872 }873 pub fn field(&mut self, name: impl Into<IStr>) -> ObjMemberBuilder<ValueBuilder<'_>> {874 let field_index = self.next_field_index;875 self.next_field_index = self.next_field_index.next();876 ObjMemberBuilder::new(ValueBuilder(self), name.into(), field_index)877 }878 879 880 881 882 pub fn method(&mut self, name: impl Into<IStr>, value: impl Into<FuncVal>) -> &mut Self {883 self.field(name).hide().value(Val::Func(value.into()));884 self885 }886 pub fn try_method(887 &mut self,888 name: impl Into<IStr>,889 value: impl Into<FuncVal>,890 ) -> Result<&mut Self> {891 self.field(name).hide().try_value(Val::Func(value.into()))?;892 Ok(self)893 }894895 pub fn build(self) -> ObjValue {896 if self.sup.is_none() && self.map.is_empty() && self.assertions.is_empty() {897 return ObjValue::new_empty();898 }899 let res = ObjValue::new(OopObject::new(Cc::new(self.map), Cc::new(self.assertions)));900 self.sup.map(|sup| res.extend_from(sup)).unwrap_or(res)901 }902}903impl Default for ObjValueBuilder {904 fn default() -> Self {905 Self::with_capacity(0)906 }907}908909#[allow(clippy::module_name_repetitions)]910#[must_use = "value not added unless binding() was called"]911pub struct ObjMemberBuilder<Kind> {912 kind: Kind,913 name: IStr,914 add: bool,915 visibility: Visibility,916 original_index: FieldIndex,917 location: Option<Span>,918}919920#[allow(clippy::missing_const_for_fn)]921impl<Kind> ObjMemberBuilder<Kind> {922 pub(crate) fn new(kind: Kind, name: IStr, original_index: FieldIndex) -> Self {923 Self {924 kind,925 name,926 original_index,927 add: false,928 visibility: Visibility::Normal,929 location: None,930 }931 }932933 pub const fn with_add(mut self, add: bool) -> Self {934 self.add = add;935 self936 }937 pub fn add(self) -> Self {938 self.with_add(true)939 }940 pub fn with_visibility(mut self, visibility: Visibility) -> Self {941 self.visibility = visibility;942 self943 }944 pub fn hide(self) -> Self {945 self.with_visibility(Visibility::Hidden)946 }947 pub fn with_location(mut self, location: Span) -> Self {948 self.location = Some(location);949 self950 }951 fn build_member(self, binding: MaybeUnbound) -> (Kind, IStr, ObjMember) {952 (953 self.kind,954 self.name,955 ObjMember {956 flags: ObjFieldFlags::new(self.add, self.visibility),957 original_index: self.original_index,958 invoke: binding,959 location: self.location,960 },961 )962 }963}964965pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);966impl ObjMemberBuilder<ValueBuilder<'_>> {967 968 pub fn value(self, value: impl Into<Val>) {969 let (receiver, name, member) =970 self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value.into())));971 let entry = receiver.0.map.entry(name);972 entry.insert_entry(member);973 }974975 976 pub fn try_value(self, value: impl Into<Val>) -> Result<()> {977 self.try_thunk(Thunk::evaluated(value.into()))978 }979 pub fn try_thunk(self, value: impl Into<Thunk<Val>>) -> Result<()> {980 self.binding(MaybeUnbound::Bound(value.into()))981 }982 pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {983 self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)))984 }985 pub fn binding(self, binding: MaybeUnbound) -> Result<()> {986 let (receiver, name, member) = self.build_member(binding);987 let location = member.location.clone();988 let old = receiver.0.map.insert(name.clone(), member);989 if old.is_some() {990 in_frame(991 CallLocation(location.as_ref()),992 || format!("field <{}> initializtion", name.clone()),993 || bail!(DuplicateFieldName(name.clone())),994 )?;995 }996 Ok(())997 }998}9991000pub struct ExtendBuilder<'v>(&'v mut ObjValue);1001impl ObjMemberBuilder<ExtendBuilder<'_>> {1002 pub fn value(self, value: impl Into<Val>) {1003 self.binding(MaybeUnbound::Bound(Thunk::evaluated(value.into())));1004 }1005 pub fn bindable(self, bindable: impl Unbound<Bound = Val>) {1006 self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)));1007 }1008 pub fn binding(self, binding: MaybeUnbound) {1009 let (receiver, name, member) = self.build_member(binding);1010 let new = receiver.0.clone();1011 *receiver.0 = new.extend_with_raw_member(name, member);1012 }1013}