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.rsdiffbeforeafterboth1use std::{cell::RefCell, iter, mem::replace, rc::Rc};23use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::{IBytes, IStr};5use jrsonnet_parser::LocExpr;67use super::ArrValue;8use crate::{9 error::ErrorKind::InfiniteRecursionDetected,10 evaluate,11 function::FuncVal,12 val::{StrValue, ThunkValue},13 Context, Error, Result, Thunk, Val,14};1516pub trait ArrayLike: Sized + Into<ArrValue> {17 fn len(&self) -> usize;18 fn is_empty(&self) -> bool {19 self.len() == 020 }21 fn get(&self, index: usize) -> Result<Option<Val>>;22 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>>;23 fn get_cheap(&self, index: usize) -> Option<Val>;2425 fn reverse(self) -> ArrValue {26 ArrValue::Reverse(Cc::new(ReverseArray(self.into())))27 }28}2930#[derive(Debug, Clone, Trace)]31pub struct SliceArray {32 pub(crate) inner: ArrValue,33 pub(crate) from: u32,34 pub(crate) to: u32,35 pub(crate) step: u32,36}3738impl SliceArray {39 fn iter(&self) -> impl Iterator<Item = Result<Val>> + '_ {40 self.inner41 .iter()42 .skip(self.from as usize)43 .take((self.to - self.from) as usize)44 .step_by(self.step as usize)45 }4647 fn iter_lazy(&self) -> impl Iterator<Item = Thunk<Val>> + '_ {48 self.inner49 .iter_lazy()50 .skip(self.from as usize)51 .take((self.to - self.from) as usize)52 .step_by(self.step as usize)53 }5455 fn iter_cheap(&self) -> Option<impl crate::arr::ArrayLikeIter<Val> + '_> {56 Some(57 self.inner58 .iter_cheap()?59 .skip(self.from as usize)60 .take((self.to - self.from) as usize)61 .step_by(self.step as usize),62 )63 }64}65impl ArrayLike for SliceArray {66 fn len(&self) -> usize {67 iter::repeat(())68 .take((self.to - self.from) as usize)69 .step_by(self.step as usize)70 .count()71 }7273 fn get(&self, index: usize) -> Result<Option<Val>> {74 self.iter().nth(index).transpose()75 }7677 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {78 self.iter_lazy().nth(index)79 }8081 fn get_cheap(&self, index: usize) -> Option<Val> {82 self.iter_cheap()?.nth(index)83 }84}85impl From<SliceArray> for ArrValue {86 fn from(value: SliceArray) -> Self {87 Self::Slice(Cc::new(value))88 }89}9091#[derive(Trace, Debug, Clone)]92pub struct CharArray(pub Rc<Vec<char>>);93impl ArrayLike for CharArray {94 fn len(&self) -> usize {95 self.0.len()96 }9798 fn get(&self, index: usize) -> Result<Option<Val>> {99 Ok(self.get_cheap(index))100 }101102 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {103 self.get_cheap(index).map(Thunk::evaluated)104 }105106 fn get_cheap(&self, index: usize) -> Option<Val> {107 self.0108 .get(index)109 .map(|v| Val::Str(StrValue::Flat(IStr::from(*v))))110 }111}112impl From<CharArray> for ArrValue {113 fn from(value: CharArray) -> Self {114 ArrValue::Chars(value)115 }116}117118#[derive(Trace, Debug, Clone)]119pub struct BytesArray(pub IBytes);120impl ArrayLike for BytesArray {121 fn len(&self) -> usize {122 self.0.len()123 }124125 fn get(&self, index: usize) -> Result<Option<Val>> {126 Ok(self.get_cheap(index))127 }128129 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {130 self.get_cheap(index).map(Thunk::evaluated)131 }132133 fn get_cheap(&self, index: usize) -> Option<Val> {134 self.0.get(index).map(|v| Val::Num(f64::from(*v)))135 }136}137impl From<BytesArray> for ArrValue {138 fn from(value: BytesArray) -> Self {139 ArrValue::Bytes(value)140 }141}142143#[derive(Debug, Trace, Clone)]144enum ArrayThunk<T: 'static + Trace> {145 Computed(Val),146 Errored(Error),147 Waiting(T),148 Pending,149}150151#[derive(Debug, Trace)]152pub struct ExprArrayInner {153 ctx: Context,154 cached: RefCell<Vec<ArrayThunk<LocExpr>>>,155}156#[derive(Debug, Trace, Clone)]157pub struct ExprArray(pub Cc<ExprArrayInner>);158impl ExprArray {159 pub fn new(ctx: Context, items: impl IntoIterator<Item = LocExpr>) -> Self {160 Self(Cc::new(ExprArrayInner {161 ctx,162 cached: RefCell::new(items.into_iter().map(ArrayThunk::Waiting).collect()),163 }))164 }165}166impl ArrayLike for ExprArray {167 fn len(&self) -> usize {168 self.0.cached.borrow().len()169 }170 fn get(&self, index: usize) -> Result<Option<Val>> {171 if index >= self.len() {172 return Ok(None);173 }174 match &self.0.cached.borrow()[index] {175 ArrayThunk::Computed(c) => return Ok(Some(c.clone())),176 ArrayThunk::Errored(e) => return Err(e.clone()),177 ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),178 ArrayThunk::Waiting(..) => {}179 };180181 let ArrayThunk::Waiting(expr) =182 replace(&mut self.0.cached.borrow_mut()[index], ArrayThunk::Pending)183 else {184 unreachable!()185 };186187 let new_value = match evaluate(self.0.ctx.clone(), &expr) {188 Ok(v) => v,189 Err(e) => {190 self.0.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());191 return Err(e);192 }193 };194 self.0.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());195 Ok(Some(new_value))196 }197 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {198 #[derive(Trace)]199 struct ArrayElement {200 arr_thunk: ExprArray,201 index: usize,202 }203204 impl ThunkValue for ArrayElement {205 type Output = Val;206207 fn get(self: Box<Self>) -> Result<Self::Output> {208 self.arr_thunk209 .get(self.index)210 .transpose()211 .expect("index checked")212 }213 }214215 if index >= self.len() {216 return None;217 }218 match &self.0.cached.borrow()[index] {219 ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),220 ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),221 ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}222 };223224 Some(Thunk::new(ArrayElement {225 arr_thunk: self.clone(),226 index,227 }))228 }229 fn get_cheap(&self, _index: usize) -> Option<Val> {230 None231 }232}233impl From<ExprArray> for ArrValue {234 fn from(value: ExprArray) -> Self {235 Self::Expr(value)236 }237}238239#[derive(Trace, Debug, Clone)]240pub struct ExtendedArray {241 pub a: ArrValue,242 pub b: ArrValue,243 split: usize,244 len: usize,245}246impl ExtendedArray {247 pub fn new(a: ArrValue, b: ArrValue) -> Self {248 let a_len = a.len();249 let b_len = b.len();250 Self {251 a,252 b,253 split: a_len,254 len: a_len.checked_add(b_len).expect("too large array value"),255 }256 }257}258259struct WithExactSize<I>(I, usize);260impl<I, T> Iterator for WithExactSize<I>261where262 I: Iterator<Item = T>,263{264 type Item = T;265266 fn next(&mut self) -> Option<Self::Item> {267 self.0.next()268 }269 fn nth(&mut self, n: usize) -> Option<Self::Item> {270 self.0.nth(n)271 }272 fn size_hint(&self) -> (usize, Option<usize>) {273 (self.1, Some(self.1))274 }275}276impl<I> DoubleEndedIterator for WithExactSize<I>277where278 I: DoubleEndedIterator,279{280 fn next_back(&mut self) -> Option<Self::Item> {281 self.0.next_back()282 }283 fn nth_back(&mut self, n: usize) -> Option<Self::Item> {284 self.0.nth_back(n)285 }286}287impl<I> ExactSizeIterator for WithExactSize<I>288where289 I: Iterator,290{291 fn len(&self) -> usize {292 self.1293 }294}295impl ArrayLike for ExtendedArray {296 fn get(&self, index: usize) -> Result<Option<Val>> {297 if self.split > index {298 self.a.get(index)299 } else {300 self.b.get(index - self.split)301 }302 }303 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {304 if self.split > index {305 self.a.get_lazy(index)306 } else {307 self.b.get_lazy(index - self.split)308 }309 }310311 fn len(&self) -> usize {312 self.len313 }314315 fn get_cheap(&self, index: usize) -> Option<Val> {316 if self.split > index {317 self.a.get_cheap(index)318 } else {319 self.b.get_cheap(index - self.split)320 }321 }322}323impl From<ExtendedArray> for ArrValue {324 fn from(value: ExtendedArray) -> Self {325 Self::Extended(Cc::new(value))326 }327}328329#[derive(Trace, Debug, Clone)]330pub struct LazyArray(pub Cc<Vec<Thunk<Val>>>);331impl ArrayLike for LazyArray {332 fn len(&self) -> usize {333 self.0.len()334 }335 fn get(&self, index: usize) -> Result<Option<Val>> {336 let Some(v) = self.0.get(index) else {337 return Ok(None);338 };339 v.evaluate().map(Some)340 }341 fn get_cheap(&self, _index: usize) -> Option<Val> {342 None343 }344 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {345 self.0.get(index).cloned()346 }347}348impl From<LazyArray> for ArrValue {349 fn from(value: LazyArray) -> Self {350 Self::Lazy(value)351 }352}353354#[derive(Trace, Debug, Clone)]355pub struct EagerArray(pub Cc<Vec<Val>>);356impl ArrayLike for EagerArray {357 fn len(&self) -> usize {358 self.0.len()359 }360361 fn get(&self, index: usize) -> Result<Option<Val>> {362 Ok(self.0.get(index).cloned())363 }364365 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {366 self.0.get(index).cloned().map(Thunk::evaluated)367 }368369 fn get_cheap(&self, index: usize) -> Option<Val> {370 self.0.get(index).cloned()371 }372}373impl From<EagerArray> for ArrValue {374 fn from(value: EagerArray) -> Self {375 Self::Eager(value)376 }377}378379/// Inclusive range type380#[derive(Debug, Trace, Clone, PartialEq, Eq)]381pub struct RangeArray {382 start: i32,383 end: i32,384}385impl RangeArray {386 pub fn empty() -> Self {387 Self::new_exclusive(0, 0)388 }389 pub fn new_exclusive(start: i32, end: i32) -> Self {390 end.checked_sub(1)391 .map_or_else(Self::empty, |end| Self { start, end })392 }393 pub fn new_inclusive(start: i32, end: i32) -> Self {394 Self { start, end }395 }396 fn range(&self) -> impl Iterator<Item = i32> + ExactSizeIterator + DoubleEndedIterator {397 WithExactSize(398 self.start..=self.end,399 (self.end as usize)400 .wrapping_sub(self.start as usize)401 .wrapping_add(1),402 )403 }404}405406impl ArrayLike for RangeArray {407 fn len(&self) -> usize {408 self.range().len()409 }410 fn is_empty(&self) -> bool {411 self.range().len() == 0412 }413414 fn get(&self, index: usize) -> Result<Option<Val>> {415 Ok(self.get_cheap(index))416 }417418 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {419 self.get_cheap(index).map(Thunk::evaluated)420 }421422 fn get_cheap(&self, index: usize) -> Option<Val> {423 self.range().nth(index).map(|i| Val::Num(f64::from(i)))424 }425}426impl From<RangeArray> for ArrValue {427 fn from(value: RangeArray) -> Self {428 Self::Range(value)429 }430}431432#[derive(Debug, Trace, Clone)]433pub struct ReverseArray(pub ArrValue);434impl ArrayLike for ReverseArray {435 fn len(&self) -> usize {436 self.0.len()437 }438439 fn get(&self, index: usize) -> Result<Option<Val>> {440 self.0.get(self.0.len() - index - 1)441 }442443 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {444 self.0.get_lazy(self.0.len() - index - 1)445 }446447 fn get_cheap(&self, index: usize) -> Option<Val> {448 self.0.get_cheap(self.0.len() - index - 1)449 }450 fn reverse(self) -> ArrValue {451 self.0452 }453}454impl From<ReverseArray> for ArrValue {455 fn from(value: ReverseArray) -> Self {456 Self::Reverse(Cc::new(value))457 }458}459460#[derive(Trace, Debug)]461pub struct MappedArrayInner {462 inner: ArrValue,463 cached: RefCell<Vec<ArrayThunk<()>>>,464 mapper: FuncVal,465}466#[derive(Trace, Debug, Clone)]467pub struct MappedArray(Cc<MappedArrayInner>);468impl MappedArray {469 pub fn new(inner: ArrValue, mapper: FuncVal) -> Self {470 let len = inner.len();471 Self(Cc::new(MappedArrayInner {472 inner,473 cached: RefCell::new(vec![ArrayThunk::Waiting(()); len]),474 mapper,475 }))476 }477}478impl ArrayLike for MappedArray {479 fn len(&self) -> usize {480 self.0.cached.borrow().len()481 }482483 fn get(&self, index: usize) -> Result<Option<Val>> {484 if index >= self.len() {485 return Ok(None);486 }487 match &self.0.cached.borrow()[index] {488 ArrayThunk::Computed(c) => return Ok(Some(c.clone())),489 ArrayThunk::Errored(e) => return Err(e.clone()),490 ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),491 ArrayThunk::Waiting(..) => {}492 };493494 let ArrayThunk::Waiting(_) =495 replace(&mut self.0.cached.borrow_mut()[index], ArrayThunk::Pending)496 else {497 unreachable!()498 };499500 let val = self501 .0502 .inner503 .get(index)504 .transpose()505 .expect("index checked")506 .and_then(|r| self.0.mapper.evaluate_simple(&(r,), false));507508 let new_value = match val {509 Ok(v) => v,510 Err(e) => {511 self.0.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());512 return Err(e);513 }514 };515 self.0.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());516 Ok(Some(new_value))517 }518 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {519 #[derive(Trace)]520 struct ArrayElement {521 arr_thunk: MappedArray,522 index: usize,523 }524525 impl ThunkValue for ArrayElement {526 type Output = Val;527528 fn get(self: Box<Self>) -> Result<Self::Output> {529 self.arr_thunk530 .get(self.index)531 .transpose()532 .expect("index checked")533 }534 }535536 if index >= self.len() {537 return None;538 }539 match &self.0.cached.borrow()[index] {540 ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),541 ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),542 ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}543 };544545 Some(Thunk::new(ArrayElement {546 arr_thunk: self.clone(),547 index,548 }))549 }550551 fn get_cheap(&self, _index: usize) -> Option<Val> {552 None553 }554}555impl From<MappedArray> for ArrValue {556 fn from(value: MappedArray) -> Self {557 Self::Mapped(value)558 }559}560561#[derive(Trace, Debug)]562pub struct RepeatedArrayInner {563 data: ArrValue,564 repeats: usize,565 total_len: usize,566}567#[derive(Trace, Debug, Clone)]568pub struct RepeatedArray(Cc<RepeatedArrayInner>);569impl RepeatedArray {570 pub fn new(data: ArrValue, repeats: usize) -> Option<Self> {571 let total_len = data.len().checked_mul(repeats)?;572 Some(Self(Cc::new(RepeatedArrayInner {573 data,574 repeats,575 total_len,576 })))577 }578 pub fn is_cheap(&self) -> bool {579 self.0.data.is_cheap()580 }581}582583impl ArrayLike for RepeatedArray {584 fn len(&self) -> usize {585 self.0.total_len586 }587588 fn get(&self, index: usize) -> Result<Option<Val>> {589 if index > self.0.total_len {590 return Ok(None);591 }592 self.0.data.get(index % self.0.data.len())593 }594595 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {596 if index > self.0.total_len {597 return None;598 }599 self.0.data.get_lazy(index % self.0.data.len())600 }601602 fn get_cheap(&self, index: usize) -> Option<Val> {603 if index > self.0.total_len {604 return None;605 }606 self.0.data.get_cheap(index % self.0.data.len())607 }608}609impl From<RepeatedArray> for ArrValue {610 fn from(value: RepeatedArray) -> Self {611 Self::Repeated(value)612 }613}614615macro_rules! pass {616 ($t:ident.$m:ident($($ident:ident),*)) => {617 match $t {618 Self::Bytes(e) => e.$m($($ident)*),619 Self::Chars(e) => e.$m($($ident)*),620 Self::Expr(e) => e.$m($($ident)*),621 Self::Lazy(e) => e.$m($($ident)*),622 Self::Eager(e) => e.$m($($ident)*),623 Self::Range(e) => e.$m($($ident)*),624 Self::Slice(e) => e.$m($($ident)*),625 Self::Extended(e) => e.$m($($ident)*),626 Self::Reverse(e) => e.$m($($ident)*),627 Self::Mapped(e) => e.$m($($ident)*),628 Self::Repeated(e) => e.$m($($ident)*),629 }630 };631}632pub(super) use pass;1use std::{any::Any, cell::RefCell, fmt::Debug, iter, mem::replace};23use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::{IBytes, IStr};5use jrsonnet_parser::LocExpr;67use super::ArrValue;8use crate::{9 error::ErrorKind::InfiniteRecursionDetected,10 evaluate,11 function::FuncVal,12 val::{StrValue, ThunkValue},13 Context, Error, Result, Thunk, Val,14};1516pub trait ArrayLike: Any + Trace + Debug {17 fn len(&self) -> usize;18 fn is_empty(&self) -> bool {19 self.len() == 020 }21 fn get(&self, index: usize) -> Result<Option<Val>>;22 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>>;23 fn get_cheap(&self, index: usize) -> Option<Val>;2425 fn is_cheap(&self) -> bool;26}2728#[derive(Debug, Trace)]29pub struct SliceArray {30 pub(crate) inner: ArrValue,31 pub(crate) from: u32,32 pub(crate) to: u32,33 pub(crate) step: u32,34}3536impl SliceArray {37 fn iter(&self) -> impl Iterator<Item = Result<Val>> + '_ {38 self.inner39 .iter()40 .skip(self.from as usize)41 .take((self.to - self.from) as usize)42 .step_by(self.step as usize)43 }4445 fn iter_lazy(&self) -> impl Iterator<Item = Thunk<Val>> + '_ {46 self.inner47 .iter_lazy()48 .skip(self.from as usize)49 .take((self.to - self.from) as usize)50 .step_by(self.step as usize)51 }5253 fn iter_cheap(&self) -> Option<impl crate::arr::ArrayLikeIter<Val> + '_> {54 Some(55 self.inner56 .iter_cheap()?57 .skip(self.from as usize)58 .take((self.to - self.from) as usize)59 .step_by(self.step as usize),60 )61 }62}63impl ArrayLike for SliceArray {64 fn len(&self) -> usize {65 iter::repeat(())66 .take((self.to - self.from) as usize)67 .step_by(self.step as usize)68 .count()69 }7071 fn get(&self, index: usize) -> Result<Option<Val>> {72 self.iter().nth(index).transpose()73 }7475 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {76 self.iter_lazy().nth(index)77 }7879 fn get_cheap(&self, index: usize) -> Option<Val> {80 self.iter_cheap()?.nth(index)81 }82 fn is_cheap(&self) -> bool {83 self.inner.is_cheap()84 }85}8687#[derive(Trace, Debug)]88pub struct CharArray(pub Vec<char>);89impl ArrayLike for CharArray {90 fn len(&self) -> usize {91 self.0.len()92 }9394 fn get(&self, index: usize) -> Result<Option<Val>> {95 Ok(self.get_cheap(index))96 }9798 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {99 self.get_cheap(index).map(Thunk::evaluated)100 }101102 fn get_cheap(&self, index: usize) -> Option<Val> {103 self.0104 .get(index)105 .map(|v| Val::Str(StrValue::Flat(IStr::from(*v))))106 }107 fn is_cheap(&self) -> bool {108 true109 }110}111112#[derive(Trace, Debug)]113pub struct BytesArray(pub IBytes);114impl ArrayLike for BytesArray {115 fn len(&self) -> usize {116 self.0.len()117 }118119 fn get(&self, index: usize) -> Result<Option<Val>> {120 Ok(self.get_cheap(index))121 }122123 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {124 self.get_cheap(index).map(Thunk::evaluated)125 }126127 fn get_cheap(&self, index: usize) -> Option<Val> {128 self.0.get(index).map(|v| Val::Num(f64::from(*v)))129 }130 fn is_cheap(&self) -> bool {131 true132 }133}134135#[derive(Debug, Trace, Clone)]136enum ArrayThunk<T: 'static + Trace> {137 Computed(Val),138 Errored(Error),139 Waiting(T),140 Pending,141}142143#[derive(Debug, Trace, Clone)]144pub struct ExprArray {145 ctx: Context,146 cached: Cc<RefCell<Vec<ArrayThunk<LocExpr>>>>,147}148impl ExprArray {149 pub fn new(ctx: Context, items: impl IntoIterator<Item = LocExpr>) -> Self {150 Self {151 ctx,152 cached: Cc::new(RefCell::new(153 items.into_iter().map(ArrayThunk::Waiting).collect(),154 )),155 }156 }157}158impl ArrayLike for ExprArray {159 fn len(&self) -> usize {160 self.cached.borrow().len()161 }162 fn get(&self, index: usize) -> Result<Option<Val>> {163 if index >= self.len() {164 return Ok(None);165 }166 match &self.cached.borrow()[index] {167 ArrayThunk::Computed(c) => return Ok(Some(c.clone())),168 ArrayThunk::Errored(e) => return Err(e.clone()),169 ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),170 ArrayThunk::Waiting(..) => {}171 };172173 let ArrayThunk::Waiting(expr) =174 replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)175 else {176 unreachable!()177 };178179 let new_value = match evaluate(self.ctx.clone(), &expr) {180 Ok(v) => v,181 Err(e) => {182 self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());183 return Err(e);184 }185 };186 self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());187 Ok(Some(new_value))188 }189 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {190 #[derive(Trace)]191 struct ArrayElement {192 arr_thunk: ExprArray,193 index: usize,194 }195196 impl ThunkValue for ArrayElement {197 type Output = Val;198199 fn get(self: Box<Self>) -> Result<Self::Output> {200 self.arr_thunk201 .get(self.index)202 .transpose()203 .expect("index checked")204 }205 }206207 if index >= self.len() {208 return None;209 }210 match &self.cached.borrow()[index] {211 ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),212 ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),213 ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}214 };215216 Some(Thunk::new(ArrayElement {217 arr_thunk: self.clone(),218 index,219 }))220 }221 fn get_cheap(&self, _index: usize) -> Option<Val> {222 None223 }224 fn is_cheap(&self) -> bool {225 false226 }227}228229#[derive(Trace, Debug)]230pub struct ExtendedArray {231 pub a: ArrValue,232 pub b: ArrValue,233 split: usize,234 len: usize,235}236impl ExtendedArray {237 pub fn new(a: ArrValue, b: ArrValue) -> Self {238 let a_len = a.len();239 let b_len = b.len();240 Self {241 a,242 b,243 split: a_len,244 len: a_len.checked_add(b_len).expect("too large array value"),245 }246 }247}248249struct WithExactSize<I>(I, usize);250impl<I, T> Iterator for WithExactSize<I>251where252 I: Iterator<Item = T>,253{254 type Item = T;255256 fn next(&mut self) -> Option<Self::Item> {257 self.0.next()258 }259 fn nth(&mut self, n: usize) -> Option<Self::Item> {260 self.0.nth(n)261 }262 fn size_hint(&self) -> (usize, Option<usize>) {263 (self.1, Some(self.1))264 }265}266impl<I> DoubleEndedIterator for WithExactSize<I>267where268 I: DoubleEndedIterator,269{270 fn next_back(&mut self) -> Option<Self::Item> {271 self.0.next_back()272 }273 fn nth_back(&mut self, n: usize) -> Option<Self::Item> {274 self.0.nth_back(n)275 }276}277impl<I> ExactSizeIterator for WithExactSize<I>278where279 I: Iterator,280{281 fn len(&self) -> usize {282 self.1283 }284}285impl ArrayLike for ExtendedArray {286 fn get(&self, index: usize) -> Result<Option<Val>> {287 if self.split > index {288 self.a.get(index)289 } else {290 self.b.get(index - self.split)291 }292 }293 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {294 if self.split > index {295 self.a.get_lazy(index)296 } else {297 self.b.get_lazy(index - self.split)298 }299 }300301 fn len(&self) -> usize {302 self.len303 }304305 fn get_cheap(&self, index: usize) -> Option<Val> {306 if self.split > index {307 self.a.get_cheap(index)308 } else {309 self.b.get_cheap(index - self.split)310 }311 }312 fn is_cheap(&self) -> bool {313 self.a.is_cheap() && self.b.is_cheap()314 }315}316317#[derive(Trace, Debug)]318pub struct LazyArray(pub Vec<Thunk<Val>>);319impl ArrayLike for LazyArray {320 fn len(&self) -> usize {321 self.0.len()322 }323 fn get(&self, index: usize) -> Result<Option<Val>> {324 let Some(v) = self.0.get(index) else {325 return Ok(None);326 };327 v.evaluate().map(Some)328 }329 fn get_cheap(&self, _index: usize) -> Option<Val> {330 None331 }332 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {333 self.0.get(index).cloned()334 }335 fn is_cheap(&self) -> bool {336 false337 }338}339340#[derive(Trace, Debug)]341pub struct EagerArray(pub Vec<Val>);342impl ArrayLike for EagerArray {343 fn len(&self) -> usize {344 self.0.len()345 }346347 fn get(&self, index: usize) -> Result<Option<Val>> {348 Ok(self.0.get(index).cloned())349 }350351 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {352 self.0.get(index).cloned().map(Thunk::evaluated)353 }354355 fn get_cheap(&self, index: usize) -> Option<Val> {356 self.0.get(index).cloned()357 }358 fn is_cheap(&self) -> bool {359 true360 }361}362363/// Inclusive range type364#[derive(Debug, Trace, PartialEq, Eq)]365pub struct RangeArray {366 start: i32,367 end: i32,368}369impl RangeArray {370 pub fn empty() -> Self {371 Self::new_exclusive(0, 0)372 }373 pub fn new_exclusive(start: i32, end: i32) -> Self {374 end.checked_sub(1)375 .map_or_else(Self::empty, |end| Self { start, end })376 }377 pub fn new_inclusive(start: i32, end: i32) -> Self {378 Self { start, end }379 }380 fn range(&self) -> impl Iterator<Item = i32> + ExactSizeIterator + DoubleEndedIterator {381 WithExactSize(382 self.start..=self.end,383 (self.end as usize)384 .wrapping_sub(self.start as usize)385 .wrapping_add(1),386 )387 }388}389390impl ArrayLike for RangeArray {391 fn len(&self) -> usize {392 self.range().len()393 }394 fn is_empty(&self) -> bool {395 self.range().len() == 0396 }397398 fn get(&self, index: usize) -> Result<Option<Val>> {399 Ok(self.get_cheap(index))400 }401402 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {403 self.get_cheap(index).map(Thunk::evaluated)404 }405406 fn get_cheap(&self, index: usize) -> Option<Val> {407 self.range().nth(index).map(|i| Val::Num(f64::from(i)))408 }409 fn is_cheap(&self) -> bool {410 true411 }412}413414#[derive(Debug, Trace)]415pub struct ReverseArray(pub ArrValue);416impl ArrayLike for ReverseArray {417 fn len(&self) -> usize {418 self.0.len()419 }420421 fn get(&self, index: usize) -> Result<Option<Val>> {422 self.0.get(self.0.len() - index - 1)423 }424425 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {426 self.0.get_lazy(self.0.len() - index - 1)427 }428429 fn get_cheap(&self, index: usize) -> Option<Val> {430 self.0.get_cheap(self.0.len() - index - 1)431 }432 fn is_cheap(&self) -> bool {433 self.0.is_cheap()434 }435}436437#[derive(Trace, Debug, Clone)]438pub struct MappedArray {439 inner: ArrValue,440 cached: Cc<RefCell<Vec<ArrayThunk<()>>>>,441 mapper: FuncVal,442}443impl MappedArray {444 pub fn new(inner: ArrValue, mapper: FuncVal) -> Self {445 let len = inner.len();446 Self {447 inner,448 cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting(()); len])),449 mapper,450 }451 }452}453impl ArrayLike for MappedArray {454 fn len(&self) -> usize {455 self.cached.borrow().len()456 }457458 fn get(&self, index: usize) -> Result<Option<Val>> {459 if index >= self.len() {460 return Ok(None);461 }462 match &self.cached.borrow()[index] {463 ArrayThunk::Computed(c) => return Ok(Some(c.clone())),464 ArrayThunk::Errored(e) => return Err(e.clone()),465 ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),466 ArrayThunk::Waiting(..) => {}467 };468469 let ArrayThunk::Waiting(_) =470 replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)471 else {472 unreachable!()473 };474475 let val = self476 .inner477 .get(index)478 .transpose()479 .expect("index checked")480 .and_then(|r| self.mapper.evaluate_simple(&(r,), false));481482 let new_value = match val {483 Ok(v) => v,484 Err(e) => {485 self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());486 return Err(e);487 }488 };489 self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());490 Ok(Some(new_value))491 }492 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {493 #[derive(Trace)]494 struct ArrayElement {495 arr_thunk: MappedArray,496 index: usize,497 }498499 impl ThunkValue for ArrayElement {500 type Output = Val;501502 fn get(self: Box<Self>) -> Result<Self::Output> {503 self.arr_thunk504 .get(self.index)505 .transpose()506 .expect("index checked")507 }508 }509510 if index >= self.len() {511 return None;512 }513 match &self.cached.borrow()[index] {514 ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),515 ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),516 ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}517 };518519 Some(Thunk::new(ArrayElement {520 arr_thunk: self.clone(),521 index,522 }))523 }524525 fn get_cheap(&self, _index: usize) -> Option<Val> {526 None527 }528 fn is_cheap(&self) -> bool {529 false530 }531}532533#[derive(Trace, Debug)]534pub struct RepeatedArray {535 data: ArrValue,536 repeats: usize,537 total_len: usize,538}539impl RepeatedArray {540 pub fn new(data: ArrValue, repeats: usize) -> Option<Self> {541 let total_len = data.len().checked_mul(repeats)?;542 Some(Self {543 data,544 repeats,545 total_len,546 })547 }548}549550impl ArrayLike for RepeatedArray {551 fn len(&self) -> usize {552 self.total_len553 }554555 fn get(&self, index: usize) -> Result<Option<Val>> {556 if index > self.total_len {557 return Ok(None);558 }559 self.data.get(index % self.data.len())560 }561562 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {563 if index > self.total_len {564 return None;565 }566 self.data.get_lazy(index % self.data.len())567 }568569 fn get_cheap(&self, index: usize) -> Option<Val> {570 if index > self.total_len {571 return None;572 }573 self.data.get_cheap(index % self.data.len())574 }575 fn is_cheap(&self) -> bool {576 self.data.is_cheap()577 }578}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.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))
}
}