From cfa49ab1b7b8a74a09fa4da0c44956fd4de9ab29 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Thu, 10 Aug 2023 21:47:30 +0000 Subject: [PATCH] perf: move std.object[Keys]Values[All] to native --- --- 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, +} + +impl PickObjectValues { + pub fn new(obj: ObjValue, keys: Vec) -> Self { + Self { obj, keys } + } +} + +impl ArrayLike for PickObjectValues { + fn len(&self) -> usize { + self.keys.len() + } + + fn get(&self, index: usize) -> Result> { + 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> { + 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 { + None + } + + fn is_cheap(&self) -> bool { + false + } +} + +#[derive(Trace, Debug)] +pub struct PickObjectKeyValues { + obj: ObjValue, + keys: Vec, +} + +impl PickObjectKeyValues { + pub fn new(obj: ObjValue, keys: Vec) -> Self { + Self { obj, keys } + } +} + +#[derive(Typed)] +pub struct KeyValue { + key: IStr, + value: Thunk, +} + +impl ArrayLike for PickObjectKeyValues { + fn len(&self) -> usize { + self.keys.len() + } + + fn get(&self, index: usize) -> Result> { + 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> { + 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 { + None + } + + fn is_cheap(&self) -> bool { + false + } +} --- 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 { + 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> { self.0.get_for_uncached(key, this) } @@ -452,6 +461,25 @@ key, })) } + pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk { + #[derive(Trace)] + struct ThunkGet { + obj: ObjValue, + key: IStr, + } + impl ThunkValue for ThunkGet { + type Output = Val; + + fn get(self: Box) -> Result { + 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 { --- 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) -> Self { + match res { + Ok(o) => Self::evaluated(o), + Err(e) => Self::errored(e), + } + } } impl Thunk --- 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 --- 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::>() } +pub fn builtin_object_values_ex( + o: ObjValue, + include_hidden: bool, + #[cfg(feature = "exp-preserve-order")] preserve_order: Option, +) -> 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, +) -> 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, +) -> 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, +) -> 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, +) -> 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, +) -> 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) --- 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]), -- gitstuff