difftreelog
perf move std.object[Keys]Values[All] to native
in: master
6 files changed
crates/jrsonnet-evaluator/src/arr/spec.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/arr/spec.rs
+++ b/crates/jrsonnet-evaluator/src/arr/spec.rs
@@ -9,8 +9,9 @@
error::ErrorKind::InfiniteRecursionDetected,
evaluate,
function::FuncVal,
+ typed::Typed,
val::{StrValue, ThunkValue},
- Context, Error, Result, Thunk, Val,
+ Context, Error, ObjValue, Result, Thunk, Val,
};
pub trait ArrayLike: Any + Trace + Debug {
@@ -576,3 +577,103 @@
self.data.is_cheap()
}
}
+
+#[derive(Trace, Debug)]
+pub struct PickObjectValues {
+ obj: ObjValue,
+ keys: Vec<IStr>,
+}
+
+impl PickObjectValues {
+ pub fn new(obj: ObjValue, keys: Vec<IStr>) -> Self {
+ Self { obj, keys }
+ }
+}
+
+impl ArrayLike for PickObjectValues {
+ fn len(&self) -> usize {
+ self.keys.len()
+ }
+
+ fn get(&self, index: usize) -> Result<Option<Val>> {
+ let Some(key) = self.keys.get(index) else {
+ return Ok(None);
+ };
+ Ok(Some(self.obj.get_or_bail(key.clone())?))
+ }
+
+ fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
+ let Some(key) = self.keys.get(index) else {
+ return None;
+ };
+ Some(self.obj.get_lazy_or_bail(key.clone()))
+ }
+
+ fn get_cheap(&self, _index: usize) -> Option<Val> {
+ None
+ }
+
+ fn is_cheap(&self) -> bool {
+ false
+ }
+}
+
+#[derive(Trace, Debug)]
+pub struct PickObjectKeyValues {
+ obj: ObjValue,
+ keys: Vec<IStr>,
+}
+
+impl PickObjectKeyValues {
+ pub fn new(obj: ObjValue, keys: Vec<IStr>) -> Self {
+ Self { obj, keys }
+ }
+}
+
+#[derive(Typed)]
+pub struct KeyValue {
+ key: IStr,
+ value: Thunk<Val>,
+}
+
+impl ArrayLike for PickObjectKeyValues {
+ fn len(&self) -> usize {
+ self.keys.len()
+ }
+
+ fn get(&self, index: usize) -> Result<Option<Val>> {
+ let Some(key) = self.keys.get(index) else {
+ return Ok(None);
+ };
+ Ok(Some(
+ KeyValue::into_untyped(KeyValue {
+ key: key.clone(),
+ value: Thunk::evaluated(self.obj.get_or_bail(key.clone())?),
+ })
+ .expect("convertible"),
+ ))
+ }
+
+ fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
+ let Some(key) = self.keys.get(index) else {
+ return None;
+ };
+ // Nothing can fail in the key part, yet value is still
+ // lazy-evaluated
+ Some(Thunk::evaluated(
+ KeyValue::into_untyped(KeyValue {
+ key: key.clone(),
+ value: self.obj.get_lazy_or_bail(key.clone()),
+ })
+ .expect("convertible"),
+ ))
+ }
+
+ fn get_cheap(&self, _index: usize) -> Option<Val> {
+ None
+ }
+
+ fn is_cheap(&self) -> bool {
+ false
+ }
+}
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 error::{Error, ErrorKind::*},16 function::CallLocation,17 gc::{GcHashMap, GcHashSet, TraceBox},18 operator::evaluate_add_op,19 tb, throw,20 val::ThunkValue,21 MaybeUnbound, Result, State, 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 const fn deeper(self) -> Self {45 Self(())46 }47 }4849 #[derive(Clone, Copy)]50 pub struct FieldSortKey(());51 impl FieldSortKey {52 pub const fn new(_: SuperDepth, _: FieldIndex) -> Self {53 Self(())54 }55 }56}5758#[cfg(feature = "exp-preserve-order")]59mod ordering {60 use std::cmp::{Ordering, Reverse};6162 use jrsonnet_gcmodule::Trace;6364 #[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]65 pub struct FieldIndex(u32);66 impl FieldIndex {67 pub fn next(self) -> Self {68 Self(self.0 + 1)69 }70 }7172 #[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Debug)]73 pub struct SuperDepth(u32);74 impl SuperDepth {75 pub fn deeper(self) -> Self {76 Self(self.0 + 1)77 }78 }7980 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]81 pub struct FieldSortKey(Reverse<SuperDepth>, FieldIndex);82 impl FieldSortKey {83 pub fn new(depth: SuperDepth, index: FieldIndex) -> Self {84 Self(Reverse(depth), index)85 }86 pub fn collide(self, other: Self) -> Self {87 match self.0 .0.cmp(&other.0 .0) {88 Ordering::Greater => self,89 Ordering::Less => other,90 Ordering::Equal => unreachable!("object can't have two fields with the same name"),91 }92 }93 }94}9596use ordering::*;9798// 0 - add99// 12 - visibility100#[derive(Clone, Copy)]101pub struct ObjFieldFlags(u8);102impl ObjFieldFlags {103 fn new(add: bool, visibility: Visibility) -> Self {104 let mut v = 0;105 if add {106 v |= 1;107 }108 v |= match visibility {109 Visibility::Normal => 0b000,110 Visibility::Hidden => 0b010,111 Visibility::Unhide => 0b100,112 };113 Self(v)114 }115 pub fn add(&self) -> bool {116 self.0 & 1 != 0117 }118 pub fn visibility(&self) -> Visibility {119 match (self.0 & 0b110) >> 1 {120 0b00 => Visibility::Normal,121 0b01 => Visibility::Hidden,122 0b10 => Visibility::Unhide,123 _ => unreachable!(),124 }125 }126}127impl Debug for ObjFieldFlags {128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {129 f.debug_struct("ObjFieldFlags")130 .field("add", &self.add())131 .field("visibility", &self.visibility())132 .finish()133 }134}135136#[allow(clippy::module_name_repetitions)]137#[derive(Debug, Trace)]138pub struct ObjMember {139 #[trace(skip)]140 flags: ObjFieldFlags,141 original_index: FieldIndex,142 pub invoke: MaybeUnbound,143 pub location: Option<ExprLocation>,144}145146pub trait ObjectAssertion: Trace {147 fn run(&self, super_obj: Option<ObjValue>, this: Option<ObjValue>) -> Result<()>;148}149150// Field => This151152#[derive(Trace)]153enum CacheValue {154 Cached(Val),155 NotFound,156 Pending,157 Errored(Error),158}159160#[allow(clippy::module_name_repetitions)]161#[derive(Trace)]162#[trace(tracking(force))]163pub struct OopObject {164 sup: Option<ObjValue>,165 // this: Option<ObjValue>,166 assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,167 assertions_ran: RefCell<GcHashSet<ObjValue>>,168 this_entries: Cc<GcHashMap<IStr, ObjMember>>,169 value_cache: RefCell<GcHashMap<(IStr, Option<WeakObjValue>), CacheValue>>,170}171impl Debug for OopObject {172 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {173 f.debug_struct("OopObject")174 .field("sup", &self.sup)175 // .field("assertions", &self.assertions)176 // .field("assertions_ran", &self.assertions_ran)177 .field("this_entries", &self.this_entries)178 // .field("value_cache", &self.value_cache)179 .finish()180 }181}182183type EnumFieldsHandler<'a> = dyn FnMut(SuperDepth, FieldIndex, IStr, Visibility) -> bool + 'a;184185pub trait ObjectLike: Trace + Any + Debug {186 fn extend_from(&self, sup: ObjValue) -> ObjValue;187 /// When using standalone super in object, `this.super_obj.with_this(this)` is executed188 fn with_this(&self, me: ObjValue, this: ObjValue) -> ObjValue {189 ObjValue::new(ThisOverride { inner: me, this })190 }191 fn this(&self) -> Option<ObjValue> {192 None193 }194 fn len(&self) -> usize;195 fn is_empty(&self) -> bool;196 // If callback returns false, iteration stops197 fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool;198199 fn has_field_include_hidden(&self, name: IStr) -> bool;200 fn has_field(&self, name: IStr) -> bool;201202 fn get_for(&self, key: IStr, this: ObjValue) -> Result<Option<Val>>;203 fn get_for_uncached(&self, key: IStr, this: ObjValue) -> Result<Option<Val>>;204 fn field_visibility(&self, field: IStr) -> Option<Visibility>;205206 fn run_assertions_raw(&self, this: ObjValue) -> Result<()>;207}208209#[derive(Clone, Trace)]210pub struct WeakObjValue(#[trace(skip)] pub(crate) Weak<TraceBox<dyn ObjectLike>>);211212impl PartialEq for WeakObjValue {213 fn eq(&self, other: &Self) -> bool {214 Weak::ptr_eq(&self.0, &other.0)215 }216}217218impl Eq for WeakObjValue {}219impl Hash for WeakObjValue {220 fn hash<H: Hasher>(&self, hasher: &mut H) {221 // Safety: usize is POD222 let addr = unsafe { *std::ptr::addr_of!(self.0).cast() };223 hasher.write_usize(addr);224 }225}226227#[allow(clippy::module_name_repetitions)]228#[derive(Clone, Trace, Debug)]229pub struct ObjValue(pub(crate) Cc<TraceBox<dyn ObjectLike>>);230231#[derive(Debug, Trace)]232struct EmptyObject;233impl ObjectLike for EmptyObject {234 fn extend_from(&self, sup: ObjValue) -> ObjValue {235 // obj + {} == obj236 sup237 }238239 fn this(&self) -> Option<ObjValue> {240 None241 }242243 fn len(&self) -> usize {244 0245 }246247 fn is_empty(&self) -> bool {248 true249 }250251 fn enum_fields(&self, _depth: SuperDepth, _handler: &mut EnumFieldsHandler<'_>) -> bool {252 false253 }254255 fn has_field_include_hidden(&self, _name: IStr) -> bool {256 false257 }258259 fn has_field(&self, _name: IStr) -> bool {260 false261 }262263 fn get_for(&self, _key: IStr, _this: ObjValue) -> Result<Option<Val>> {264 Ok(None)265 }266 fn get_for_uncached(&self, _key: IStr, _this: ObjValue) -> Result<Option<Val>> {267 Ok(None)268 }269270 fn run_assertions_raw(&self, _this: ObjValue) -> Result<()> {271 Ok(())272 }273274 fn field_visibility(&self, _field: IStr) -> Option<Visibility> {275 None276 }277}278279#[derive(Trace, Debug)]280struct ThisOverride {281 inner: ObjValue,282 this: ObjValue,283}284impl ObjectLike for ThisOverride {285 fn with_this(&self, _me: ObjValue, this: ObjValue) -> ObjValue {286 ObjValue::new(ThisOverride {287 inner: self.inner.clone(),288 this,289 })290 }291292 fn extend_from(&self, sup: ObjValue) -> ObjValue {293 self.inner.extend_from(sup).with_this(self.this.clone())294 }295296 fn this(&self) -> Option<ObjValue> {297 Some(self.this.clone())298 }299300 fn len(&self) -> usize {301 self.inner.len()302 }303304 fn is_empty(&self) -> bool {305 self.inner.is_empty()306 }307308 fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool {309 self.inner.enum_fields(depth, handler)310 }311312 fn has_field_include_hidden(&self, name: IStr) -> bool {313 self.inner.has_field_include_hidden(name)314 }315316 fn has_field(&self, name: IStr) -> bool {317 self.inner.has_field(name)318 }319320 fn get_for(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {321 self.inner.get_for(key, this)322 }323324 fn get_for_uncached(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {325 self.inner.get_raw(key, this)326 }327328 fn field_visibility(&self, field: IStr) -> Option<Visibility> {329 self.inner.field_visibility(field)330 }331332 fn run_assertions_raw(&self, this: ObjValue) -> Result<()> {333 self.inner.run_assertions_raw(this)334 }335}336337impl ObjValue {338 pub fn new(v: impl ObjectLike) -> Self {339 Self(Cc::new(tb!(v)))340 }341 pub fn new_empty() -> Self {342 Self::new(EmptyObject)343 }344 pub fn builder() -> ObjValueBuilder {345 ObjValueBuilder::new()346 }347 pub fn builder_with_capacity(capacity: usize) -> ObjValueBuilder {348 ObjValueBuilder::with_capacity(capacity)349 }350 pub(crate) fn extend_with_raw_member(self, key: IStr, value: ObjMember) -> Self {351 // let mut new = GcHashMap::with_capacity(1);352 // new.insert(key, value);353 // Self::new(Some(self), Cc::new(new), Cc::new(Vec::new()))354 todo!()355 }356 pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder<'_>> {357 ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())358 }359360 #[must_use]361 pub fn extend_from(&self, sup: Self) -> Self {362 self.0.extend_from(sup)363 }364 #[must_use]365 pub fn with_this(&self, this: Self) -> Self {366 self.0.with_this(self.clone(), this)367 }368 pub fn len(&self) -> usize {369 self.0.len()370 }371 pub fn is_empty(&self) -> bool {372 self.0.is_empty()373 }374 pub fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool {375 self.0.enum_fields(depth, handler)376 }377378 pub fn has_field_include_hidden(&self, name: IStr) -> bool {379 self.0.has_field_include_hidden(name)380 }381 pub fn has_field(&self, name: IStr) -> bool {382 self.0.has_field(name)383 }384 pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {385 if include_hidden {386 self.has_field_include_hidden(name)387 } else {388 self.has_field(name)389 }390 }391392 pub fn get(&self, key: IStr) -> Result<Option<Val>> {393 self.run_assertions()?;394 self.get_for(key, self.0.this().unwrap_or(self.clone()))395 }396397 pub fn get_for(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {398 self.0.get_for(key, this)399 }400401 fn get_raw(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {402 self.0.get_for_uncached(key, this)403 }404405 fn field_visibility(&self, field: IStr) -> Option<Visibility> {406 self.0.field_visibility(field)407 }408409 pub fn run_assertions(&self) -> Result<()> {410 // FIXME: Should it use `self.0.this()` in case of standalone super?411 self.run_assertions_raw(self.clone())412 }413 fn run_assertions_raw(&self, this: ObjValue) -> Result<()> {414 self.0.run_assertions_raw(this)415 }416417 pub fn iter(418 &self,419 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,420 ) -> impl Iterator<Item = (IStr, Result<Val>)> + '_ {421 let fields = self.fields(422 #[cfg(feature = "exp-preserve-order")]423 preserve_order,424 );425 fields.into_iter().map(|field| {426 (427 field.clone(),428 self.get(field)429 .map(|opt| opt.expect("iterating over keys, field exists")),430 )431 })432 }433 pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {434 #[derive(Trace)]435 struct ThunkGet {436 obj: ObjValue,437 key: IStr,438 }439 impl ThunkValue for ThunkGet {440 type Output = Val;441442 fn get(self: Box<Self>) -> Result<Self::Output> {443 Ok(self.obj.get(self.key)?.expect("field exists"))444 }445 }446447 if !self.has_field_ex(key.clone(), true) {448 return None;449 }450 Some(Thunk::new(ThunkGet {451 obj: self.clone(),452 key,453 }))454 }455 pub fn ptr_eq(a: &Self, b: &Self) -> bool {456 Cc::ptr_eq(&a.0, &b.0)457 }458 pub fn downgrade(self) -> WeakObjValue {459 WeakObjValue(self.0.downgrade())460 }461 fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {462 let mut out = FxHashMap::default();463 self.enum_fields(464 SuperDepth::default(),465 &mut |depth, index, name, visibility| {466 let new_sort_key = FieldSortKey::new(depth, index);467 let entry = out.entry(name.clone());468 let (visible, _) = entry.or_insert((true, new_sort_key));469 match visibility {470 Visibility::Normal => {}471 Visibility::Hidden => {472 *visible = false;473 }474 Visibility::Unhide => {475 *visible = true;476 }477 };478 false479 },480 );481 out482 }483 pub fn fields_ex(484 &self,485 include_hidden: bool,486 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,487 ) -> Vec<IStr> {488 #[cfg(feature = "exp-preserve-order")]489 if preserve_order {490 let (mut fields, mut keys): (Vec<_>, Vec<_>) = self491 .fields_visibility()492 .into_iter()493 .filter(|(_, (visible, _))| include_hidden || *visible)494 .enumerate()495 .map(|(idx, (k, (_, sk)))| (k, (sk, idx)))496 .unzip();497 keys.sort_unstable_by_key(|v| v.0);498 // Reorder in-place by resulting indexes499 for i in 0..fields.len() {500 let x = fields[i].clone();501 let mut j = i;502 loop {503 let k = keys[j].1;504 keys[j].1 = j;505 if k == i {506 break;507 }508 fields[j] = fields[k].clone();509 j = k;510 }511 fields[j] = x;512 }513 return fields;514 }515516 let mut fields: Vec<_> = self517 .fields_visibility()518 .into_iter()519 .filter(|(_, (visible, _))| include_hidden || *visible)520 .map(|(k, _)| k)521 .collect();522 fields.sort_unstable();523 fields524 }525 pub fn fields(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Vec<IStr> {526 self.fields_ex(527 false,528 #[cfg(feature = "exp-preserve-order")]529 preserve_order,530 )531 }532}533534impl OopObject {535 pub fn new(536 sup: Option<ObjValue>,537 this_entries: Cc<GcHashMap<IStr, ObjMember>>,538 assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,539 ) -> Self {540 Self {541 sup,542 // this: None,543 assertions,544 assertions_ran: RefCell::new(GcHashSet::new()),545 this_entries,546 value_cache: RefCell::new(GcHashMap::new()),547 }548 }549550 fn evaluate_this(&self, v: &ObjMember, real_this: ObjValue) -> Result<Val> {551 v.invoke.evaluate(self.sup.clone(), Some(real_this))552 }553554 // FIXME: Duplication between ObjValue and OopObject555 fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {556 let mut out = FxHashMap::default();557 self.enum_fields(558 SuperDepth::default(),559 &mut |depth, index, name, visibility| {560 let new_sort_key = FieldSortKey::new(depth, index);561 let entry = out.entry(name.clone());562 let (visible, _) = entry.or_insert((true, new_sort_key));563 match visibility {564 Visibility::Normal => {}565 Visibility::Hidden => {566 *visible = false;567 }568 Visibility::Unhide => {569 *visible = true;570 }571 };572 false573 },574 );575 out576 }577}578579impl ObjectLike for OopObject {580 fn extend_from(&self, sup: ObjValue) -> ObjValue {581 ObjValue::new(match &self.sup {582 None => Self::new(583 Some(sup),584 self.this_entries.clone(),585 self.assertions.clone(),586 ),587 Some(v) => Self::new(588 Some(v.extend_from(sup)),589 self.this_entries.clone(),590 self.assertions.clone(),591 ),592 })593 }594595 fn len(&self) -> usize {596 self.fields_visibility()597 .into_iter()598 .filter(|(_, (visible, _))| *visible)599 .count()600 }601602 fn is_empty(&self) -> bool {603 if !self.this_entries.is_empty() {604 return false;605 }606 self.sup.as_ref().map_or(true, ObjValue::is_empty)607 }608609 /// Run callback for every field found in object610 ///611 /// Returns true if ended prematurely612 fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool {613 if let Some(s) = &self.sup {614 if s.enum_fields(depth.deeper(), handler) {615 return true;616 }617 }618 for (name, member) in self.this_entries.iter() {619 if handler(620 depth,621 member.original_index,622 name.clone(),623 member.flags.visibility(),624 ) {625 return true;626 }627 }628 false629 }630631 fn has_field_include_hidden(&self, name: IStr) -> bool {632 if self.this_entries.contains_key(&name) {633 true634 } else if let Some(super_obj) = &self.sup {635 super_obj.has_field_include_hidden(name)636 } else {637 false638 }639 }640 fn has_field(&self, name: IStr) -> bool {641 self.field_visibility(name)642 .map_or(false, |v| v.is_visible())643 }644645 fn get_for(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {646 let cache_key = (key.clone(), Some(this.clone().downgrade()));647 if let Some(v) = self.value_cache.borrow().get(&cache_key) {648 return Ok(match v {649 CacheValue::Cached(v) => Some(v.clone()),650 CacheValue::NotFound => None,651 CacheValue::Pending => throw!(InfiniteRecursionDetected),652 CacheValue::Errored(e) => return Err(e.clone()),653 });654 }655 self.value_cache656 .borrow_mut()657 .insert(cache_key.clone(), CacheValue::Pending);658 let value = self.get_for_uncached(key, this).map_err(|e| {659 self.value_cache660 .borrow_mut()661 .insert(cache_key.clone(), CacheValue::Errored(e.clone()));662 e663 })?;664 self.value_cache.borrow_mut().insert(665 cache_key,666 value667 .as_ref()668 .map_or(CacheValue::NotFound, |v| CacheValue::Cached(v.clone())),669 );670 Ok(value)671 }672 fn get_for_uncached(&self, key: IStr, real_this: ObjValue) -> Result<Option<Val>> {673 match (self.this_entries.get(&key), &self.sup) {674 (Some(k), None) => Ok(Some(self.evaluate_this(k, real_this)?)),675 (Some(k), Some(super_obj)) => {676 let our = self.evaluate_this(k, real_this.clone())?;677 if k.flags.add() {678 super_obj679 .get_raw(key, real_this)?680 .map_or(Ok(Some(our.clone())), |v| {681 Ok(Some(evaluate_add_op(&v, &our)?))682 })683 } else {684 Ok(Some(our))685 }686 }687 (None, Some(super_obj)) => super_obj.get_raw(key, real_this),688 (None, None) => Ok(None),689 }690 }691 fn field_visibility(&self, name: IStr) -> Option<Visibility> {692 if let Some(m) = self.this_entries.get(&name) {693 Some(match &m.flags.visibility() {694 Visibility::Normal => self695 .sup696 .as_ref()697 .and_then(|super_obj| super_obj.field_visibility(name))698 .unwrap_or(Visibility::Normal),699 v => *v,700 })701 } else if let Some(super_obj) = &self.sup {702 super_obj.field_visibility(name)703 } else {704 None705 }706 }707708 fn run_assertions_raw(&self, real_this: ObjValue) -> Result<()> {709 if self.assertions.is_empty() {710 if let Some(super_obj) = &self.sup {711 super_obj.run_assertions_raw(real_this)?;712 }713 return Ok(());714 }715 if self.assertions_ran.borrow_mut().insert(real_this.clone()) {716 for assertion in self.assertions.iter() {717 if let Err(e) = assertion.run(self.sup.clone(), Some(real_this.clone())) {718 self.assertions_ran.borrow_mut().remove(&real_this);719 return Err(e);720 }721 }722 if let Some(super_obj) = &self.sup {723 super_obj.run_assertions_raw(real_this)?;724 }725 }726 Ok(())727 }728}729730impl PartialEq for ObjValue {731 fn eq(&self, other: &Self) -> bool {732 Cc::ptr_eq(&self.0, &other.0)733 }734}735736impl Eq for ObjValue {}737impl Hash for ObjValue {738 fn hash<H: Hasher>(&self, hasher: &mut H) {739 hasher.write_usize(addr_of!(*self.0) as usize);740 }741}742743#[allow(clippy::module_name_repetitions)]744pub struct ObjValueBuilder {745 sup: Option<ObjValue>,746 map: GcHashMap<IStr, ObjMember>,747 assertions: Vec<TraceBox<dyn ObjectAssertion>>,748 next_field_index: FieldIndex,749}750impl ObjValueBuilder {751 pub fn new() -> Self {752 Self::with_capacity(0)753 }754 pub fn with_capacity(capacity: usize) -> Self {755 Self {756 sup: None,757 map: GcHashMap::with_capacity(capacity),758 assertions: Vec::new(),759 next_field_index: FieldIndex::default(),760 }761 }762 pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self {763 self.assertions.reserve_exact(capacity);764 self765 }766 pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {767 self.sup = Some(super_obj);768 self769 }770771 pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {772 self.assertions.push(tb!(assertion));773 self774 }775 pub fn member(&mut self, name: IStr) -> ObjMemberBuilder<ValueBuilder<'_>> {776 let field_index = self.next_field_index;777 self.next_field_index = self.next_field_index.next();778 ObjMemberBuilder::new(ValueBuilder(self), name, field_index)779 }780781 pub fn build(self) -> ObjValue {782 if self.sup.is_none() && self.map.is_empty() && self.assertions.is_empty() {783 return ObjValue::new_empty();784 }785 ObjValue::new(OopObject::new(786 self.sup,787 Cc::new(self.map),788 Cc::new(self.assertions),789 ))790 }791}792impl Default for ObjValueBuilder {793 fn default() -> Self {794 Self::with_capacity(0)795 }796}797798#[allow(clippy::module_name_repetitions)]799#[must_use = "value not added unless binding() was called"]800pub struct ObjMemberBuilder<Kind> {801 kind: Kind,802 name: IStr,803 add: bool,804 visibility: Visibility,805 original_index: FieldIndex,806 location: Option<ExprLocation>,807}808809#[allow(clippy::missing_const_for_fn)]810impl<Kind> ObjMemberBuilder<Kind> {811 pub(crate) fn new(kind: Kind, name: IStr, original_index: FieldIndex) -> Self {812 Self {813 kind,814 name,815 original_index,816 add: false,817 visibility: Visibility::Normal,818 location: None,819 }820 }821822 pub const fn with_add(mut self, add: bool) -> Self {823 self.add = add;824 self825 }826 pub fn add(self) -> Self {827 self.with_add(true)828 }829 pub fn with_visibility(mut self, visibility: Visibility) -> Self {830 self.visibility = visibility;831 self832 }833 pub fn hide(self) -> Self {834 self.with_visibility(Visibility::Hidden)835 }836 pub fn with_location(mut self, location: ExprLocation) -> Self {837 self.location = Some(location);838 self839 }840 fn build_member(self, binding: MaybeUnbound) -> (Kind, IStr, ObjMember) {841 (842 self.kind,843 self.name,844 ObjMember {845 flags: ObjFieldFlags::new(self.add, self.visibility),846 original_index: self.original_index,847 invoke: binding,848 location: self.location,849 },850 )851 }852}853854pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);855impl ObjMemberBuilder<ValueBuilder<'_>> {856 /// Inserts value, replacing if it is already defined857 pub fn value_unchecked(self, value: Val) {858 let (receiver, name, member) =859 self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value)));860 let entry = receiver.0.map.entry(name);861 entry.insert(member);862 }863864 pub fn value(self, value: Val) -> Result<()> {865 self.thunk(Thunk::evaluated(value))866 }867 pub fn thunk(self, value: Thunk<Val>) -> Result<()> {868 self.binding(MaybeUnbound::Bound(value))869 }870 pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {871 self.binding(MaybeUnbound::Unbound(Cc::new(tb!(bindable))))872 }873 pub fn binding(self, binding: MaybeUnbound) -> Result<()> {874 let (receiver, name, member) = self.build_member(binding);875 let location = member.location.clone();876 let old = receiver.0.map.insert(name.clone(), member);877 if old.is_some() {878 State::push(879 CallLocation(location.as_ref()),880 || format!("field <{}> initializtion", name.clone()),881 || throw!(DuplicateFieldName(name.clone())),882 )?;883 }884 Ok(())885 }886}887888pub struct ExtendBuilder<'v>(&'v mut ObjValue);889impl ObjMemberBuilder<ExtendBuilder<'_>> {890 pub fn value(self, value: Val) {891 self.binding(MaybeUnbound::Bound(Thunk::evaluated(value)));892 }893 pub fn bindable(self, bindable: TraceBox<dyn Unbound<Bound = Val>>) {894 self.binding(MaybeUnbound::Unbound(Cc::new(bindable)));895 }896 pub fn binding(self, binding: MaybeUnbound) {897 let (receiver, name, member) = self.build_member(binding);898 let new = receiver.0.clone();899 *receiver.0 = new.extend_with_raw_member(name, member);900 }901}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::{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 new = GcHashMap::with_capacity(1);353 // new.insert(key, value);354 // Self::new(Some(self), Cc::new(new), Cc::new(Vec::new()))355 todo!()356 }357 pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder<'_>> {358 ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())359 }360361 #[must_use]362 pub fn extend_from(&self, sup: Self) -> Self {363 self.0.extend_from(sup)364 }365 #[must_use]366 pub fn with_this(&self, this: Self) -> Self {367 self.0.with_this(self.clone(), this)368 }369 pub fn len(&self) -> usize {370 self.0.len()371 }372 pub fn is_empty(&self) -> bool {373 self.0.is_empty()374 }375 pub fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool {376 self.0.enum_fields(depth, handler)377 }378379 pub fn has_field_include_hidden(&self, name: IStr) -> bool {380 self.0.has_field_include_hidden(name)381 }382 pub fn has_field(&self, name: IStr) -> bool {383 self.0.has_field(name)384 }385 pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {386 if include_hidden {387 self.has_field_include_hidden(name)388 } else {389 self.has_field(name)390 }391 }392393 pub fn get(&self, key: IStr) -> Result<Option<Val>> {394 self.run_assertions()?;395 self.get_for(key, self.0.this().unwrap_or(self.clone()))396 }397398 pub fn get_for(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {399 self.0.get_for(key, this)400 }401402 pub fn get_or_bail(&self, key: IStr) -> Result<Val> {403 let Some(value) = self.get(key.clone())? else {404 let suggestions = suggest_object_fields(self, key.clone());405 throw!(NoSuchField(key, suggestions))406 };407 Ok(value)408 }409410 fn get_raw(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {411 self.0.get_for_uncached(key, this)412 }413414 fn field_visibility(&self, field: IStr) -> Option<Visibility> {415 self.0.field_visibility(field)416 }417418 pub fn run_assertions(&self) -> Result<()> {419 // FIXME: Should it use `self.0.this()` in case of standalone super?420 self.run_assertions_raw(self.clone())421 }422 fn run_assertions_raw(&self, this: ObjValue) -> Result<()> {423 self.0.run_assertions_raw(this)424 }425426 pub fn iter(427 &self,428 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,429 ) -> impl Iterator<Item = (IStr, Result<Val>)> + '_ {430 let fields = self.fields(431 #[cfg(feature = "exp-preserve-order")]432 preserve_order,433 );434 fields.into_iter().map(|field| {435 (436 field.clone(),437 self.get(field)438 .map(|opt| opt.expect("iterating over keys, field exists")),439 )440 })441 }442 pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {443 #[derive(Trace)]444 struct ThunkGet {445 obj: ObjValue,446 key: IStr,447 }448 impl ThunkValue for ThunkGet {449 type Output = Val;450451 fn get(self: Box<Self>) -> Result<Self::Output> {452 Ok(self.obj.get(self.key)?.expect("field exists"))453 }454 }455456 if !self.has_field_ex(key.clone(), true) {457 return None;458 }459 Some(Thunk::new(ThunkGet {460 obj: self.clone(),461 key,462 }))463 }464 pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {465 #[derive(Trace)]466 struct ThunkGet {467 obj: ObjValue,468 key: IStr,469 }470 impl ThunkValue for ThunkGet {471 type Output = Val;472473 fn get(self: Box<Self>) -> Result<Self::Output> {474 Ok(self.obj.get_or_bail(self.key)?)475 }476 }477478 Thunk::new(ThunkGet {479 obj: self.clone(),480 key,481 })482 }483 pub fn ptr_eq(a: &Self, b: &Self) -> bool {484 Cc::ptr_eq(&a.0, &b.0)485 }486 pub fn downgrade(self) -> WeakObjValue {487 WeakObjValue(self.0.downgrade())488 }489 fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {490 let mut out = FxHashMap::default();491 self.enum_fields(492 SuperDepth::default(),493 &mut |depth, index, name, visibility| {494 let new_sort_key = FieldSortKey::new(depth, index);495 let entry = out.entry(name.clone());496 let (visible, _) = entry.or_insert((true, new_sort_key));497 match visibility {498 Visibility::Normal => {}499 Visibility::Hidden => {500 *visible = false;501 }502 Visibility::Unhide => {503 *visible = true;504 }505 };506 false507 },508 );509 out510 }511 pub fn fields_ex(512 &self,513 include_hidden: bool,514 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,515 ) -> Vec<IStr> {516 #[cfg(feature = "exp-preserve-order")]517 if preserve_order {518 let (mut fields, mut keys): (Vec<_>, Vec<_>) = self519 .fields_visibility()520 .into_iter()521 .filter(|(_, (visible, _))| include_hidden || *visible)522 .enumerate()523 .map(|(idx, (k, (_, sk)))| (k, (sk, idx)))524 .unzip();525 keys.sort_unstable_by_key(|v| v.0);526 // Reorder in-place by resulting indexes527 for i in 0..fields.len() {528 let x = fields[i].clone();529 let mut j = i;530 loop {531 let k = keys[j].1;532 keys[j].1 = j;533 if k == i {534 break;535 }536 fields[j] = fields[k].clone();537 j = k;538 }539 fields[j] = x;540 }541 return fields;542 }543544 let mut fields: Vec<_> = self545 .fields_visibility()546 .into_iter()547 .filter(|(_, (visible, _))| include_hidden || *visible)548 .map(|(k, _)| k)549 .collect();550 fields.sort_unstable();551 fields552 }553 pub fn fields(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Vec<IStr> {554 self.fields_ex(555 false,556 #[cfg(feature = "exp-preserve-order")]557 preserve_order,558 )559 }560 pub fn values_ex(561 &self,562 include_hidden: bool,563 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,564 ) -> ArrValue {565 ArrValue::new(PickObjectValues::new(566 self.clone(),567 self.fields_ex(568 include_hidden,569 #[cfg(feature = "exp-preserve-order")]570 preserve_order,571 ),572 ))573 }574 pub fn values(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> ArrValue {575 self.values_ex(576 false,577 #[cfg(feature = "exp-preserve-order")]578 preserve_order,579 )580 }581 pub fn key_values_ex(582 &self,583 include_hidden: bool,584 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,585 ) -> ArrValue {586 ArrValue::new(PickObjectKeyValues::new(587 self.clone(),588 self.fields_ex(589 include_hidden,590 #[cfg(feature = "exp-preserve-order")]591 preserve_order,592 ),593 ))594 }595 pub fn key_values(596 &self,597 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,598 ) -> ArrValue {599 self.key_values_ex(600 false,601 #[cfg(feature = "exp-preserve-order")]602 preserve_order,603 )604 }605}606607impl OopObject {608 pub fn new(609 sup: Option<ObjValue>,610 this_entries: Cc<GcHashMap<IStr, ObjMember>>,611 assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,612 ) -> Self {613 Self {614 sup,615 // this: None,616 assertions,617 assertions_ran: RefCell::new(GcHashSet::new()),618 this_entries,619 value_cache: RefCell::new(GcHashMap::new()),620 }621 }622623 fn evaluate_this(&self, v: &ObjMember, real_this: ObjValue) -> Result<Val> {624 v.invoke.evaluate(self.sup.clone(), Some(real_this))625 }626627 // FIXME: Duplication between ObjValue and OopObject628 fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {629 let mut out = FxHashMap::default();630 self.enum_fields(631 SuperDepth::default(),632 &mut |depth, index, name, visibility| {633 let new_sort_key = FieldSortKey::new(depth, index);634 let entry = out.entry(name.clone());635 let (visible, _) = entry.or_insert((true, new_sort_key));636 match visibility {637 Visibility::Normal => {}638 Visibility::Hidden => {639 *visible = false;640 }641 Visibility::Unhide => {642 *visible = true;643 }644 };645 false646 },647 );648 out649 }650}651652impl ObjectLike for OopObject {653 fn extend_from(&self, sup: ObjValue) -> ObjValue {654 ObjValue::new(match &self.sup {655 None => Self::new(656 Some(sup),657 self.this_entries.clone(),658 self.assertions.clone(),659 ),660 Some(v) => Self::new(661 Some(v.extend_from(sup)),662 self.this_entries.clone(),663 self.assertions.clone(),664 ),665 })666 }667668 fn len(&self) -> usize {669 self.fields_visibility()670 .into_iter()671 .filter(|(_, (visible, _))| *visible)672 .count()673 }674675 fn is_empty(&self) -> bool {676 if !self.this_entries.is_empty() {677 return false;678 }679 self.sup.as_ref().map_or(true, ObjValue::is_empty)680 }681682 /// Run callback for every field found in object683 ///684 /// Returns true if ended prematurely685 fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool {686 if let Some(s) = &self.sup {687 if s.enum_fields(depth.deeper(), handler) {688 return true;689 }690 }691 for (name, member) in self.this_entries.iter() {692 if handler(693 depth,694 member.original_index,695 name.clone(),696 member.flags.visibility(),697 ) {698 return true;699 }700 }701 false702 }703704 fn has_field_include_hidden(&self, name: IStr) -> bool {705 if self.this_entries.contains_key(&name) {706 true707 } else if let Some(super_obj) = &self.sup {708 super_obj.has_field_include_hidden(name)709 } else {710 false711 }712 }713 fn has_field(&self, name: IStr) -> bool {714 self.field_visibility(name)715 .map_or(false, |v| v.is_visible())716 }717718 fn get_for(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {719 let cache_key = (key.clone(), Some(this.clone().downgrade()));720 if let Some(v) = self.value_cache.borrow().get(&cache_key) {721 return Ok(match v {722 CacheValue::Cached(v) => Some(v.clone()),723 CacheValue::NotFound => None,724 CacheValue::Pending => throw!(InfiniteRecursionDetected),725 CacheValue::Errored(e) => return Err(e.clone()),726 });727 }728 self.value_cache729 .borrow_mut()730 .insert(cache_key.clone(), CacheValue::Pending);731 let value = self.get_for_uncached(key, this).map_err(|e| {732 self.value_cache733 .borrow_mut()734 .insert(cache_key.clone(), CacheValue::Errored(e.clone()));735 e736 })?;737 self.value_cache.borrow_mut().insert(738 cache_key,739 value740 .as_ref()741 .map_or(CacheValue::NotFound, |v| CacheValue::Cached(v.clone())),742 );743 Ok(value)744 }745 fn get_for_uncached(&self, key: IStr, real_this: ObjValue) -> Result<Option<Val>> {746 match (self.this_entries.get(&key), &self.sup) {747 (Some(k), None) => Ok(Some(self.evaluate_this(k, real_this)?)),748 (Some(k), Some(super_obj)) => {749 let our = self.evaluate_this(k, real_this.clone())?;750 if k.flags.add() {751 super_obj752 .get_raw(key, real_this)?753 .map_or(Ok(Some(our.clone())), |v| {754 Ok(Some(evaluate_add_op(&v, &our)?))755 })756 } else {757 Ok(Some(our))758 }759 }760 (None, Some(super_obj)) => super_obj.get_raw(key, real_this),761 (None, None) => Ok(None),762 }763 }764 fn field_visibility(&self, name: IStr) -> Option<Visibility> {765 if let Some(m) = self.this_entries.get(&name) {766 Some(match &m.flags.visibility() {767 Visibility::Normal => self768 .sup769 .as_ref()770 .and_then(|super_obj| super_obj.field_visibility(name))771 .unwrap_or(Visibility::Normal),772 v => *v,773 })774 } else if let Some(super_obj) = &self.sup {775 super_obj.field_visibility(name)776 } else {777 None778 }779 }780781 fn run_assertions_raw(&self, real_this: ObjValue) -> Result<()> {782 if self.assertions.is_empty() {783 if let Some(super_obj) = &self.sup {784 super_obj.run_assertions_raw(real_this)?;785 }786 return Ok(());787 }788 if self.assertions_ran.borrow_mut().insert(real_this.clone()) {789 for assertion in self.assertions.iter() {790 if let Err(e) = assertion.run(self.sup.clone(), Some(real_this.clone())) {791 self.assertions_ran.borrow_mut().remove(&real_this);792 return Err(e);793 }794 }795 if let Some(super_obj) = &self.sup {796 super_obj.run_assertions_raw(real_this)?;797 }798 }799 Ok(())800 }801}802803impl PartialEq for ObjValue {804 fn eq(&self, other: &Self) -> bool {805 Cc::ptr_eq(&self.0, &other.0)806 }807}808809impl Eq for ObjValue {}810impl Hash for ObjValue {811 fn hash<H: Hasher>(&self, hasher: &mut H) {812 hasher.write_usize(addr_of!(*self.0) as usize);813 }814}815816#[allow(clippy::module_name_repetitions)]817pub struct ObjValueBuilder {818 sup: Option<ObjValue>,819 map: GcHashMap<IStr, ObjMember>,820 assertions: Vec<TraceBox<dyn ObjectAssertion>>,821 next_field_index: FieldIndex,822}823impl ObjValueBuilder {824 pub fn new() -> Self {825 Self::with_capacity(0)826 }827 pub fn with_capacity(capacity: usize) -> Self {828 Self {829 sup: None,830 map: GcHashMap::with_capacity(capacity),831 assertions: Vec::new(),832 next_field_index: FieldIndex::default(),833 }834 }835 pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self {836 self.assertions.reserve_exact(capacity);837 self838 }839 pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {840 self.sup = Some(super_obj);841 self842 }843844 pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {845 self.assertions.push(tb!(assertion));846 self847 }848 pub fn member(&mut self, name: IStr) -> ObjMemberBuilder<ValueBuilder<'_>> {849 let field_index = self.next_field_index;850 self.next_field_index = self.next_field_index.next();851 ObjMemberBuilder::new(ValueBuilder(self), name, field_index)852 }853854 pub fn build(self) -> ObjValue {855 if self.sup.is_none() && self.map.is_empty() && self.assertions.is_empty() {856 return ObjValue::new_empty();857 }858 ObjValue::new(OopObject::new(859 self.sup,860 Cc::new(self.map),861 Cc::new(self.assertions),862 ))863 }864}865impl Default for ObjValueBuilder {866 fn default() -> Self {867 Self::with_capacity(0)868 }869}870871#[allow(clippy::module_name_repetitions)]872#[must_use = "value not added unless binding() was called"]873pub struct ObjMemberBuilder<Kind> {874 kind: Kind,875 name: IStr,876 add: bool,877 visibility: Visibility,878 original_index: FieldIndex,879 location: Option<ExprLocation>,880}881882#[allow(clippy::missing_const_for_fn)]883impl<Kind> ObjMemberBuilder<Kind> {884 pub(crate) fn new(kind: Kind, name: IStr, original_index: FieldIndex) -> Self {885 Self {886 kind,887 name,888 original_index,889 add: false,890 visibility: Visibility::Normal,891 location: None,892 }893 }894895 pub const fn with_add(mut self, add: bool) -> Self {896 self.add = add;897 self898 }899 pub fn add(self) -> Self {900 self.with_add(true)901 }902 pub fn with_visibility(mut self, visibility: Visibility) -> Self {903 self.visibility = visibility;904 self905 }906 pub fn hide(self) -> Self {907 self.with_visibility(Visibility::Hidden)908 }909 pub fn with_location(mut self, location: ExprLocation) -> Self {910 self.location = Some(location);911 self912 }913 fn build_member(self, binding: MaybeUnbound) -> (Kind, IStr, ObjMember) {914 (915 self.kind,916 self.name,917 ObjMember {918 flags: ObjFieldFlags::new(self.add, self.visibility),919 original_index: self.original_index,920 invoke: binding,921 location: self.location,922 },923 )924 }925}926927pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);928impl ObjMemberBuilder<ValueBuilder<'_>> {929 /// Inserts value, replacing if it is already defined930 pub fn value_unchecked(self, value: Val) {931 let (receiver, name, member) =932 self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value)));933 let entry = receiver.0.map.entry(name);934 entry.insert(member);935 }936937 pub fn value(self, value: Val) -> Result<()> {938 self.thunk(Thunk::evaluated(value))939 }940 pub fn thunk(self, value: Thunk<Val>) -> Result<()> {941 self.binding(MaybeUnbound::Bound(value))942 }943 pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {944 self.binding(MaybeUnbound::Unbound(Cc::new(tb!(bindable))))945 }946 pub fn binding(self, binding: MaybeUnbound) -> Result<()> {947 let (receiver, name, member) = self.build_member(binding);948 let location = member.location.clone();949 let old = receiver.0.map.insert(name.clone(), member);950 if old.is_some() {951 State::push(952 CallLocation(location.as_ref()),953 || format!("field <{}> initializtion", name.clone()),954 || throw!(DuplicateFieldName(name.clone())),955 )?;956 }957 Ok(())958 }959}960961pub struct ExtendBuilder<'v>(&'v mut ObjValue);962impl ObjMemberBuilder<ExtendBuilder<'_>> {963 pub fn value(self, value: Val) {964 self.binding(MaybeUnbound::Bound(Thunk::evaluated(value)));965 }966 pub fn bindable(self, bindable: TraceBox<dyn Unbound<Bound = Val>>) {967 self.binding(MaybeUnbound::Unbound(Cc::new(bindable)));968 }969 pub fn binding(self, binding: MaybeUnbound) {970 let (receiver, name, member) = self.build_member(binding);971 let new = receiver.0.clone();972 *receiver.0 = new.extend_with_raw_member(name, member);973 }974}crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -1,7 +1,6 @@
use std::{
cell::RefCell,
fmt::{self, Debug, Display},
- hash::Hasher,
mem::replace,
rc::Rc,
};
@@ -9,7 +8,6 @@
use jrsonnet_gcmodule::{Cc, Trace};
use jrsonnet_interner::IStr;
use jrsonnet_types::ValType;
-use rustc_hash::FxHasher;
pub use crate::arr::{ArrValue, ArrayLike};
use crate::{
@@ -50,6 +48,12 @@
pub fn errored(e: Error) -> Self {
Self(Cc::new(RefCell::new(ThunkInner::Errored(e))))
}
+ pub fn result(res: Result<T, Error>) -> Self {
+ match res {
+ Ok(o) => Self::evaluated(o),
+ Err(e) => Self::errored(e),
+ }
+ }
}
impl<T> Thunk<T>
crates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/lib.rs
+++ b/crates/jrsonnet-stdlib/src/lib.rs
@@ -138,6 +138,10 @@
("base64DecodeBytes", builtin_base64_decode_bytes::INST),
// Objects
("objectFieldsEx", builtin_object_fields_ex::INST),
+ ("objectValues", builtin_object_values::INST),
+ ("objectValuesAll", builtin_object_values_all::INST),
+ ("objectKeysValues", builtin_object_keys_values::INST),
+ ("objectKeysValuesAll", builtin_object_keys_values_all::INST),
("objectHasEx", builtin_object_has_ex::INST),
("objectRemoveKey", builtin_object_remove_key::INST),
// Manifest
crates/jrsonnet-stdlib/src/objects.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/objects.rs
+++ b/crates/jrsonnet-stdlib/src/objects.rs
@@ -1,6 +1,6 @@
use jrsonnet_evaluator::{
function::builtin,
- val::{StrValue, Val},
+ val::{ArrValue, StrValue, Val},
IStr, ObjValue, ObjValueBuilder,
};
@@ -23,6 +23,82 @@
.collect::<Vec<_>>()
}
+pub fn builtin_object_values_ex(
+ o: ObjValue,
+ include_hidden: bool,
+ #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
+) -> ArrValue {
+ #[cfg(feature = "exp-preserve-order")]
+ let preserve_order = preserve_order.unwrap_or(false);
+ o.values_ex(
+ include_hidden,
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order,
+ )
+}
+#[builtin]
+pub fn builtin_object_values(
+ o: ObjValue,
+ #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
+) -> ArrValue {
+ builtin_object_values_ex(
+ o,
+ false,
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order,
+ )
+}
+#[builtin]
+pub fn builtin_object_values_all(
+ o: ObjValue,
+ #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
+) -> ArrValue {
+ builtin_object_values_ex(
+ o,
+ true,
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order,
+ )
+}
+
+pub fn builtin_object_keys_values_ex(
+ o: ObjValue,
+ include_hidden: bool,
+ #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
+) -> ArrValue {
+ #[cfg(feature = "exp-preserve-order")]
+ let preserve_order = preserve_order.unwrap_or(false);
+ o.key_values_ex(
+ include_hidden,
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order,
+ )
+}
+#[builtin]
+pub fn builtin_object_keys_values(
+ o: ObjValue,
+ #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
+) -> ArrValue {
+ builtin_object_keys_values_ex(
+ o,
+ false,
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order,
+ )
+}
+#[builtin]
+pub fn builtin_object_keys_values_all(
+ o: ObjValue,
+ #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
+) -> ArrValue {
+ builtin_object_keys_values_ex(
+ o,
+ true,
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order,
+ )
+}
+
#[builtin]
pub fn builtin_object_has_ex(obj: ObjValue, fname: IStr, hidden: bool) -> bool {
obj.has_field_ex(fname, hidden)
crates/jrsonnet-stdlib/src/std.jsonnetdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/std.jsonnet
+++ b/crates/jrsonnet-stdlib/src/std.jsonnet
@@ -268,18 +268,6 @@
objectHasAll(o, f)::
std.objectHasEx(o, f, true),
- objectValues(o)::
- [o[k] for k in std.objectFields(o)],
-
- objectValuesAll(o)::
- [o[k] for k in std.objectFieldsAll(o)],
-
- objectKeysValues(o)::
- [{ key: k, value: o[k] } for k in std.objectFields(o)],
-
- objectKeysValuesAll(o)::
- [{ key: k, value: o[k] } for k in std.objectFieldsAll(o)],
-
resolvePath(f, r)::
local arr = std.split(f, '/');
std.join('/', std.makeArray(std.length(arr) - 1, function(i) arr[i]) + [r]),