1use std::{2 any::Any,3 cell::RefCell,4 fmt::Debug,5 hash::{Hash, Hasher},6 ptr::addr_of,7};89use jrsonnet_gcmodule::{Cc, Trace, Weak};10use jrsonnet_interner::IStr;11use jrsonnet_parser::{ExprLocation, Visibility};12use rustc_hash::FxHashMap;1314use crate::{15 arr::{PickObjectKeyValues, PickObjectValues},16 error::{suggest_object_fields, Error, ErrorKind::*},17 function::CallLocation,18 gc::{GcHashMap, GcHashSet, TraceBox},19 operator::evaluate_add_op,20 tb, throw,21 val::{ArrValue, ThunkValue},22 MaybeUnbound, Result, State, Thunk, Unbound, Val,23};2425#[cfg(not(feature = "exp-preserve-order"))]26mod ordering {27 #![allow(28 29 clippy::unused_self,30 )]3132 use jrsonnet_gcmodule::Trace;3334 #[derive(Clone, Copy, Default, Debug, Trace)]35 pub struct FieldIndex(());36 impl FieldIndex {37 pub const fn next(self) -> Self {38 Self(())39 }40 }4142 #[derive(Clone, Copy, Default, Debug, Trace)]43 pub struct SuperDepth(());44 impl SuperDepth {45 pub const fn deeper(self) -> Self {46 Self(())47 }48 }4950 #[derive(Clone, Copy)]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::{Ordering, 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 fn deeper(self) -> Self {77 Self(self.0 + 1)78 }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 pub fn collide(self, other: Self) -> Self {88 match self.0 .0.cmp(&other.0 .0) {89 Ordering::Greater => self,90 Ordering::Less => other,91 Ordering::Equal => unreachable!("object can't have two fields with the same name"),92 }93 }94 }95}9697use ordering::*;9899100101#[derive(Clone, Copy)]102pub struct ObjFieldFlags(u8);103impl ObjFieldFlags {104 fn new(add: bool, visibility: Visibility) -> Self {105 let mut v = 0;106 if add {107 v |= 1;108 }109 v |= match visibility {110 Visibility::Normal => 0b000,111 Visibility::Hidden => 0b010,112 Visibility::Unhide => 0b100,113 };114 Self(v)115 }116 pub fn add(&self) -> bool {117 self.0 & 1 != 0118 }119 pub fn visibility(&self) -> Visibility {120 match (self.0 & 0b110) >> 1 {121 0b00 => Visibility::Normal,122 0b01 => Visibility::Hidden,123 0b10 => Visibility::Unhide,124 _ => unreachable!(),125 }126 }127}128impl Debug for ObjFieldFlags {129 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {130 f.debug_struct("ObjFieldFlags")131 .field("add", &self.add())132 .field("visibility", &self.visibility())133 .finish()134 }135}136137#[allow(clippy::module_name_repetitions)]138#[derive(Debug, Trace)]139pub struct ObjMember {140 #[trace(skip)]141 flags: ObjFieldFlags,142 original_index: FieldIndex,143 pub invoke: MaybeUnbound,144 pub location: Option<ExprLocation>,145}146147pub trait ObjectAssertion: Trace {148 fn run(&self, super_obj: Option<ObjValue>, this: Option<ObjValue>) -> Result<()>;149}150151152153#[derive(Trace)]154enum CacheValue {155 Cached(Val),156 NotFound,157 Pending,158 Errored(Error),159}160161#[allow(clippy::module_name_repetitions)]162#[derive(Trace)]163#[trace(tracking(force))]164pub struct OopObject {165 sup: Option<ObjValue>,166 167 assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,168 assertions_ran: RefCell<GcHashSet<ObjValue>>,169 this_entries: Cc<GcHashMap<IStr, ObjMember>>,170 value_cache: RefCell<GcHashMap<(IStr, Option<WeakObjValue>), CacheValue>>,171}172impl Debug for OopObject {173 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {174 f.debug_struct("OopObject")175 .field("sup", &self.sup)176 177 178 .field("this_entries", &self.this_entries)179 180 .finish()181 }182}183184type EnumFieldsHandler<'a> = dyn FnMut(SuperDepth, FieldIndex, IStr, Visibility) -> bool + 'a;185186pub trait ObjectLike: Trace + Any + Debug {187 fn extend_from(&self, sup: ObjValue) -> ObjValue;188 189 fn with_this(&self, me: ObjValue, this: ObjValue) -> ObjValue {190 ObjValue::new(ThisOverride { inner: me, this })191 }192 fn this(&self) -> Option<ObjValue> {193 None194 }195 fn len(&self) -> usize;196 fn is_empty(&self) -> bool;197 198 fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool;199200 fn has_field_include_hidden(&self, name: IStr) -> bool;201 fn has_field(&self, name: IStr) -> bool;202203 fn get_for(&self, key: IStr, this: ObjValue) -> Result<Option<Val>>;204 fn get_for_uncached(&self, key: IStr, this: ObjValue) -> Result<Option<Val>>;205 fn field_visibility(&self, field: IStr) -> Option<Visibility>;206207 fn run_assertions_raw(&self, this: ObjValue) -> Result<()>;208}209210#[derive(Clone, Trace)]211pub struct WeakObjValue(#[trace(skip)] pub(crate) Weak<TraceBox<dyn ObjectLike>>);212213impl PartialEq for WeakObjValue {214 fn eq(&self, other: &Self) -> bool {215 Weak::ptr_eq(&self.0, &other.0)216 }217}218219impl Eq for WeakObjValue {}220impl Hash for WeakObjValue {221 fn hash<H: Hasher>(&self, hasher: &mut H) {222 223 let addr = unsafe { *std::ptr::addr_of!(self.0).cast() };224 hasher.write_usize(addr);225 }226}227228#[allow(clippy::module_name_repetitions)]229#[derive(Clone, Trace, Debug)]230pub struct ObjValue(pub(crate) Cc<TraceBox<dyn ObjectLike>>);231232#[derive(Debug, Trace)]233struct EmptyObject;234impl ObjectLike for EmptyObject {235 fn extend_from(&self, sup: ObjValue) -> ObjValue {236 237 sup238 }239240 fn this(&self) -> Option<ObjValue> {241 None242 }243244 fn len(&self) -> usize {245 0246 }247248 fn is_empty(&self) -> bool {249 true250 }251252 fn enum_fields(&self, _depth: SuperDepth, _handler: &mut EnumFieldsHandler<'_>) -> bool {253 false254 }255256 fn has_field_include_hidden(&self, _name: IStr) -> bool {257 false258 }259260 fn has_field(&self, _name: IStr) -> bool {261 false262 }263264 fn get_for(&self, _key: IStr, _this: ObjValue) -> Result<Option<Val>> {265 Ok(None)266 }267 fn get_for_uncached(&self, _key: IStr, _this: ObjValue) -> Result<Option<Val>> {268 Ok(None)269 }270271 fn run_assertions_raw(&self, _this: ObjValue) -> Result<()> {272 Ok(())273 }274275 fn field_visibility(&self, _field: IStr) -> Option<Visibility> {276 None277 }278}279280#[derive(Trace, Debug)]281struct ThisOverride {282 inner: ObjValue,283 this: ObjValue,284}285impl ObjectLike for ThisOverride {286 fn with_this(&self, _me: ObjValue, this: ObjValue) -> ObjValue {287 ObjValue::new(ThisOverride {288 inner: self.inner.clone(),289 this,290 })291 }292293 fn extend_from(&self, sup: ObjValue) -> ObjValue {294 self.inner.extend_from(sup).with_this(self.this.clone())295 }296297 fn this(&self) -> Option<ObjValue> {298 Some(self.this.clone())299 }300301 fn len(&self) -> usize {302 self.inner.len()303 }304305 fn is_empty(&self) -> bool {306 self.inner.is_empty()307 }308309 fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool {310 self.inner.enum_fields(depth, handler)311 }312313 fn has_field_include_hidden(&self, name: IStr) -> bool {314 self.inner.has_field_include_hidden(name)315 }316317 fn has_field(&self, name: IStr) -> bool {318 self.inner.has_field(name)319 }320321 fn get_for(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {322 self.inner.get_for(key, this)323 }324325 fn get_for_uncached(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {326 self.inner.get_raw(key, this)327 }328329 fn field_visibility(&self, field: IStr) -> Option<Visibility> {330 self.inner.field_visibility(field)331 }332333 fn run_assertions_raw(&self, this: ObjValue) -> Result<()> {334 self.inner.run_assertions_raw(this)335 }336}337338impl ObjValue {339 pub fn new(v: impl ObjectLike) -> Self {340 Self(Cc::new(tb!(v)))341 }342 pub fn new_empty() -> Self {343 Self::new(EmptyObject)344 }345 pub fn builder() -> ObjValueBuilder {346 ObjValueBuilder::new()347 }348 pub fn builder_with_capacity(capacity: usize) -> ObjValueBuilder {349 ObjValueBuilder::with_capacity(capacity)350 }351 pub(crate) fn extend_with_raw_member(self, key: IStr, value: ObjMember) -> Self {352 353 354 355 todo!()356 }357 pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder<'_>> {358 ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())359 }360361 #[must_use]362 pub fn extend_from(&self, sup: Self) -> Self {363 self.0.extend_from(sup)364 }365 #[must_use]366 pub fn with_this(&self, this: Self) -> Self {367 self.0.with_this(self.clone(), this)368 }369 pub fn len(&self) -> usize {370 self.0.len()371 }372 pub fn is_empty(&self) -> bool {373 self.0.is_empty()374 }375 pub fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool {376 self.0.enum_fields(depth, handler)377 }378379 pub fn has_field_include_hidden(&self, name: IStr) -> bool {380 self.0.has_field_include_hidden(name)381 }382 pub fn has_field(&self, name: IStr) -> bool {383 self.0.has_field(name)384 }385 pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {386 if include_hidden {387 self.has_field_include_hidden(name)388 } else {389 self.has_field(name)390 }391 }392393 pub fn get(&self, key: IStr) -> Result<Option<Val>> {394 self.run_assertions()?;395 self.get_for(key, self.0.this().unwrap_or(self.clone()))396 }397398 pub fn get_for(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {399 self.0.get_for(key, this)400 }401402 pub fn get_or_bail(&self, key: IStr) -> Result<Val> {403 let Some(value) = self.get(key.clone())? else {404 let suggestions = suggest_object_fields(self, key.clone());405 throw!(NoSuchField(key, suggestions))406 };407 Ok(value)408 }409410 fn get_raw(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {411 self.0.get_for_uncached(key, this)412 }413414 fn field_visibility(&self, field: IStr) -> Option<Visibility> {415 self.0.field_visibility(field)416 }417418 pub fn run_assertions(&self) -> Result<()> {419 420 self.run_assertions_raw(self.clone())421 }422 fn run_assertions_raw(&self, this: ObjValue) -> Result<()> {423 self.0.run_assertions_raw(this)424 }425426 pub fn iter(427 &self,428 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,429 ) -> impl Iterator<Item = (IStr, Result<Val>)> + '_ {430 let fields = self.fields(431 #[cfg(feature = "exp-preserve-order")]432 preserve_order,433 );434 fields.into_iter().map(|field| {435 (436 field.clone(),437 self.get(field)438 .map(|opt| opt.expect("iterating over keys, field exists")),439 )440 })441 }442 pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {443 #[derive(Trace)]444 struct ThunkGet {445 obj: ObjValue,446 key: IStr,447 }448 impl ThunkValue for ThunkGet {449 type Output = Val;450451 fn get(self: Box<Self>) -> Result<Self::Output> {452 Ok(self.obj.get(self.key)?.expect("field exists"))453 }454 }455456 if !self.has_field_ex(key.clone(), true) {457 return None;458 }459 Some(Thunk::new(ThunkGet {460 obj: self.clone(),461 key,462 }))463 }464 pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {465 #[derive(Trace)]466 struct ThunkGet {467 obj: ObjValue,468 key: IStr,469 }470 impl ThunkValue for ThunkGet {471 type Output = Val;472473 fn get(self: Box<Self>) -> Result<Self::Output> {474 Ok(self.obj.get_or_bail(self.key)?)475 }476 }477478 Thunk::new(ThunkGet {479 obj: self.clone(),480 key,481 })482 }483 pub fn ptr_eq(a: &Self, b: &Self) -> bool {484 Cc::ptr_eq(&a.0, &b.0)485 }486 pub fn downgrade(self) -> WeakObjValue {487 WeakObjValue(self.0.downgrade())488 }489 fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {490 let mut out = FxHashMap::default();491 self.enum_fields(492 SuperDepth::default(),493 &mut |depth, index, name, visibility| {494 let new_sort_key = FieldSortKey::new(depth, index);495 let entry = out.entry(name.clone());496 let (visible, _) = entry.or_insert((true, new_sort_key));497 match visibility {498 Visibility::Normal => {}499 Visibility::Hidden => {500 *visible = false;501 }502 Visibility::Unhide => {503 *visible = true;504 }505 };506 false507 },508 );509 out510 }511 pub fn fields_ex(512 &self,513 include_hidden: bool,514 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,515 ) -> Vec<IStr> {516 #[cfg(feature = "exp-preserve-order")]517 if preserve_order {518 let (mut fields, mut keys): (Vec<_>, Vec<_>) = self519 .fields_visibility()520 .into_iter()521 .filter(|(_, (visible, _))| include_hidden || *visible)522 .enumerate()523 .map(|(idx, (k, (_, sk)))| (k, (sk, idx)))524 .unzip();525 keys.sort_unstable_by_key(|v| v.0);526 527 for i in 0..fields.len() {528 let x = fields[i].clone();529 let mut j = i;530 loop {531 let k = keys[j].1;532 keys[j].1 = j;533 if k == i {534 break;535 }536 fields[j] = fields[k].clone();537 j = k;538 }539 fields[j] = x;540 }541 return fields;542 }543544 let mut fields: Vec<_> = self545 .fields_visibility()546 .into_iter()547 .filter(|(_, (visible, _))| include_hidden || *visible)548 .map(|(k, _)| k)549 .collect();550 fields.sort_unstable();551 fields552 }553 pub fn fields(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Vec<IStr> {554 self.fields_ex(555 false,556 #[cfg(feature = "exp-preserve-order")]557 preserve_order,558 )559 }560 pub fn values_ex(561 &self,562 include_hidden: bool,563 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,564 ) -> ArrValue {565 ArrValue::new(PickObjectValues::new(566 self.clone(),567 self.fields_ex(568 include_hidden,569 #[cfg(feature = "exp-preserve-order")]570 preserve_order,571 ),572 ))573 }574 pub fn values(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> ArrValue {575 self.values_ex(576 false,577 #[cfg(feature = "exp-preserve-order")]578 preserve_order,579 )580 }581 pub fn key_values_ex(582 &self,583 include_hidden: bool,584 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,585 ) -> ArrValue {586 ArrValue::new(PickObjectKeyValues::new(587 self.clone(),588 self.fields_ex(589 include_hidden,590 #[cfg(feature = "exp-preserve-order")]591 preserve_order,592 ),593 ))594 }595 pub fn key_values(596 &self,597 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,598 ) -> ArrValue {599 self.key_values_ex(600 false,601 #[cfg(feature = "exp-preserve-order")]602 preserve_order,603 )604 }605}606607impl OopObject {608 pub fn new(609 sup: Option<ObjValue>,610 this_entries: Cc<GcHashMap<IStr, ObjMember>>,611 assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,612 ) -> Self {613 Self {614 sup,615 616 assertions,617 assertions_ran: RefCell::new(GcHashSet::new()),618 this_entries,619 value_cache: RefCell::new(GcHashMap::new()),620 }621 }622623 fn evaluate_this(&self, v: &ObjMember, real_this: ObjValue) -> Result<Val> {624 v.invoke.evaluate(self.sup.clone(), Some(real_this))625 }626627 628 fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {629 let mut out = FxHashMap::default();630 self.enum_fields(631 SuperDepth::default(),632 &mut |depth, index, name, visibility| {633 let new_sort_key = FieldSortKey::new(depth, index);634 let entry = out.entry(name.clone());635 let (visible, _) = entry.or_insert((true, new_sort_key));636 match visibility {637 Visibility::Normal => {}638 Visibility::Hidden => {639 *visible = false;640 }641 Visibility::Unhide => {642 *visible = true;643 }644 };645 false646 },647 );648 out649 }650}651652impl ObjectLike for OopObject {653 fn extend_from(&self, sup: ObjValue) -> ObjValue {654 ObjValue::new(match &self.sup {655 None => Self::new(656 Some(sup),657 self.this_entries.clone(),658 self.assertions.clone(),659 ),660 Some(v) => Self::new(661 Some(v.extend_from(sup)),662 self.this_entries.clone(),663 self.assertions.clone(),664 ),665 })666 }667668 fn len(&self) -> usize {669 self.fields_visibility()670 .into_iter()671 .filter(|(_, (visible, _))| *visible)672 .count()673 }674675 fn is_empty(&self) -> bool {676 if !self.this_entries.is_empty() {677 return false;678 }679 self.sup.as_ref().map_or(true, ObjValue::is_empty)680 }681682 683 684 685 fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool {686 if let Some(s) = &self.sup {687 if s.enum_fields(depth.deeper(), handler) {688 return true;689 }690 }691 for (name, member) in self.this_entries.iter() {692 if handler(693 depth,694 member.original_index,695 name.clone(),696 member.flags.visibility(),697 ) {698 return true;699 }700 }701 false702 }703704 fn has_field_include_hidden(&self, name: IStr) -> bool {705 if self.this_entries.contains_key(&name) {706 true707 } else if let Some(super_obj) = &self.sup {708 super_obj.has_field_include_hidden(name)709 } else {710 false711 }712 }713 fn has_field(&self, name: IStr) -> bool {714 self.field_visibility(name)715 .map_or(false, |v| v.is_visible())716 }717718 fn get_for(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {719 let cache_key = (key.clone(), Some(this.clone().downgrade()));720 if let Some(v) = self.value_cache.borrow().get(&cache_key) {721 return Ok(match v {722 CacheValue::Cached(v) => Some(v.clone()),723 CacheValue::NotFound => None,724 CacheValue::Pending => throw!(InfiniteRecursionDetected),725 CacheValue::Errored(e) => return Err(e.clone()),726 });727 }728 self.value_cache729 .borrow_mut()730 .insert(cache_key.clone(), CacheValue::Pending);731 let value = self.get_for_uncached(key, this).map_err(|e| {732 self.value_cache733 .borrow_mut()734 .insert(cache_key.clone(), CacheValue::Errored(e.clone()));735 e736 })?;737 self.value_cache.borrow_mut().insert(738 cache_key,739 value740 .as_ref()741 .map_or(CacheValue::NotFound, |v| CacheValue::Cached(v.clone())),742 );743 Ok(value)744 }745 fn get_for_uncached(&self, key: IStr, real_this: ObjValue) -> Result<Option<Val>> {746 match (self.this_entries.get(&key), &self.sup) {747 (Some(k), None) => Ok(Some(self.evaluate_this(k, real_this)?)),748 (Some(k), Some(super_obj)) => {749 let our = self.evaluate_this(k, real_this.clone())?;750 if k.flags.add() {751 super_obj752 .get_raw(key, real_this)?753 .map_or(Ok(Some(our.clone())), |v| {754 Ok(Some(evaluate_add_op(&v, &our)?))755 })756 } else {757 Ok(Some(our))758 }759 }760 (None, Some(super_obj)) => super_obj.get_raw(key, real_this),761 (None, None) => Ok(None),762 }763 }764 fn field_visibility(&self, name: IStr) -> Option<Visibility> {765 if let Some(m) = self.this_entries.get(&name) {766 Some(match &m.flags.visibility() {767 Visibility::Normal => self768 .sup769 .as_ref()770 .and_then(|super_obj| super_obj.field_visibility(name))771 .unwrap_or(Visibility::Normal),772 v => *v,773 })774 } else if let Some(super_obj) = &self.sup {775 super_obj.field_visibility(name)776 } else {777 None778 }779 }780781 fn run_assertions_raw(&self, real_this: ObjValue) -> Result<()> {782 if self.assertions.is_empty() {783 if let Some(super_obj) = &self.sup {784 super_obj.run_assertions_raw(real_this)?;785 }786 return Ok(());787 }788 if self.assertions_ran.borrow_mut().insert(real_this.clone()) {789 for assertion in self.assertions.iter() {790 if let Err(e) = assertion.run(self.sup.clone(), Some(real_this.clone())) {791 self.assertions_ran.borrow_mut().remove(&real_this);792 return Err(e);793 }794 }795 if let Some(super_obj) = &self.sup {796 super_obj.run_assertions_raw(real_this)?;797 }798 }799 Ok(())800 }801}802803impl PartialEq for ObjValue {804 fn eq(&self, other: &Self) -> bool {805 Cc::ptr_eq(&self.0, &other.0)806 }807}808809impl Eq for ObjValue {}810impl Hash for ObjValue {811 fn hash<H: Hasher>(&self, hasher: &mut H) {812 hasher.write_usize(addr_of!(*self.0) as usize);813 }814}815816#[allow(clippy::module_name_repetitions)]817pub struct ObjValueBuilder {818 sup: Option<ObjValue>,819 map: GcHashMap<IStr, ObjMember>,820 assertions: Vec<TraceBox<dyn ObjectAssertion>>,821 next_field_index: FieldIndex,822}823impl ObjValueBuilder {824 pub fn new() -> Self {825 Self::with_capacity(0)826 }827 pub fn with_capacity(capacity: usize) -> Self {828 Self {829 sup: None,830 map: GcHashMap::with_capacity(capacity),831 assertions: Vec::new(),832 next_field_index: FieldIndex::default(),833 }834 }835 pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self {836 self.assertions.reserve_exact(capacity);837 self838 }839 pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {840 self.sup = Some(super_obj);841 self842 }843844 pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {845 self.assertions.push(tb!(assertion));846 self847 }848 pub fn member(&mut self, name: IStr) -> ObjMemberBuilder<ValueBuilder<'_>> {849 let field_index = self.next_field_index;850 self.next_field_index = self.next_field_index.next();851 ObjMemberBuilder::new(ValueBuilder(self), name, field_index)852 }853854 pub fn build(self) -> ObjValue {855 if self.sup.is_none() && self.map.is_empty() && self.assertions.is_empty() {856 return ObjValue::new_empty();857 }858 ObjValue::new(OopObject::new(859 self.sup,860 Cc::new(self.map),861 Cc::new(self.assertions),862 ))863 }864}865impl Default for ObjValueBuilder {866 fn default() -> Self {867 Self::with_capacity(0)868 }869}870871#[allow(clippy::module_name_repetitions)]872#[must_use = "value not added unless binding() was called"]873pub struct ObjMemberBuilder<Kind> {874 kind: Kind,875 name: IStr,876 add: bool,877 visibility: Visibility,878 original_index: FieldIndex,879 location: Option<ExprLocation>,880}881882#[allow(clippy::missing_const_for_fn)]883impl<Kind> ObjMemberBuilder<Kind> {884 pub(crate) fn new(kind: Kind, name: IStr, original_index: FieldIndex) -> Self {885 Self {886 kind,887 name,888 original_index,889 add: false,890 visibility: Visibility::Normal,891 location: None,892 }893 }894895 pub const fn with_add(mut self, add: bool) -> Self {896 self.add = add;897 self898 }899 pub fn add(self) -> Self {900 self.with_add(true)901 }902 pub fn with_visibility(mut self, visibility: Visibility) -> Self {903 self.visibility = visibility;904 self905 }906 pub fn hide(self) -> Self {907 self.with_visibility(Visibility::Hidden)908 }909 pub fn with_location(mut self, location: ExprLocation) -> Self {910 self.location = Some(location);911 self912 }913 fn build_member(self, binding: MaybeUnbound) -> (Kind, IStr, ObjMember) {914 (915 self.kind,916 self.name,917 ObjMember {918 flags: ObjFieldFlags::new(self.add, self.visibility),919 original_index: self.original_index,920 invoke: binding,921 location: self.location,922 },923 )924 }925}926927pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);928impl ObjMemberBuilder<ValueBuilder<'_>> {929 930 pub fn value_unchecked(self, value: Val) {931 let (receiver, name, member) =932 self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value)));933 let entry = receiver.0.map.entry(name);934 entry.insert(member);935 }936937 pub fn value(self, value: Val) -> Result<()> {938 self.thunk(Thunk::evaluated(value))939 }940 pub fn thunk(self, value: Thunk<Val>) -> Result<()> {941 self.binding(MaybeUnbound::Bound(value))942 }943 pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {944 self.binding(MaybeUnbound::Unbound(Cc::new(tb!(bindable))))945 }946 pub fn binding(self, binding: MaybeUnbound) -> Result<()> {947 let (receiver, name, member) = self.build_member(binding);948 let location = member.location.clone();949 let old = receiver.0.map.insert(name.clone(), member);950 if old.is_some() {951 State::push(952 CallLocation(location.as_ref()),953 || format!("field <{}> initializtion", name.clone()),954 || throw!(DuplicateFieldName(name.clone())),955 )?;956 }957 Ok(())958 }959}960961pub struct ExtendBuilder<'v>(&'v mut ObjValue);962impl ObjMemberBuilder<ExtendBuilder<'_>> {963 pub fn value(self, value: Val) {964 self.binding(MaybeUnbound::Bound(Thunk::evaluated(value)));965 }966 pub fn bindable(self, bindable: TraceBox<dyn Unbound<Bound = Val>>) {967 self.binding(MaybeUnbound::Unbound(Cc::new(bindable)));968 }969 pub fn binding(self, binding: MaybeUnbound) {970 let (receiver, name, member) = self.build_member(binding);971 let new = receiver.0.clone();972 *receiver.0 = new.extend_with_raw_member(name, member);973 }974}