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::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 }88}8990use ordering::*;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 std::fmt::Formatter<'_>) -> std::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<ExprLocation>,138}139140pub trait ObjectAssertion: Trace {141 fn run(&self, super_obj: Option<ObjValue>, this: Option<ObjValue>) -> Result<()>;142}143144145146#[derive(Trace)]147enum CacheValue {148 Cached(Val),149 NotFound,150 Pending,151 Errored(Error),152}153154#[allow(clippy::module_name_repetitions)]155#[derive(Trace)]156#[trace(tracking(force))]157pub struct OopObject {158 sup: Option<ObjValue>,159 160 assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,161 assertions_ran: RefCell<GcHashSet<ObjValue>>,162 this_entries: Cc<GcHashMap<IStr, ObjMember>>,163 value_cache: RefCell<GcHashMap<(IStr, Option<WeakObjValue>), CacheValue>>,164}165impl Debug for OopObject {166 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {167 f.debug_struct("OopObject")168 .field("sup", &self.sup)169 170 171 .field("this_entries", &self.this_entries)172 173 .finish()174 }175}176177type EnumFieldsHandler<'a> = dyn FnMut(SuperDepth, FieldIndex, IStr, Visibility) -> bool + 'a;178179pub trait ObjectLike: Trace + Any + Debug {180 fn extend_from(&self, sup: ObjValue) -> ObjValue;181 182 fn with_this(&self, me: ObjValue, this: ObjValue) -> ObjValue {183 ObjValue::new(ThisOverride { inner: me, this })184 }185 fn this(&self) -> Option<ObjValue> {186 None187 }188 fn len(&self) -> usize;189 fn is_empty(&self) -> bool;190 191 fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool;192193 fn has_field_include_hidden(&self, name: IStr) -> bool;194 fn has_field(&self, name: IStr) -> bool;195196 fn get_for(&self, key: IStr, this: ObjValue) -> Result<Option<Val>>;197 fn get_for_uncached(&self, key: IStr, this: ObjValue) -> Result<Option<Val>>;198 fn field_visibility(&self, field: IStr) -> Option<Visibility>;199200 fn run_assertions_raw(&self, this: ObjValue) -> Result<()>;201}202203#[derive(Clone, Trace)]204pub struct WeakObjValue(#[trace(skip)] pub(crate) Weak<TraceBox<dyn ObjectLike>>);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}220221#[allow(clippy::module_name_repetitions)]222#[derive(Clone, Trace, Debug)]223pub struct ObjValue(pub(crate) Cc<TraceBox<dyn ObjectLike>>);224225#[derive(Debug, Trace)]226struct EmptyObject;227impl ObjectLike for EmptyObject {228 fn extend_from(&self, sup: ObjValue) -> ObjValue {229 230 sup231 }232233 fn this(&self) -> Option<ObjValue> {234 None235 }236237 fn len(&self) -> usize {238 0239 }240241 fn is_empty(&self) -> bool {242 true243 }244245 fn enum_fields(&self, _depth: SuperDepth, _handler: &mut EnumFieldsHandler<'_>) -> bool {246 false247 }248249 fn has_field_include_hidden(&self, _name: IStr) -> bool {250 false251 }252253 fn has_field(&self, _name: IStr) -> bool {254 false255 }256257 fn get_for(&self, _key: IStr, _this: ObjValue) -> Result<Option<Val>> {258 Ok(None)259 }260 fn get_for_uncached(&self, _key: IStr, _this: ObjValue) -> Result<Option<Val>> {261 Ok(None)262 }263264 fn run_assertions_raw(&self, _this: ObjValue) -> Result<()> {265 Ok(())266 }267268 fn field_visibility(&self, _field: IStr) -> Option<Visibility> {269 None270 }271}272273#[derive(Trace, Debug)]274struct ThisOverride {275 inner: ObjValue,276 this: ObjValue,277}278impl ObjectLike for ThisOverride {279 fn with_this(&self, _me: ObjValue, this: ObjValue) -> ObjValue {280 ObjValue::new(ThisOverride {281 inner: self.inner.clone(),282 this,283 })284 }285286 fn extend_from(&self, sup: ObjValue) -> ObjValue {287 self.inner.extend_from(sup).with_this(self.this.clone())288 }289290 fn this(&self) -> Option<ObjValue> {291 Some(self.this.clone())292 }293294 fn len(&self) -> usize {295 self.inner.len()296 }297298 fn is_empty(&self) -> bool {299 self.inner.is_empty()300 }301302 fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool {303 self.inner.enum_fields(depth, handler)304 }305306 fn has_field_include_hidden(&self, name: IStr) -> bool {307 self.inner.has_field_include_hidden(name)308 }309310 fn has_field(&self, name: IStr) -> bool {311 self.inner.has_field(name)312 }313314 fn get_for(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {315 self.inner.get_for(key, this)316 }317318 fn get_for_uncached(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {319 self.inner.get_raw(key, this)320 }321322 fn field_visibility(&self, field: IStr) -> Option<Visibility> {323 self.inner.field_visibility(field)324 }325326 fn run_assertions_raw(&self, this: ObjValue) -> Result<()> {327 self.inner.run_assertions_raw(this)328 }329}330331impl ObjValue {332 pub fn new(v: impl ObjectLike) -> Self {333 Self(Cc::new(tb!(v)))334 }335 pub fn new_empty() -> Self {336 Self::new(EmptyObject)337 }338 pub fn builder() -> ObjValueBuilder {339 ObjValueBuilder::new()340 }341 pub fn builder_with_capacity(capacity: usize) -> ObjValueBuilder {342 ObjValueBuilder::with_capacity(capacity)343 }344 pub(crate) fn extend_with_raw_member(self, key: IStr, value: ObjMember) -> Self {345 let mut out = ObjValueBuilder::with_capacity(1);346 out.with_super(self);347 let mut member = out.member(key);348 if value.flags.add() {349 member = member.add()350 }351 if let Some(loc) = value.location {352 member = member.with_location(loc);353 }354 let _ = member355 .with_visibility(value.flags.visibility())356 .binding(value.invoke);357 out.build()358 }359 pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder<'_>> {360 ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())361 }362363 #[must_use]364 pub fn extend_from(&self, sup: Self) -> Self {365 self.0.extend_from(sup)366 }367 #[must_use]368 pub fn with_this(&self, this: Self) -> Self {369 self.0.with_this(self.clone(), this)370 }371 pub fn len(&self) -> usize {372 self.0.len()373 }374 pub fn is_empty(&self) -> bool {375 self.0.is_empty()376 }377 pub fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool {378 self.0.enum_fields(depth, handler)379 }380381 pub fn has_field_include_hidden(&self, name: IStr) -> bool {382 self.0.has_field_include_hidden(name)383 }384 pub fn has_field(&self, name: IStr) -> bool {385 self.0.has_field(name)386 }387 pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {388 if include_hidden {389 self.has_field_include_hidden(name)390 } else {391 self.has_field(name)392 }393 }394395 pub fn get(&self, key: IStr) -> Result<Option<Val>> {396 self.run_assertions()?;397 self.get_for(key, self.0.this().unwrap_or(self.clone()))398 }399400 pub fn get_for(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {401 self.0.get_for(key, this)402 }403404 pub fn get_or_bail(&self, key: IStr) -> Result<Val> {405 let Some(value) = self.get(key.clone())? else {406 let suggestions = suggest_object_fields(self, key.clone());407 throw!(NoSuchField(key, suggestions))408 };409 Ok(value)410 }411412 fn get_raw(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {413 self.0.get_for_uncached(key, this)414 }415416 fn field_visibility(&self, field: IStr) -> Option<Visibility> {417 self.0.field_visibility(field)418 }419420 pub fn run_assertions(&self) -> Result<()> {421 422 self.run_assertions_raw(self.clone())423 }424 fn run_assertions_raw(&self, this: ObjValue) -> Result<()> {425 self.0.run_assertions_raw(this)426 }427428 pub fn iter(429 &self,430 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,431 ) -> impl Iterator<Item = (IStr, Result<Val>)> + '_ {432 let fields = self.fields(433 #[cfg(feature = "exp-preserve-order")]434 preserve_order,435 );436 fields.into_iter().map(|field| {437 (438 field.clone(),439 self.get(field)440 .map(|opt| opt.expect("iterating over keys, field exists")),441 )442 })443 }444 pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {445 #[derive(Trace)]446 struct ThunkGet {447 obj: ObjValue,448 key: IStr,449 }450 impl ThunkValue for ThunkGet {451 type Output = Val;452453 fn get(self: Box<Self>) -> Result<Self::Output> {454 Ok(self.obj.get(self.key)?.expect("field exists"))455 }456 }457458 if !self.has_field_ex(key.clone(), true) {459 return None;460 }461 Some(Thunk::new(ThunkGet {462 obj: self.clone(),463 key,464 }))465 }466 pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {467 #[derive(Trace)]468 struct ThunkGet {469 obj: ObjValue,470 key: IStr,471 }472 impl ThunkValue for ThunkGet {473 type Output = Val;474475 fn get(self: Box<Self>) -> Result<Self::Output> {476 Ok(self.obj.get_or_bail(self.key)?)477 }478 }479480 Thunk::new(ThunkGet {481 obj: self.clone(),482 key,483 })484 }485 pub fn ptr_eq(a: &Self, b: &Self) -> bool {486 Cc::ptr_eq(&a.0, &b.0)487 }488 pub fn downgrade(self) -> WeakObjValue {489 WeakObjValue(self.0.downgrade())490 }491 fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {492 let mut out = FxHashMap::default();493 self.enum_fields(494 SuperDepth::default(),495 &mut |depth, index, name, visibility| {496 let new_sort_key = FieldSortKey::new(depth, index);497 let entry = out.entry(name.clone());498 let (visible, _) = entry.or_insert((true, new_sort_key));499 match visibility {500 Visibility::Normal => {}501 Visibility::Hidden => {502 *visible = false;503 }504 Visibility::Unhide => {505 *visible = true;506 }507 };508 false509 },510 );511 out512 }513 pub fn fields_ex(514 &self,515 include_hidden: bool,516 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,517 ) -> Vec<IStr> {518 #[cfg(feature = "exp-preserve-order")]519 if preserve_order {520 let (mut fields, mut keys): (Vec<_>, Vec<_>) = self521 .fields_visibility()522 .into_iter()523 .filter(|(_, (visible, _))| include_hidden || *visible)524 .enumerate()525 .map(|(idx, (k, (_, sk)))| (k, (sk, idx)))526 .unzip();527 keys.sort_unstable_by_key(|v| v.0);528 529 for i in 0..fields.len() {530 let x = fields[i].clone();531 let mut j = i;532 loop {533 let k = keys[j].1;534 keys[j].1 = j;535 if k == i {536 break;537 }538 fields[j] = fields[k].clone();539 j = k;540 }541 fields[j] = x;542 }543 return fields;544 }545546 let mut fields: Vec<_> = self547 .fields_visibility()548 .into_iter()549 .filter(|(_, (visible, _))| include_hidden || *visible)550 .map(|(k, _)| k)551 .collect();552 fields.sort_unstable();553 fields554 }555 pub fn fields(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Vec<IStr> {556 self.fields_ex(557 false,558 #[cfg(feature = "exp-preserve-order")]559 preserve_order,560 )561 }562 pub fn values_ex(563 &self,564 include_hidden: bool,565 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,566 ) -> ArrValue {567 ArrValue::new(PickObjectValues::new(568 self.clone(),569 self.fields_ex(570 include_hidden,571 #[cfg(feature = "exp-preserve-order")]572 preserve_order,573 ),574 ))575 }576 pub fn values(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> ArrValue {577 self.values_ex(578 false,579 #[cfg(feature = "exp-preserve-order")]580 preserve_order,581 )582 }583 pub fn key_values_ex(584 &self,585 include_hidden: bool,586 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,587 ) -> ArrValue {588 ArrValue::new(PickObjectKeyValues::new(589 self.clone(),590 self.fields_ex(591 include_hidden,592 #[cfg(feature = "exp-preserve-order")]593 preserve_order,594 ),595 ))596 }597 pub fn key_values(598 &self,599 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,600 ) -> ArrValue {601 self.key_values_ex(602 false,603 #[cfg(feature = "exp-preserve-order")]604 preserve_order,605 )606 }607}608609impl OopObject {610 pub fn new(611 sup: Option<ObjValue>,612 this_entries: Cc<GcHashMap<IStr, ObjMember>>,613 assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,614 ) -> Self {615 Self {616 sup,617 618 assertions,619 assertions_ran: RefCell::new(GcHashSet::new()),620 this_entries,621 value_cache: RefCell::new(GcHashMap::new()),622 }623 }624625 fn evaluate_this(&self, v: &ObjMember, real_this: ObjValue) -> Result<Val> {626 v.invoke.evaluate(self.sup.clone(), Some(real_this))627 }628629 630 fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {631 let mut out = FxHashMap::default();632 self.enum_fields(633 SuperDepth::default(),634 &mut |depth, index, name, visibility| {635 let new_sort_key = FieldSortKey::new(depth, index);636 let entry = out.entry(name.clone());637 let (visible, _) = entry.or_insert((true, new_sort_key));638 match visibility {639 Visibility::Normal => {}640 Visibility::Hidden => {641 *visible = false;642 }643 Visibility::Unhide => {644 *visible = true;645 }646 };647 false648 },649 );650 out651 }652}653654impl ObjectLike for OopObject {655 fn extend_from(&self, sup: ObjValue) -> ObjValue {656 ObjValue::new(match &self.sup {657 None => Self::new(658 Some(sup),659 self.this_entries.clone(),660 self.assertions.clone(),661 ),662 Some(v) => Self::new(663 Some(v.extend_from(sup)),664 self.this_entries.clone(),665 self.assertions.clone(),666 ),667 })668 }669670 fn len(&self) -> usize {671 self.fields_visibility()672 .into_iter()673 .filter(|(_, (visible, _))| *visible)674 .count()675 }676677 fn is_empty(&self) -> bool {678 if !self.this_entries.is_empty() {679 return false;680 }681 self.sup.as_ref().map_or(true, ObjValue::is_empty)682 }683684 685 686 687 fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool {688 if let Some(s) = &self.sup {689 if s.enum_fields(depth.deeper(), handler) {690 return true;691 }692 }693 for (name, member) in self.this_entries.iter() {694 if handler(695 depth,696 member.original_index,697 name.clone(),698 member.flags.visibility(),699 ) {700 return true;701 }702 }703 false704 }705706 fn has_field_include_hidden(&self, name: IStr) -> bool {707 if self.this_entries.contains_key(&name) {708 true709 } else if let Some(super_obj) = &self.sup {710 super_obj.has_field_include_hidden(name)711 } else {712 false713 }714 }715 fn has_field(&self, name: IStr) -> bool {716 self.field_visibility(name)717 .map_or(false, |v| v.is_visible())718 }719720 fn get_for(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {721 let cache_key = (key.clone(), Some(this.clone().downgrade()));722 if let Some(v) = self.value_cache.borrow().get(&cache_key) {723 return Ok(match v {724 CacheValue::Cached(v) => Some(v.clone()),725 CacheValue::NotFound => None,726 CacheValue::Pending => throw!(InfiniteRecursionDetected),727 CacheValue::Errored(e) => return Err(e.clone()),728 });729 }730 self.value_cache731 .borrow_mut()732 .insert(cache_key.clone(), CacheValue::Pending);733 let value = self.get_for_uncached(key, this).map_err(|e| {734 self.value_cache735 .borrow_mut()736 .insert(cache_key.clone(), CacheValue::Errored(e.clone()));737 e738 })?;739 self.value_cache.borrow_mut().insert(740 cache_key,741 value742 .as_ref()743 .map_or(CacheValue::NotFound, |v| CacheValue::Cached(v.clone())),744 );745 Ok(value)746 }747 fn get_for_uncached(&self, key: IStr, real_this: ObjValue) -> Result<Option<Val>> {748 match (self.this_entries.get(&key), &self.sup) {749 (Some(k), None) => Ok(Some(self.evaluate_this(k, real_this)?)),750 (Some(k), Some(super_obj)) => {751 let our = self.evaluate_this(k, real_this.clone())?;752 if k.flags.add() {753 super_obj754 .get_raw(key, real_this)?755 .map_or(Ok(Some(our.clone())), |v| {756 Ok(Some(evaluate_add_op(&v, &our)?))757 })758 } else {759 Ok(Some(our))760 }761 }762 (None, Some(super_obj)) => super_obj.get_raw(key, real_this),763 (None, None) => Ok(None),764 }765 }766 fn field_visibility(&self, name: IStr) -> Option<Visibility> {767 if let Some(m) = self.this_entries.get(&name) {768 Some(match &m.flags.visibility() {769 Visibility::Normal => self770 .sup771 .as_ref()772 .and_then(|super_obj| super_obj.field_visibility(name))773 .unwrap_or(Visibility::Normal),774 v => *v,775 })776 } else if let Some(super_obj) = &self.sup {777 super_obj.field_visibility(name)778 } else {779 None780 }781 }782783 fn run_assertions_raw(&self, real_this: ObjValue) -> Result<()> {784 if self.assertions.is_empty() {785 if let Some(super_obj) = &self.sup {786 super_obj.run_assertions_raw(real_this)?;787 }788 return Ok(());789 }790 if self.assertions_ran.borrow_mut().insert(real_this.clone()) {791 for assertion in self.assertions.iter() {792 if let Err(e) = assertion.run(self.sup.clone(), Some(real_this.clone())) {793 self.assertions_ran.borrow_mut().remove(&real_this);794 return Err(e);795 }796 }797 if let Some(super_obj) = &self.sup {798 super_obj.run_assertions_raw(real_this)?;799 }800 }801 Ok(())802 }803}804805impl PartialEq for ObjValue {806 fn eq(&self, other: &Self) -> bool {807 Cc::ptr_eq(&self.0, &other.0)808 }809}810811impl Eq for ObjValue {}812impl Hash for ObjValue {813 fn hash<H: Hasher>(&self, hasher: &mut H) {814 hasher.write_usize(addr_of!(*self.0) as usize);815 }816}817818#[allow(clippy::module_name_repetitions)]819pub struct ObjValueBuilder {820 sup: Option<ObjValue>,821 map: GcHashMap<IStr, ObjMember>,822 assertions: Vec<TraceBox<dyn ObjectAssertion>>,823 next_field_index: FieldIndex,824}825impl ObjValueBuilder {826 pub fn new() -> Self {827 Self::with_capacity(0)828 }829 pub fn with_capacity(capacity: usize) -> Self {830 Self {831 sup: None,832 map: GcHashMap::with_capacity(capacity),833 assertions: Vec::new(),834 next_field_index: FieldIndex::default(),835 }836 }837 pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self {838 self.assertions.reserve_exact(capacity);839 self840 }841 pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {842 self.sup = Some(super_obj);843 self844 }845846 pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {847 self.assertions.push(tb!(assertion));848 self849 }850 pub fn member(&mut self, name: IStr) -> ObjMemberBuilder<ValueBuilder<'_>> {851 let field_index = self.next_field_index;852 self.next_field_index = self.next_field_index.next();853 ObjMemberBuilder::new(ValueBuilder(self), name, field_index)854 }855856 pub fn build(self) -> ObjValue {857 if self.sup.is_none() && self.map.is_empty() && self.assertions.is_empty() {858 return ObjValue::new_empty();859 }860 ObjValue::new(OopObject::new(861 self.sup,862 Cc::new(self.map),863 Cc::new(self.assertions),864 ))865 }866}867impl Default for ObjValueBuilder {868 fn default() -> Self {869 Self::with_capacity(0)870 }871}872873#[allow(clippy::module_name_repetitions)]874#[must_use = "value not added unless binding() was called"]875pub struct ObjMemberBuilder<Kind> {876 kind: Kind,877 name: IStr,878 add: bool,879 visibility: Visibility,880 original_index: FieldIndex,881 location: Option<ExprLocation>,882}883884#[allow(clippy::missing_const_for_fn)]885impl<Kind> ObjMemberBuilder<Kind> {886 pub(crate) fn new(kind: Kind, name: IStr, original_index: FieldIndex) -> Self {887 Self {888 kind,889 name,890 original_index,891 add: false,892 visibility: Visibility::Normal,893 location: None,894 }895 }896897 pub const fn with_add(mut self, add: bool) -> Self {898 self.add = add;899 self900 }901 pub fn add(self) -> Self {902 self.with_add(true)903 }904 pub fn with_visibility(mut self, visibility: Visibility) -> Self {905 self.visibility = visibility;906 self907 }908 pub fn hide(self) -> Self {909 self.with_visibility(Visibility::Hidden)910 }911 pub fn with_location(mut self, location: ExprLocation) -> Self {912 self.location = Some(location);913 self914 }915 fn build_member(self, binding: MaybeUnbound) -> (Kind, IStr, ObjMember) {916 (917 self.kind,918 self.name,919 ObjMember {920 flags: ObjFieldFlags::new(self.add, self.visibility),921 original_index: self.original_index,922 invoke: binding,923 location: self.location,924 },925 )926 }927}928929pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);930impl ObjMemberBuilder<ValueBuilder<'_>> {931 932 pub fn value_unchecked(self, value: Val) {933 let (receiver, name, member) =934 self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value)));935 let entry = receiver.0.map.entry(name);936 entry.insert(member);937 }938939 pub fn value(self, value: Val) -> Result<()> {940 self.thunk(Thunk::evaluated(value))941 }942 pub fn thunk(self, value: Thunk<Val>) -> Result<()> {943 self.binding(MaybeUnbound::Bound(value))944 }945 pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {946 self.binding(MaybeUnbound::Unbound(Cc::new(tb!(bindable))))947 }948 pub fn binding(self, binding: MaybeUnbound) -> Result<()> {949 let (receiver, name, member) = self.build_member(binding);950 let location = member.location.clone();951 let old = receiver.0.map.insert(name.clone(), member);952 if old.is_some() {953 State::push(954 CallLocation(location.as_ref()),955 || format!("field <{}> initializtion", name.clone()),956 || throw!(DuplicateFieldName(name.clone())),957 )?;958 }959 Ok(())960 }961}962963pub struct ExtendBuilder<'v>(&'v mut ObjValue);964impl ObjMemberBuilder<ExtendBuilder<'_>> {965 pub fn value(self, value: Val) {966 self.binding(MaybeUnbound::Bound(Thunk::evaluated(value)));967 }968 pub fn bindable(self, bindable: TraceBox<dyn Unbound<Bound = Val>>) {969 self.binding(MaybeUnbound::Unbound(Cc::new(bindable)));970 }971 pub fn binding(self, binding: MaybeUnbound) {972 let (receiver, name, member) = self.build_member(binding);973 let new = receiver.0.clone();974 *receiver.0 = new.extend_with_raw_member(name, member);975 }976}