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}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]),