difftreelog
refactor move arrays to use dyn ArrayLike
in: master
8 files changed
crates/jrsonnet-evaluator/src/arr/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/arr/mod.rs
+++ b/crates/jrsonnet-evaluator/src/arr/mod.rs
@@ -1,44 +1,20 @@
-use std::rc::Rc;
+use std::any::Any;
use jrsonnet_gcmodule::{Cc, Trace};
use jrsonnet_interner::IBytes;
use jrsonnet_parser::LocExpr;
-use crate::{function::FuncVal, Context, Result, Thunk, Val};
+use crate::{function::FuncVal, gc::TraceBox, tb, Context, Result, Thunk, Val};
mod spec;
-use spec::*;
+pub use spec::ArrayLike;
+pub(crate) use spec::*;
/// Represents a Jsonnet array value.
#[derive(Debug, Clone, Trace)]
// may contrain other ArrValue
#[trace(tracking(force))]
-pub enum ArrValue {
- /// Layout optimized byte array.
- Bytes(BytesArray),
- /// Layout optimized char array.
- Chars(CharArray),
- /// Every element is lazy evaluated.
- Lazy(LazyArray),
- /// Every element is defined somewhere in source code
- Expr(ExprArray),
- /// Every field is already evaluated.
- Eager(EagerArray),
- /// Concatenation of two arrays of any kind.
- Extended(Cc<ExtendedArray>),
- /// Represents a integer array in form `[start, start + 1, ... end - 1, end]`.
- /// This kind of arrays is generated by `std.range(start, end)` call, and used for loops.
- Range(RangeArray),
- /// Sliced array view.
- Slice(Cc<SliceArray>),
- /// Reversed array view.
- /// Returned by `std.reverse(other)` call
- Reverse(Cc<ReverseArray>),
- /// Returned by `std.map` call
- Mapped(MappedArray),
- /// Returned by `std.repeat` call
- Repeated(RepeatedArray),
-}
+pub struct ArrValue(Cc<TraceBox<dyn ArrayLike>>);
pub trait ArrayLikeIter<T>: Iterator<Item = T> + DoubleEndedIterator + ExactSizeIterator {}
impl<I, T> ArrayLikeIter<T> for I where
@@ -47,36 +23,39 @@
}
impl ArrValue {
+ pub fn new(v: impl ArrayLike) -> Self {
+ Self(Cc::new(tb!(v)))
+ }
pub fn empty() -> Self {
- Self::Range(RangeArray::empty())
+ Self::new(RangeArray::empty())
}
pub fn expr(ctx: Context, exprs: impl IntoIterator<Item = LocExpr>) -> Self {
- Self::Expr(ExprArray::new(ctx, exprs))
+ Self::new(ExprArray::new(ctx, exprs))
}
- pub fn lazy(thunks: Cc<Vec<Thunk<Val>>>) -> Self {
- Self::Lazy(LazyArray(thunks))
+ pub fn lazy(thunks: Vec<Thunk<Val>>) -> Self {
+ Self::new(LazyArray(thunks))
}
pub fn eager(values: Vec<Val>) -> Self {
- Self::Eager(EagerArray(Cc::new(values)))
+ Self::new(EagerArray(values))
}
pub fn repeated(data: ArrValue, repeats: usize) -> Option<Self> {
- Some(Self::Repeated(RepeatedArray::new(data, repeats)?))
+ Some(Self::new(RepeatedArray::new(data, repeats)?))
}
pub fn bytes(bytes: IBytes) -> Self {
- Self::Bytes(BytesArray(bytes))
+ Self::new(BytesArray(bytes))
}
pub fn chars(chars: impl Iterator<Item = char>) -> Self {
- Self::Chars(CharArray(Rc::new(chars.collect())))
+ Self::new(CharArray(chars.collect()))
}
#[must_use]
pub fn map(self, mapper: FuncVal) -> Self {
- Self::Mapped(MappedArray::new(self, mapper))
+ Self::new(MappedArray::new(self, mapper))
}
pub fn filter(self, filter: impl Fn(&Val) -> Result<bool>) -> Result<Self> {
@@ -100,7 +79,7 @@
} else if b.is_empty() {
a
} else if a.len() + b.len() > ARR_EXTEND_THRESHOLD {
- Self::Extended(Cc::new(ExtendedArray::new(a, b)))
+ Self::new(ExtendedArray::new(a, b))
} else if let (Some(a), Some(b)) = (a.iter_cheap(), b.iter_cheap()) {
let mut out = Vec::with_capacity(a.len() + b.len());
out.extend(a);
@@ -110,15 +89,15 @@
let mut out = Vec::with_capacity(a.len() + b.len());
out.extend(a.iter_lazy());
out.extend(b.iter_lazy());
- Self::lazy(Cc::new(out))
+ Self::lazy(out)
}
}
pub fn range_exclusive(a: i32, b: i32) -> Self {
- Self::Range(RangeArray::new_exclusive(a, b))
+ Self::new(RangeArray::new_exclusive(a, b))
}
pub fn range_inclusive(a: i32, b: i32) -> Self {
- Self::Range(RangeArray::new_inclusive(a, b))
+ Self::new(RangeArray::new_inclusive(a, b))
}
#[must_use]
@@ -136,53 +115,42 @@
if from >= to || step == 0 {
return None;
}
- // match self {
- // ArrValue::Slice(slice) => {
- // return Some(Self::Slice(Cc::new(SliceArray {
- // inner: slice.inner.clone(),
- // from: slice.from + slice.step * (from as u32),
- // to: slice.from + (to as u32) * slice.step,
- // step: slice.step * step as u32,
- // })))
- // }
- // _ => {}
- // }
- Some(Self::Slice(Cc::new(SliceArray {
+ Some(Self::new(SliceArray {
inner: self,
from: from as u32,
to: to as u32,
step: step as u32,
- })))
+ }))
}
/// Array length.
pub fn len(&self) -> usize {
- pass!(self.len())
+ self.0.len()
}
/// Is array contains no elements?
pub fn is_empty(&self) -> bool {
- pass!(self.is_empty())
+ self.0.is_empty()
}
/// Get array element by index, evaluating it, if it is lazy.
///
/// Returns `None` on out-of-bounds condition.
pub fn get(&self, index: usize) -> Result<Option<Val>> {
- pass!(self.get(index))
+ self.0.get(index)
}
/// Returns None if get is either non cheap, or out of bounds
fn get_cheap(&self, index: usize) -> Option<Val> {
- pass!(self.get_cheap(index))
+ self.0.get_cheap(index)
}
/// Get array element by index, without evaluation.
///
/// Returns `None` on out-of-bounds condition.
pub fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
- pass!(self.get_lazy(index))
+ self.0.get_lazy(index)
}
pub fn iter(&self) -> impl ArrayLikeIter<Result<Val>> + '_ {
@@ -205,33 +173,20 @@
/// Return a reversed view on current array.
#[must_use]
pub fn reversed(self) -> Self {
- Self::Reverse(Cc::new(ReverseArray(self)))
+ Self::new(ReverseArray(self))
}
pub fn ptr_eq(a: &Self, b: &Self) -> bool {
- match (a, b) {
- (ArrValue::Bytes(a), ArrValue::Bytes(b)) => a.0 == b.0,
- (ArrValue::Lazy(a), ArrValue::Lazy(b)) => Cc::ptr_eq(&a.0, &b.0),
- (ArrValue::Expr(a), ArrValue::Expr(b)) => Cc::ptr_eq(&a.0, &b.0),
- (ArrValue::Eager(a), ArrValue::Eager(b)) => Cc::ptr_eq(&a.0, &b.0),
- (ArrValue::Extended(a), ArrValue::Extended(b)) => Cc::ptr_eq(a, b),
- (ArrValue::Range(a), ArrValue::Range(b)) => a == b,
- _ => false,
- }
+ Cc::ptr_eq(&a.0, &b.0)
}
/// Is this vec supports `.get_cheap()?`
pub fn is_cheap(&self) -> bool {
- match self {
- ArrValue::Eager(_) | ArrValue::Range(..) | ArrValue::Bytes(_) | ArrValue::Chars(_) => {
- true
- }
- ArrValue::Extended(v) => v.a.is_cheap() && v.b.is_cheap(),
- ArrValue::Slice(r) => r.inner.is_cheap(),
- ArrValue::Reverse(i) => i.0.is_cheap(),
- ArrValue::Repeated(v) => v.is_cheap(),
- ArrValue::Expr(_) | ArrValue::Lazy(_) | ArrValue::Mapped(_) => false,
- }
+ self.0.is_cheap()
+ }
+
+ pub fn as_any(&self) -> &dyn Any {
+ &self.0
}
}
impl From<Vec<Val>> for ArrValue {
@@ -241,7 +196,7 @@
}
impl From<Vec<Thunk<Val>>> for ArrValue {
fn from(value: Vec<Thunk<Val>>) -> Self {
- Self::lazy(Cc::new(value))
+ Self::lazy(value)
}
}
impl FromIterator<Val> for ArrValue {
@@ -249,6 +204,27 @@
Self::eager(iter.into_iter().collect())
}
}
+impl ArrayLike for ArrValue {
+ fn len(&self) -> usize {
+ self.0.len()
+ }
+ fn get(&self, index: usize) -> Result<Option<Val>> {
+ self.0.get(index)
+ }
+
+ fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
+ self.0.get_lazy(index)
+ }
+
+ fn get_cheap(&self, index: usize) -> Option<Val> {
+ self.0.get_cheap(index)
+ }
+
+ fn is_cheap(&self) -> bool {
+ self.0.is_cheap()
+ }
+}
+
#[cfg(target_pointer_width = "64")]
-static_assertions::assert_eq_size!(ArrValue, [u8; 16]);
+static_assertions::assert_eq_size!(ArrValue, [u8; 8]);
crates/jrsonnet-evaluator/src/arr/spec.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/arr/spec.rs
+++ b/crates/jrsonnet-evaluator/src/arr/spec.rs
@@ -1,4 +1,4 @@
-use std::{cell::RefCell, iter, mem::replace, rc::Rc};
+use std::{any::Any, cell::RefCell, fmt::Debug, iter, mem::replace};
use jrsonnet_gcmodule::{Cc, Trace};
use jrsonnet_interner::{IBytes, IStr};
@@ -13,7 +13,7 @@
Context, Error, Result, Thunk, Val,
};
-pub trait ArrayLike: Sized + Into<ArrValue> {
+pub trait ArrayLike: Any + Trace + Debug {
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
@@ -22,12 +22,10 @@
fn get_lazy(&self, index: usize) -> Option<Thunk<Val>>;
fn get_cheap(&self, index: usize) -> Option<Val>;
- fn reverse(self) -> ArrValue {
- ArrValue::Reverse(Cc::new(ReverseArray(self.into())))
- }
+ fn is_cheap(&self) -> bool;
}
-#[derive(Debug, Clone, Trace)]
+#[derive(Debug, Trace)]
pub struct SliceArray {
pub(crate) inner: ArrValue,
pub(crate) from: u32,
@@ -81,15 +79,13 @@
fn get_cheap(&self, index: usize) -> Option<Val> {
self.iter_cheap()?.nth(index)
}
-}
-impl From<SliceArray> for ArrValue {
- fn from(value: SliceArray) -> Self {
- Self::Slice(Cc::new(value))
+ fn is_cheap(&self) -> bool {
+ self.inner.is_cheap()
}
}
-#[derive(Trace, Debug, Clone)]
-pub struct CharArray(pub Rc<Vec<char>>);
+#[derive(Trace, Debug)]
+pub struct CharArray(pub Vec<char>);
impl ArrayLike for CharArray {
fn len(&self) -> usize {
self.0.len()
@@ -108,14 +104,12 @@
.get(index)
.map(|v| Val::Str(StrValue::Flat(IStr::from(*v))))
}
-}
-impl From<CharArray> for ArrValue {
- fn from(value: CharArray) -> Self {
- ArrValue::Chars(value)
+ fn is_cheap(&self) -> bool {
+ true
}
}
-#[derive(Trace, Debug, Clone)]
+#[derive(Trace, Debug)]
pub struct BytesArray(pub IBytes);
impl ArrayLike for BytesArray {
fn len(&self) -> usize {
@@ -133,10 +127,8 @@
fn get_cheap(&self, index: usize) -> Option<Val> {
self.0.get(index).map(|v| Val::Num(f64::from(*v)))
}
-}
-impl From<BytesArray> for ArrValue {
- fn from(value: BytesArray) -> Self {
- ArrValue::Bytes(value)
+ fn is_cheap(&self) -> bool {
+ true
}
}
@@ -148,30 +140,30 @@
Pending,
}
-#[derive(Debug, Trace)]
-pub struct ExprArrayInner {
+#[derive(Debug, Trace, Clone)]
+pub struct ExprArray {
ctx: Context,
- cached: RefCell<Vec<ArrayThunk<LocExpr>>>,
+ cached: Cc<RefCell<Vec<ArrayThunk<LocExpr>>>>,
}
-#[derive(Debug, Trace, Clone)]
-pub struct ExprArray(pub Cc<ExprArrayInner>);
impl ExprArray {
pub fn new(ctx: Context, items: impl IntoIterator<Item = LocExpr>) -> Self {
- Self(Cc::new(ExprArrayInner {
+ Self {
ctx,
- cached: RefCell::new(items.into_iter().map(ArrayThunk::Waiting).collect()),
- }))
+ cached: Cc::new(RefCell::new(
+ items.into_iter().map(ArrayThunk::Waiting).collect(),
+ )),
+ }
}
}
impl ArrayLike for ExprArray {
fn len(&self) -> usize {
- self.0.cached.borrow().len()
+ self.cached.borrow().len()
}
fn get(&self, index: usize) -> Result<Option<Val>> {
if index >= self.len() {
return Ok(None);
}
- match &self.0.cached.borrow()[index] {
+ match &self.cached.borrow()[index] {
ArrayThunk::Computed(c) => return Ok(Some(c.clone())),
ArrayThunk::Errored(e) => return Err(e.clone()),
ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),
@@ -179,19 +171,19 @@
};
let ArrayThunk::Waiting(expr) =
- replace(&mut self.0.cached.borrow_mut()[index], ArrayThunk::Pending)
+ replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)
else {
unreachable!()
};
- let new_value = match evaluate(self.0.ctx.clone(), &expr) {
+ let new_value = match evaluate(self.ctx.clone(), &expr) {
Ok(v) => v,
Err(e) => {
- self.0.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());
+ self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());
return Err(e);
}
};
- self.0.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());
+ self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());
Ok(Some(new_value))
}
fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
@@ -215,7 +207,7 @@
if index >= self.len() {
return None;
}
- match &self.0.cached.borrow()[index] {
+ match &self.cached.borrow()[index] {
ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),
ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),
ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}
@@ -229,14 +221,12 @@
fn get_cheap(&self, _index: usize) -> Option<Val> {
None
}
-}
-impl From<ExprArray> for ArrValue {
- fn from(value: ExprArray) -> Self {
- Self::Expr(value)
+ fn is_cheap(&self) -> bool {
+ false
}
}
-#[derive(Trace, Debug, Clone)]
+#[derive(Trace, Debug)]
pub struct ExtendedArray {
pub a: ArrValue,
pub b: ArrValue,
@@ -319,15 +309,13 @@
self.b.get_cheap(index - self.split)
}
}
-}
-impl From<ExtendedArray> for ArrValue {
- fn from(value: ExtendedArray) -> Self {
- Self::Extended(Cc::new(value))
+ fn is_cheap(&self) -> bool {
+ self.a.is_cheap() && self.b.is_cheap()
}
}
-#[derive(Trace, Debug, Clone)]
-pub struct LazyArray(pub Cc<Vec<Thunk<Val>>>);
+#[derive(Trace, Debug)]
+pub struct LazyArray(pub Vec<Thunk<Val>>);
impl ArrayLike for LazyArray {
fn len(&self) -> usize {
self.0.len()
@@ -344,15 +332,13 @@
fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
self.0.get(index).cloned()
}
-}
-impl From<LazyArray> for ArrValue {
- fn from(value: LazyArray) -> Self {
- Self::Lazy(value)
+ fn is_cheap(&self) -> bool {
+ false
}
}
-#[derive(Trace, Debug, Clone)]
-pub struct EagerArray(pub Cc<Vec<Val>>);
+#[derive(Trace, Debug)]
+pub struct EagerArray(pub Vec<Val>);
impl ArrayLike for EagerArray {
fn len(&self) -> usize {
self.0.len()
@@ -369,15 +355,13 @@
fn get_cheap(&self, index: usize) -> Option<Val> {
self.0.get(index).cloned()
}
-}
-impl From<EagerArray> for ArrValue {
- fn from(value: EagerArray) -> Self {
- Self::Eager(value)
+ fn is_cheap(&self) -> bool {
+ true
}
}
/// Inclusive range type
-#[derive(Debug, Trace, Clone, PartialEq, Eq)]
+#[derive(Debug, Trace, PartialEq, Eq)]
pub struct RangeArray {
start: i32,
end: i32,
@@ -422,14 +406,12 @@
fn get_cheap(&self, index: usize) -> Option<Val> {
self.range().nth(index).map(|i| Val::Num(f64::from(i)))
}
-}
-impl From<RangeArray> for ArrValue {
- fn from(value: RangeArray) -> Self {
- Self::Range(value)
+ fn is_cheap(&self) -> bool {
+ true
}
}
-#[derive(Debug, Trace, Clone)]
+#[derive(Debug, Trace)]
pub struct ReverseArray(pub ArrValue);
impl ArrayLike for ReverseArray {
fn len(&self) -> usize {
@@ -447,44 +429,37 @@
fn get_cheap(&self, index: usize) -> Option<Val> {
self.0.get_cheap(self.0.len() - index - 1)
}
- fn reverse(self) -> ArrValue {
- self.0
- }
-}
-impl From<ReverseArray> for ArrValue {
- fn from(value: ReverseArray) -> Self {
- Self::Reverse(Cc::new(value))
+ fn is_cheap(&self) -> bool {
+ self.0.is_cheap()
}
}
-#[derive(Trace, Debug)]
-pub struct MappedArrayInner {
+#[derive(Trace, Debug, Clone)]
+pub struct MappedArray {
inner: ArrValue,
- cached: RefCell<Vec<ArrayThunk<()>>>,
+ cached: Cc<RefCell<Vec<ArrayThunk<()>>>>,
mapper: FuncVal,
}
-#[derive(Trace, Debug, Clone)]
-pub struct MappedArray(Cc<MappedArrayInner>);
impl MappedArray {
pub fn new(inner: ArrValue, mapper: FuncVal) -> Self {
let len = inner.len();
- Self(Cc::new(MappedArrayInner {
+ Self {
inner,
- cached: RefCell::new(vec![ArrayThunk::Waiting(()); len]),
+ cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting(()); len])),
mapper,
- }))
+ }
}
}
impl ArrayLike for MappedArray {
fn len(&self) -> usize {
- self.0.cached.borrow().len()
+ self.cached.borrow().len()
}
fn get(&self, index: usize) -> Result<Option<Val>> {
if index >= self.len() {
return Ok(None);
}
- match &self.0.cached.borrow()[index] {
+ match &self.cached.borrow()[index] {
ArrayThunk::Computed(c) => return Ok(Some(c.clone())),
ArrayThunk::Errored(e) => return Err(e.clone()),
ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),
@@ -492,27 +467,26 @@
};
let ArrayThunk::Waiting(_) =
- replace(&mut self.0.cached.borrow_mut()[index], ArrayThunk::Pending)
+ replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)
else {
unreachable!()
};
let val = self
- .0
.inner
.get(index)
.transpose()
.expect("index checked")
- .and_then(|r| self.0.mapper.evaluate_simple(&(r,), false));
+ .and_then(|r| self.mapper.evaluate_simple(&(r,), false));
let new_value = match val {
Ok(v) => v,
Err(e) => {
- self.0.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());
+ self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());
return Err(e);
}
};
- self.0.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());
+ self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());
Ok(Some(new_value))
}
fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
@@ -536,7 +510,7 @@
if index >= self.len() {
return None;
}
- match &self.0.cached.borrow()[index] {
+ match &self.cached.borrow()[index] {
ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),
ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),
ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}
@@ -551,82 +525,54 @@
fn get_cheap(&self, _index: usize) -> Option<Val> {
None
}
-}
-impl From<MappedArray> for ArrValue {
- fn from(value: MappedArray) -> Self {
- Self::Mapped(value)
+ fn is_cheap(&self) -> bool {
+ false
}
}
#[derive(Trace, Debug)]
-pub struct RepeatedArrayInner {
+pub struct RepeatedArray {
data: ArrValue,
repeats: usize,
total_len: usize,
}
-#[derive(Trace, Debug, Clone)]
-pub struct RepeatedArray(Cc<RepeatedArrayInner>);
impl RepeatedArray {
pub fn new(data: ArrValue, repeats: usize) -> Option<Self> {
let total_len = data.len().checked_mul(repeats)?;
- Some(Self(Cc::new(RepeatedArrayInner {
+ Some(Self {
data,
repeats,
total_len,
- })))
- }
- pub fn is_cheap(&self) -> bool {
- self.0.data.is_cheap()
+ })
}
}
impl ArrayLike for RepeatedArray {
fn len(&self) -> usize {
- self.0.total_len
+ self.total_len
}
fn get(&self, index: usize) -> Result<Option<Val>> {
- if index > self.0.total_len {
+ if index > self.total_len {
return Ok(None);
}
- self.0.data.get(index % self.0.data.len())
+ self.data.get(index % self.data.len())
}
fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
- if index > self.0.total_len {
+ if index > self.total_len {
return None;
}
- self.0.data.get_lazy(index % self.0.data.len())
+ self.data.get_lazy(index % self.data.len())
}
fn get_cheap(&self, index: usize) -> Option<Val> {
- if index > self.0.total_len {
+ if index > self.total_len {
return None;
}
- self.0.data.get_cheap(index % self.0.data.len())
+ self.data.get_cheap(index % self.data.len())
}
-}
-impl From<RepeatedArray> for ArrValue {
- fn from(value: RepeatedArray) -> Self {
- Self::Repeated(value)
+ fn is_cheap(&self) -> bool {
+ self.data.is_cheap()
}
}
-
-macro_rules! pass {
- ($t:ident.$m:ident($($ident:ident),*)) => {
- match $t {
- Self::Bytes(e) => e.$m($($ident)*),
- Self::Chars(e) => e.$m($($ident)*),
- Self::Expr(e) => e.$m($($ident)*),
- Self::Lazy(e) => e.$m($($ident)*),
- Self::Eager(e) => e.$m($($ident)*),
- Self::Range(e) => e.$m($($ident)*),
- Self::Slice(e) => e.$m($($ident)*),
- Self::Extended(e) => e.$m($($ident)*),
- Self::Reverse(e) => e.$m($($ident)*),
- Self::Mapped(e) => e.$m($($ident)*),
- Self::Repeated(e) => e.$m($($ident)*),
- }
- };
-}
-pub(super) use pass;
crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -573,10 +573,10 @@
evaluate(self.ctx, &self.item)
}
}
- Val::Arr(ArrValue::lazy(Cc::new(vec![Thunk::new(ArrayElement {
+ Val::Arr(ArrValue::lazy(vec![Thunk::new(ArrayElement {
ctx,
item: items[0].clone(),
- })])))
+ })]))
} else {
Val::Arr(ArrValue::expr(ctx, items.iter().cloned()))
}
crates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/obj.rs
+++ b/crates/jrsonnet-evaluator/src/obj.rs
@@ -17,7 +17,7 @@
operator::evaluate_add_op,
tb, throw,
val::ThunkValue,
- MaybeUnbound, Result, ResultExt, State, Thunk, Unbound, Val,
+ MaybeUnbound, Result, State, Thunk, Unbound, Val,
};
#[cfg(not(feature = "exp-preserve-order"))]
crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -6,7 +6,7 @@
use jrsonnet_types::{ComplexValType, ValType};
use crate::{
- arr::ArrValue,
+ arr::{ArrValue, BytesArray},
error::Result,
function::{native::NativeDesc, FuncDesc, FuncVal},
throw,
@@ -434,12 +434,13 @@
}
fn from_untyped(value: Val) -> Result<Self> {
- if let Val::Arr(ArrValue::Bytes(bytes)) = value {
- return Ok(bytes.0);
- }
- <Self as Typed>::TYPE.check(&value)?;
- match value {
+ match &value {
Val::Arr(a) => {
+ if let Some(bytes) = a.as_any().downcast_ref::<BytesArray>() {
+ return Ok(bytes.0.as_slice().into());
+ };
+ <Self as Typed>::TYPE.check(&value)?;
+ // Any::downcast_ref::<ByteArray>(&a);
let mut out = Vec::with_capacity(a.len());
for e in a.iter() {
let r = e?;
@@ -447,7 +448,10 @@
}
Ok(out.as_slice().into())
}
- _ => unreachable!(),
+ _ => {
+ <Self as Typed>::TYPE.check(&value)?;
+ unreachable!()
+ }
}
}
}
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth1use std::{2 cell::RefCell,3 fmt::{self, Debug, Display},4 mem::replace,5 rc::Rc,6};78use jrsonnet_gcmodule::{Cc, Trace};9use jrsonnet_interner::IStr;10use jrsonnet_types::ValType;1112pub use crate::arr::ArrValue;13use crate::{14 error::{Error, ErrorKind::*},15 function::FuncVal,16 gc::{GcHashMap, TraceBox},17 manifest::{ManifestFormat, ToStringFormat},18 tb, throw,19 typed::BoundedUsize,20 ObjValue, Result, Unbound, WeakObjValue,21};2223pub trait ThunkValue: Trace {24 type Output;25 fn get(self: Box<Self>) -> Result<Self::Output>;26}2728#[derive(Trace)]29enum ThunkInner<T: Trace> {30 Computed(T),31 Errored(Error),32 Waiting(TraceBox<dyn ThunkValue<Output = T>>),33 Pending,34}3536/// Lazily evaluated value37#[allow(clippy::module_name_repetitions)]38#[derive(Clone, Trace)]39pub struct Thunk<T: Trace>(Cc<RefCell<ThunkInner<T>>>);4041impl<T: Trace> Thunk<T> {42 pub fn evaluated(val: T) -> Self {43 Self(Cc::new(RefCell::new(ThunkInner::Computed(val))))44 }45 pub fn new(f: impl ThunkValue<Output = T> + 'static) -> Self {46 Self(Cc::new(RefCell::new(ThunkInner::Waiting(tb!(f)))))47 }48 pub fn errored(e: Error) -> Self {49 Self(Cc::new(RefCell::new(ThunkInner::Errored(e))))50 }51}5253impl<T> Thunk<T>54where55 T: Clone + Trace,56{57 pub fn force(&self) -> Result<()> {58 self.evaluate()?;59 Ok(())60 }6162 /// Evaluate thunk, or return cached value63 ///64 /// # Errors65 ///66 /// - Lazy value evaluation returned error67 /// - This method was called during inner value evaluation68 pub fn evaluate(&self) -> Result<T> {69 match &*self.0.borrow() {70 ThunkInner::Computed(v) => return Ok(v.clone()),71 ThunkInner::Errored(e) => return Err(e.clone()),72 ThunkInner::Pending => return Err(InfiniteRecursionDetected.into()),73 ThunkInner::Waiting(..) => (),74 };75 let ThunkInner::Waiting(value) = replace(&mut *self.0.borrow_mut(), ThunkInner::Pending)76 else {77 unreachable!();78 };79 let new_value = match value.0.get() {80 Ok(v) => v,81 Err(e) => {82 *self.0.borrow_mut() = ThunkInner::Errored(e.clone());83 return Err(e);84 }85 };86 *self.0.borrow_mut() = ThunkInner::Computed(new_value.clone());87 Ok(new_value)88 }89}9091pub trait ThunkMapper<Input>: Trace {92 type Output;93 fn map(self, from: Input) -> Result<Self::Output>;94}95impl<Input> Thunk<Input>96where97 Input: Trace + Clone,98{99 pub fn map<M>(self, mapper: M) -> Thunk<M::Output>100 where101 M: ThunkMapper<Input>,102 M::Output: Trace,103 {104 #[derive(Trace)]105 struct Mapped<Input: Trace, Mapper: Trace> {106 inner: Thunk<Input>,107 mapper: Mapper,108 }109 impl<Input, Mapper> ThunkValue for Mapped<Input, Mapper>110 where111 Input: Trace + Clone,112 Mapper: ThunkMapper<Input>,113 {114 type Output = Mapper::Output;115116 fn get(self: Box<Self>) -> Result<Self::Output> {117 let value = self.inner.evaluate()?;118 let mapped = self.mapper.map(value)?;119 Ok(mapped)120 }121 }122123 Thunk::new(Mapped::<Input, M> {124 inner: self,125 mapper,126 })127 }128}129130impl<T: Trace> From<Result<T>> for Thunk<T> {131 fn from(value: Result<T>) -> Self {132 match value {133 Ok(o) => Self::evaluated(o),134 Err(e) => Self::errored(e),135 }136 }137}138139impl<T: Trace + Default> Default for Thunk<T> {140 fn default() -> Self {141 Self::evaluated(T::default())142 }143}144145type CacheKey = (Option<WeakObjValue>, Option<WeakObjValue>);146147#[derive(Trace, Clone)]148pub struct CachedUnbound<I, T>149where150 I: Unbound<Bound = T>,151 T: Trace,152{153 cache: Cc<RefCell<GcHashMap<CacheKey, T>>>,154 value: I,155}156impl<I: Unbound<Bound = T>, T: Trace> CachedUnbound<I, T> {157 pub fn new(value: I) -> Self {158 Self {159 cache: Cc::new(RefCell::new(GcHashMap::new())),160 value,161 }162 }163}164impl<I: Unbound<Bound = T>, T: Clone + Trace> Unbound for CachedUnbound<I, T> {165 type Bound = T;166 fn bind(&self, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<T> {167 let cache_key = (168 sup.as_ref().map(|s| s.clone().downgrade()),169 this.as_ref().map(|t| t.clone().downgrade()),170 );171 {172 if let Some(t) = self.cache.borrow().get(&cache_key) {173 return Ok(t.clone());174 }175 }176 let bound = self.value.bind(sup, this)?;177178 {179 let mut cache = self.cache.borrow_mut();180 cache.insert(cache_key, bound.clone());181 }182183 Ok(bound)184 }185}186187impl<T: Debug + Trace> Debug for Thunk<T> {188 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {189 write!(f, "Lazy")190 }191}192impl<T: Trace> PartialEq for Thunk<T> {193 fn eq(&self, other: &Self) -> bool {194 Cc::ptr_eq(&self.0, &other.0)195 }196}197198/// Represents a Jsonnet value, which can be sliced or indexed (string or array).199#[allow(clippy::module_name_repetitions)]200pub enum IndexableVal {201 /// String.202 Str(IStr),203 /// Array.204 Arr(ArrValue),205}206impl IndexableVal {207 pub fn to_array(self) -> ArrValue {208 match self {209 IndexableVal::Str(s) => ArrValue::chars(s.chars()),210 IndexableVal::Arr(arr) => arr,211 }212 }213 /// Slice the value.214 ///215 /// # Implementation216 ///217 /// For strings, will create a copy of specified interval.218 ///219 /// For arrays, nothing will be copied on this call, instead [`ArrValue::Slice`] view will be returned.220 pub fn slice(221 self,222 index: Option<BoundedUsize<0, { i32::MAX as usize }>>,223 end: Option<BoundedUsize<0, { i32::MAX as usize }>>,224 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,225 ) -> Result<Self> {226 match &self {227 IndexableVal::Str(s) => {228 let index = index.as_deref().copied().unwrap_or(0);229 let end = end.as_deref().copied().unwrap_or(usize::MAX);230 let step = step.as_deref().copied().unwrap_or(1);231232 if index >= end {233 return Ok(Self::Str("".into()));234 }235236 Ok(Self::Str(237 (s.chars()238 .skip(index)239 .take(end - index)240 .step_by(step)241 .collect::<String>())242 .into(),243 ))244 }245 IndexableVal::Arr(arr) => {246 let index = index.as_deref().copied().unwrap_or(0);247 let end = end.as_deref().copied().unwrap_or(usize::MAX).min(arr.len());248 let step = step.as_deref().copied().unwrap_or(1);249250 if index >= end {251 return Ok(Self::Arr(ArrValue::empty()));252 }253254 Ok(Self::Arr(255 arr.clone()256 .slice(Some(index), Some(end), Some(step))257 .expect("arguments checked"),258 ))259 }260 }261 }262}263264#[derive(Debug, Clone, Trace)]265pub enum StrValue {266 Flat(IStr),267 Tree(Rc<(StrValue, StrValue, usize)>),268}269impl StrValue {270 pub fn concat(a: StrValue, b: StrValue) -> Self {271 // TODO: benchmark for an optimal value, currently just a arbitrary choice272 const STRING_EXTEND_THRESHOLD: usize = 100;273274 if a.is_empty() {275 b276 } else if b.is_empty() {277 a278 } else if a.len() + b.len() < STRING_EXTEND_THRESHOLD {279 Self::Flat(format!("{a}{b}").into())280 } else {281 let len = a.len() + b.len();282 Self::Tree(Rc::new((a, b, len)))283 }284 }285 pub fn into_flat(self) -> IStr {286 #[cold]287 fn write_buf(s: &StrValue, out: &mut String) {288 match s {289 StrValue::Flat(f) => out.push_str(f),290 StrValue::Tree(t) => {291 write_buf(&t.0, out);292 write_buf(&t.1, out);293 }294 }295 }296 match self {297 StrValue::Flat(f) => f,298 StrValue::Tree(_) => {299 let mut buf = String::with_capacity(self.len());300 write_buf(&self, &mut buf);301 buf.into()302 }303 }304 }305 pub fn len(&self) -> usize {306 match self {307 StrValue::Flat(v) => v.len(),308 StrValue::Tree(t) => t.2,309 }310 }311 pub fn is_empty(&self) -> bool {312 match self {313 Self::Flat(v) => v.is_empty(),314 // Can't create non-flat empty string315 Self::Tree(_) => false,316 }317 }318}319impl From<&str> for StrValue {320 fn from(value: &str) -> Self {321 Self::Flat(value.into())322 }323}324impl From<String> for StrValue {325 fn from(value: String) -> Self {326 Self::Flat(value.into())327 }328}329impl From<IStr> for StrValue {330 fn from(value: IStr) -> Self {331 Self::Flat(value)332 }333}334impl Display for StrValue {335 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {336 match self {337 StrValue::Flat(v) => write!(f, "{v}"),338 StrValue::Tree(t) => {339 write!(f, "{}", t.0)?;340 write!(f, "{}", t.1)341 }342 }343 }344}345impl PartialEq for StrValue {346 fn eq(&self, other: &Self) -> bool {347 let a = self.clone().into_flat();348 let b = other.clone().into_flat();349 a == b350 }351}352impl Eq for StrValue {}353impl PartialOrd for StrValue {354 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {355 Some(self.cmp(other))356 }357}358impl Ord for StrValue {359 fn cmp(&self, other: &Self) -> std::cmp::Ordering {360 let a = self.clone().into_flat();361 let b = other.clone().into_flat();362 a.cmp(&b)363 }364}365366/// Represents any valid Jsonnet value.367#[derive(Debug, Clone, Trace, Default)]368pub enum Val {369 /// Represents a Jsonnet boolean.370 Bool(bool),371 /// Represents a Jsonnet null value.372 #[default]373 Null,374 /// Represents a Jsonnet string.375 Str(StrValue),376 /// Represents a Jsonnet number.377 /// Should be finite, and not NaN378 /// This restriction isn't enforced by enum, as enum field can't be marked as private379 Num(f64),380 /// Experimental bigint381 #[cfg(feature = "exp-bigint")]382 BigInt(#[trace(skip)] Box<num_bigint::BigInt>),383 /// Represents a Jsonnet array.384 Arr(ArrValue),385 /// Represents a Jsonnet object.386 Obj(ObjValue),387 /// Represents a Jsonnet function.388 Func(FuncVal),389}390391#[cfg(target_pointer_width = "64")]392static_assertions::assert_eq_size!(Val, [u8; 24]);393394impl From<IndexableVal> for Val {395 fn from(v: IndexableVal) -> Self {396 match v {397 IndexableVal::Str(s) => Self::Str(StrValue::Flat(s)),398 IndexableVal::Arr(a) => Self::Arr(a),399 }400 }401}402403impl Val {404 pub const fn as_bool(&self) -> Option<bool> {405 match self {406 Self::Bool(v) => Some(*v),407 _ => None,408 }409 }410 pub const fn as_null(&self) -> Option<()> {411 match self {412 Self::Null => Some(()),413 _ => None,414 }415 }416 pub fn as_str(&self) -> Option<IStr> {417 match self {418 Self::Str(s) => Some(s.clone().into_flat()),419 _ => None,420 }421 }422 pub const fn as_num(&self) -> Option<f64> {423 match self {424 Self::Num(n) => Some(*n),425 _ => None,426 }427 }428 pub fn as_arr(&self) -> Option<ArrValue> {429 match self {430 Self::Arr(a) => Some(a.clone()),431 _ => None,432 }433 }434 pub fn as_obj(&self) -> Option<ObjValue> {435 match self {436 Self::Obj(o) => Some(o.clone()),437 _ => None,438 }439 }440 pub fn as_func(&self) -> Option<FuncVal> {441 match self {442 Self::Func(f) => Some(f.clone()),443 _ => None,444 }445 }446447 /// Creates `Val::Num` after checking for numeric overflow.448 /// As numbers are `f64`, we can just check for their finity.449 pub fn new_checked_num(num: f64) -> Result<Self> {450 if num.is_finite() {451 Ok(Self::Num(num))452 } else {453 throw!("overflow")454 }455 }456457 pub const fn value_type(&self) -> ValType {458 match self {459 Self::Str(..) => ValType::Str,460 Self::Num(..) => ValType::Num,461 #[cfg(feature = "exp-bigint")]462 Self::BigInt(..) => ValType::BigInt,463 Self::Arr(..) => ValType::Arr,464 Self::Obj(..) => ValType::Obj,465 Self::Bool(_) => ValType::Bool,466 Self::Null => ValType::Null,467 Self::Func(..) => ValType::Func,468 }469 }470471 pub fn manifest(&self, format: impl ManifestFormat) -> Result<String> {472 fn manifest_dyn(val: &Val, manifest: &dyn ManifestFormat) -> Result<String> {473 manifest.manifest(val.clone())474 }475 manifest_dyn(self, &format)476 }477478 pub fn to_string(&self) -> Result<IStr> {479 Ok(match self {480 Self::Bool(true) => "true".into(),481 Self::Bool(false) => "false".into(),482 Self::Null => "null".into(),483 Self::Str(s) => s.clone().into_flat(),484 _ => self.manifest(ToStringFormat).map(IStr::from)?,485 })486 }487488 pub fn into_indexable(self) -> Result<IndexableVal> {489 Ok(match self {490 Val::Str(s) => IndexableVal::Str(s.into_flat()),491 Val::Arr(arr) => IndexableVal::Arr(arr),492 _ => throw!(ValueIsNotIndexable(self.value_type())),493 })494 }495}496497const fn is_function_like(val: &Val) -> bool {498 matches!(val, Val::Func(_))499}500501/// Native implementation of `std.primitiveEquals`502pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {503 Ok(match (val_a, val_b) {504 (Val::Bool(a), Val::Bool(b)) => a == b,505 (Val::Null, Val::Null) => true,506 (Val::Str(a), Val::Str(b)) => a == b,507 (Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,508 #[cfg(feature = "exp-bigint")]509 (Val::BigInt(a), Val::BigInt(b)) => a == b,510 (Val::Arr(_), Val::Arr(_)) => {511 throw!("primitiveEquals operates on primitive types, got array")512 }513 (Val::Obj(_), Val::Obj(_)) => {514 throw!("primitiveEquals operates on primitive types, got object")515 }516 (a, b) if is_function_like(a) && is_function_like(b) => {517 throw!("cannot test equality of functions")518 }519 (_, _) => false,520 })521}522523/// Native implementation of `std.equals`524pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {525 if val_a.value_type() != val_b.value_type() {526 return Ok(false);527 }528 match (val_a, val_b) {529 (Val::Arr(a), Val::Arr(b)) => {530 if ArrValue::ptr_eq(a, b) {531 return Ok(true);532 }533 if a.len() != b.len() {534 return Ok(false);535 }536 for (a, b) in a.iter().zip(b.iter()) {537 if !equals(&a?, &b?)? {538 return Ok(false);539 }540 }541 Ok(true)542 }543 (Val::Obj(a), Val::Obj(b)) => {544 if ObjValue::ptr_eq(a, b) {545 return Ok(true);546 }547 let fields = a.fields(548 #[cfg(feature = "exp-preserve-order")]549 false,550 );551 if fields552 != b.fields(553 #[cfg(feature = "exp-preserve-order")]554 false,555 ) {556 return Ok(false);557 }558 for field in fields {559 if !equals(560 &a.get(field.clone())?.expect("field exists"),561 &b.get(field)?.expect("field exists"),562 )? {563 return Ok(false);564 }565 }566 Ok(true)567 }568 (a, b) => Ok(primitive_equals(a, b)?),569 }570}1use std::{2 cell::RefCell,3 fmt::{self, Debug, Display},4 mem::replace,5 rc::Rc,6};78use jrsonnet_gcmodule::{Cc, Trace};9use jrsonnet_interner::IStr;10use jrsonnet_types::ValType;1112pub use crate::arr::{ArrValue, ArrayLike};13use crate::{14 error::{Error, ErrorKind::*},15 function::FuncVal,16 gc::{GcHashMap, TraceBox},17 manifest::{ManifestFormat, ToStringFormat},18 tb, throw,19 typed::BoundedUsize,20 ObjValue, Result, Unbound, WeakObjValue,21};2223pub trait ThunkValue: Trace {24 type Output;25 fn get(self: Box<Self>) -> Result<Self::Output>;26}2728#[derive(Trace)]29enum ThunkInner<T: Trace> {30 Computed(T),31 Errored(Error),32 Waiting(TraceBox<dyn ThunkValue<Output = T>>),33 Pending,34}3536/// Lazily evaluated value37#[allow(clippy::module_name_repetitions)]38#[derive(Clone, Trace)]39pub struct Thunk<T: Trace>(Cc<RefCell<ThunkInner<T>>>);4041impl<T: Trace> Thunk<T> {42 pub fn evaluated(val: T) -> Self {43 Self(Cc::new(RefCell::new(ThunkInner::Computed(val))))44 }45 pub fn new(f: impl ThunkValue<Output = T> + 'static) -> Self {46 Self(Cc::new(RefCell::new(ThunkInner::Waiting(tb!(f)))))47 }48 pub fn errored(e: Error) -> Self {49 Self(Cc::new(RefCell::new(ThunkInner::Errored(e))))50 }51}5253impl<T> Thunk<T>54where55 T: Clone + Trace,56{57 pub fn force(&self) -> Result<()> {58 self.evaluate()?;59 Ok(())60 }6162 /// Evaluate thunk, or return cached value63 ///64 /// # Errors65 ///66 /// - Lazy value evaluation returned error67 /// - This method was called during inner value evaluation68 pub fn evaluate(&self) -> Result<T> {69 match &*self.0.borrow() {70 ThunkInner::Computed(v) => return Ok(v.clone()),71 ThunkInner::Errored(e) => return Err(e.clone()),72 ThunkInner::Pending => return Err(InfiniteRecursionDetected.into()),73 ThunkInner::Waiting(..) => (),74 };75 let ThunkInner::Waiting(value) = replace(&mut *self.0.borrow_mut(), ThunkInner::Pending)76 else {77 unreachable!();78 };79 let new_value = match value.0.get() {80 Ok(v) => v,81 Err(e) => {82 *self.0.borrow_mut() = ThunkInner::Errored(e.clone());83 return Err(e);84 }85 };86 *self.0.borrow_mut() = ThunkInner::Computed(new_value.clone());87 Ok(new_value)88 }89}9091pub trait ThunkMapper<Input>: Trace {92 type Output;93 fn map(self, from: Input) -> Result<Self::Output>;94}95impl<Input> Thunk<Input>96where97 Input: Trace + Clone,98{99 pub fn map<M>(self, mapper: M) -> Thunk<M::Output>100 where101 M: ThunkMapper<Input>,102 M::Output: Trace,103 {104 #[derive(Trace)]105 struct Mapped<Input: Trace, Mapper: Trace> {106 inner: Thunk<Input>,107 mapper: Mapper,108 }109 impl<Input, Mapper> ThunkValue for Mapped<Input, Mapper>110 where111 Input: Trace + Clone,112 Mapper: ThunkMapper<Input>,113 {114 type Output = Mapper::Output;115116 fn get(self: Box<Self>) -> Result<Self::Output> {117 let value = self.inner.evaluate()?;118 let mapped = self.mapper.map(value)?;119 Ok(mapped)120 }121 }122123 Thunk::new(Mapped::<Input, M> {124 inner: self,125 mapper,126 })127 }128}129130impl<T: Trace> From<Result<T>> for Thunk<T> {131 fn from(value: Result<T>) -> Self {132 match value {133 Ok(o) => Self::evaluated(o),134 Err(e) => Self::errored(e),135 }136 }137}138139impl<T: Trace + Default> Default for Thunk<T> {140 fn default() -> Self {141 Self::evaluated(T::default())142 }143}144145type CacheKey = (Option<WeakObjValue>, Option<WeakObjValue>);146147#[derive(Trace, Clone)]148pub struct CachedUnbound<I, T>149where150 I: Unbound<Bound = T>,151 T: Trace,152{153 cache: Cc<RefCell<GcHashMap<CacheKey, T>>>,154 value: I,155}156impl<I: Unbound<Bound = T>, T: Trace> CachedUnbound<I, T> {157 pub fn new(value: I) -> Self {158 Self {159 cache: Cc::new(RefCell::new(GcHashMap::new())),160 value,161 }162 }163}164impl<I: Unbound<Bound = T>, T: Clone + Trace> Unbound for CachedUnbound<I, T> {165 type Bound = T;166 fn bind(&self, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<T> {167 let cache_key = (168 sup.as_ref().map(|s| s.clone().downgrade()),169 this.as_ref().map(|t| t.clone().downgrade()),170 );171 {172 if let Some(t) = self.cache.borrow().get(&cache_key) {173 return Ok(t.clone());174 }175 }176 let bound = self.value.bind(sup, this)?;177178 {179 let mut cache = self.cache.borrow_mut();180 cache.insert(cache_key, bound.clone());181 }182183 Ok(bound)184 }185}186187impl<T: Debug + Trace> Debug for Thunk<T> {188 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {189 write!(f, "Lazy")190 }191}192impl<T: Trace> PartialEq for Thunk<T> {193 fn eq(&self, other: &Self) -> bool {194 Cc::ptr_eq(&self.0, &other.0)195 }196}197198/// Represents a Jsonnet value, which can be sliced or indexed (string or array).199#[allow(clippy::module_name_repetitions)]200pub enum IndexableVal {201 /// String.202 Str(IStr),203 /// Array.204 Arr(ArrValue),205}206impl IndexableVal {207 pub fn to_array(self) -> ArrValue {208 match self {209 IndexableVal::Str(s) => ArrValue::chars(s.chars()),210 IndexableVal::Arr(arr) => arr,211 }212 }213 /// Slice the value.214 ///215 /// # Implementation216 ///217 /// For strings, will create a copy of specified interval.218 ///219 /// For arrays, nothing will be copied on this call, instead [`ArrValue::Slice`] view will be returned.220 pub fn slice(221 self,222 index: Option<BoundedUsize<0, { i32::MAX as usize }>>,223 end: Option<BoundedUsize<0, { i32::MAX as usize }>>,224 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,225 ) -> Result<Self> {226 match &self {227 IndexableVal::Str(s) => {228 let index = index.as_deref().copied().unwrap_or(0);229 let end = end.as_deref().copied().unwrap_or(usize::MAX);230 let step = step.as_deref().copied().unwrap_or(1);231232 if index >= end {233 return Ok(Self::Str("".into()));234 }235236 Ok(Self::Str(237 (s.chars()238 .skip(index)239 .take(end - index)240 .step_by(step)241 .collect::<String>())242 .into(),243 ))244 }245 IndexableVal::Arr(arr) => {246 let index = index.as_deref().copied().unwrap_or(0);247 let end = end.as_deref().copied().unwrap_or(usize::MAX).min(arr.len());248 let step = step.as_deref().copied().unwrap_or(1);249250 if index >= end {251 return Ok(Self::Arr(ArrValue::empty()));252 }253254 Ok(Self::Arr(255 arr.clone()256 .slice(Some(index), Some(end), Some(step))257 .expect("arguments checked"),258 ))259 }260 }261 }262}263264#[derive(Debug, Clone, Trace)]265pub enum StrValue {266 Flat(IStr),267 Tree(Rc<(StrValue, StrValue, usize)>),268}269impl StrValue {270 pub fn concat(a: StrValue, b: StrValue) -> Self {271 // TODO: benchmark for an optimal value, currently just a arbitrary choice272 const STRING_EXTEND_THRESHOLD: usize = 100;273274 if a.is_empty() {275 b276 } else if b.is_empty() {277 a278 } else if a.len() + b.len() < STRING_EXTEND_THRESHOLD {279 Self::Flat(format!("{a}{b}").into())280 } else {281 let len = a.len() + b.len();282 Self::Tree(Rc::new((a, b, len)))283 }284 }285 pub fn into_flat(self) -> IStr {286 #[cold]287 fn write_buf(s: &StrValue, out: &mut String) {288 match s {289 StrValue::Flat(f) => out.push_str(f),290 StrValue::Tree(t) => {291 write_buf(&t.0, out);292 write_buf(&t.1, out);293 }294 }295 }296 match self {297 StrValue::Flat(f) => f,298 StrValue::Tree(_) => {299 let mut buf = String::with_capacity(self.len());300 write_buf(&self, &mut buf);301 buf.into()302 }303 }304 }305 pub fn len(&self) -> usize {306 match self {307 StrValue::Flat(v) => v.len(),308 StrValue::Tree(t) => t.2,309 }310 }311 pub fn is_empty(&self) -> bool {312 match self {313 Self::Flat(v) => v.is_empty(),314 // Can't create non-flat empty string315 Self::Tree(_) => false,316 }317 }318}319impl From<&str> for StrValue {320 fn from(value: &str) -> Self {321 Self::Flat(value.into())322 }323}324impl From<String> for StrValue {325 fn from(value: String) -> Self {326 Self::Flat(value.into())327 }328}329impl From<IStr> for StrValue {330 fn from(value: IStr) -> Self {331 Self::Flat(value)332 }333}334impl Display for StrValue {335 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {336 match self {337 StrValue::Flat(v) => write!(f, "{v}"),338 StrValue::Tree(t) => {339 write!(f, "{}", t.0)?;340 write!(f, "{}", t.1)341 }342 }343 }344}345impl PartialEq for StrValue {346 fn eq(&self, other: &Self) -> bool {347 let a = self.clone().into_flat();348 let b = other.clone().into_flat();349 a == b350 }351}352impl Eq for StrValue {}353impl PartialOrd for StrValue {354 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {355 Some(self.cmp(other))356 }357}358impl Ord for StrValue {359 fn cmp(&self, other: &Self) -> std::cmp::Ordering {360 let a = self.clone().into_flat();361 let b = other.clone().into_flat();362 a.cmp(&b)363 }364}365366/// Represents any valid Jsonnet value.367#[derive(Debug, Clone, Trace, Default)]368pub enum Val {369 /// Represents a Jsonnet boolean.370 Bool(bool),371 /// Represents a Jsonnet null value.372 #[default]373 Null,374 /// Represents a Jsonnet string.375 Str(StrValue),376 /// Represents a Jsonnet number.377 /// Should be finite, and not NaN378 /// This restriction isn't enforced by enum, as enum field can't be marked as private379 Num(f64),380 /// Experimental bigint381 #[cfg(feature = "exp-bigint")]382 BigInt(#[trace(skip)] Box<num_bigint::BigInt>),383 /// Represents a Jsonnet array.384 Arr(ArrValue),385 /// Represents a Jsonnet object.386 Obj(ObjValue),387 /// Represents a Jsonnet function.388 Func(FuncVal),389}390391#[cfg(target_pointer_width = "64")]392static_assertions::assert_eq_size!(Val, [u8; 24]);393394impl From<IndexableVal> for Val {395 fn from(v: IndexableVal) -> Self {396 match v {397 IndexableVal::Str(s) => Self::Str(StrValue::Flat(s)),398 IndexableVal::Arr(a) => Self::Arr(a),399 }400 }401}402403impl Val {404 pub const fn as_bool(&self) -> Option<bool> {405 match self {406 Self::Bool(v) => Some(*v),407 _ => None,408 }409 }410 pub const fn as_null(&self) -> Option<()> {411 match self {412 Self::Null => Some(()),413 _ => None,414 }415 }416 pub fn as_str(&self) -> Option<IStr> {417 match self {418 Self::Str(s) => Some(s.clone().into_flat()),419 _ => None,420 }421 }422 pub const fn as_num(&self) -> Option<f64> {423 match self {424 Self::Num(n) => Some(*n),425 _ => None,426 }427 }428 pub fn as_arr(&self) -> Option<ArrValue> {429 match self {430 Self::Arr(a) => Some(a.clone()),431 _ => None,432 }433 }434 pub fn as_obj(&self) -> Option<ObjValue> {435 match self {436 Self::Obj(o) => Some(o.clone()),437 _ => None,438 }439 }440 pub fn as_func(&self) -> Option<FuncVal> {441 match self {442 Self::Func(f) => Some(f.clone()),443 _ => None,444 }445 }446447 /// Creates `Val::Num` after checking for numeric overflow.448 /// As numbers are `f64`, we can just check for their finity.449 pub fn new_checked_num(num: f64) -> Result<Self> {450 if num.is_finite() {451 Ok(Self::Num(num))452 } else {453 throw!("overflow")454 }455 }456457 pub const fn value_type(&self) -> ValType {458 match self {459 Self::Str(..) => ValType::Str,460 Self::Num(..) => ValType::Num,461 #[cfg(feature = "exp-bigint")]462 Self::BigInt(..) => ValType::BigInt,463 Self::Arr(..) => ValType::Arr,464 Self::Obj(..) => ValType::Obj,465 Self::Bool(_) => ValType::Bool,466 Self::Null => ValType::Null,467 Self::Func(..) => ValType::Func,468 }469 }470471 pub fn manifest(&self, format: impl ManifestFormat) -> Result<String> {472 fn manifest_dyn(val: &Val, manifest: &dyn ManifestFormat) -> Result<String> {473 manifest.manifest(val.clone())474 }475 manifest_dyn(self, &format)476 }477478 pub fn to_string(&self) -> Result<IStr> {479 Ok(match self {480 Self::Bool(true) => "true".into(),481 Self::Bool(false) => "false".into(),482 Self::Null => "null".into(),483 Self::Str(s) => s.clone().into_flat(),484 _ => self.manifest(ToStringFormat).map(IStr::from)?,485 })486 }487488 pub fn into_indexable(self) -> Result<IndexableVal> {489 Ok(match self {490 Val::Str(s) => IndexableVal::Str(s.into_flat()),491 Val::Arr(arr) => IndexableVal::Arr(arr),492 _ => throw!(ValueIsNotIndexable(self.value_type())),493 })494 }495}496497const fn is_function_like(val: &Val) -> bool {498 matches!(val, Val::Func(_))499}500501/// Native implementation of `std.primitiveEquals`502pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {503 Ok(match (val_a, val_b) {504 (Val::Bool(a), Val::Bool(b)) => a == b,505 (Val::Null, Val::Null) => true,506 (Val::Str(a), Val::Str(b)) => a == b,507 (Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,508 #[cfg(feature = "exp-bigint")]509 (Val::BigInt(a), Val::BigInt(b)) => a == b,510 (Val::Arr(_), Val::Arr(_)) => {511 throw!("primitiveEquals operates on primitive types, got array")512 }513 (Val::Obj(_), Val::Obj(_)) => {514 throw!("primitiveEquals operates on primitive types, got object")515 }516 (a, b) if is_function_like(a) && is_function_like(b) => {517 throw!("cannot test equality of functions")518 }519 (_, _) => false,520 })521}522523/// Native implementation of `std.equals`524pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {525 if val_a.value_type() != val_b.value_type() {526 return Ok(false);527 }528 match (val_a, val_b) {529 (Val::Arr(a), Val::Arr(b)) => {530 if ArrValue::ptr_eq(a, b) {531 return Ok(true);532 }533 if a.len() != b.len() {534 return Ok(false);535 }536 for (a, b) in a.iter().zip(b.iter()) {537 if !equals(&a?, &b?)? {538 return Ok(false);539 }540 }541 Ok(true)542 }543 (Val::Obj(a), Val::Obj(b)) => {544 if ObjValue::ptr_eq(a, b) {545 return Ok(true);546 }547 let fields = a.fields(548 #[cfg(feature = "exp-preserve-order")]549 false,550 );551 if fields552 != b.fields(553 #[cfg(feature = "exp-preserve-order")]554 false,555 ) {556 return Ok(false);557 }558 for field in fields {559 if !equals(560 &a.get(field.clone())?.expect("field exists"),561 &b.get(field)?.expect("field exists"),562 )? {563 return Ok(false);564 }565 }566 Ok(true)567 }568 (a, b) => Ok(primitive_equals(a, b)?),569 }570}crates/jrsonnet-stdlib/src/sets.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/sets.rs
+++ b/crates/jrsonnet-stdlib/src/sets.rs
@@ -7,7 +7,6 @@
val::ArrValue,
Thunk, Val,
};
-use jrsonnet_gcmodule::Cc;
use jrsonnet_parser::BinaryOpType;
#[builtin]
@@ -70,5 +69,5 @@
}
};
}
- Ok(ArrValue::lazy(Cc::new(out)))
+ Ok(ArrValue::lazy(out))
}
crates/jrsonnet-stdlib/src/sort.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/sort.rs
+++ b/crates/jrsonnet-stdlib/src/sort.rs
@@ -10,7 +10,6 @@
val::{equals, ArrValue},
Thunk, Val,
};
-use jrsonnet_gcmodule::Cc;
use jrsonnet_parser::BinaryOpType;
use crate::eval_on_empty;
@@ -136,7 +135,7 @@
values.iter().collect::<Result<Vec<Val>>>()?,
)?))
} else {
- Ok(ArrValue::lazy(Cc::new(sort_keyf(values, key_getter)?)))
+ Ok(ArrValue::lazy(sort_keyf(values, key_getter)?))
}
}
@@ -186,7 +185,7 @@
arr.iter().collect::<Result<Vec<Val>>>()?,
)?))
} else {
- Ok(ArrValue::lazy(Cc::new(uniq_keyf(arr, keyF)?)))
+ Ok(ArrValue::lazy(uniq_keyf(arr, keyF)?))
}
}
@@ -204,8 +203,8 @@
Ok(ArrValue::eager(arr))
} else {
let arr = sort_keyf(arr, keyF.clone())?;
- let arr = uniq_keyf(ArrValue::lazy(Cc::new(arr)), keyF)?;
- Ok(ArrValue::lazy(Cc::new(arr)))
+ let arr = uniq_keyf(ArrValue::lazy(arr), keyF)?;
+ Ok(ArrValue::lazy(arr))
}
}