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}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 // 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::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::*;9192// 0 - add93// 12 - visibility94#[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}143144// Field => This145146#[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 // this: Option<ObjValue>,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 // .field("assertions", &self.assertions)170 // .field("assertions_ran", &self.assertions_ran)171 .field("this_entries", &self.this_entries)172 // .field("value_cache", &self.value_cache)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 /// When using standalone super in object, `this.super_obj.with_this(this)` is executed182 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 // If callback returns false, iteration stops191 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 // Safety: usize is POD216 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 // obj + {} == obj230 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 // FIXME: Should it use `self.0.this()` in case of standalone super?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 // Reorder in-place by resulting indexes529 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 // this: None,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 // FIXME: Duplication between ObjValue and OopObject630 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 /// Run callback for every field found in object685 ///686 /// Returns true if ended prematurely687 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 /// Inserts value, replacing if it is already defined932 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}