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.rsdiffbeforeafterboth1use std::{2 cell::RefCell,3 fmt::Debug,4 hash::{Hash, Hasher},5 ptr::addr_of,6};78use jrsonnet_gcmodule::{Cc, Trace, Weak};9use jrsonnet_interner::IStr;10use jrsonnet_parser::{ExprLocation, Visibility};11use rustc_hash::FxHashMap;1213use crate::{14 error::{Error, ErrorKind::*},15 function::CallLocation,16 gc::{GcHashMap, GcHashSet, TraceBox},17 operator::evaluate_add_op,18 tb, throw,19 val::ThunkValue,20 MaybeUnbound, Result, ResultExt, State, Thunk, Unbound, Val,21};2223#[cfg(not(feature = "exp-preserve-order"))]24mod ordering {25 #![allow(26 // This module works as stub for preserve-order feature27 clippy::unused_self,28 )]2930 use jrsonnet_gcmodule::Trace;3132 #[derive(Clone, Copy, Default, Debug, Trace)]33 pub struct FieldIndex(());34 impl FieldIndex {35 pub const fn next(self) -> Self {36 Self(())37 }38 }3940 #[derive(Clone, Copy, Default, Debug, Trace)]41 pub struct SuperDepth(());42 impl SuperDepth {43 pub const fn deeper(self) -> Self {44 Self(())45 }46 }4748 #[derive(Clone, Copy)]49 pub struct FieldSortKey(());50 impl FieldSortKey {51 pub const fn new(_: SuperDepth, _: FieldIndex) -> Self {52 Self(())53 }54 }55}5657#[cfg(feature = "exp-preserve-order")]58mod ordering {59 use std::cmp::{Ordering, Reverse};6061 use jrsonnet_gcmodule::Trace;6263 #[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]64 pub struct FieldIndex(u32);65 impl FieldIndex {66 pub fn next(self) -> Self {67 Self(self.0 + 1)68 }69 }7071 #[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Debug)]72 pub struct SuperDepth(u32);73 impl SuperDepth {74 pub fn deeper(self) -> Self {75 Self(self.0 + 1)76 }77 }7879 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]80 pub struct FieldSortKey(Reverse<SuperDepth>, FieldIndex);81 impl FieldSortKey {82 pub fn new(depth: SuperDepth, index: FieldIndex) -> Self {83 Self(Reverse(depth), index)84 }85 pub fn collide(self, other: Self) -> Self {86 match self.0 .0.cmp(&other.0 .0) {87 Ordering::Greater => self,88 Ordering::Less => other,89 Ordering::Equal => unreachable!("object can't have two fields with the same name"),90 }91 }92 }93}9495use ordering::*;9697#[allow(clippy::module_name_repetitions)]98#[derive(Debug, Trace)]99pub struct ObjMember {100 pub add: bool,101 pub visibility: Visibility,102 original_index: FieldIndex,103 pub invoke: MaybeUnbound,104 pub location: Option<ExprLocation>,105}106107pub trait ObjectAssertion: Trace {108 fn run(&self, super_obj: Option<ObjValue>, this: Option<ObjValue>) -> Result<()>;109}110111// Field => This112113#[derive(Trace)]114enum CacheValue {115 Cached(Val),116 NotFound,117 Pending,118 Errored(Error),119}120121#[allow(clippy::module_name_repetitions)]122#[derive(Trace)]123#[trace(tracking(force))]124pub struct ObjValueInternals {125 sup: Option<ObjValue>,126 this: Option<ObjValue>,127128 assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,129 assertions_ran: RefCell<GcHashSet<ObjValue>>,130 this_entries: Cc<GcHashMap<IStr, ObjMember>>,131 value_cache: RefCell<GcHashMap<(IStr, Option<WeakObjValue>), CacheValue>>,132}133134#[derive(Clone, Trace)]135pub struct WeakObjValue(#[trace(skip)] pub(crate) Weak<ObjValueInternals>);136137impl PartialEq for WeakObjValue {138 fn eq(&self, other: &Self) -> bool {139 Weak::ptr_eq(&self.0, &other.0)140 }141}142143impl Eq for WeakObjValue {}144impl Hash for WeakObjValue {145 fn hash<H: Hasher>(&self, hasher: &mut H) {146 // Safety: usize is POD147 let addr = unsafe { *std::ptr::addr_of!(self.0).cast() };148 hasher.write_usize(addr);149 }150}151152#[allow(clippy::module_name_repetitions)]153#[derive(Clone, Trace)]154pub struct ObjValue(pub(crate) Cc<ObjValueInternals>);155impl Debug for ObjValue {156 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {157 if let Some(super_obj) = self.0.sup.as_ref() {158 if f.alternate() {159 write!(f, "{super_obj:#?}")?;160 } else {161 write!(f, "{super_obj:?}")?;162 }163 write!(f, " + ")?;164 }165 let mut debug = f.debug_struct("ObjValue");166 for (name, member) in self.0.this_entries.iter() {167 debug.field(name, member);168 }169 debug.finish_non_exhaustive()170 }171}172173impl ObjValue {174 pub fn new(175 sup: Option<Self>,176 this_entries: Cc<GcHashMap<IStr, ObjMember>>,177 assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,178 ) -> Self {179 Self(Cc::new(ObjValueInternals {180 sup,181 this: None,182 assertions,183 assertions_ran: RefCell::new(GcHashSet::new()),184 this_entries,185 value_cache: RefCell::new(GcHashMap::new()),186 }))187 }188 pub fn new_empty() -> Self {189 Self::new(None, Cc::new(GcHashMap::new()), Cc::new(Vec::new()))190 }191 pub fn builder() -> ObjValueBuilder {192 ObjValueBuilder::new()193 }194 pub fn builder_with_capacity(capacity: usize) -> ObjValueBuilder {195 ObjValueBuilder::with_capacity(capacity)196 }197 #[must_use]198 pub fn extend_from(&self, sup: Self) -> Self {199 match &self.0.sup {200 None => Self::new(201 Some(sup),202 self.0.this_entries.clone(),203 self.0.assertions.clone(),204 ),205 Some(v) => Self::new(206 Some(v.extend_from(sup)),207 self.0.this_entries.clone(),208 self.0.assertions.clone(),209 ),210 }211 }212 pub(crate) fn extend_with_raw_member(self, key: IStr, value: ObjMember) -> Self {213 let mut new = GcHashMap::with_capacity(1);214 new.insert(key, value);215 Self::new(Some(self), Cc::new(new), Cc::new(Vec::new()))216 }217 pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder<'_>> {218 ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())219 }220221 #[must_use]222 pub fn with_this(&self, this: Self) -> Self {223 Self(Cc::new(ObjValueInternals {224 sup: self.0.sup.clone(),225 assertions: self.0.assertions.clone(),226 assertions_ran: RefCell::new(GcHashSet::new()),227 this: Some(this),228 this_entries: self.0.this_entries.clone(),229 value_cache: RefCell::new(GcHashMap::new()),230 }))231 }232233 pub fn len(&self) -> usize {234 self.fields_visibility()235 .into_iter()236 .filter(|(_, (visible, _))| *visible)237 .count()238 }239240 pub fn is_empty(&self) -> bool {241 if !self.0.this_entries.is_empty() {242 return false;243 }244 self.0.sup.as_ref().map_or(true, Self::is_empty)245 }246247 /// Run callback for every field found in object248 ///249 /// Returns true if ended prematurely250 pub(crate) fn enum_fields(251 &self,252 depth: SuperDepth,253 handler: &mut impl FnMut(SuperDepth, &IStr, &ObjMember) -> bool,254 ) -> bool {255 if let Some(s) = &self.0.sup {256 if s.enum_fields(depth.deeper(), handler) {257 return true;258 }259 }260 for (name, member) in self.0.this_entries.iter() {261 if handler(depth, name, member) {262 return true;263 }264 }265 false266 }267268 pub fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {269 let mut out = FxHashMap::default();270 self.enum_fields(SuperDepth::default(), &mut |depth, name, member| {271 let new_sort_key = FieldSortKey::new(depth, member.original_index);272 let entry = out.entry(name.clone());273 let (visible, _) = entry.or_insert((true, new_sort_key));274 match member.visibility {275 Visibility::Normal => {}276 Visibility::Hidden => {277 *visible = false;278 }279 Visibility::Unhide => {280 *visible = true;281 }282 };283 false284 });285 out286 }287 pub fn fields_ex(288 &self,289 include_hidden: bool,290 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,291 ) -> Vec<IStr> {292 #[cfg(feature = "exp-preserve-order")]293 if preserve_order {294 let (mut fields, mut keys): (Vec<_>, Vec<_>) = self295 .fields_visibility()296 .into_iter()297 .filter(|(_, (visible, _))| include_hidden || *visible)298 .enumerate()299 .map(|(idx, (k, (_, sk)))| (k, (sk, idx)))300 .unzip();301 keys.sort_unstable_by_key(|v| v.0);302 // Reorder in-place by resulting indexes303 for i in 0..fields.len() {304 let x = fields[i].clone();305 let mut j = i;306 loop {307 let k = keys[j].1;308 keys[j].1 = j;309 if k == i {310 break;311 }312 fields[j] = fields[k].clone();313 j = k;314 }315 fields[j] = x;316 }317 return fields;318 }319320 let mut fields: Vec<_> = self321 .fields_visibility()322 .into_iter()323 .filter(|(_, (visible, _))| include_hidden || *visible)324 .map(|(k, _)| k)325 .collect();326 fields.sort_unstable();327 fields328 }329 pub fn fields(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Vec<IStr> {330 self.fields_ex(331 false,332 #[cfg(feature = "exp-preserve-order")]333 preserve_order,334 )335 }336337 pub fn field_visibility(&self, name: IStr) -> Option<Visibility> {338 if let Some(m) = self.0.this_entries.get(&name) {339 Some(match &m.visibility {340 Visibility::Normal => self341 .0342 .sup343 .as_ref()344 .and_then(|super_obj| super_obj.field_visibility(name))345 .unwrap_or(Visibility::Normal),346 v => *v,347 })348 } else if let Some(super_obj) = &self.0.sup {349 super_obj.field_visibility(name)350 } else {351 None352 }353 }354355 fn has_field_include_hidden(&self, name: IStr) -> bool {356 if self.0.this_entries.contains_key(&name) {357 true358 } else if let Some(super_obj) = &self.0.sup {359 super_obj.has_field_include_hidden(name)360 } else {361 false362 }363 }364365 pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {366 if include_hidden {367 self.has_field_include_hidden(name)368 } else {369 self.has_field(name)370 }371 }372 pub fn has_field(&self, name: IStr) -> bool {373 self.field_visibility(name)374 .map_or(false, |v| v.is_visible())375 }376377 pub fn iter(378 &self,379 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,380 ) -> impl Iterator<Item = (IStr, Result<Val>)> + '_ {381 let fields = self.fields(382 #[cfg(feature = "exp-preserve-order")]383 preserve_order,384 );385 fields.into_iter().map(|field| {386 (387 field.clone(),388 self.get(field)389 .map(|opt| opt.expect("iterating over keys, field exists")),390 )391 })392 }393 pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {394 #[derive(Trace)]395 struct ThunkGet {396 obj: ObjValue,397 key: IStr,398 }399 impl ThunkValue for ThunkGet {400 type Output = Val;401402 fn get(self: Box<Self>) -> Result<Self::Output> {403 Ok(self.obj.get(self.key)?.expect("field exists"))404 }405 }406407 if !self.has_field_ex(key.clone(), true) {408 return None;409 }410 Some(Thunk::new(ThunkGet {411 obj: self.clone(),412 key,413 }))414 }415 pub fn get(&self, key: IStr) -> Result<Option<Val>> {416 self.get_for(key, self.0.this.clone().unwrap_or_else(|| self.clone()))417 }418 pub fn get_for(&self, key: IStr, this: Self) -> Result<Option<Val>> {419 self.run_assertions()?;420 let cache_key = (421 key.clone(),422 (!ObjValue::ptr_eq(&this, self)).then(|| this.clone().downgrade()),423 );424 if let Some(v) = self.0.value_cache.borrow().get(&cache_key) {425 return Ok(match v {426 CacheValue::Cached(v) => Some(v.clone()),427 CacheValue::NotFound => None,428 CacheValue::Pending => throw!(InfiniteRecursionDetected),429 CacheValue::Errored(e) => return Err(e.clone()),430 });431 }432 self.0433 .value_cache434 .borrow_mut()435 .insert(cache_key.clone(), CacheValue::Pending);436 let value = self.get_raw(key, this).map_err(|e| {437 self.0438 .value_cache439 .borrow_mut()440 .insert(cache_key.clone(), CacheValue::Errored(e.clone()));441 e442 })?;443 self.0.value_cache.borrow_mut().insert(444 cache_key,445 value446 .as_ref()447 .map_or(CacheValue::NotFound, |v| CacheValue::Cached(v.clone())),448 );449 Ok(value)450 }451452 fn get_raw(&self, key: IStr, real_this: Self) -> Result<Option<Val>> {453 match (self.0.this_entries.get(&key), &self.0.sup) {454 (Some(k), None) => Ok(Some(self.evaluate_this(k, real_this)?)),455 (Some(k), Some(super_obj)) => {456 let our = self.evaluate_this(k, real_this.clone())?;457 if k.add {458 super_obj459 .get_raw(key, real_this)?460 .map_or(Ok(Some(our.clone())), |v| {461 Ok(Some(evaluate_add_op(&v, &our)?))462 })463 } else {464 Ok(Some(our))465 }466 }467 (None, Some(super_obj)) => super_obj.get_raw(key, real_this),468 (None, None) => Ok(None),469 }470 }471 fn evaluate_this(&self, v: &ObjMember, real_this: Self) -> Result<Val> {472 v.invoke.evaluate(self.0.sup.clone(), Some(real_this))473 }474475 fn run_assertions_raw(&self, real_this: &Self) -> Result<()> {476 if self.0.assertions.is_empty() {477 if let Some(super_obj) = &self.0.sup {478 super_obj.run_assertions_raw(real_this)?;479 }480 return Ok(());481 }482 if self.0.assertions_ran.borrow_mut().insert(real_this.clone()) {483 for assertion in self.0.assertions.iter() {484 if let Err(e) = assertion.run(self.0.sup.clone(), Some(real_this.clone())) {485 self.0.assertions_ran.borrow_mut().remove(real_this);486 return Err(e);487 }488 }489 if let Some(super_obj) = &self.0.sup {490 super_obj.run_assertions_raw(real_this)?;491 }492 }493 Ok(())494 }495 pub fn run_assertions(&self) -> Result<()> {496 self.run_assertions_raw(self)497 }498499 pub fn ptr_eq(a: &Self, b: &Self) -> bool {500 Cc::ptr_eq(&a.0, &b.0)501 }502 pub fn downgrade(self) -> WeakObjValue {503 WeakObjValue(self.0.downgrade())504 }505}506507impl PartialEq for ObjValue {508 fn eq(&self, other: &Self) -> bool {509 Cc::ptr_eq(&self.0, &other.0)510 }511}512513impl Eq for ObjValue {}514impl Hash for ObjValue {515 fn hash<H: Hasher>(&self, hasher: &mut H) {516 hasher.write_usize(addr_of!(*self.0) as usize);517 }518}519520#[allow(clippy::module_name_repetitions)]521pub struct ObjValueBuilder {522 sup: Option<ObjValue>,523 map: GcHashMap<IStr, ObjMember>,524 assertions: Vec<TraceBox<dyn ObjectAssertion>>,525 next_field_index: FieldIndex,526}527impl ObjValueBuilder {528 pub fn new() -> Self {529 Self::with_capacity(0)530 }531 pub fn with_capacity(capacity: usize) -> Self {532 Self {533 sup: None,534 map: GcHashMap::with_capacity(capacity),535 assertions: Vec::new(),536 next_field_index: FieldIndex::default(),537 }538 }539 pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self {540 self.assertions.reserve_exact(capacity);541 self542 }543 pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {544 self.sup = Some(super_obj);545 self546 }547548 pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {549 self.assertions.push(tb!(assertion));550 self551 }552 pub fn member(&mut self, name: IStr) -> ObjMemberBuilder<ValueBuilder<'_>> {553 let field_index = self.next_field_index;554 self.next_field_index = self.next_field_index.next();555 ObjMemberBuilder::new(ValueBuilder(self), name, field_index)556 }557558 pub fn build(self) -> ObjValue {559 ObjValue::new(self.sup, Cc::new(self.map), Cc::new(self.assertions))560 }561}562impl Default for ObjValueBuilder {563 fn default() -> Self {564 Self::with_capacity(0)565 }566}567568#[allow(clippy::module_name_repetitions)]569#[must_use = "value not added unless binding() was called"]570pub struct ObjMemberBuilder<Kind> {571 kind: Kind,572 name: IStr,573 add: bool,574 visibility: Visibility,575 original_index: FieldIndex,576 location: Option<ExprLocation>,577}578579#[allow(clippy::missing_const_for_fn)]580impl<Kind> ObjMemberBuilder<Kind> {581 pub(crate) fn new(kind: Kind, name: IStr, original_index: FieldIndex) -> Self {582 Self {583 kind,584 name,585 original_index,586 add: false,587 visibility: Visibility::Normal,588 location: None,589 }590 }591592 pub const fn with_add(mut self, add: bool) -> Self {593 self.add = add;594 self595 }596 pub fn add(self) -> Self {597 self.with_add(true)598 }599 pub fn with_visibility(mut self, visibility: Visibility) -> Self {600 self.visibility = visibility;601 self602 }603 pub fn hide(self) -> Self {604 self.with_visibility(Visibility::Hidden)605 }606 pub fn with_location(mut self, location: ExprLocation) -> Self {607 self.location = Some(location);608 self609 }610 fn build_member(self, binding: MaybeUnbound) -> (Kind, IStr, ObjMember) {611 (612 self.kind,613 self.name,614 ObjMember {615 add: self.add,616 visibility: self.visibility,617 original_index: self.original_index,618 invoke: binding,619 location: self.location,620 },621 )622 }623}624625pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);626impl ObjMemberBuilder<ValueBuilder<'_>> {627 /// Inserts value, replacing if it is already defined628 pub fn value_unchecked(self, value: Val) {629 let (receiver, name, member) =630 self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value)));631 let entry = receiver.0.map.entry(name);632 entry.insert(member);633 }634635 pub fn value(self, value: Val) -> Result<()> {636 self.thunk(Thunk::evaluated(value))637 }638 pub fn thunk(self, value: Thunk<Val>) -> Result<()> {639 self.binding(MaybeUnbound::Bound(value))640 }641 pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {642 self.binding(MaybeUnbound::Unbound(Cc::new(tb!(bindable))))643 }644 pub fn binding(self, binding: MaybeUnbound) -> Result<()> {645 let (receiver, name, member) = self.build_member(binding);646 let location = member.location.clone();647 let old = receiver.0.map.insert(name.clone(), member);648 if old.is_some() {649 State::push(650 CallLocation(location.as_ref()),651 || format!("field <{}> initializtion", name.clone()),652 || throw!(DuplicateFieldName(name.clone())),653 )?;654 }655 Ok(())656 }657}658659pub struct ExtendBuilder<'v>(&'v mut ObjValue);660impl ObjMemberBuilder<ExtendBuilder<'_>> {661 pub fn value(self, value: Val) {662 self.binding(MaybeUnbound::Bound(Thunk::evaluated(value)));663 }664 pub fn bindable(self, bindable: TraceBox<dyn Unbound<Bound = Val>>) {665 self.binding(MaybeUnbound::Unbound(Cc::new(bindable)));666 }667 pub fn binding(self, binding: MaybeUnbound) {668 let (receiver, name, member) = self.build_member(binding);669 let new = receiver.0.clone();670 *receiver.0 = new.extend_with_raw_member(name, member);671 }672}1use std::{2 cell::RefCell,3 fmt::Debug,4 hash::{Hash, Hasher},5 ptr::addr_of,6};78use jrsonnet_gcmodule::{Cc, Trace, Weak};9use jrsonnet_interner::IStr;10use jrsonnet_parser::{ExprLocation, Visibility};11use rustc_hash::FxHashMap;1213use crate::{14 error::{Error, ErrorKind::*},15 function::CallLocation,16 gc::{GcHashMap, GcHashSet, TraceBox},17 operator::evaluate_add_op,18 tb, throw,19 val::ThunkValue,20 MaybeUnbound, Result, State, Thunk, Unbound, Val,21};2223#[cfg(not(feature = "exp-preserve-order"))]24mod ordering {25 #![allow(26 // This module works as stub for preserve-order feature27 clippy::unused_self,28 )]2930 use jrsonnet_gcmodule::Trace;3132 #[derive(Clone, Copy, Default, Debug, Trace)]33 pub struct FieldIndex(());34 impl FieldIndex {35 pub const fn next(self) -> Self {36 Self(())37 }38 }3940 #[derive(Clone, Copy, Default, Debug, Trace)]41 pub struct SuperDepth(());42 impl SuperDepth {43 pub const fn deeper(self) -> Self {44 Self(())45 }46 }4748 #[derive(Clone, Copy)]49 pub struct FieldSortKey(());50 impl FieldSortKey {51 pub const fn new(_: SuperDepth, _: FieldIndex) -> Self {52 Self(())53 }54 }55}5657#[cfg(feature = "exp-preserve-order")]58mod ordering {59 use std::cmp::{Ordering, Reverse};6061 use jrsonnet_gcmodule::Trace;6263 #[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]64 pub struct FieldIndex(u32);65 impl FieldIndex {66 pub fn next(self) -> Self {67 Self(self.0 + 1)68 }69 }7071 #[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Debug)]72 pub struct SuperDepth(u32);73 impl SuperDepth {74 pub fn deeper(self) -> Self {75 Self(self.0 + 1)76 }77 }7879 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]80 pub struct FieldSortKey(Reverse<SuperDepth>, FieldIndex);81 impl FieldSortKey {82 pub fn new(depth: SuperDepth, index: FieldIndex) -> Self {83 Self(Reverse(depth), index)84 }85 pub fn collide(self, other: Self) -> Self {86 match self.0 .0.cmp(&other.0 .0) {87 Ordering::Greater => self,88 Ordering::Less => other,89 Ordering::Equal => unreachable!("object can't have two fields with the same name"),90 }91 }92 }93}9495use ordering::*;9697#[allow(clippy::module_name_repetitions)]98#[derive(Debug, Trace)]99pub struct ObjMember {100 pub add: bool,101 pub visibility: Visibility,102 original_index: FieldIndex,103 pub invoke: MaybeUnbound,104 pub location: Option<ExprLocation>,105}106107pub trait ObjectAssertion: Trace {108 fn run(&self, super_obj: Option<ObjValue>, this: Option<ObjValue>) -> Result<()>;109}110111// Field => This112113#[derive(Trace)]114enum CacheValue {115 Cached(Val),116 NotFound,117 Pending,118 Errored(Error),119}120121#[allow(clippy::module_name_repetitions)]122#[derive(Trace)]123#[trace(tracking(force))]124pub struct ObjValueInternals {125 sup: Option<ObjValue>,126 this: Option<ObjValue>,127128 assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,129 assertions_ran: RefCell<GcHashSet<ObjValue>>,130 this_entries: Cc<GcHashMap<IStr, ObjMember>>,131 value_cache: RefCell<GcHashMap<(IStr, Option<WeakObjValue>), CacheValue>>,132}133134#[derive(Clone, Trace)]135pub struct WeakObjValue(#[trace(skip)] pub(crate) Weak<ObjValueInternals>);136137impl PartialEq for WeakObjValue {138 fn eq(&self, other: &Self) -> bool {139 Weak::ptr_eq(&self.0, &other.0)140 }141}142143impl Eq for WeakObjValue {}144impl Hash for WeakObjValue {145 fn hash<H: Hasher>(&self, hasher: &mut H) {146 // Safety: usize is POD147 let addr = unsafe { *std::ptr::addr_of!(self.0).cast() };148 hasher.write_usize(addr);149 }150}151152#[allow(clippy::module_name_repetitions)]153#[derive(Clone, Trace)]154pub struct ObjValue(pub(crate) Cc<ObjValueInternals>);155impl Debug for ObjValue {156 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {157 if let Some(super_obj) = self.0.sup.as_ref() {158 if f.alternate() {159 write!(f, "{super_obj:#?}")?;160 } else {161 write!(f, "{super_obj:?}")?;162 }163 write!(f, " + ")?;164 }165 let mut debug = f.debug_struct("ObjValue");166 for (name, member) in self.0.this_entries.iter() {167 debug.field(name, member);168 }169 debug.finish_non_exhaustive()170 }171}172173impl ObjValue {174 pub fn new(175 sup: Option<Self>,176 this_entries: Cc<GcHashMap<IStr, ObjMember>>,177 assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,178 ) -> Self {179 Self(Cc::new(ObjValueInternals {180 sup,181 this: None,182 assertions,183 assertions_ran: RefCell::new(GcHashSet::new()),184 this_entries,185 value_cache: RefCell::new(GcHashMap::new()),186 }))187 }188 pub fn new_empty() -> Self {189 Self::new(None, Cc::new(GcHashMap::new()), Cc::new(Vec::new()))190 }191 pub fn builder() -> ObjValueBuilder {192 ObjValueBuilder::new()193 }194 pub fn builder_with_capacity(capacity: usize) -> ObjValueBuilder {195 ObjValueBuilder::with_capacity(capacity)196 }197 #[must_use]198 pub fn extend_from(&self, sup: Self) -> Self {199 match &self.0.sup {200 None => Self::new(201 Some(sup),202 self.0.this_entries.clone(),203 self.0.assertions.clone(),204 ),205 Some(v) => Self::new(206 Some(v.extend_from(sup)),207 self.0.this_entries.clone(),208 self.0.assertions.clone(),209 ),210 }211 }212 pub(crate) fn extend_with_raw_member(self, key: IStr, value: ObjMember) -> Self {213 let mut new = GcHashMap::with_capacity(1);214 new.insert(key, value);215 Self::new(Some(self), Cc::new(new), Cc::new(Vec::new()))216 }217 pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder<'_>> {218 ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())219 }220221 #[must_use]222 pub fn with_this(&self, this: Self) -> Self {223 Self(Cc::new(ObjValueInternals {224 sup: self.0.sup.clone(),225 assertions: self.0.assertions.clone(),226 assertions_ran: RefCell::new(GcHashSet::new()),227 this: Some(this),228 this_entries: self.0.this_entries.clone(),229 value_cache: RefCell::new(GcHashMap::new()),230 }))231 }232233 pub fn len(&self) -> usize {234 self.fields_visibility()235 .into_iter()236 .filter(|(_, (visible, _))| *visible)237 .count()238 }239240 pub fn is_empty(&self) -> bool {241 if !self.0.this_entries.is_empty() {242 return false;243 }244 self.0.sup.as_ref().map_or(true, Self::is_empty)245 }246247 /// Run callback for every field found in object248 ///249 /// Returns true if ended prematurely250 pub(crate) fn enum_fields(251 &self,252 depth: SuperDepth,253 handler: &mut impl FnMut(SuperDepth, &IStr, &ObjMember) -> bool,254 ) -> bool {255 if let Some(s) = &self.0.sup {256 if s.enum_fields(depth.deeper(), handler) {257 return true;258 }259 }260 for (name, member) in self.0.this_entries.iter() {261 if handler(depth, name, member) {262 return true;263 }264 }265 false266 }267268 pub fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {269 let mut out = FxHashMap::default();270 self.enum_fields(SuperDepth::default(), &mut |depth, name, member| {271 let new_sort_key = FieldSortKey::new(depth, member.original_index);272 let entry = out.entry(name.clone());273 let (visible, _) = entry.or_insert((true, new_sort_key));274 match member.visibility {275 Visibility::Normal => {}276 Visibility::Hidden => {277 *visible = false;278 }279 Visibility::Unhide => {280 *visible = true;281 }282 };283 false284 });285 out286 }287 pub fn fields_ex(288 &self,289 include_hidden: bool,290 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,291 ) -> Vec<IStr> {292 #[cfg(feature = "exp-preserve-order")]293 if preserve_order {294 let (mut fields, mut keys): (Vec<_>, Vec<_>) = self295 .fields_visibility()296 .into_iter()297 .filter(|(_, (visible, _))| include_hidden || *visible)298 .enumerate()299 .map(|(idx, (k, (_, sk)))| (k, (sk, idx)))300 .unzip();301 keys.sort_unstable_by_key(|v| v.0);302 // Reorder in-place by resulting indexes303 for i in 0..fields.len() {304 let x = fields[i].clone();305 let mut j = i;306 loop {307 let k = keys[j].1;308 keys[j].1 = j;309 if k == i {310 break;311 }312 fields[j] = fields[k].clone();313 j = k;314 }315 fields[j] = x;316 }317 return fields;318 }319320 let mut fields: Vec<_> = self321 .fields_visibility()322 .into_iter()323 .filter(|(_, (visible, _))| include_hidden || *visible)324 .map(|(k, _)| k)325 .collect();326 fields.sort_unstable();327 fields328 }329 pub fn fields(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Vec<IStr> {330 self.fields_ex(331 false,332 #[cfg(feature = "exp-preserve-order")]333 preserve_order,334 )335 }336337 pub fn field_visibility(&self, name: IStr) -> Option<Visibility> {338 if let Some(m) = self.0.this_entries.get(&name) {339 Some(match &m.visibility {340 Visibility::Normal => self341 .0342 .sup343 .as_ref()344 .and_then(|super_obj| super_obj.field_visibility(name))345 .unwrap_or(Visibility::Normal),346 v => *v,347 })348 } else if let Some(super_obj) = &self.0.sup {349 super_obj.field_visibility(name)350 } else {351 None352 }353 }354355 fn has_field_include_hidden(&self, name: IStr) -> bool {356 if self.0.this_entries.contains_key(&name) {357 true358 } else if let Some(super_obj) = &self.0.sup {359 super_obj.has_field_include_hidden(name)360 } else {361 false362 }363 }364365 pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {366 if include_hidden {367 self.has_field_include_hidden(name)368 } else {369 self.has_field(name)370 }371 }372 pub fn has_field(&self, name: IStr) -> bool {373 self.field_visibility(name)374 .map_or(false, |v| v.is_visible())375 }376377 pub fn iter(378 &self,379 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,380 ) -> impl Iterator<Item = (IStr, Result<Val>)> + '_ {381 let fields = self.fields(382 #[cfg(feature = "exp-preserve-order")]383 preserve_order,384 );385 fields.into_iter().map(|field| {386 (387 field.clone(),388 self.get(field)389 .map(|opt| opt.expect("iterating over keys, field exists")),390 )391 })392 }393 pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {394 #[derive(Trace)]395 struct ThunkGet {396 obj: ObjValue,397 key: IStr,398 }399 impl ThunkValue for ThunkGet {400 type Output = Val;401402 fn get(self: Box<Self>) -> Result<Self::Output> {403 Ok(self.obj.get(self.key)?.expect("field exists"))404 }405 }406407 if !self.has_field_ex(key.clone(), true) {408 return None;409 }410 Some(Thunk::new(ThunkGet {411 obj: self.clone(),412 key,413 }))414 }415 pub fn get(&self, key: IStr) -> Result<Option<Val>> {416 self.get_for(key, self.0.this.clone().unwrap_or_else(|| self.clone()))417 }418 pub fn get_for(&self, key: IStr, this: Self) -> Result<Option<Val>> {419 self.run_assertions()?;420 let cache_key = (421 key.clone(),422 (!ObjValue::ptr_eq(&this, self)).then(|| this.clone().downgrade()),423 );424 if let Some(v) = self.0.value_cache.borrow().get(&cache_key) {425 return Ok(match v {426 CacheValue::Cached(v) => Some(v.clone()),427 CacheValue::NotFound => None,428 CacheValue::Pending => throw!(InfiniteRecursionDetected),429 CacheValue::Errored(e) => return Err(e.clone()),430 });431 }432 self.0433 .value_cache434 .borrow_mut()435 .insert(cache_key.clone(), CacheValue::Pending);436 let value = self.get_raw(key, this).map_err(|e| {437 self.0438 .value_cache439 .borrow_mut()440 .insert(cache_key.clone(), CacheValue::Errored(e.clone()));441 e442 })?;443 self.0.value_cache.borrow_mut().insert(444 cache_key,445 value446 .as_ref()447 .map_or(CacheValue::NotFound, |v| CacheValue::Cached(v.clone())),448 );449 Ok(value)450 }451452 fn get_raw(&self, key: IStr, real_this: Self) -> Result<Option<Val>> {453 match (self.0.this_entries.get(&key), &self.0.sup) {454 (Some(k), None) => Ok(Some(self.evaluate_this(k, real_this)?)),455 (Some(k), Some(super_obj)) => {456 let our = self.evaluate_this(k, real_this.clone())?;457 if k.add {458 super_obj459 .get_raw(key, real_this)?460 .map_or(Ok(Some(our.clone())), |v| {461 Ok(Some(evaluate_add_op(&v, &our)?))462 })463 } else {464 Ok(Some(our))465 }466 }467 (None, Some(super_obj)) => super_obj.get_raw(key, real_this),468 (None, None) => Ok(None),469 }470 }471 fn evaluate_this(&self, v: &ObjMember, real_this: Self) -> Result<Val> {472 v.invoke.evaluate(self.0.sup.clone(), Some(real_this))473 }474475 fn run_assertions_raw(&self, real_this: &Self) -> Result<()> {476 if self.0.assertions.is_empty() {477 if let Some(super_obj) = &self.0.sup {478 super_obj.run_assertions_raw(real_this)?;479 }480 return Ok(());481 }482 if self.0.assertions_ran.borrow_mut().insert(real_this.clone()) {483 for assertion in self.0.assertions.iter() {484 if let Err(e) = assertion.run(self.0.sup.clone(), Some(real_this.clone())) {485 self.0.assertions_ran.borrow_mut().remove(real_this);486 return Err(e);487 }488 }489 if let Some(super_obj) = &self.0.sup {490 super_obj.run_assertions_raw(real_this)?;491 }492 }493 Ok(())494 }495 pub fn run_assertions(&self) -> Result<()> {496 self.run_assertions_raw(self)497 }498499 pub fn ptr_eq(a: &Self, b: &Self) -> bool {500 Cc::ptr_eq(&a.0, &b.0)501 }502 pub fn downgrade(self) -> WeakObjValue {503 WeakObjValue(self.0.downgrade())504 }505}506507impl PartialEq for ObjValue {508 fn eq(&self, other: &Self) -> bool {509 Cc::ptr_eq(&self.0, &other.0)510 }511}512513impl Eq for ObjValue {}514impl Hash for ObjValue {515 fn hash<H: Hasher>(&self, hasher: &mut H) {516 hasher.write_usize(addr_of!(*self.0) as usize);517 }518}519520#[allow(clippy::module_name_repetitions)]521pub struct ObjValueBuilder {522 sup: Option<ObjValue>,523 map: GcHashMap<IStr, ObjMember>,524 assertions: Vec<TraceBox<dyn ObjectAssertion>>,525 next_field_index: FieldIndex,526}527impl ObjValueBuilder {528 pub fn new() -> Self {529 Self::with_capacity(0)530 }531 pub fn with_capacity(capacity: usize) -> Self {532 Self {533 sup: None,534 map: GcHashMap::with_capacity(capacity),535 assertions: Vec::new(),536 next_field_index: FieldIndex::default(),537 }538 }539 pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self {540 self.assertions.reserve_exact(capacity);541 self542 }543 pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {544 self.sup = Some(super_obj);545 self546 }547548 pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {549 self.assertions.push(tb!(assertion));550 self551 }552 pub fn member(&mut self, name: IStr) -> ObjMemberBuilder<ValueBuilder<'_>> {553 let field_index = self.next_field_index;554 self.next_field_index = self.next_field_index.next();555 ObjMemberBuilder::new(ValueBuilder(self), name, field_index)556 }557558 pub fn build(self) -> ObjValue {559 ObjValue::new(self.sup, Cc::new(self.map), Cc::new(self.assertions))560 }561}562impl Default for ObjValueBuilder {563 fn default() -> Self {564 Self::with_capacity(0)565 }566}567568#[allow(clippy::module_name_repetitions)]569#[must_use = "value not added unless binding() was called"]570pub struct ObjMemberBuilder<Kind> {571 kind: Kind,572 name: IStr,573 add: bool,574 visibility: Visibility,575 original_index: FieldIndex,576 location: Option<ExprLocation>,577}578579#[allow(clippy::missing_const_for_fn)]580impl<Kind> ObjMemberBuilder<Kind> {581 pub(crate) fn new(kind: Kind, name: IStr, original_index: FieldIndex) -> Self {582 Self {583 kind,584 name,585 original_index,586 add: false,587 visibility: Visibility::Normal,588 location: None,589 }590 }591592 pub const fn with_add(mut self, add: bool) -> Self {593 self.add = add;594 self595 }596 pub fn add(self) -> Self {597 self.with_add(true)598 }599 pub fn with_visibility(mut self, visibility: Visibility) -> Self {600 self.visibility = visibility;601 self602 }603 pub fn hide(self) -> Self {604 self.with_visibility(Visibility::Hidden)605 }606 pub fn with_location(mut self, location: ExprLocation) -> Self {607 self.location = Some(location);608 self609 }610 fn build_member(self, binding: MaybeUnbound) -> (Kind, IStr, ObjMember) {611 (612 self.kind,613 self.name,614 ObjMember {615 add: self.add,616 visibility: self.visibility,617 original_index: self.original_index,618 invoke: binding,619 location: self.location,620 },621 )622 }623}624625pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);626impl ObjMemberBuilder<ValueBuilder<'_>> {627 /// Inserts value, replacing if it is already defined628 pub fn value_unchecked(self, value: Val) {629 let (receiver, name, member) =630 self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value)));631 let entry = receiver.0.map.entry(name);632 entry.insert(member);633 }634635 pub fn value(self, value: Val) -> Result<()> {636 self.thunk(Thunk::evaluated(value))637 }638 pub fn thunk(self, value: Thunk<Val>) -> Result<()> {639 self.binding(MaybeUnbound::Bound(value))640 }641 pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {642 self.binding(MaybeUnbound::Unbound(Cc::new(tb!(bindable))))643 }644 pub fn binding(self, binding: MaybeUnbound) -> Result<()> {645 let (receiver, name, member) = self.build_member(binding);646 let location = member.location.clone();647 let old = receiver.0.map.insert(name.clone(), member);648 if old.is_some() {649 State::push(650 CallLocation(location.as_ref()),651 || format!("field <{}> initializtion", name.clone()),652 || throw!(DuplicateFieldName(name.clone())),653 )?;654 }655 Ok(())656 }657}658659pub struct ExtendBuilder<'v>(&'v mut ObjValue);660impl ObjMemberBuilder<ExtendBuilder<'_>> {661 pub fn value(self, value: Val) {662 self.binding(MaybeUnbound::Bound(Thunk::evaluated(value)));663 }664 pub fn bindable(self, bindable: TraceBox<dyn Unbound<Bound = Val>>) {665 self.binding(MaybeUnbound::Unbound(Cc::new(bindable)));666 }667 pub fn binding(self, binding: MaybeUnbound) {668 let (receiver, name, member) = self.build_member(binding);669 let new = receiver.0.clone();670 *receiver.0 = new.extend_with_raw_member(name, member);671 }672}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.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -9,7 +9,7 @@
use jrsonnet_interner::IStr;
use jrsonnet_types::ValType;
-pub use crate::arr::ArrValue;
+pub use crate::arr::{ArrValue, ArrayLike};
use crate::{
error::{Error, ErrorKind::*},
function::FuncVal,
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))
}
}