difftreelog
style accidental dbg push
in: master
2 files changed
crates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth1use std::{2 any::Any, cell::{Cell, RefCell}, collections::hash_map::Entry, fmt::{self, Debug}, hash::{Hash, Hasher}3};45use jrsonnet_gcmodule::{cc_dyn, Cc, Trace, Weak};6use educe::Educe;7use jrsonnet_interner::IStr;8use jrsonnet_parser::{Span, Visibility};9use rustc_hash::{FxHashMap, FxHashSet};1011use crate::{12 arr::{PickObjectKeyValues, PickObjectValues},13 bail,14 error::{suggest_object_fields, ErrorKind::*},15 function::{CallLocation, FuncVal},16 gc::WithCapacityExt as _,17 in_frame,18 identity_hash, 19 operator::evaluate_add_op,20 val::ArrValue,21 CcUnbound, MaybeUnbound, Result, Thunk, Unbound, Val,22};2324#[cfg(not(feature = "exp-preserve-order"))]25mod ordering {26 #![allow(27 // This module works as stub for preserve-order feature28 clippy::unused_self,29 )]3031 use jrsonnet_gcmodule::Trace;3233 #[derive(Clone, Copy, Default, Debug, Trace)]34 pub struct FieldIndex(());35 impl FieldIndex {36 pub const fn next(self) -> Self {37 Self(())38 }39 }4041 #[derive(Clone, Copy, Default, Debug, Trace)]42 pub struct SuperDepth(());43 impl SuperDepth {44 pub(super) fn deepen(self) {}45 }4647 #[derive(Clone, Copy, Debug)]48 pub struct FieldSortKey(());49 impl FieldSortKey {50 pub const fn new(_: SuperDepth, _: FieldIndex) -> Self {51 Self(())52 }53 }54}5556#[cfg(feature = "exp-preserve-order")]57mod ordering {58 use std::cmp::Reverse;5960 use jrsonnet_gcmodule::Trace;6162 #[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]63 pub struct FieldIndex(u32);64 impl FieldIndex {65 pub fn next(self) -> Self {66 Self(self.0 + 1)67 }68 }6970 #[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Debug)]71 pub struct SuperDepth(u32);72 impl SuperDepth {73 pub(super) fn deepen(&mut self) {74 *self.0 += 175 }76 }7778 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]79 pub struct FieldSortKey(Reverse<SuperDepth>, FieldIndex);80 impl FieldSortKey {81 pub fn new(depth: SuperDepth, index: FieldIndex) -> Self {82 Self(Reverse(depth), index)83 }84 }85}8687use ordering::{FieldIndex, FieldSortKey, SuperDepth};8889// 0 - add90// 12 - visibility91#[derive(Clone, Copy)]92pub struct ObjFieldFlags(u8);93impl ObjFieldFlags {94 fn new(add: bool, visibility: Visibility) -> Self {95 let mut v = 0;96 if add {97 v |= 1;98 }99 v |= match visibility {100 Visibility::Normal => 0b000,101 Visibility::Hidden => 0b010,102 Visibility::Unhide => 0b100,103 };104 Self(v)105 }106 pub fn add(&self) -> bool {107 self.0 & 1 != 0108 }109 pub fn visibility(&self) -> Visibility {110 match (self.0 & 0b110) >> 1 {111 0b00 => Visibility::Normal,112 0b01 => Visibility::Hidden,113 0b10 => Visibility::Unhide,114 _ => unreachable!(),115 }116 }117}118impl Debug for ObjFieldFlags {119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {120 f.debug_struct("ObjFieldFlags")121 .field("add", &self.add())122 .field("visibility", &self.visibility())123 .finish()124 }125}126127#[allow(clippy::module_name_repetitions)]128#[derive(Debug, Trace)]129pub struct ObjMember {130 #[trace(skip)]131 flags: ObjFieldFlags,132 original_index: FieldIndex,133 pub invoke: MaybeUnbound,134 pub location: Option<Span>,135}136137cc_dyn!(CcObjectAssertion, ObjectAssertion);138pub trait ObjectAssertion: Trace {139 fn run(&self, sup_this: SupThis) -> Result<()>;140}141142// Field => This143144#[derive(Trace, Debug)]145enum CacheValue {146 Cached(Result<Option<Val>>),147 Pending,148}149150#[allow(clippy::module_name_repetitions)]151#[derive(Trace)]152#[trace(tracking(force))]153pub struct OopObject {154 // this: Option<ObjValue>,155 assertions: Cc<Vec<CcObjectAssertion>>,156 this_entries: Cc<FxHashMap<IStr, ObjMember>>,157 value_cache: RefCell<FxHashMap<(IStr, Option<WeakObjValue>), CacheValue>>,158}159impl Debug for OopObject {160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {161 f.debug_struct("OopObject")162 // .field("assertions", &self.assertions)163 // .field("assertions_ran", &self.assertions_ran)164 .field("this_entries", &self.this_entries)165 // .field("value_cache", &self.value_cache)166 .finish_non_exhaustive()167 }168}169170type EnumFieldsHandler<'a> = dyn FnMut(SuperDepth, FieldIndex, IStr, Visibility) -> bool + 'a;171172#[derive(Trace, Clone)]173pub enum ValueProcess {174 None,175 SuperPlus,176}177178pub trait ObjectCore: Trace + Any + Debug {179 // If callback returns false, iteration stops, and this call returns false.180 fn enum_fields_core(181 &self,182 super_depth: &mut SuperDepth,183 handler: &mut EnumFieldsHandler<'_>,184 ) -> bool;185186 fn has_field_include_hidden(&self, name: IStr) -> bool;187188 fn get_for(&self, key: IStr, sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>>;189 // fn get_for_uncached(&self, key: IStr, this: ObjValue) -> Result<Option<(Val, ValueProcess)>>;190 fn field_visibility(&self, field: IStr) -> Option<Visibility>;191192 fn run_assertions_raw(&self, sup_this: SupThis) -> Result<()>;193}194195#[derive(Clone, Trace)]196pub struct WeakObjValue(#[trace(skip)] Weak<ObjValueInner>);197impl Debug for WeakObjValue {198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {199 f.debug_tuple("WeakObjValue").finish()200 }201}202203impl PartialEq for WeakObjValue {204 fn eq(&self, other: &Self) -> bool {205 Weak::ptr_eq(&self.0, &other.0)206 }207}208209impl Eq for WeakObjValue {}210impl Hash for WeakObjValue {211 fn hash<H: Hasher>(&self, hasher: &mut H) {212 // Safety: usize is POD213 let addr = unsafe { *std::ptr::addr_of!(self.0).cast() };214 hasher.write_usize(addr);215 }216}217218cc_dyn!(219 #[derive(Clone, Debug)]220 ObjCore, ObjectCore,221 pub fn new() {...}222);223#[derive(Trace, Educe)]224#[educe(Debug)]225struct ObjValueInner {226 cores: Vec<ObjCore>,227 assertions_ran: Cell<bool>,228 value_cache: RefCell<FxHashMap<(IStr, CoreIdx), CacheValue>>,229}230231thread_local! {232 static RUNNING_ASSERTIONS: RefCell<FxHashSet<ObjValue>> = RefCell::default();233}234fn is_asserting(obj: &ObjValue) -> bool {235 RUNNING_ASSERTIONS.with_borrow(|v| v.contains(obj))236}237/// Returns false if already asserting238fn start_asserting(obj: &ObjValue) -> bool {239 RUNNING_ASSERTIONS.with_borrow_mut(|v| v.insert(obj.clone()))240}241fn finish_asserting(obj: &ObjValue) {242 RUNNING_ASSERTIONS.with_borrow_mut(|v| {243 let r = v.remove(obj);244 debug_assert!(245 r,246 "finish_asserting was called before start_asserting or twice"247 );248 });249}250251#[allow(clippy::module_name_repetitions)]252#[derive(Clone, Trace, Debug, Educe)]253#[educe(PartialEq, Hash, Eq)]254pub struct ObjValue(255 #[educe(PartialEq(method(Cc::ptr_eq)), Hash(method(identity_hash)))] Cc<ObjValueInner>,256);257258#[derive(Trace, Debug)]259struct StandaloneSuperCore {260 sup: CoreIdx,261 this: ObjValue,262}263impl ObjectCore for StandaloneSuperCore {264 fn enum_fields_core(265 &self,266 super_depth: &mut SuperDepth,267 handler: &mut EnumFieldsHandler<'_>,268 ) -> bool {269 self.this270 .enum_fields_internal(super_depth, handler, self.sup)271 }272273 fn has_field_include_hidden(&self, name: IStr) -> bool {274 self.this.has_field_include_hidden_idx(name, self.sup)275 }276277 fn get_for(&self, key: IStr, _sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>> {278 let v = self.this.get_idx(key, self.sup)?;279 Ok(v.map(|v| (v, ValueProcess::None)))280 }281282 fn field_visibility(&self, field: IStr) -> Option<Visibility> {283 self.this.field_visibility_idx(field, self.sup)284 }285286 fn run_assertions_raw(&self, _sup_this: SupThis) -> Result<()> {287 self.this.run_assertions()288 }289}290291#[derive(Debug, Trace)]292struct EmptyObject;293impl ObjectCore for EmptyObject {294 fn enum_fields_core(295 &self,296 _super_depth: &mut SuperDepth,297 _handler: &mut EnumFieldsHandler<'_>,298 ) -> bool {299 true300 }301302 fn has_field_include_hidden(&self, _name: IStr) -> bool {303 false304 }305306 fn get_for(&self, _key: IStr, _sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>> {307 Ok(None)308 }309310 fn run_assertions_raw(&self, _sup_this: SupThis) -> Result<()> {311 Ok(())312 }313314 fn field_visibility(&self, _field: IStr) -> Option<Visibility> {315 None316 }317}318319#[derive(Hash, PartialEq, Eq, Trace, Clone, Copy, Debug)]320struct CoreIdx {321 idx: usize,322}323impl CoreIdx {324 fn super_exists(self) -> bool {325 self.idx != 0326 }327}328#[derive(Trace, Clone, PartialEq, Eq, Hash, Debug)]329pub struct SupThis {330 sup: CoreIdx,331 this: ObjValue,332}333impl SupThis {334 pub fn has_super(&self) -> bool {335 self.sup.super_exists()336 }337 /// Implementation of `"field" in super` operation,338 /// works faster than standalone super path.339 ///340 /// In case of no `super` existence, returns false.341 pub fn field_in_super(&self, field: IStr) -> bool {342 self.this.has_field_include_hidden_idx(field, self.sup)343 }344 /// Implementation of `super.field` operation,345 /// works faster than standalone super path.346 ///347 /// In case of no `super` existence, returns `NoSuperFound`348 pub fn get_super(&self, field: IStr) -> Result<Option<Val>> {349 if !self.sup.super_exists() {350 bail!(NoSuperFound);351 }352 self.this.get_idx(field, self.sup)353 }354 /// `super` with `self` overriden for top-level lookups.355 /// Exists when super appears outside of `super.field`/`"field" in super` expressions356 /// Exclusive to jrsonnet.357 ///358 /// Might return `NoSuperFound` error.359 pub fn standalone_super(&self) -> Result<ObjValue> {360 if !self.sup.super_exists() {361 bail!(NoSuperFound)362 }363 Ok(ObjValue::new(StandaloneSuperCore {364 sup: self.sup,365 this: self.this.clone(),366 }))367 }368 pub fn this(&self) -> &ObjValue {369 &self.this370 }371 pub fn downgrade(self) -> WeakSupThis {372 WeakSupThis {373 sup: self.sup,374 this: self.this.downgrade(),375 }376 }377}378#[derive(Trace, PartialEq, Eq, Hash, Debug)]379pub struct WeakSupThis {380 sup: CoreIdx,381 this: WeakObjValue,382}383384impl ObjValue {385 pub fn new(v: impl ObjectCore) -> Self {386 Self(Cc::new(ObjValueInner {387 cores: vec![ObjCore::new(v)],388 assertions_ran: Cell::new(false),389 value_cache: RefCell::new(FxHashMap::new()),390 }))391 }392 pub fn new_empty() -> Self {393 Self::new(EmptyObject)394 }395 pub fn builder() -> ObjValueBuilder {396 ObjValueBuilder::new()397 }398 pub fn builder_with_capacity(capacity: usize) -> ObjValueBuilder {399 ObjValueBuilder::with_capacity(capacity)400 }401 pub(crate) fn extend_with_raw_member(self, key: IStr, value: ObjMember) -> Self {402 let mut out = ObjValueBuilder::with_capacity(1);403 out.with_super(self);404 let mut member = out.field(key);405 if value.flags.add() {406 member = member.add();407 }408 if let Some(loc) = value.location {409 member = member.with_location(loc);410 }411 let _ = member412 .with_visibility(value.flags.visibility())413 .binding(value.invoke);414 out.build()415 }416 pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder<'_>> {417 ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())418 }419420 #[must_use]421 pub fn extend_from(&self, sup: Self) -> Self {422 let mut cores = sup.0.cores.clone();423 cores.extend(self.0.cores.iter().cloned());424 ObjValue(Cc::new(ObjValueInner {425 cores,426 value_cache: RefCell::default(),427 assertions_ran: Cell::new(false),428 }))429 }430 // #[must_use]431 // pub fn with_this(&self, this: Self) -> Self {432 // self.0.with_this(self.clone(), this)433 // }434 /// Returns amount of visible object fields435 /// If object only contains hidden fields - may return zero.436 pub fn len(&self) -> usize {437 self.fields_visibility()438 .iter()439 .filter(|(_, (visible, _))| *visible)440 .count()441 }442 pub fn is_empty(&self) -> bool {443 self.len() == 0444 }445 /// For each field, calls callback.446 /// If callback returns false - ends iteration prematurely.447 ///448 /// Returns false if ended prematurely449 pub fn enum_fields(&self, handler: &mut EnumFieldsHandler<'_>) -> bool {450 let mut super_depth = SuperDepth::default();451 self.enum_fields_internal(452 &mut super_depth,453 handler,454 CoreIdx {455 idx: self.0.cores.len(),456 },457 )458 }459 fn enum_fields_internal(460 &self,461 super_depth: &mut SuperDepth,462 handler: &mut EnumFieldsHandler<'_>,463 idx: CoreIdx,464 ) -> bool {465 for core in self.0.cores[..idx.idx].iter() {466 if !core.0.enum_fields_core(super_depth, handler) {467 return false;468 }469 super_depth.deepen();470 }471 true472 }473474 pub fn has_field_include_hidden(&self, name: IStr) -> bool {475 self.has_field_include_hidden_idx(476 name,477 CoreIdx {478 idx: self.0.cores.len(),479 },480 )481 }482 fn has_field_include_hidden_idx(&self, name: IStr, core: CoreIdx) -> bool {483 self.0.cores[..core.idx]484 .iter()485 .rev()486 .any(|v| v.0.has_field_include_hidden(name.clone()))487 }488 pub fn has_field(&self, name: IStr) -> bool {489 match self.field_visibility(name) {490 Some(Visibility::Unhide | Visibility::Normal) => true,491 Some(Visibility::Hidden) | None => false,492 }493 }494 pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {495 if include_hidden {496 self.has_field_include_hidden(name)497 } else {498 self.has_field(name)499 }500 }501 pub fn get(&self, key: IStr) -> Result<Option<Val>> {502 self.get_idx(503 key,504 CoreIdx {505 idx: self.0.cores.len(),506 },507 )508 }509510 fn get_idx(&self, key: IStr, core: CoreIdx) -> Result<Option<Val>> {511 let cache_key = (key.clone(), core);512 {513 let mut cache = self.0.value_cache.borrow_mut();514 // entry_ref candidate?515 match cache.entry(cache_key.clone()) {516 Entry::Occupied(v) => match v.get() {517 CacheValue::Cached(v) => return v.clone(),518 CacheValue::Pending => {519 if !is_asserting(self) {520 bail!(InfiniteRecursionDetected);521 }522 }523 },524 Entry::Vacant(v) => {525 v.insert(CacheValue::Pending);526 }527 };528 }529 let result = self.get_idx_uncached(key, core);530 {531 let mut cache = self.0.value_cache.borrow_mut();532 cache.insert(cache_key, CacheValue::Cached(result.clone()));533 }534 result535 }536 fn get_idx_uncached(&self, key: IStr, core: CoreIdx) -> Result<Option<Val>> {537 self.run_assertions()?;538 let mut add_stack = Vec::with_capacity(2);539 for (sup, core) in self.0.cores[..core.idx].iter().enumerate().rev() {540 let sup_this = SupThis {541 sup: CoreIdx { idx: sup },542 this: self.clone(),543 };544 if let Some((val, proc)) = core.0.get_for(key.clone(), sup_this)? {545 match proc {546 ValueProcess::None if add_stack.is_empty() => return Ok(Some(val)),547 ValueProcess::None => {548 add_stack.push(val);549 break;550 }551 ValueProcess::SuperPlus => {552 add_stack.push(val);553 }554 }555 }556 }557 if add_stack.is_empty() {558 // None of layers had this field559 return Ok(None);560 } else if add_stack.len() == 1 {561 // A layer had this field, but it wanted this field to be added with super.562 // However, no super had this field, fail-safe563 return Ok(Some(add_stack.pop().expect("single element on stack")));564 }565 let mut values = add_stack.into_iter().rev();566 let init = values.next().expect("at least 2 elements");567568 values569 .try_fold(init, |a, b| evaluate_add_op(&a, &b))570 .map(Some)571572 // self.0.get_raw(key, this)573 }574575 pub fn get_or_bail(&self, key: IStr) -> Result<Val> {576 let Some(value) = self.get(key.clone())? else {577 let suggestions = suggest_object_fields(self, key.clone());578 bail!(NoSuchField(key, suggestions))579 };580 Ok(value)581 }582583 fn field_visibility(&self, field: IStr) -> Option<Visibility> {584 self.field_visibility_idx(585 field,586 CoreIdx {587 idx: self.0.cores.len(),588 },589 )590 }591 fn field_visibility_idx(&self, field: IStr, core: CoreIdx) -> Option<Visibility> {592 let mut exists = false;593 for ele in self.0.cores[..core.idx].iter().rev() {594 let vis = ele.0.field_visibility(field.clone());595 match vis {596 Some(Visibility::Unhide | Visibility::Hidden) => return vis,597 Some(Visibility::Normal) => exists = true,598 None => {}599 }600 }601 exists.then_some(Visibility::Normal)602 }603604 pub fn run_assertions(&self) -> Result<()> {605 if self.0.assertions_ran.get() {606 return Ok(());607 }608 if !start_asserting(self) {609 return Ok(());610 }611 for (idx, ele) in self.0.cores.iter().enumerate() {612 let sup_this = SupThis {613 sup: CoreIdx { idx },614 this: self.clone(),615 };616 ele.0.run_assertions_raw(sup_this).inspect_err(|_e| {617 finish_asserting(self);618 })?;619 }620 finish_asserting(self);621 self.0.assertions_ran.set(true);622 Ok(())623 }624625 pub fn iter(626 &self,627 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,628 ) -> impl Iterator<Item = (IStr, Result<Val>)> + '_ {629 let fields = self.fields(630 #[cfg(feature = "exp-preserve-order")]631 preserve_order,632 );633 fields.into_iter().map(|field| {634 (635 field.clone(),636 self.get(field)637 .map(|opt| opt.expect("iterating over keys, field exists")),638 )639 })640 }641 pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {642 if !self.has_field_ex(key.clone(), true) {643 return None;644 }645 let obj = self.clone();646647 Some(Thunk!(move || Ok(obj.get(key)?.expect("field exists"))))648 }649 pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {650 let obj = self.clone();651 Thunk!(move || obj.get_or_bail(key))652 }653 pub fn ptr_eq(a: &Self, b: &Self) -> bool {654 Cc::ptr_eq(&a.0, &b.0)655 }656 pub fn downgrade(self) -> WeakObjValue {657 WeakObjValue(self.0.downgrade())658 }659 fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {660 let mut out = FxHashMap::default();661 self.enum_fields(&mut |depth, index, name, visibility| {662 dbg!(&name, visibility);663 let new_sort_key = FieldSortKey::new(depth, index);664 let entry = out.entry(name);665 let (visible, _) = entry.or_insert((true, new_sort_key));666 match visibility {667 Visibility::Normal => {}668 Visibility::Hidden => {669 *visible = false;670 }671 Visibility::Unhide => {672 *visible = true;673 }674 };675 false676 });677 out678 }679 pub fn fields_ex(680 &self,681 include_hidden: bool,682 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,683 ) -> Vec<IStr> {684 #[cfg(feature = "exp-preserve-order")]685 if preserve_order {686 let (mut fields, mut keys): (Vec<_>, Vec<_>) = self687 .fields_visibility()688 .into_iter()689 .filter(|(_, (visible, _))| include_hidden || *visible)690 .enumerate()691 .map(|(idx, (k, (_, sk)))| (k, (sk, idx)))692 .unzip();693 keys.sort_unstable_by_key(|v| v.0);694 // Reorder in-place by resulting indexes695 for i in 0..fields.len() {696 let x = fields[i].clone();697 let mut j = i;698 loop {699 let k = keys[j].1;700 keys[j].1 = j;701 if k == i {702 break;703 }704 fields[j] = fields[k].clone();705 j = k;706 }707 fields[j] = x;708 }709 return fields;710 }711712 let mut fields: Vec<_> = dbg!(self713 .fields_visibility())714 .into_iter()715 .filter(|(_, (visible, _))| include_hidden || *visible)716 .map(|(k, _)| k)717 .collect();718 fields.sort_unstable();719 fields720 }721 pub fn fields(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Vec<IStr> {722 self.fields_ex(723 false,724 #[cfg(feature = "exp-preserve-order")]725 preserve_order,726 )727 }728 pub fn values_ex(729 &self,730 include_hidden: bool,731 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,732 ) -> ArrValue {733 ArrValue::new(PickObjectValues::new(734 self.clone(),735 self.fields_ex(736 include_hidden,737 #[cfg(feature = "exp-preserve-order")]738 preserve_order,739 ),740 ))741 }742 pub fn values(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> ArrValue {743 self.values_ex(744 false,745 #[cfg(feature = "exp-preserve-order")]746 preserve_order,747 )748 }749 pub fn key_values_ex(750 &self,751 include_hidden: bool,752 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,753 ) -> ArrValue {754 ArrValue::new(PickObjectKeyValues::new(755 self.clone(),756 self.fields_ex(757 include_hidden,758 #[cfg(feature = "exp-preserve-order")]759 preserve_order,760 ),761 ))762 }763 pub fn key_values(764 &self,765 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,766 ) -> ArrValue {767 self.key_values_ex(768 false,769 #[cfg(feature = "exp-preserve-order")]770 preserve_order,771 )772 }773}774775impl OopObject {776 pub fn new(777 this_entries: Cc<FxHashMap<IStr, ObjMember>>,778 assertions: Cc<Vec<CcObjectAssertion>>,779 ) -> Self {780 Self {781 this_entries,782 value_cache: RefCell::new(FxHashMap::new()),783 assertions,784 }785 }786}787788impl ObjectCore for OopObject {789 fn enum_fields_core(790 &self,791 super_depth: &mut SuperDepth,792 handler: &mut EnumFieldsHandler<'_>,793 ) -> bool {794 for (name, member) in self.this_entries.iter() {795 if handler(796 *super_depth,797 member.original_index,798 name.clone(),799 member.flags.visibility(),800 ) {801 return false;802 }803 }804 true805 }806807 fn has_field_include_hidden(&self, name: IStr) -> bool {808 self.this_entries.contains_key(&name)809 }810811 fn get_for(&self, key: IStr, sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>> {812 match self.this_entries.get(&key) {813 Some(k) => Ok(Some((814 k.invoke.evaluate(sup_this)?,815 if k.flags.add() {816 ValueProcess::SuperPlus817 } else {818 ValueProcess::None819 },820 ))),821 None => Ok(None),822 }823 }824 fn field_visibility(&self, name: IStr) -> Option<Visibility> {825 Some(self.this_entries.get(&name)?.flags.visibility())826 }827828 fn run_assertions_raw(&self, sup_this: SupThis) -> Result<()> {829 if self.assertions.is_empty() {830 return Ok(());831 }832 for assertion in self.assertions.iter() {833 assertion.0.run(sup_this.clone())?;834 }835 Ok(())836 }837}838839#[allow(clippy::module_name_repetitions)]840pub struct ObjValueBuilder {841 sup: Option<ObjValue>,842 map: FxHashMap<IStr, ObjMember>,843 assertions: Vec<CcObjectAssertion>,844 next_field_index: FieldIndex,845}846impl ObjValueBuilder {847 pub fn new() -> Self {848 Self::with_capacity(0)849 }850 pub fn with_capacity(capacity: usize) -> Self {851 Self {852 sup: None,853 map: FxHashMap::with_capacity(capacity),854 assertions: Vec::new(),855 next_field_index: FieldIndex::default(),856 }857 }858 pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self {859 self.assertions.reserve_exact(capacity);860 self861 }862 pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {863 self.sup = Some(super_obj);864 self865 }866867 pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {868 self.assertions.push(CcObjectAssertion::new(assertion));869 self870 }871 pub fn field(&mut self, name: impl Into<IStr>) -> ObjMemberBuilder<ValueBuilder<'_>> {872 let field_index = self.next_field_index;873 self.next_field_index = self.next_field_index.next();874 ObjMemberBuilder::new(ValueBuilder(self), name.into(), field_index)875 }876 /// Preset for common method definiton pattern:877 /// Create a hidden field with the function value.878 ///879 /// `.field(name).hide().value(Val::function(value))`880 pub fn method(&mut self, name: impl Into<IStr>, value: impl Into<FuncVal>) -> &mut Self {881 self.field(name).hide().value(Val::Func(value.into()));882 self883 }884 pub fn try_method(885 &mut self,886 name: impl Into<IStr>,887 value: impl Into<FuncVal>,888 ) -> Result<&mut Self> {889 self.field(name).hide().try_value(Val::Func(value.into()))?;890 Ok(self)891 }892893 pub fn build(self) -> ObjValue {894 if self.sup.is_none() && self.map.is_empty() && self.assertions.is_empty() {895 return ObjValue::new_empty();896 }897 let res = ObjValue::new(OopObject::new(Cc::new(self.map), Cc::new(self.assertions)));898 self.sup.map(|sup| res.extend_from(sup)).unwrap_or(res)899 }900}901impl Default for ObjValueBuilder {902 fn default() -> Self {903 Self::with_capacity(0)904 }905}906907#[allow(clippy::module_name_repetitions)]908#[must_use = "value not added unless binding() was called"]909pub struct ObjMemberBuilder<Kind> {910 kind: Kind,911 name: IStr,912 add: bool,913 visibility: Visibility,914 original_index: FieldIndex,915 location: Option<Span>,916}917918#[allow(clippy::missing_const_for_fn)]919impl<Kind> ObjMemberBuilder<Kind> {920 pub(crate) fn new(kind: Kind, name: IStr, original_index: FieldIndex) -> Self {921 Self {922 kind,923 name,924 original_index,925 add: false,926 visibility: Visibility::Normal,927 location: None,928 }929 }930931 pub const fn with_add(mut self, add: bool) -> Self {932 self.add = add;933 self934 }935 pub fn add(self) -> Self {936 self.with_add(true)937 }938 pub fn with_visibility(mut self, visibility: Visibility) -> Self {939 self.visibility = visibility;940 self941 }942 pub fn hide(self) -> Self {943 self.with_visibility(Visibility::Hidden)944 }945 pub fn with_location(mut self, location: Span) -> Self {946 self.location = Some(location);947 self948 }949 fn build_member(self, binding: MaybeUnbound) -> (Kind, IStr, ObjMember) {950 (951 self.kind,952 self.name,953 ObjMember {954 flags: ObjFieldFlags::new(self.add, self.visibility),955 original_index: self.original_index,956 invoke: binding,957 location: self.location,958 },959 )960 }961}962963pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);964impl ObjMemberBuilder<ValueBuilder<'_>> {965 /// Inserts value, replacing if it is already defined966 pub fn value(self, value: impl Into<Val>) {967 let (receiver, name, member) =968 self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value.into())));969 let entry = receiver.0.map.entry(name);970 entry.insert_entry(member);971 }972973 /// Tries to insert value, returns an error if it was already defined974 pub fn try_value(self, value: impl Into<Val>) -> Result<()> {975 self.try_thunk(Thunk::evaluated(value.into()))976 }977 pub fn try_thunk(self, value: impl Into<Thunk<Val>>) -> Result<()> {978 self.binding(MaybeUnbound::Bound(value.into()))979 }980 pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {981 self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)))982 }983 pub fn binding(self, binding: MaybeUnbound) -> Result<()> {984 let (receiver, name, member) = self.build_member(binding);985 let location = member.location.clone();986 let old = receiver.0.map.insert(name.clone(), member);987 if old.is_some() {988 in_frame(989 CallLocation(location.as_ref()),990 || format!("field <{}> initializtion", name.clone()),991 || bail!(DuplicateFieldName(name.clone())),992 )?;993 }994 Ok(())995 }996}997998pub struct ExtendBuilder<'v>(&'v mut ObjValue);999impl ObjMemberBuilder<ExtendBuilder<'_>> {1000 pub fn value(self, value: impl Into<Val>) {1001 self.binding(MaybeUnbound::Bound(Thunk::evaluated(value.into())));1002 }1003 pub fn bindable(self, bindable: impl Unbound<Bound = Val>) {1004 self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)));1005 }1006 pub fn binding(self, binding: MaybeUnbound) {1007 let (receiver, name, member) = self.build_member(binding);1008 let new = receiver.0.clone();1009 *receiver.0 = new.extend_with_raw_member(name, member);1010 }1011}1use std::{2 any::Any,3 cell::{Cell, RefCell},4 collections::hash_map::Entry,5 fmt::{self, Debug},6 hash::{Hash, Hasher},7};89use educe::Educe;10use jrsonnet_gcmodule::{cc_dyn, Cc, Trace, Weak};11use jrsonnet_interner::IStr;12use jrsonnet_parser::{Span, Visibility};13use rustc_hash::{FxHashMap, FxHashSet};1415use crate::{16 arr::{PickObjectKeyValues, PickObjectValues},17 bail,18 error::{suggest_object_fields, ErrorKind::*},19 function::{CallLocation, FuncVal},20 gc::WithCapacityExt as _,21 identity_hash, in_frame,22 operator::evaluate_add_op,23 val::ArrValue,24 CcUnbound, MaybeUnbound, Result, Thunk, Unbound, Val,25};2627#[cfg(not(feature = "exp-preserve-order"))]28mod ordering {29 #![allow(30 // This module works as stub for preserve-order feature31 clippy::unused_self,32 )]3334 use jrsonnet_gcmodule::Trace;3536 #[derive(Clone, Copy, Default, Debug, Trace)]37 pub struct FieldIndex(());38 impl FieldIndex {39 pub const fn next(self) -> Self {40 Self(())41 }42 }4344 #[derive(Clone, Copy, Default, Debug, Trace)]45 pub struct SuperDepth(());46 impl SuperDepth {47 pub(super) fn deepen(self) {}48 }4950 #[derive(Clone, Copy, Debug)]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(super) fn deepen(&mut self) {77 *self.0 += 178 }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::{FieldIndex, FieldSortKey, SuperDepth};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 fmt::Formatter<'_>) -> 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<Span>,138}139140cc_dyn!(CcObjectAssertion, ObjectAssertion);141pub trait ObjectAssertion: Trace {142 fn run(&self, sup_this: SupThis) -> Result<()>;143}144145// Field => This146147#[derive(Trace, Debug)]148enum CacheValue {149 Cached(Result<Option<Val>>),150 Pending,151}152153#[allow(clippy::module_name_repetitions)]154#[derive(Trace)]155#[trace(tracking(force))]156pub struct OopObject {157 // this: Option<ObjValue>,158 assertions: Cc<Vec<CcObjectAssertion>>,159 this_entries: Cc<FxHashMap<IStr, ObjMember>>,160 value_cache: RefCell<FxHashMap<(IStr, Option<WeakObjValue>), CacheValue>>,161}162impl Debug for OopObject {163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {164 f.debug_struct("OopObject")165 // .field("assertions", &self.assertions)166 // .field("assertions_ran", &self.assertions_ran)167 .field("this_entries", &self.this_entries)168 // .field("value_cache", &self.value_cache)169 .finish_non_exhaustive()170 }171}172173type EnumFieldsHandler<'a> = dyn FnMut(SuperDepth, FieldIndex, IStr, Visibility) -> bool + 'a;174175#[derive(Trace, Clone)]176pub enum ValueProcess {177 None,178 SuperPlus,179}180181pub trait ObjectCore: Trace + Any + Debug {182 // If callback returns false, iteration stops, and this call returns false.183 fn enum_fields_core(184 &self,185 super_depth: &mut SuperDepth,186 handler: &mut EnumFieldsHandler<'_>,187 ) -> bool;188189 fn has_field_include_hidden(&self, name: IStr) -> bool;190191 fn get_for(&self, key: IStr, sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>>;192 // fn get_for_uncached(&self, key: IStr, this: ObjValue) -> Result<Option<(Val, ValueProcess)>>;193 fn field_visibility(&self, field: IStr) -> Option<Visibility>;194195 fn run_assertions_raw(&self, sup_this: SupThis) -> Result<()>;196}197198#[derive(Clone, Trace)]199pub struct WeakObjValue(#[trace(skip)] Weak<ObjValueInner>);200impl Debug for WeakObjValue {201 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {202 f.debug_tuple("WeakObjValue").finish()203 }204}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}220221cc_dyn!(222 #[derive(Clone, Debug)]223 ObjCore, ObjectCore,224 pub fn new() {...}225);226#[derive(Trace, Educe)]227#[educe(Debug)]228struct ObjValueInner {229 cores: Vec<ObjCore>,230 assertions_ran: Cell<bool>,231 value_cache: RefCell<FxHashMap<(IStr, CoreIdx), CacheValue>>,232}233234thread_local! {235 static RUNNING_ASSERTIONS: RefCell<FxHashSet<ObjValue>> = RefCell::default();236}237fn is_asserting(obj: &ObjValue) -> bool {238 RUNNING_ASSERTIONS.with_borrow(|v| v.contains(obj))239}240/// Returns false if already asserting241fn start_asserting(obj: &ObjValue) -> bool {242 RUNNING_ASSERTIONS.with_borrow_mut(|v| v.insert(obj.clone()))243}244fn finish_asserting(obj: &ObjValue) {245 RUNNING_ASSERTIONS.with_borrow_mut(|v| {246 let r = v.remove(obj);247 debug_assert!(248 r,249 "finish_asserting was called before start_asserting or twice"250 );251 });252}253254#[allow(clippy::module_name_repetitions)]255#[derive(Clone, Trace, Debug, Educe)]256#[educe(PartialEq, Hash, Eq)]257pub struct ObjValue(258 #[educe(PartialEq(method(Cc::ptr_eq)), Hash(method(identity_hash)))] Cc<ObjValueInner>,259);260261#[derive(Trace, Debug)]262struct StandaloneSuperCore {263 sup: CoreIdx,264 this: ObjValue,265}266impl ObjectCore for StandaloneSuperCore {267 fn enum_fields_core(268 &self,269 super_depth: &mut SuperDepth,270 handler: &mut EnumFieldsHandler<'_>,271 ) -> bool {272 self.this273 .enum_fields_internal(super_depth, handler, self.sup)274 }275276 fn has_field_include_hidden(&self, name: IStr) -> bool {277 self.this.has_field_include_hidden_idx(name, self.sup)278 }279280 fn get_for(&self, key: IStr, _sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>> {281 let v = self.this.get_idx(key, self.sup)?;282 Ok(v.map(|v| (v, ValueProcess::None)))283 }284285 fn field_visibility(&self, field: IStr) -> Option<Visibility> {286 self.this.field_visibility_idx(field, self.sup)287 }288289 fn run_assertions_raw(&self, _sup_this: SupThis) -> Result<()> {290 self.this.run_assertions()291 }292}293294#[derive(Debug, Trace)]295struct EmptyObject;296impl ObjectCore for EmptyObject {297 fn enum_fields_core(298 &self,299 _super_depth: &mut SuperDepth,300 _handler: &mut EnumFieldsHandler<'_>,301 ) -> bool {302 true303 }304305 fn has_field_include_hidden(&self, _name: IStr) -> bool {306 false307 }308309 fn get_for(&self, _key: IStr, _sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>> {310 Ok(None)311 }312313 fn run_assertions_raw(&self, _sup_this: SupThis) -> Result<()> {314 Ok(())315 }316317 fn field_visibility(&self, _field: IStr) -> Option<Visibility> {318 None319 }320}321322#[derive(Hash, PartialEq, Eq, Trace, Clone, Copy, Debug)]323struct CoreIdx {324 idx: usize,325}326impl CoreIdx {327 fn super_exists(self) -> bool {328 self.idx != 0329 }330}331#[derive(Trace, Clone, PartialEq, Eq, Hash, Debug)]332pub struct SupThis {333 sup: CoreIdx,334 this: ObjValue,335}336impl SupThis {337 pub fn has_super(&self) -> bool {338 self.sup.super_exists()339 }340 /// Implementation of `"field" in super` operation,341 /// works faster than standalone super path.342 ///343 /// In case of no `super` existence, returns false.344 pub fn field_in_super(&self, field: IStr) -> bool {345 self.this.has_field_include_hidden_idx(field, self.sup)346 }347 /// Implementation of `super.field` operation,348 /// works faster than standalone super path.349 ///350 /// In case of no `super` existence, returns `NoSuperFound`351 pub fn get_super(&self, field: IStr) -> Result<Option<Val>> {352 if !self.sup.super_exists() {353 bail!(NoSuperFound);354 }355 self.this.get_idx(field, self.sup)356 }357 /// `super` with `self` overriden for top-level lookups.358 /// Exists when super appears outside of `super.field`/`"field" in super` expressions359 /// Exclusive to jrsonnet.360 ///361 /// Might return `NoSuperFound` error.362 pub fn standalone_super(&self) -> Result<ObjValue> {363 if !self.sup.super_exists() {364 bail!(NoSuperFound)365 }366 Ok(ObjValue::new(StandaloneSuperCore {367 sup: self.sup,368 this: self.this.clone(),369 }))370 }371 pub fn this(&self) -> &ObjValue {372 &self.this373 }374 pub fn downgrade(self) -> WeakSupThis {375 WeakSupThis {376 sup: self.sup,377 this: self.this.downgrade(),378 }379 }380}381#[derive(Trace, PartialEq, Eq, Hash, Debug)]382pub struct WeakSupThis {383 sup: CoreIdx,384 this: WeakObjValue,385}386387impl ObjValue {388 pub fn new(v: impl ObjectCore) -> Self {389 Self(Cc::new(ObjValueInner {390 cores: vec![ObjCore::new(v)],391 assertions_ran: Cell::new(false),392 value_cache: RefCell::new(FxHashMap::new()),393 }))394 }395 pub fn new_empty() -> Self {396 Self::new(EmptyObject)397 }398 pub fn builder() -> ObjValueBuilder {399 ObjValueBuilder::new()400 }401 pub fn builder_with_capacity(capacity: usize) -> ObjValueBuilder {402 ObjValueBuilder::with_capacity(capacity)403 }404 pub(crate) fn extend_with_raw_member(self, key: IStr, value: ObjMember) -> Self {405 let mut out = ObjValueBuilder::with_capacity(1);406 out.with_super(self);407 let mut member = out.field(key);408 if value.flags.add() {409 member = member.add();410 }411 if let Some(loc) = value.location {412 member = member.with_location(loc);413 }414 let _ = member415 .with_visibility(value.flags.visibility())416 .binding(value.invoke);417 out.build()418 }419 pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder<'_>> {420 ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())421 }422423 #[must_use]424 pub fn extend_from(&self, sup: Self) -> Self {425 let mut cores = sup.0.cores.clone();426 cores.extend(self.0.cores.iter().cloned());427 ObjValue(Cc::new(ObjValueInner {428 cores,429 value_cache: RefCell::default(),430 assertions_ran: Cell::new(false),431 }))432 }433 // #[must_use]434 // pub fn with_this(&self, this: Self) -> Self {435 // self.0.with_this(self.clone(), this)436 // }437 /// Returns amount of visible object fields438 /// If object only contains hidden fields - may return zero.439 pub fn len(&self) -> usize {440 self.fields_visibility()441 .iter()442 .filter(|(_, (visible, _))| *visible)443 .count()444 }445 pub fn is_empty(&self) -> bool {446 self.len() == 0447 }448 /// For each field, calls callback.449 /// If callback returns false - ends iteration prematurely.450 ///451 /// Returns false if ended prematurely452 pub fn enum_fields(&self, handler: &mut EnumFieldsHandler<'_>) -> bool {453 let mut super_depth = SuperDepth::default();454 self.enum_fields_internal(455 &mut super_depth,456 handler,457 CoreIdx {458 idx: self.0.cores.len(),459 },460 )461 }462 fn enum_fields_internal(463 &self,464 super_depth: &mut SuperDepth,465 handler: &mut EnumFieldsHandler<'_>,466 idx: CoreIdx,467 ) -> bool {468 for core in self.0.cores[..idx.idx].iter() {469 if !core.0.enum_fields_core(super_depth, handler) {470 return false;471 }472 super_depth.deepen();473 }474 true475 }476477 pub fn has_field_include_hidden(&self, name: IStr) -> bool {478 self.has_field_include_hidden_idx(479 name,480 CoreIdx {481 idx: self.0.cores.len(),482 },483 )484 }485 fn has_field_include_hidden_idx(&self, name: IStr, core: CoreIdx) -> bool {486 self.0.cores[..core.idx]487 .iter()488 .rev()489 .any(|v| v.0.has_field_include_hidden(name.clone()))490 }491 pub fn has_field(&self, name: IStr) -> bool {492 match self.field_visibility(name) {493 Some(Visibility::Unhide | Visibility::Normal) => true,494 Some(Visibility::Hidden) | None => false,495 }496 }497 pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {498 if include_hidden {499 self.has_field_include_hidden(name)500 } else {501 self.has_field(name)502 }503 }504 pub fn get(&self, key: IStr) -> Result<Option<Val>> {505 self.get_idx(506 key,507 CoreIdx {508 idx: self.0.cores.len(),509 },510 )511 }512513 fn get_idx(&self, key: IStr, core: CoreIdx) -> Result<Option<Val>> {514 let cache_key = (key.clone(), core);515 {516 let mut cache = self.0.value_cache.borrow_mut();517 // entry_ref candidate?518 match cache.entry(cache_key.clone()) {519 Entry::Occupied(v) => match v.get() {520 CacheValue::Cached(v) => return v.clone(),521 CacheValue::Pending => {522 if !is_asserting(self) {523 bail!(InfiniteRecursionDetected);524 }525 }526 },527 Entry::Vacant(v) => {528 v.insert(CacheValue::Pending);529 }530 };531 }532 let result = self.get_idx_uncached(key, core);533 {534 let mut cache = self.0.value_cache.borrow_mut();535 cache.insert(cache_key, CacheValue::Cached(result.clone()));536 }537 result538 }539 fn get_idx_uncached(&self, key: IStr, core: CoreIdx) -> Result<Option<Val>> {540 self.run_assertions()?;541 let mut add_stack = Vec::with_capacity(2);542 for (sup, core) in self.0.cores[..core.idx].iter().enumerate().rev() {543 let sup_this = SupThis {544 sup: CoreIdx { idx: sup },545 this: self.clone(),546 };547 if let Some((val, proc)) = core.0.get_for(key.clone(), sup_this)? {548 match proc {549 ValueProcess::None if add_stack.is_empty() => return Ok(Some(val)),550 ValueProcess::None => {551 add_stack.push(val);552 break;553 }554 ValueProcess::SuperPlus => {555 add_stack.push(val);556 }557 }558 }559 }560 if add_stack.is_empty() {561 // None of layers had this field562 return Ok(None);563 } else if add_stack.len() == 1 {564 // A layer had this field, but it wanted this field to be added with super.565 // However, no super had this field, fail-safe566 return Ok(Some(add_stack.pop().expect("single element on stack")));567 }568 let mut values = add_stack.into_iter().rev();569 let init = values.next().expect("at least 2 elements");570571 values572 .try_fold(init, |a, b| evaluate_add_op(&a, &b))573 .map(Some)574575 // self.0.get_raw(key, this)576 }577578 pub fn get_or_bail(&self, key: IStr) -> Result<Val> {579 let Some(value) = self.get(key.clone())? else {580 let suggestions = suggest_object_fields(self, key.clone());581 bail!(NoSuchField(key, suggestions))582 };583 Ok(value)584 }585586 fn field_visibility(&self, field: IStr) -> Option<Visibility> {587 self.field_visibility_idx(588 field,589 CoreIdx {590 idx: self.0.cores.len(),591 },592 )593 }594 fn field_visibility_idx(&self, field: IStr, core: CoreIdx) -> Option<Visibility> {595 let mut exists = false;596 for ele in self.0.cores[..core.idx].iter().rev() {597 let vis = ele.0.field_visibility(field.clone());598 match vis {599 Some(Visibility::Unhide | Visibility::Hidden) => return vis,600 Some(Visibility::Normal) => exists = true,601 None => {}602 }603 }604 exists.then_some(Visibility::Normal)605 }606607 pub fn run_assertions(&self) -> Result<()> {608 if self.0.assertions_ran.get() {609 return Ok(());610 }611 if !start_asserting(self) {612 return Ok(());613 }614 for (idx, ele) in self.0.cores.iter().enumerate() {615 let sup_this = SupThis {616 sup: CoreIdx { idx },617 this: self.clone(),618 };619 ele.0.run_assertions_raw(sup_this).inspect_err(|_e| {620 finish_asserting(self);621 })?;622 }623 finish_asserting(self);624 self.0.assertions_ran.set(true);625 Ok(())626 }627628 pub fn iter(629 &self,630 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,631 ) -> impl Iterator<Item = (IStr, Result<Val>)> + '_ {632 let fields = self.fields(633 #[cfg(feature = "exp-preserve-order")]634 preserve_order,635 );636 fields.into_iter().map(|field| {637 (638 field.clone(),639 self.get(field)640 .map(|opt| opt.expect("iterating over keys, field exists")),641 )642 })643 }644 pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {645 if !self.has_field_ex(key.clone(), true) {646 return None;647 }648 let obj = self.clone();649650 Some(Thunk!(move || Ok(obj.get(key)?.expect("field exists"))))651 }652 pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {653 let obj = self.clone();654 Thunk!(move || obj.get_or_bail(key))655 }656 pub fn ptr_eq(a: &Self, b: &Self) -> bool {657 Cc::ptr_eq(&a.0, &b.0)658 }659 pub fn downgrade(self) -> WeakObjValue {660 WeakObjValue(self.0.downgrade())661 }662 fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {663 let mut out = FxHashMap::default();664 self.enum_fields(&mut |depth, index, name, visibility| {665 let new_sort_key = FieldSortKey::new(depth, index);666 let entry = out.entry(name);667 let (visible, _) = entry.or_insert((true, new_sort_key));668 match visibility {669 Visibility::Normal => {}670 Visibility::Hidden => {671 *visible = false;672 }673 Visibility::Unhide => {674 *visible = true;675 }676 };677 false678 });679 out680 }681 pub fn fields_ex(682 &self,683 include_hidden: bool,684 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,685 ) -> Vec<IStr> {686 #[cfg(feature = "exp-preserve-order")]687 if preserve_order {688 let (mut fields, mut keys): (Vec<_>, Vec<_>) = self689 .fields_visibility()690 .into_iter()691 .filter(|(_, (visible, _))| include_hidden || *visible)692 .enumerate()693 .map(|(idx, (k, (_, sk)))| (k, (sk, idx)))694 .unzip();695 keys.sort_unstable_by_key(|v| v.0);696 // Reorder in-place by resulting indexes697 for i in 0..fields.len() {698 let x = fields[i].clone();699 let mut j = i;700 loop {701 let k = keys[j].1;702 keys[j].1 = j;703 if k == i {704 break;705 }706 fields[j] = fields[k].clone();707 j = k;708 }709 fields[j] = x;710 }711 return fields;712 }713714 let mut fields: Vec<_> = self715 .fields_visibility()716 .into_iter()717 .filter(|(_, (visible, _))| include_hidden || *visible)718 .map(|(k, _)| k)719 .collect();720 fields.sort_unstable();721 fields722 }723 pub fn fields(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Vec<IStr> {724 self.fields_ex(725 false,726 #[cfg(feature = "exp-preserve-order")]727 preserve_order,728 )729 }730 pub fn values_ex(731 &self,732 include_hidden: bool,733 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,734 ) -> ArrValue {735 ArrValue::new(PickObjectValues::new(736 self.clone(),737 self.fields_ex(738 include_hidden,739 #[cfg(feature = "exp-preserve-order")]740 preserve_order,741 ),742 ))743 }744 pub fn values(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> ArrValue {745 self.values_ex(746 false,747 #[cfg(feature = "exp-preserve-order")]748 preserve_order,749 )750 }751 pub fn key_values_ex(752 &self,753 include_hidden: bool,754 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,755 ) -> ArrValue {756 ArrValue::new(PickObjectKeyValues::new(757 self.clone(),758 self.fields_ex(759 include_hidden,760 #[cfg(feature = "exp-preserve-order")]761 preserve_order,762 ),763 ))764 }765 pub fn key_values(766 &self,767 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,768 ) -> ArrValue {769 self.key_values_ex(770 false,771 #[cfg(feature = "exp-preserve-order")]772 preserve_order,773 )774 }775}776777impl OopObject {778 pub fn new(779 this_entries: Cc<FxHashMap<IStr, ObjMember>>,780 assertions: Cc<Vec<CcObjectAssertion>>,781 ) -> Self {782 Self {783 this_entries,784 value_cache: RefCell::new(FxHashMap::new()),785 assertions,786 }787 }788}789790impl ObjectCore for OopObject {791 fn enum_fields_core(792 &self,793 super_depth: &mut SuperDepth,794 handler: &mut EnumFieldsHandler<'_>,795 ) -> bool {796 for (name, member) in self.this_entries.iter() {797 if handler(798 *super_depth,799 member.original_index,800 name.clone(),801 member.flags.visibility(),802 ) {803 return false;804 }805 }806 true807 }808809 fn has_field_include_hidden(&self, name: IStr) -> bool {810 self.this_entries.contains_key(&name)811 }812813 fn get_for(&self, key: IStr, sup_this: SupThis) -> Result<Option<(Val, ValueProcess)>> {814 match self.this_entries.get(&key) {815 Some(k) => Ok(Some((816 k.invoke.evaluate(sup_this)?,817 if k.flags.add() {818 ValueProcess::SuperPlus819 } else {820 ValueProcess::None821 },822 ))),823 None => Ok(None),824 }825 }826 fn field_visibility(&self, name: IStr) -> Option<Visibility> {827 Some(self.this_entries.get(&name)?.flags.visibility())828 }829830 fn run_assertions_raw(&self, sup_this: SupThis) -> Result<()> {831 if self.assertions.is_empty() {832 return Ok(());833 }834 for assertion in self.assertions.iter() {835 assertion.0.run(sup_this.clone())?;836 }837 Ok(())838 }839}840841#[allow(clippy::module_name_repetitions)]842pub struct ObjValueBuilder {843 sup: Option<ObjValue>,844 map: FxHashMap<IStr, ObjMember>,845 assertions: Vec<CcObjectAssertion>,846 next_field_index: FieldIndex,847}848impl ObjValueBuilder {849 pub fn new() -> Self {850 Self::with_capacity(0)851 }852 pub fn with_capacity(capacity: usize) -> Self {853 Self {854 sup: None,855 map: FxHashMap::with_capacity(capacity),856 assertions: Vec::new(),857 next_field_index: FieldIndex::default(),858 }859 }860 pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self {861 self.assertions.reserve_exact(capacity);862 self863 }864 pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {865 self.sup = Some(super_obj);866 self867 }868869 pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {870 self.assertions.push(CcObjectAssertion::new(assertion));871 self872 }873 pub fn field(&mut self, name: impl Into<IStr>) -> ObjMemberBuilder<ValueBuilder<'_>> {874 let field_index = self.next_field_index;875 self.next_field_index = self.next_field_index.next();876 ObjMemberBuilder::new(ValueBuilder(self), name.into(), field_index)877 }878 /// Preset for common method definiton pattern:879 /// Create a hidden field with the function value.880 ///881 /// `.field(name).hide().value(Val::function(value))`882 pub fn method(&mut self, name: impl Into<IStr>, value: impl Into<FuncVal>) -> &mut Self {883 self.field(name).hide().value(Val::Func(value.into()));884 self885 }886 pub fn try_method(887 &mut self,888 name: impl Into<IStr>,889 value: impl Into<FuncVal>,890 ) -> Result<&mut Self> {891 self.field(name).hide().try_value(Val::Func(value.into()))?;892 Ok(self)893 }894895 pub fn build(self) -> ObjValue {896 if self.sup.is_none() && self.map.is_empty() && self.assertions.is_empty() {897 return ObjValue::new_empty();898 }899 let res = ObjValue::new(OopObject::new(Cc::new(self.map), Cc::new(self.assertions)));900 self.sup.map(|sup| res.extend_from(sup)).unwrap_or(res)901 }902}903impl Default for ObjValueBuilder {904 fn default() -> Self {905 Self::with_capacity(0)906 }907}908909#[allow(clippy::module_name_repetitions)]910#[must_use = "value not added unless binding() was called"]911pub struct ObjMemberBuilder<Kind> {912 kind: Kind,913 name: IStr,914 add: bool,915 visibility: Visibility,916 original_index: FieldIndex,917 location: Option<Span>,918}919920#[allow(clippy::missing_const_for_fn)]921impl<Kind> ObjMemberBuilder<Kind> {922 pub(crate) fn new(kind: Kind, name: IStr, original_index: FieldIndex) -> Self {923 Self {924 kind,925 name,926 original_index,927 add: false,928 visibility: Visibility::Normal,929 location: None,930 }931 }932933 pub const fn with_add(mut self, add: bool) -> Self {934 self.add = add;935 self936 }937 pub fn add(self) -> Self {938 self.with_add(true)939 }940 pub fn with_visibility(mut self, visibility: Visibility) -> Self {941 self.visibility = visibility;942 self943 }944 pub fn hide(self) -> Self {945 self.with_visibility(Visibility::Hidden)946 }947 pub fn with_location(mut self, location: Span) -> Self {948 self.location = Some(location);949 self950 }951 fn build_member(self, binding: MaybeUnbound) -> (Kind, IStr, ObjMember) {952 (953 self.kind,954 self.name,955 ObjMember {956 flags: ObjFieldFlags::new(self.add, self.visibility),957 original_index: self.original_index,958 invoke: binding,959 location: self.location,960 },961 )962 }963}964965pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);966impl ObjMemberBuilder<ValueBuilder<'_>> {967 /// Inserts value, replacing if it is already defined968 pub fn value(self, value: impl Into<Val>) {969 let (receiver, name, member) =970 self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value.into())));971 let entry = receiver.0.map.entry(name);972 entry.insert_entry(member);973 }974975 /// Tries to insert value, returns an error if it was already defined976 pub fn try_value(self, value: impl Into<Val>) -> Result<()> {977 self.try_thunk(Thunk::evaluated(value.into()))978 }979 pub fn try_thunk(self, value: impl Into<Thunk<Val>>) -> Result<()> {980 self.binding(MaybeUnbound::Bound(value.into()))981 }982 pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {983 self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)))984 }985 pub fn binding(self, binding: MaybeUnbound) -> Result<()> {986 let (receiver, name, member) = self.build_member(binding);987 let location = member.location.clone();988 let old = receiver.0.map.insert(name.clone(), member);989 if old.is_some() {990 in_frame(991 CallLocation(location.as_ref()),992 || format!("field <{}> initializtion", name.clone()),993 || bail!(DuplicateFieldName(name.clone())),994 )?;995 }996 Ok(())997 }998}9991000pub struct ExtendBuilder<'v>(&'v mut ObjValue);1001impl ObjMemberBuilder<ExtendBuilder<'_>> {1002 pub fn value(self, value: impl Into<Val>) {1003 self.binding(MaybeUnbound::Bound(Thunk::evaluated(value.into())));1004 }1005 pub fn bindable(self, bindable: impl Unbound<Bound = Val>) {1006 self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)));1007 }1008 pub fn binding(self, binding: MaybeUnbound) {1009 let (receiver, name, member) = self.build_member(binding);1010 let new = receiver.0.clone();1011 *receiver.0 = new.extend_with_raw_member(name, member);1012 }1013}crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -414,7 +414,7 @@
self.0
}
pub(crate) fn truncate_for_bitwise(&self) -> Result<i64> {
- if self.0 < MIN_SAFE_INTEGER || self.0 > dbg!(MAX_SAFE_INTEGER) {
+ if self.0 < MIN_SAFE_INTEGER || self.0 > MAX_SAFE_INTEGER {
bail!("numberic value outside of safe integer range for bitwise operation");
}
Ok(self.0 as i64)