1use std::{2 any::Any, cell::{Cell, RefCell}, collections::hash_map::Entry, fmt::{self, Debug}, hash::{Hash, Hasher}3};45use jrsonnet_gcmodule::{cc_dyn, Cc, Trace, Weak};6use educe::Educe;7use jrsonnet_interner::IStr;8use jrsonnet_parser::{Span, Visibility};9use rustc_hash::{FxHashMap, FxHashSet};1011use crate::{12 arr::{PickObjectKeyValues, PickObjectValues},13 bail,14 error::{suggest_object_fields, ErrorKind::*},15 function::{CallLocation, FuncVal},16 gc::WithCapacityExt as _,17 in_frame,18 identity_hash, 19 operator::evaluate_add_op,20 val::ArrValue,21 CcUnbound, MaybeUnbound, Result, Thunk, Unbound, Val,22};2324#[cfg(not(feature = "exp-preserve-order"))]25mod ordering {26 #![allow(27 28 clippy::unused_self,29 )]3031 use jrsonnet_gcmodule::Trace;3233 #[derive(Clone, Copy, Default, Debug, Trace)]34 pub struct FieldIndex(());35 impl FieldIndex {36 pub const fn next(self) -> Self {37 Self(())38 }39 }4041 #[derive(Clone, Copy, Default, Debug, Trace)]42 pub struct SuperDepth(());43 impl SuperDepth {44 pub(super) fn deepen(self) {}45 }4647 #[derive(Clone, Copy, Debug)]48 pub struct FieldSortKey(());49 impl FieldSortKey {50 pub const fn new(_: SuperDepth, _: FieldIndex) -> Self {51 Self(())52 }53 }54}5556#[cfg(feature = "exp-preserve-order")]57mod ordering {58 use std::cmp::Reverse;5960 use jrsonnet_gcmodule::Trace;6162 #[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]63 pub struct FieldIndex(u32);64 impl FieldIndex {65 pub fn next(self) -> Self {66 Self(self.0 + 1)67 }68 }6970 #[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Debug)]71 pub struct SuperDepth(u32);72 impl SuperDepth {73 pub(super) fn deepen(&mut self) {74 *self.0 += 175 }76 }7778 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]79 pub struct FieldSortKey(Reverse<SuperDepth>, FieldIndex);80 impl FieldSortKey {81 pub fn new(depth: SuperDepth, index: FieldIndex) -> Self {82 Self(Reverse(depth), index)83 }84 }85}8687use ordering::{FieldIndex, FieldSortKey, SuperDepth};88899091#[derive(Clone, Copy)]92pub struct ObjFieldFlags(u8);93impl ObjFieldFlags {94 fn new(add: bool, visibility: Visibility) -> Self {95 let mut v = 0;96 if add {97 v |= 1;98 }99 v |= match visibility {100 Visibility::Normal => 0b000,101 Visibility::Hidden => 0b010,102 Visibility::Unhide => 0b100,103 };104 Self(v)105 }106 pub fn add(&self) -> bool {107 self.0 & 1 != 0108 }109 pub fn visibility(&self) -> Visibility {110 match (self.0 & 0b110) >> 1 {111 0b00 => Visibility::Normal,112 0b01 => Visibility::Hidden,113 0b10 => Visibility::Unhide,114 _ => unreachable!(),115 }116 }117}118impl Debug for ObjFieldFlags {119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {120 f.debug_struct("ObjFieldFlags")121 .field("add", &self.add())122 .field("visibility", &self.visibility())123 .finish()124 }125}126127#[allow(clippy::module_name_repetitions)]128#[derive(Debug, Trace)]129pub struct ObjMember {130 #[trace(skip)]131 flags: ObjFieldFlags,132 original_index: FieldIndex,133 pub invoke: MaybeUnbound,134 pub location: Option<Span>,135}136137cc_dyn!(CcObjectAssertion, ObjectAssertion);138pub trait ObjectAssertion: Trace {139 fn run(&self, sup_this: SupThis) -> Result<()>;140}141142143144#[derive(Trace, Debug)]145enum CacheValue {146 Cached(Result<Option<Val>>),147 Pending,148}149150#[allow(clippy::module_name_repetitions)]151#[derive(Trace)]152#[trace(tracking(force))]153pub struct OopObject {154 155 assertions: Cc<Vec<CcObjectAssertion>>,156 this_entries: Cc<FxHashMap<IStr, ObjMember>>,157 value_cache: RefCell<FxHashMap<(IStr, Option<WeakObjValue>), CacheValue>>,158}159impl Debug for OopObject {160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {161 f.debug_struct("OopObject")162 163 164 .field("this_entries", &self.this_entries)165 166 .finish_non_exhaustive()167 }168}169170type EnumFieldsHandler<'a> = dyn FnMut(SuperDepth, FieldIndex, IStr, Visibility) -> bool + 'a;171172#[derive(Trace, Clone)]173pub enum ValueProcess {174 None,175 SuperPlus,176}177178pub trait ObjectCore: Trace + Any + Debug {179 180 fn enum_fields_core(181 &self,182 super_depth: &mut SuperDepth,183 handler: &mut EnumFieldsHandler<'_>,184 ) -> bool;185186 fn has_field_include_hidden(&self, name: IStr) -> bool;187188 fn get_for(&self, key: IStr, sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>>;189 190 fn field_visibility(&self, field: IStr) -> Option<Visibility>;191192 fn run_assertions_raw(&self, sup_this: SupThis) -> Result<()>;193}194195#[derive(Clone, Trace)]196pub struct WeakObjValue(#[trace(skip)] Weak<ObjValueInner>);197impl Debug for WeakObjValue {198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {199 f.debug_tuple("WeakObjValue").finish()200 }201}202203impl PartialEq for WeakObjValue {204 fn eq(&self, other: &Self) -> bool {205 Weak::ptr_eq(&self.0, &other.0)206 }207}208209impl Eq for WeakObjValue {}210impl Hash for WeakObjValue {211 fn hash<H: Hasher>(&self, hasher: &mut H) {212 213 let addr = unsafe { *std::ptr::addr_of!(self.0).cast() };214 hasher.write_usize(addr);215 }216}217218cc_dyn!(219 #[derive(Clone, Debug)]220 ObjCore, ObjectCore,221 pub fn new() {...}222);223#[derive(Trace, Educe)]224#[educe(Debug)]225struct ObjValueInner {226 cores: Vec<ObjCore>,227 assertions_ran: Cell<bool>,228 value_cache: RefCell<FxHashMap<(IStr, CoreIdx), CacheValue>>,229}230231thread_local! {232 static RUNNING_ASSERTIONS: RefCell<FxHashSet<ObjValue>> = RefCell::default();233}234fn is_asserting(obj: &ObjValue) -> bool {235 RUNNING_ASSERTIONS.with_borrow(|v| v.contains(obj))236}237238fn start_asserting(obj: &ObjValue) -> bool {239 RUNNING_ASSERTIONS.with_borrow_mut(|v| v.insert(obj.clone()))240}241fn finish_asserting(obj: &ObjValue) {242 RUNNING_ASSERTIONS.with_borrow_mut(|v| {243 let r = v.remove(obj);244 debug_assert!(245 r,246 "finish_asserting was called before start_asserting or twice"247 );248 });249}250251#[allow(clippy::module_name_repetitions)]252#[derive(Clone, Trace, Debug, Educe)]253#[educe(PartialEq, Hash, Eq)]254pub struct ObjValue(255 #[educe(PartialEq(method(Cc::ptr_eq)), Hash(method(identity_hash)))] Cc<ObjValueInner>,256);257258#[derive(Trace, Debug)]259struct StandaloneSuperCore {260 sup: CoreIdx,261 this: ObjValue,262}263impl ObjectCore for StandaloneSuperCore {264 fn enum_fields_core(265 &self,266 super_depth: &mut SuperDepth,267 handler: &mut EnumFieldsHandler<'_>,268 ) -> bool {269 self.this270 .enum_fields_internal(super_depth, handler, self.sup)271 }272273 fn has_field_include_hidden(&self, name: IStr) -> bool {274 self.this.has_field_include_hidden_idx(name, self.sup)275 }276277 fn get_for(&self, key: IStr, _sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>> {278 let v = self.this.get_idx(key, self.sup)?;279 Ok(v.map(|v| (v, ValueProcess::None)))280 }281282 fn field_visibility(&self, field: IStr) -> Option<Visibility> {283 self.this.field_visibility_idx(field, self.sup)284 }285286 fn run_assertions_raw(&self, _sup_this: SupThis) -> Result<()> {287 self.this.run_assertions()288 }289}290291#[derive(Debug, Trace)]292struct EmptyObject;293impl ObjectCore for EmptyObject {294 fn enum_fields_core(295 &self,296 _super_depth: &mut SuperDepth,297 _handler: &mut EnumFieldsHandler<'_>,298 ) -> bool {299 true300 }301302 fn has_field_include_hidden(&self, _name: IStr) -> bool {303 false304 }305306 fn get_for(&self, _key: IStr, _sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>> {307 Ok(None)308 }309310 fn run_assertions_raw(&self, _sup_this: SupThis) -> Result<()> {311 Ok(())312 }313314 fn field_visibility(&self, _field: IStr) -> Option<Visibility> {315 None316 }317}318319#[derive(Hash, PartialEq, Eq, Trace, Clone, Copy, Debug)]320struct CoreIdx {321 idx: usize,322}323impl CoreIdx {324 fn super_exists(self) -> bool {325 self.idx != 0326 }327}328#[derive(Trace, Clone, PartialEq, Eq, Hash, Debug)]329pub struct SupThis {330 sup: CoreIdx,331 this: ObjValue,332}333impl SupThis {334 pub fn has_super(&self) -> bool {335 self.sup.super_exists()336 }337 338 339 340 341 pub fn field_in_super(&self, field: IStr) -> bool {342 self.this.has_field_include_hidden_idx(field, self.sup)343 }344 345 346 347 348 pub fn get_super(&self, field: IStr) -> Result<Option<Val>> {349 if !self.sup.super_exists() {350 bail!(NoSuperFound);351 }352 self.this.get_idx(field, self.sup)353 }354 355 356 357 358 359 pub fn standalone_super(&self) -> Result<ObjValue> {360 if !self.sup.super_exists() {361 bail!(NoSuperFound)362 }363 Ok(ObjValue::new(StandaloneSuperCore {364 sup: self.sup,365 this: self.this.clone(),366 }))367 }368 pub fn this(&self) -> &ObjValue {369 &self.this370 }371 pub fn downgrade(self) -> WeakSupThis {372 WeakSupThis {373 sup: self.sup,374 this: self.this.downgrade(),375 }376 }377}378#[derive(Trace, PartialEq, Eq, Hash, Debug)]379pub struct WeakSupThis {380 sup: CoreIdx,381 this: WeakObjValue,382}383384impl ObjValue {385 pub fn new(v: impl ObjectCore) -> Self {386 Self(Cc::new(ObjValueInner {387 cores: vec![ObjCore::new(v)],388 assertions_ran: Cell::new(false),389 value_cache: RefCell::new(FxHashMap::new()),390 }))391 }392 pub fn new_empty() -> Self {393 Self::new(EmptyObject)394 }395 pub fn builder() -> ObjValueBuilder {396 ObjValueBuilder::new()397 }398 pub fn builder_with_capacity(capacity: usize) -> ObjValueBuilder {399 ObjValueBuilder::with_capacity(capacity)400 }401 pub(crate) fn extend_with_raw_member(self, key: IStr, value: ObjMember) -> Self {402 let mut out = ObjValueBuilder::with_capacity(1);403 out.with_super(self);404 let mut member = out.field(key);405 if value.flags.add() {406 member = member.add();407 }408 if let Some(loc) = value.location {409 member = member.with_location(loc);410 }411 let _ = member412 .with_visibility(value.flags.visibility())413 .binding(value.invoke);414 out.build()415 }416 pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder<'_>> {417 ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())418 }419420 #[must_use]421 pub fn extend_from(&self, sup: Self) -> Self {422 let mut cores = sup.0.cores.clone();423 cores.extend(self.0.cores.iter().cloned());424 ObjValue(Cc::new(ObjValueInner {425 cores,426 value_cache: RefCell::default(),427 assertions_ran: Cell::new(false),428 }))429 }430 431 432 433 434 435 436 pub fn len(&self) -> usize {437 self.fields_visibility()438 .iter()439 .filter(|(_, (visible, _))| *visible)440 .count()441 }442 pub fn is_empty(&self) -> bool {443 self.len() == 0444 }445 446 447 448 449 pub fn enum_fields(&self, handler: &mut EnumFieldsHandler<'_>) -> bool {450 let mut super_depth = SuperDepth::default();451 self.enum_fields_internal(452 &mut super_depth,453 handler,454 CoreIdx {455 idx: self.0.cores.len(),456 },457 )458 }459 fn enum_fields_internal(460 &self,461 super_depth: &mut SuperDepth,462 handler: &mut EnumFieldsHandler<'_>,463 idx: CoreIdx,464 ) -> bool {465 for core in self.0.cores[..idx.idx].iter() {466 if !core.0.enum_fields_core(super_depth, handler) {467 return false;468 }469 super_depth.deepen();470 }471 true472 }473474 pub fn has_field_include_hidden(&self, name: IStr) -> bool {475 self.has_field_include_hidden_idx(476 name,477 CoreIdx {478 idx: self.0.cores.len(),479 },480 )481 }482 fn has_field_include_hidden_idx(&self, name: IStr, core: CoreIdx) -> bool {483 self.0.cores[..core.idx]484 .iter()485 .rev()486 .any(|v| v.0.has_field_include_hidden(name.clone()))487 }488 pub fn has_field(&self, name: IStr) -> bool {489 match self.field_visibility(name) {490 Some(Visibility::Unhide | Visibility::Normal) => true,491 Some(Visibility::Hidden) | None => false,492 }493 }494 pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {495 if include_hidden {496 self.has_field_include_hidden(name)497 } else {498 self.has_field(name)499 }500 }501 pub fn get(&self, key: IStr) -> Result<Option<Val>> {502 self.get_idx(503 key,504 CoreIdx {505 idx: self.0.cores.len(),506 },507 )508 }509510 fn get_idx(&self, key: IStr, core: CoreIdx) -> Result<Option<Val>> {511 let cache_key = (key.clone(), core);512 {513 let mut cache = self.0.value_cache.borrow_mut();514 515 match cache.entry(cache_key.clone()) {516 Entry::Occupied(v) => match v.get() {517 CacheValue::Cached(v) => return v.clone(),518 CacheValue::Pending => {519 if !is_asserting(self) {520 bail!(InfiniteRecursionDetected);521 }522 }523 },524 Entry::Vacant(v) => {525 v.insert(CacheValue::Pending);526 }527 };528 }529 let result = self.get_idx_uncached(key, core);530 {531 let mut cache = self.0.value_cache.borrow_mut();532 cache.insert(cache_key, CacheValue::Cached(result.clone()));533 }534 result535 }536 fn get_idx_uncached(&self, key: IStr, core: CoreIdx) -> Result<Option<Val>> {537 self.run_assertions()?;538 let mut add_stack = Vec::with_capacity(2);539 for (sup, core) in self.0.cores[..core.idx].iter().enumerate().rev() {540 let sup_this = SupThis {541 sup: CoreIdx { idx: sup },542 this: self.clone(),543 };544 if let Some((val, proc)) = core.0.get_for(key.clone(), sup_this)? {545 match proc {546 ValueProcess::None if add_stack.is_empty() => return Ok(Some(val)),547 ValueProcess::None => {548 add_stack.push(val);549 break;550 }551 ValueProcess::SuperPlus => {552 add_stack.push(val);553 }554 }555 }556 }557 if add_stack.is_empty() {558 559 return Ok(None);560 } else if add_stack.len() == 1 {561 562 563 return Ok(Some(add_stack.pop().expect("single element on stack")));564 }565 let mut values = add_stack.into_iter().rev();566 let init = values.next().expect("at least 2 elements");567568 values569 .try_fold(init, |a, b| evaluate_add_op(&a, &b))570 .map(Some)571572 573 }574575 pub fn get_or_bail(&self, key: IStr) -> Result<Val> {576 let Some(value) = self.get(key.clone())? else {577 let suggestions = suggest_object_fields(self, key.clone());578 bail!(NoSuchField(key, suggestions))579 };580 Ok(value)581 }582583 fn field_visibility(&self, field: IStr) -> Option<Visibility> {584 self.field_visibility_idx(585 field,586 CoreIdx {587 idx: self.0.cores.len(),588 },589 )590 }591 fn field_visibility_idx(&self, field: IStr, core: CoreIdx) -> Option<Visibility> {592 let mut exists = false;593 for ele in self.0.cores[..core.idx].iter().rev() {594 let vis = ele.0.field_visibility(field.clone());595 match vis {596 Some(Visibility::Unhide | Visibility::Hidden) => return vis,597 Some(Visibility::Normal) => exists = true,598 None => {}599 }600 }601 exists.then_some(Visibility::Normal)602 }603604 pub fn run_assertions(&self) -> Result<()> {605 if self.0.assertions_ran.get() {606 return Ok(());607 }608 if !start_asserting(self) {609 return Ok(());610 }611 for (idx, ele) in self.0.cores.iter().enumerate() {612 let sup_this = SupThis {613 sup: CoreIdx { idx },614 this: self.clone(),615 };616 ele.0.run_assertions_raw(sup_this).inspect_err(|_e| {617 finish_asserting(self);618 })?;619 }620 finish_asserting(self);621 self.0.assertions_ran.set(true);622 Ok(())623 }624625 pub fn iter(626 &self,627 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,628 ) -> impl Iterator<Item = (IStr, Result<Val>)> + '_ {629 let fields = self.fields(630 #[cfg(feature = "exp-preserve-order")]631 preserve_order,632 );633 fields.into_iter().map(|field| {634 (635 field.clone(),636 self.get(field)637 .map(|opt| opt.expect("iterating over keys, field exists")),638 )639 })640 }641 pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {642 if !self.has_field_ex(key.clone(), true) {643 return None;644 }645 let obj = self.clone();646647 Some(Thunk!(move || Ok(obj.get(key)?.expect("field exists"))))648 }649 pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {650 let obj = self.clone();651 Thunk!(move || obj.get_or_bail(key))652 }653 pub fn ptr_eq(a: &Self, b: &Self) -> bool {654 Cc::ptr_eq(&a.0, &b.0)655 }656 pub fn downgrade(self) -> WeakObjValue {657 WeakObjValue(self.0.downgrade())658 }659 fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {660 let mut out = FxHashMap::default();661 self.enum_fields(&mut |depth, index, name, visibility| {662 dbg!(&name, visibility);663 let new_sort_key = FieldSortKey::new(depth, index);664 let entry = out.entry(name);665 let (visible, _) = entry.or_insert((true, new_sort_key));666 match visibility {667 Visibility::Normal => {}668 Visibility::Hidden => {669 *visible = false;670 }671 Visibility::Unhide => {672 *visible = true;673 }674 };675 false676 });677 out678 }679 pub fn fields_ex(680 &self,681 include_hidden: bool,682 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,683 ) -> Vec<IStr> {684 #[cfg(feature = "exp-preserve-order")]685 if preserve_order {686 let (mut fields, mut keys): (Vec<_>, Vec<_>) = self687 .fields_visibility()688 .into_iter()689 .filter(|(_, (visible, _))| include_hidden || *visible)690 .enumerate()691 .map(|(idx, (k, (_, sk)))| (k, (sk, idx)))692 .unzip();693 keys.sort_unstable_by_key(|v| v.0);694 695 for i in 0..fields.len() {696 let x = fields[i].clone();697 let mut j = i;698 loop {699 let k = keys[j].1;700 keys[j].1 = j;701 if k == i {702 break;703 }704 fields[j] = fields[k].clone();705 j = k;706 }707 fields[j] = x;708 }709 return fields;710 }711712 let mut fields: Vec<_> = dbg!(self713 .fields_visibility())714 .into_iter()715 .filter(|(_, (visible, _))| include_hidden || *visible)716 .map(|(k, _)| k)717 .collect();718 fields.sort_unstable();719 fields720 }721 pub fn fields(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Vec<IStr> {722 self.fields_ex(723 false,724 #[cfg(feature = "exp-preserve-order")]725 preserve_order,726 )727 }728 pub fn values_ex(729 &self,730 include_hidden: bool,731 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,732 ) -> ArrValue {733 ArrValue::new(PickObjectValues::new(734 self.clone(),735 self.fields_ex(736 include_hidden,737 #[cfg(feature = "exp-preserve-order")]738 preserve_order,739 ),740 ))741 }742 pub fn values(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> ArrValue {743 self.values_ex(744 false,745 #[cfg(feature = "exp-preserve-order")]746 preserve_order,747 )748 }749 pub fn key_values_ex(750 &self,751 include_hidden: bool,752 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,753 ) -> ArrValue {754 ArrValue::new(PickObjectKeyValues::new(755 self.clone(),756 self.fields_ex(757 include_hidden,758 #[cfg(feature = "exp-preserve-order")]759 preserve_order,760 ),761 ))762 }763 pub fn key_values(764 &self,765 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,766 ) -> ArrValue {767 self.key_values_ex(768 false,769 #[cfg(feature = "exp-preserve-order")]770 preserve_order,771 )772 }773}774775impl OopObject {776 pub fn new(777 this_entries: Cc<FxHashMap<IStr, ObjMember>>,778 assertions: Cc<Vec<CcObjectAssertion>>,779 ) -> Self {780 Self {781 this_entries,782 value_cache: RefCell::new(FxHashMap::new()),783 assertions,784 }785 }786}787788impl ObjectCore for OopObject {789 fn enum_fields_core(790 &self,791 super_depth: &mut SuperDepth,792 handler: &mut EnumFieldsHandler<'_>,793 ) -> bool {794 for (name, member) in self.this_entries.iter() {795 if handler(796 *super_depth,797 member.original_index,798 name.clone(),799 member.flags.visibility(),800 ) {801 return false;802 }803 }804 true805 }806807 fn has_field_include_hidden(&self, name: IStr) -> bool {808 self.this_entries.contains_key(&name)809 }810811 fn get_for(&self, key: IStr, sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>> {812 match self.this_entries.get(&key) {813 Some(k) => Ok(Some((814 k.invoke.evaluate(sup_this)?,815 if k.flags.add() {816 ValueProcess::SuperPlus817 } else {818 ValueProcess::None819 },820 ))),821 None => Ok(None),822 }823 }824 fn field_visibility(&self, name: IStr) -> Option<Visibility> {825 Some(self.this_entries.get(&name)?.flags.visibility())826 }827828 fn run_assertions_raw(&self, sup_this: SupThis) -> Result<()> {829 if self.assertions.is_empty() {830 return Ok(());831 }832 for assertion in self.assertions.iter() {833 assertion.0.run(sup_this.clone())?;834 }835 Ok(())836 }837}838839#[allow(clippy::module_name_repetitions)]840pub struct ObjValueBuilder {841 sup: Option<ObjValue>,842 map: FxHashMap<IStr, ObjMember>,843 assertions: Vec<CcObjectAssertion>,844 next_field_index: FieldIndex,845}846impl ObjValueBuilder {847 pub fn new() -> Self {848 Self::with_capacity(0)849 }850 pub fn with_capacity(capacity: usize) -> Self {851 Self {852 sup: None,853 map: FxHashMap::with_capacity(capacity),854 assertions: Vec::new(),855 next_field_index: FieldIndex::default(),856 }857 }858 pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self {859 self.assertions.reserve_exact(capacity);860 self861 }862 pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {863 self.sup = Some(super_obj);864 self865 }866867 pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {868 self.assertions.push(CcObjectAssertion::new(assertion));869 self870 }871 pub fn field(&mut self, name: impl Into<IStr>) -> ObjMemberBuilder<ValueBuilder<'_>> {872 let field_index = self.next_field_index;873 self.next_field_index = self.next_field_index.next();874 ObjMemberBuilder::new(ValueBuilder(self), name.into(), field_index)875 }876 877 878 879 880 pub fn method(&mut self, name: impl Into<IStr>, value: impl Into<FuncVal>) -> &mut Self {881 self.field(name).hide().value(Val::Func(value.into()));882 self883 }884 pub fn try_method(885 &mut self,886 name: impl Into<IStr>,887 value: impl Into<FuncVal>,888 ) -> Result<&mut Self> {889 self.field(name).hide().try_value(Val::Func(value.into()))?;890 Ok(self)891 }892893 pub fn build(self) -> ObjValue {894 if self.sup.is_none() && self.map.is_empty() && self.assertions.is_empty() {895 return ObjValue::new_empty();896 }897 let res = ObjValue::new(OopObject::new(Cc::new(self.map), Cc::new(self.assertions)));898 self.sup.map(|sup| res.extend_from(sup)).unwrap_or(res)899 }900}901impl Default for ObjValueBuilder {902 fn default() -> Self {903 Self::with_capacity(0)904 }905}906907#[allow(clippy::module_name_repetitions)]908#[must_use = "value not added unless binding() was called"]909pub struct ObjMemberBuilder<Kind> {910 kind: Kind,911 name: IStr,912 add: bool,913 visibility: Visibility,914 original_index: FieldIndex,915 location: Option<Span>,916}917918#[allow(clippy::missing_const_for_fn)]919impl<Kind> ObjMemberBuilder<Kind> {920 pub(crate) fn new(kind: Kind, name: IStr, original_index: FieldIndex) -> Self {921 Self {922 kind,923 name,924 original_index,925 add: false,926 visibility: Visibility::Normal,927 location: None,928 }929 }930931 pub const fn with_add(mut self, add: bool) -> Self {932 self.add = add;933 self934 }935 pub fn add(self) -> Self {936 self.with_add(true)937 }938 pub fn with_visibility(mut self, visibility: Visibility) -> Self {939 self.visibility = visibility;940 self941 }942 pub fn hide(self) -> Self {943 self.with_visibility(Visibility::Hidden)944 }945 pub fn with_location(mut self, location: Span) -> Self {946 self.location = Some(location);947 self948 }949 fn build_member(self, binding: MaybeUnbound) -> (Kind, IStr, ObjMember) {950 (951 self.kind,952 self.name,953 ObjMember {954 flags: ObjFieldFlags::new(self.add, self.visibility),955 original_index: self.original_index,956 invoke: binding,957 location: self.location,958 },959 )960 }961}962963pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);964impl ObjMemberBuilder<ValueBuilder<'_>> {965 966 pub fn value(self, value: impl Into<Val>) {967 let (receiver, name, member) =968 self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value.into())));969 let entry = receiver.0.map.entry(name);970 entry.insert_entry(member);971 }972973 974 pub fn try_value(self, value: impl Into<Val>) -> Result<()> {975 self.try_thunk(Thunk::evaluated(value.into()))976 }977 pub fn try_thunk(self, value: impl Into<Thunk<Val>>) -> Result<()> {978 self.binding(MaybeUnbound::Bound(value.into()))979 }980 pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {981 self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)))982 }983 pub fn binding(self, binding: MaybeUnbound) -> Result<()> {984 let (receiver, name, member) = self.build_member(binding);985 let location = member.location.clone();986 let old = receiver.0.map.insert(name.clone(), member);987 if old.is_some() {988 in_frame(989 CallLocation(location.as_ref()),990 || format!("field <{}> initializtion", name.clone()),991 || bail!(DuplicateFieldName(name.clone())),992 )?;993 }994 Ok(())995 }996}997998pub struct ExtendBuilder<'v>(&'v mut ObjValue);999impl ObjMemberBuilder<ExtendBuilder<'_>> {1000 pub fn value(self, value: impl Into<Val>) {1001 self.binding(MaybeUnbound::Bound(Thunk::evaluated(value.into())));1002 }1003 pub fn bindable(self, bindable: impl Unbound<Bound = Val>) {1004 self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)));1005 }1006 pub fn binding(self, binding: MaybeUnbound) {1007 let (receiver, name, member) = self.build_member(binding);1008 let new = receiver.0.clone();1009 *receiver.0 = new.extend_with_raw_member(name, member);1010 }1011}