difftreelog
style remove unused code
in: master
1 file changed
crates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth1use 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 // This module works as stub for preserve-order feature29 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::*;9899// 0 - add100// 12 - visibility101#[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}150151// Field => This152153#[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 // this: Option<ObjValue>,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 // .field("assertions", &self.assertions)177 // .field("assertions_ran", &self.assertions_ran)178 .field("this_entries", &self.this_entries)179 // .field("value_cache", &self.value_cache)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 /// When using standalone super in object, `this.super_obj.with_this(this)` is executed189 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 // If callback returns false, iteration stops198 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 // Safety: usize is POD223 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 // obj + {} == obj237 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 let mut out = ObjValueBuilder::with_capacity(1);353 out.with_super(self);354 let mut member = out.member(key);355 if value.flags.add() {356 member = member.add()357 }358 if let Some(loc) = value.location {359 member = member.with_location(loc);360 }361 let _ = member362 .with_visibility(value.flags.visibility())363 .binding(value.invoke);364 out.build()365 }366 pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder<'_>> {367 ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())368 }369370 #[must_use]371 pub fn extend_from(&self, sup: Self) -> Self {372 self.0.extend_from(sup)373 }374 #[must_use]375 pub fn with_this(&self, this: Self) -> Self {376 self.0.with_this(self.clone(), this)377 }378 pub fn len(&self) -> usize {379 self.0.len()380 }381 pub fn is_empty(&self) -> bool {382 self.0.is_empty()383 }384 pub fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool {385 self.0.enum_fields(depth, handler)386 }387388 pub fn has_field_include_hidden(&self, name: IStr) -> bool {389 self.0.has_field_include_hidden(name)390 }391 pub fn has_field(&self, name: IStr) -> bool {392 self.0.has_field(name)393 }394 pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {395 if include_hidden {396 self.has_field_include_hidden(name)397 } else {398 self.has_field(name)399 }400 }401402 pub fn get(&self, key: IStr) -> Result<Option<Val>> {403 self.run_assertions()?;404 self.get_for(key, self.0.this().unwrap_or(self.clone()))405 }406407 pub fn get_for(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {408 self.0.get_for(key, this)409 }410411 pub fn get_or_bail(&self, key: IStr) -> Result<Val> {412 let Some(value) = self.get(key.clone())? else {413 let suggestions = suggest_object_fields(self, key.clone());414 throw!(NoSuchField(key, suggestions))415 };416 Ok(value)417 }418419 fn get_raw(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {420 self.0.get_for_uncached(key, this)421 }422423 fn field_visibility(&self, field: IStr) -> Option<Visibility> {424 self.0.field_visibility(field)425 }426427 pub fn run_assertions(&self) -> Result<()> {428 // FIXME: Should it use `self.0.this()` in case of standalone super?429 self.run_assertions_raw(self.clone())430 }431 fn run_assertions_raw(&self, this: ObjValue) -> Result<()> {432 self.0.run_assertions_raw(this)433 }434435 pub fn iter(436 &self,437 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,438 ) -> impl Iterator<Item = (IStr, Result<Val>)> + '_ {439 let fields = self.fields(440 #[cfg(feature = "exp-preserve-order")]441 preserve_order,442 );443 fields.into_iter().map(|field| {444 (445 field.clone(),446 self.get(field)447 .map(|opt| opt.expect("iterating over keys, field exists")),448 )449 })450 }451 pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {452 #[derive(Trace)]453 struct ThunkGet {454 obj: ObjValue,455 key: IStr,456 }457 impl ThunkValue for ThunkGet {458 type Output = Val;459460 fn get(self: Box<Self>) -> Result<Self::Output> {461 Ok(self.obj.get(self.key)?.expect("field exists"))462 }463 }464465 if !self.has_field_ex(key.clone(), true) {466 return None;467 }468 Some(Thunk::new(ThunkGet {469 obj: self.clone(),470 key,471 }))472 }473 pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {474 #[derive(Trace)]475 struct ThunkGet {476 obj: ObjValue,477 key: IStr,478 }479 impl ThunkValue for ThunkGet {480 type Output = Val;481482 fn get(self: Box<Self>) -> Result<Self::Output> {483 Ok(self.obj.get_or_bail(self.key)?)484 }485 }486487 Thunk::new(ThunkGet {488 obj: self.clone(),489 key,490 })491 }492 pub fn ptr_eq(a: &Self, b: &Self) -> bool {493 Cc::ptr_eq(&a.0, &b.0)494 }495 pub fn downgrade(self) -> WeakObjValue {496 WeakObjValue(self.0.downgrade())497 }498 fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {499 let mut out = FxHashMap::default();500 self.enum_fields(501 SuperDepth::default(),502 &mut |depth, index, name, visibility| {503 let new_sort_key = FieldSortKey::new(depth, index);504 let entry = out.entry(name.clone());505 let (visible, _) = entry.or_insert((true, new_sort_key));506 match visibility {507 Visibility::Normal => {}508 Visibility::Hidden => {509 *visible = false;510 }511 Visibility::Unhide => {512 *visible = true;513 }514 };515 false516 },517 );518 out519 }520 pub fn fields_ex(521 &self,522 include_hidden: bool,523 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,524 ) -> Vec<IStr> {525 #[cfg(feature = "exp-preserve-order")]526 if preserve_order {527 let (mut fields, mut keys): (Vec<_>, Vec<_>) = self528 .fields_visibility()529 .into_iter()530 .filter(|(_, (visible, _))| include_hidden || *visible)531 .enumerate()532 .map(|(idx, (k, (_, sk)))| (k, (sk, idx)))533 .unzip();534 keys.sort_unstable_by_key(|v| v.0);535 // Reorder in-place by resulting indexes536 for i in 0..fields.len() {537 let x = fields[i].clone();538 let mut j = i;539 loop {540 let k = keys[j].1;541 keys[j].1 = j;542 if k == i {543 break;544 }545 fields[j] = fields[k].clone();546 j = k;547 }548 fields[j] = x;549 }550 return fields;551 }552553 let mut fields: Vec<_> = self554 .fields_visibility()555 .into_iter()556 .filter(|(_, (visible, _))| include_hidden || *visible)557 .map(|(k, _)| k)558 .collect();559 fields.sort_unstable();560 fields561 }562 pub fn fields(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Vec<IStr> {563 self.fields_ex(564 false,565 #[cfg(feature = "exp-preserve-order")]566 preserve_order,567 )568 }569 pub fn values_ex(570 &self,571 include_hidden: bool,572 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,573 ) -> ArrValue {574 ArrValue::new(PickObjectValues::new(575 self.clone(),576 self.fields_ex(577 include_hidden,578 #[cfg(feature = "exp-preserve-order")]579 preserve_order,580 ),581 ))582 }583 pub fn values(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> ArrValue {584 self.values_ex(585 false,586 #[cfg(feature = "exp-preserve-order")]587 preserve_order,588 )589 }590 pub fn key_values_ex(591 &self,592 include_hidden: bool,593 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,594 ) -> ArrValue {595 ArrValue::new(PickObjectKeyValues::new(596 self.clone(),597 self.fields_ex(598 include_hidden,599 #[cfg(feature = "exp-preserve-order")]600 preserve_order,601 ),602 ))603 }604 pub fn key_values(605 &self,606 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,607 ) -> ArrValue {608 self.key_values_ex(609 false,610 #[cfg(feature = "exp-preserve-order")]611 preserve_order,612 )613 }614}615616impl OopObject {617 pub fn new(618 sup: Option<ObjValue>,619 this_entries: Cc<GcHashMap<IStr, ObjMember>>,620 assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,621 ) -> Self {622 Self {623 sup,624 // this: None,625 assertions,626 assertions_ran: RefCell::new(GcHashSet::new()),627 this_entries,628 value_cache: RefCell::new(GcHashMap::new()),629 }630 }631632 fn evaluate_this(&self, v: &ObjMember, real_this: ObjValue) -> Result<Val> {633 v.invoke.evaluate(self.sup.clone(), Some(real_this))634 }635636 // FIXME: Duplication between ObjValue and OopObject637 fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {638 let mut out = FxHashMap::default();639 self.enum_fields(640 SuperDepth::default(),641 &mut |depth, index, name, visibility| {642 let new_sort_key = FieldSortKey::new(depth, index);643 let entry = out.entry(name.clone());644 let (visible, _) = entry.or_insert((true, new_sort_key));645 match visibility {646 Visibility::Normal => {}647 Visibility::Hidden => {648 *visible = false;649 }650 Visibility::Unhide => {651 *visible = true;652 }653 };654 false655 },656 );657 out658 }659}660661impl ObjectLike for OopObject {662 fn extend_from(&self, sup: ObjValue) -> ObjValue {663 ObjValue::new(match &self.sup {664 None => Self::new(665 Some(sup),666 self.this_entries.clone(),667 self.assertions.clone(),668 ),669 Some(v) => Self::new(670 Some(v.extend_from(sup)),671 self.this_entries.clone(),672 self.assertions.clone(),673 ),674 })675 }676677 fn len(&self) -> usize {678 self.fields_visibility()679 .into_iter()680 .filter(|(_, (visible, _))| *visible)681 .count()682 }683684 fn is_empty(&self) -> bool {685 if !self.this_entries.is_empty() {686 return false;687 }688 self.sup.as_ref().map_or(true, ObjValue::is_empty)689 }690691 /// Run callback for every field found in object692 ///693 /// Returns true if ended prematurely694 fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool {695 if let Some(s) = &self.sup {696 if s.enum_fields(depth.deeper(), handler) {697 return true;698 }699 }700 for (name, member) in self.this_entries.iter() {701 if handler(702 depth,703 member.original_index,704 name.clone(),705 member.flags.visibility(),706 ) {707 return true;708 }709 }710 false711 }712713 fn has_field_include_hidden(&self, name: IStr) -> bool {714 if self.this_entries.contains_key(&name) {715 true716 } else if let Some(super_obj) = &self.sup {717 super_obj.has_field_include_hidden(name)718 } else {719 false720 }721 }722 fn has_field(&self, name: IStr) -> bool {723 self.field_visibility(name)724 .map_or(false, |v| v.is_visible())725 }726727 fn get_for(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {728 let cache_key = (key.clone(), Some(this.clone().downgrade()));729 if let Some(v) = self.value_cache.borrow().get(&cache_key) {730 return Ok(match v {731 CacheValue::Cached(v) => Some(v.clone()),732 CacheValue::NotFound => None,733 CacheValue::Pending => throw!(InfiniteRecursionDetected),734 CacheValue::Errored(e) => return Err(e.clone()),735 });736 }737 self.value_cache738 .borrow_mut()739 .insert(cache_key.clone(), CacheValue::Pending);740 let value = self.get_for_uncached(key, this).map_err(|e| {741 self.value_cache742 .borrow_mut()743 .insert(cache_key.clone(), CacheValue::Errored(e.clone()));744 e745 })?;746 self.value_cache.borrow_mut().insert(747 cache_key,748 value749 .as_ref()750 .map_or(CacheValue::NotFound, |v| CacheValue::Cached(v.clone())),751 );752 Ok(value)753 }754 fn get_for_uncached(&self, key: IStr, real_this: ObjValue) -> Result<Option<Val>> {755 match (self.this_entries.get(&key), &self.sup) {756 (Some(k), None) => Ok(Some(self.evaluate_this(k, real_this)?)),757 (Some(k), Some(super_obj)) => {758 let our = self.evaluate_this(k, real_this.clone())?;759 if k.flags.add() {760 super_obj761 .get_raw(key, real_this)?762 .map_or(Ok(Some(our.clone())), |v| {763 Ok(Some(evaluate_add_op(&v, &our)?))764 })765 } else {766 Ok(Some(our))767 }768 }769 (None, Some(super_obj)) => super_obj.get_raw(key, real_this),770 (None, None) => Ok(None),771 }772 }773 fn field_visibility(&self, name: IStr) -> Option<Visibility> {774 if let Some(m) = self.this_entries.get(&name) {775 Some(match &m.flags.visibility() {776 Visibility::Normal => self777 .sup778 .as_ref()779 .and_then(|super_obj| super_obj.field_visibility(name))780 .unwrap_or(Visibility::Normal),781 v => *v,782 })783 } else if let Some(super_obj) = &self.sup {784 super_obj.field_visibility(name)785 } else {786 None787 }788 }789790 fn run_assertions_raw(&self, real_this: ObjValue) -> Result<()> {791 if self.assertions.is_empty() {792 if let Some(super_obj) = &self.sup {793 super_obj.run_assertions_raw(real_this)?;794 }795 return Ok(());796 }797 if self.assertions_ran.borrow_mut().insert(real_this.clone()) {798 for assertion in self.assertions.iter() {799 if let Err(e) = assertion.run(self.sup.clone(), Some(real_this.clone())) {800 self.assertions_ran.borrow_mut().remove(&real_this);801 return Err(e);802 }803 }804 if let Some(super_obj) = &self.sup {805 super_obj.run_assertions_raw(real_this)?;806 }807 }808 Ok(())809 }810}811812impl PartialEq for ObjValue {813 fn eq(&self, other: &Self) -> bool {814 Cc::ptr_eq(&self.0, &other.0)815 }816}817818impl Eq for ObjValue {}819impl Hash for ObjValue {820 fn hash<H: Hasher>(&self, hasher: &mut H) {821 hasher.write_usize(addr_of!(*self.0) as usize);822 }823}824825#[allow(clippy::module_name_repetitions)]826pub struct ObjValueBuilder {827 sup: Option<ObjValue>,828 map: GcHashMap<IStr, ObjMember>,829 assertions: Vec<TraceBox<dyn ObjectAssertion>>,830 next_field_index: FieldIndex,831}832impl ObjValueBuilder {833 pub fn new() -> Self {834 Self::with_capacity(0)835 }836 pub fn with_capacity(capacity: usize) -> Self {837 Self {838 sup: None,839 map: GcHashMap::with_capacity(capacity),840 assertions: Vec::new(),841 next_field_index: FieldIndex::default(),842 }843 }844 pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self {845 self.assertions.reserve_exact(capacity);846 self847 }848 pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {849 self.sup = Some(super_obj);850 self851 }852853 pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {854 self.assertions.push(tb!(assertion));855 self856 }857 pub fn member(&mut self, name: IStr) -> ObjMemberBuilder<ValueBuilder<'_>> {858 let field_index = self.next_field_index;859 self.next_field_index = self.next_field_index.next();860 ObjMemberBuilder::new(ValueBuilder(self), name, field_index)861 }862863 pub fn build(self) -> ObjValue {864 if self.sup.is_none() && self.map.is_empty() && self.assertions.is_empty() {865 return ObjValue::new_empty();866 }867 ObjValue::new(OopObject::new(868 self.sup,869 Cc::new(self.map),870 Cc::new(self.assertions),871 ))872 }873}874impl Default for ObjValueBuilder {875 fn default() -> Self {876 Self::with_capacity(0)877 }878}879880#[allow(clippy::module_name_repetitions)]881#[must_use = "value not added unless binding() was called"]882pub struct ObjMemberBuilder<Kind> {883 kind: Kind,884 name: IStr,885 add: bool,886 visibility: Visibility,887 original_index: FieldIndex,888 location: Option<ExprLocation>,889}890891#[allow(clippy::missing_const_for_fn)]892impl<Kind> ObjMemberBuilder<Kind> {893 pub(crate) fn new(kind: Kind, name: IStr, original_index: FieldIndex) -> Self {894 Self {895 kind,896 name,897 original_index,898 add: false,899 visibility: Visibility::Normal,900 location: None,901 }902 }903904 pub const fn with_add(mut self, add: bool) -> Self {905 self.add = add;906 self907 }908 pub fn add(self) -> Self {909 self.with_add(true)910 }911 pub fn with_visibility(mut self, visibility: Visibility) -> Self {912 self.visibility = visibility;913 self914 }915 pub fn hide(self) -> Self {916 self.with_visibility(Visibility::Hidden)917 }918 pub fn with_location(mut self, location: ExprLocation) -> Self {919 self.location = Some(location);920 self921 }922 fn build_member(self, binding: MaybeUnbound) -> (Kind, IStr, ObjMember) {923 (924 self.kind,925 self.name,926 ObjMember {927 flags: ObjFieldFlags::new(self.add, self.visibility),928 original_index: self.original_index,929 invoke: binding,930 location: self.location,931 },932 )933 }934}935936pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);937impl ObjMemberBuilder<ValueBuilder<'_>> {938 /// Inserts value, replacing if it is already defined939 pub fn value_unchecked(self, value: Val) {940 let (receiver, name, member) =941 self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value)));942 let entry = receiver.0.map.entry(name);943 entry.insert(member);944 }945946 pub fn value(self, value: Val) -> Result<()> {947 self.thunk(Thunk::evaluated(value))948 }949 pub fn thunk(self, value: Thunk<Val>) -> Result<()> {950 self.binding(MaybeUnbound::Bound(value))951 }952 pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {953 self.binding(MaybeUnbound::Unbound(Cc::new(tb!(bindable))))954 }955 pub fn binding(self, binding: MaybeUnbound) -> Result<()> {956 let (receiver, name, member) = self.build_member(binding);957 let location = member.location.clone();958 let old = receiver.0.map.insert(name.clone(), member);959 if old.is_some() {960 State::push(961 CallLocation(location.as_ref()),962 || format!("field <{}> initializtion", name.clone()),963 || throw!(DuplicateFieldName(name.clone())),964 )?;965 }966 Ok(())967 }968}969970pub struct ExtendBuilder<'v>(&'v mut ObjValue);971impl ObjMemberBuilder<ExtendBuilder<'_>> {972 pub fn value(self, value: Val) {973 self.binding(MaybeUnbound::Bound(Thunk::evaluated(value)));974 }975 pub fn bindable(self, bindable: TraceBox<dyn Unbound<Bound = Val>>) {976 self.binding(MaybeUnbound::Unbound(Cc::new(bindable)));977 }978 pub fn binding(self, binding: MaybeUnbound) {979 let (receiver, name, member) = self.build_member(binding);980 let new = receiver.0.clone();981 *receiver.0 = new.extend_with_raw_member(name, member);982 }983}