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.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/obj.rs
+++ b/crates/jrsonnet-evaluator/src/obj.rs
@@ -12,12 +12,13 @@
use rustc_hash::FxHashMap;
use crate::{
- error::{Error, ErrorKind::*},
+ arr::{PickObjectKeyValues, PickObjectValues},
+ error::{suggest_object_fields, Error, ErrorKind::*},
function::CallLocation,
gc::{GcHashMap, GcHashSet, TraceBox},
operator::evaluate_add_op,
tb, throw,
- val::ThunkValue,
+ val::{ArrValue, ThunkValue},
MaybeUnbound, Result, State, Thunk, Unbound, Val,
};
@@ -398,6 +399,14 @@
self.0.get_for(key, this)
}
+ pub fn get_or_bail(&self, key: IStr) -> Result<Val> {
+ let Some(value) = self.get(key.clone())? else {
+ let suggestions = suggest_object_fields(self, key.clone());
+ throw!(NoSuchField(key, suggestions))
+ };
+ Ok(value)
+ }
+
fn get_raw(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {
self.0.get_for_uncached(key, this)
}
@@ -452,6 +461,25 @@
key,
}))
}
+ pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {
+ #[derive(Trace)]
+ struct ThunkGet {
+ obj: ObjValue,
+ key: IStr,
+ }
+ impl ThunkValue for ThunkGet {
+ type Output = Val;
+
+ fn get(self: Box<Self>) -> Result<Self::Output> {
+ Ok(self.obj.get_or_bail(self.key)?)
+ }
+ }
+
+ Thunk::new(ThunkGet {
+ obj: self.clone(),
+ key,
+ })
+ }
pub fn ptr_eq(a: &Self, b: &Self) -> bool {
Cc::ptr_eq(&a.0, &b.0)
}
@@ -529,6 +557,51 @@
preserve_order,
)
}
+ pub fn values_ex(
+ &self,
+ include_hidden: bool,
+ #[cfg(feature = "exp-preserve-order")] preserve_order: bool,
+ ) -> ArrValue {
+ ArrValue::new(PickObjectValues::new(
+ self.clone(),
+ self.fields_ex(
+ include_hidden,
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order,
+ ),
+ ))
+ }
+ pub fn values(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> ArrValue {
+ self.values_ex(
+ false,
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order,
+ )
+ }
+ pub fn key_values_ex(
+ &self,
+ include_hidden: bool,
+ #[cfg(feature = "exp-preserve-order")] preserve_order: bool,
+ ) -> ArrValue {
+ ArrValue::new(PickObjectKeyValues::new(
+ self.clone(),
+ self.fields_ex(
+ include_hidden,
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order,
+ ),
+ ))
+ }
+ pub fn key_values(
+ &self,
+ #[cfg(feature = "exp-preserve-order")] preserve_order: bool,
+ ) -> ArrValue {
+ self.key_values_ex(
+ false,
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order,
+ )
+ }
}
impl OopObject {
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.rsdiffbeforeafterboth1use jrsonnet_evaluator::{2 function::builtin,3 val::{StrValue, Val},4 IStr, ObjValue, ObjValueBuilder,5};67#[builtin]8pub fn builtin_object_fields_ex(9 obj: ObjValue,10 hidden: bool,11 #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,12) -> Vec<Val> {13 #[cfg(feature = "exp-preserve-order")]14 let preserve_order = preserve_order.unwrap_or(false);15 let out = obj.fields_ex(16 hidden,17 #[cfg(feature = "exp-preserve-order")]18 preserve_order,19 );20 out.into_iter()21 .map(StrValue::Flat)22 .map(Val::Str)23 .collect::<Vec<_>>()24}2526#[builtin]27pub fn builtin_object_has_ex(obj: ObjValue, fname: IStr, hidden: bool) -> bool {28 obj.has_field_ex(fname, hidden)29}3031#[builtin]32pub fn builtin_object_remove_key(33 obj: ObjValue,34 key: IStr,35 // Standard implementation uses std.objectFields without such argument, we can't36 // assume order preservation should always be enabled/disabled37 #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,38) -> ObjValue {39 #[cfg(feature = "exp-preserve-order")]40 let preserve_order = preserve_order.unwrap_or(false);41 let mut new_obj = ObjValueBuilder::with_capacity(obj.len() - 1);42 for (k, v) in obj.iter(43 #[cfg(feature = "exp-preserve-order")]44 preserve_order,45 ) {46 if k == key {47 continue;48 }49 new_obj.member(k).value_unchecked(v.unwrap())50 }5152 new_obj.build()53}1use jrsonnet_evaluator::{2 function::builtin,3 val::{ArrValue, StrValue, Val},4 IStr, ObjValue, ObjValueBuilder,5};67#[builtin]8pub fn builtin_object_fields_ex(9 obj: ObjValue,10 hidden: bool,11 #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,12) -> Vec<Val> {13 #[cfg(feature = "exp-preserve-order")]14 let preserve_order = preserve_order.unwrap_or(false);15 let out = obj.fields_ex(16 hidden,17 #[cfg(feature = "exp-preserve-order")]18 preserve_order,19 );20 out.into_iter()21 .map(StrValue::Flat)22 .map(Val::Str)23 .collect::<Vec<_>>()24}2526pub fn builtin_object_values_ex(27 o: ObjValue,28 include_hidden: bool,29 #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,30) -> ArrValue {31 #[cfg(feature = "exp-preserve-order")]32 let preserve_order = preserve_order.unwrap_or(false);33 o.values_ex(34 include_hidden,35 #[cfg(feature = "exp-preserve-order")]36 preserve_order,37 )38}39#[builtin]40pub fn builtin_object_values(41 o: ObjValue,42 #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,43) -> ArrValue {44 builtin_object_values_ex(45 o,46 false,47 #[cfg(feature = "exp-preserve-order")]48 preserve_order,49 )50}51#[builtin]52pub fn builtin_object_values_all(53 o: ObjValue,54 #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,55) -> ArrValue {56 builtin_object_values_ex(57 o,58 true,59 #[cfg(feature = "exp-preserve-order")]60 preserve_order,61 )62}6364pub fn builtin_object_keys_values_ex(65 o: ObjValue,66 include_hidden: bool,67 #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,68) -> ArrValue {69 #[cfg(feature = "exp-preserve-order")]70 let preserve_order = preserve_order.unwrap_or(false);71 o.key_values_ex(72 include_hidden,73 #[cfg(feature = "exp-preserve-order")]74 preserve_order,75 )76}77#[builtin]78pub fn builtin_object_keys_values(79 o: ObjValue,80 #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,81) -> ArrValue {82 builtin_object_keys_values_ex(83 o,84 false,85 #[cfg(feature = "exp-preserve-order")]86 preserve_order,87 )88}89#[builtin]90pub fn builtin_object_keys_values_all(91 o: ObjValue,92 #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,93) -> ArrValue {94 builtin_object_keys_values_ex(95 o,96 true,97 #[cfg(feature = "exp-preserve-order")]98 preserve_order,99 )100}101102#[builtin]103pub fn builtin_object_has_ex(obj: ObjValue, fname: IStr, hidden: bool) -> bool {104 obj.has_field_ex(fname, hidden)105}106107#[builtin]108pub fn builtin_object_remove_key(109 obj: ObjValue,110 key: IStr,111 // Standard implementation uses std.objectFields without such argument, we can't112 // assume order preservation should always be enabled/disabled113 #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,114) -> ObjValue {115 #[cfg(feature = "exp-preserve-order")]116 let preserve_order = preserve_order.unwrap_or(false);117 let mut new_obj = ObjValueBuilder::with_capacity(obj.len() - 1);118 for (k, v) in obj.iter(119 #[cfg(feature = "exp-preserve-order")]120 preserve_order,121 ) {122 if k == key {123 continue;124 }125 new_obj.member(k).value_unchecked(v.unwrap())126 }127128 new_obj.build()129}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]),