1use std::{2 cell::RefCell,3 cmp::Ordering,4 fmt::{self, Debug, Display},5 marker::PhantomData,6 mem::replace,7 num::NonZeroU32,8 ops::Deref,9 rc::Rc,10};1112use jrsonnet_gcmodule::{cc_dyn, Acyclic, Cc, Trace};13use jrsonnet_interner::IStr;14pub use jrsonnet_macros::Thunk;15use jrsonnet_types::ValType;16use rustc_hash::FxHashMap;17use thiserror::Error;1819pub use crate::arr::{ArrValue, ArrayLike};20use crate::{21 bail,22 error::{Error, ErrorKind::*},23 function::FuncVal,24 gc::WithCapacityExt as _,25 manifest::{ManifestFormat, ToStringFormat},26 typed::{BoundedUsize, MAX_SAFE_INTEGER, MIN_SAFE_INTEGER},27 ObjValue, Result, SupThis, Unbound, WeakSupThis,28};2930pub trait ThunkValue: Trace {31 type Output;32 fn get(&self) -> Result<Self::Output>;33}3435#[derive(Trace)]36enum MemoizedClusureThunkInner<D: Trace, T: Trace> {37 Computed(T),38 Errored(Error),39 Waiting {40 env: D,41 42 43 #[trace(skip)]44 closure: fn(D) -> Result<T>,45 },46 Pending,47}48#[derive(Trace)]49pub struct MemoizedClosureThunk<D: Trace, T: Trace>(RefCell<MemoizedClusureThunkInner<D, T>>);50impl<D: Trace, T: Trace> MemoizedClosureThunk<D, T> {51 pub fn new(env: D, closure: fn(D) -> Result<T>) -> Self {52 Self(RefCell::new(MemoizedClusureThunkInner::Waiting {53 env,54 closure,55 }))56 }57}5859impl<D: Trace, T: Trace + Clone> ThunkValue for MemoizedClosureThunk<D, T> {60 type Output = T;6162 fn get(&self) -> Result<Self::Output> {63 match &*self.0.borrow() {64 MemoizedClusureThunkInner::Computed(v) => return Ok(v.clone()),65 MemoizedClusureThunkInner::Errored(e) => return Err(e.clone()),66 MemoizedClusureThunkInner::Pending => return Err(InfiniteRecursionDetected.into()),67 MemoizedClusureThunkInner::Waiting { .. } => (),68 }69 let MemoizedClusureThunkInner::Waiting { env, closure } = replace(70 &mut *self.0.borrow_mut(),71 MemoizedClusureThunkInner::Pending,72 ) else {73 unreachable!();74 };75 let new_value = match closure(env) {76 Ok(v) => v,77 Err(e) => {78 *self.0.borrow_mut() = MemoizedClusureThunkInner::Errored(e.clone());79 return Err(e);80 }81 };82 *self.0.borrow_mut() = MemoizedClusureThunkInner::Computed(new_value.clone());83 Ok(new_value)84 }85}8687cc_dyn!(88 89 #[derive(Clone)] Thunk<V: Trace>,90 ThunkValue<Output = V>,91 pub fn new() {...}92);9394impl<T: Trace> Thunk<T> {95 pub fn evaluated(val: T) -> Self96 where97 T: Clone,98 {99 #[derive(Trace)]100 struct EvaluatedThunk<T: Trace>(T);101 impl<T> ThunkValue for EvaluatedThunk<T>102 where103 T: Clone + Trace,104 {105 type Output = T;106107 fn get(&self) -> Result<Self::Output> {108 Ok(self.0.clone())109 }110 }111 Self::new(EvaluatedThunk(val))112 }113 pub fn errored(e: Error) -> Self {114 #[derive(Trace)]115 struct ErroredThunk<T: Trace>(Error, PhantomData<T>);116 impl<T> ThunkValue for ErroredThunk<T>117 where118 T: Trace,119 {120 type Output = T;121122 fn get(&self) -> Result<Self::Output> {123 Err(self.0.clone())124 }125 }126 Self::new(ErroredThunk(e, PhantomData))127 }128 pub fn result(res: Result<T, Error>) -> Self129 where130 T: Clone,131 {132 match res {133 Ok(o) => Self::evaluated(o),134 Err(e) => Self::errored(e),135 }136 }137}138139impl<T> Thunk<T>140where141 T: Clone + Trace,142{143 pub fn force(&self) -> Result<()> {144 self.evaluate()?;145 Ok(())146 }147148 149 150 151 152 153 154 pub fn evaluate(&self) -> Result<T> {155 self.0.get()156 }157}158159pub trait ThunkMapper<Input>: Trace {160 type Output;161 fn map(self, from: Input) -> Result<Self::Output>;162}163impl<Input> Thunk<Input>164where165 Input: Trace + Clone,166{167 pub fn map<M>(self, mapper: M) -> Thunk<M::Output>168 where169 M: ThunkMapper<Input>,170 M::Output: Trace + Clone,171 {172 let inner = self;173 Thunk!(move || {174 let value = inner.evaluate()?;175 let mapped = mapper.map(value)?;176 Ok(mapped)177 })178 }179}180181impl<T: Trace + Clone> From<Result<T>> for Thunk<T> {182 fn from(value: Result<T>) -> Self {183 match value {184 Ok(o) => Self::evaluated(o),185 Err(e) => Self::errored(e),186 }187 }188}189impl<T, V: Trace> From<T> for Thunk<V>190where191 T: ThunkValue<Output = V>,192{193 fn from(value: T) -> Self {194 Self::new(value)195 }196}197198impl<T: Trace + Default + Clone> Default for Thunk<T> {199 fn default() -> Self {200 Self::evaluated(T::default())201 }202}203204#[derive(Trace, Clone)]205pub struct CachedUnbound<I, T>206where207 I: Unbound<Bound = T>,208 T: Trace,209{210 cache: Cc<RefCell<FxHashMap<WeakSupThis, T>>>,211 value: I,212}213impl<I: Unbound<Bound = T>, T: Trace> CachedUnbound<I, T> {214 pub fn new(value: I) -> Self {215 Self {216 cache: Cc::new(RefCell::new(FxHashMap::new())),217 value,218 }219 }220}221impl<I: Unbound<Bound = T>, T: Clone + Trace> Unbound for CachedUnbound<I, T> {222 type Bound = T;223 fn bind(&self, sup_this: SupThis) -> Result<T> {224 let cache_key = sup_this.clone().downgrade();225 {226 if let Some(t) = self.cache.borrow().get(&cache_key) {227 return Ok(t.clone());228 }229 }230 let bound = self.value.bind(sup_this)?;231232 {233 let mut cache = self.cache.borrow_mut();234 cache.insert(cache_key, bound.clone());235 }236237 Ok(bound)238 }239}240241impl<T: Debug + Trace> Debug for Thunk<T> {242 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {243 write!(f, "Lazy")244 }245}246impl<T: Trace> PartialEq for Thunk<T> {247 fn eq(&self, other: &Self) -> bool {248 Cc::ptr_eq(&self.0, &other.0)249 }250}251252253#[allow(clippy::module_name_repetitions)]254pub enum IndexableVal {255 256 Str(IStr),257 258 Arr(ArrValue),259}260impl IndexableVal {261 pub fn is_empty(&self) -> bool {262 match self {263 Self::Str(s) => s.is_empty(),264 Self::Arr(s) => s.is_empty(),265 }266 }267268 pub fn to_array(self) -> ArrValue {269 match self {270 Self::Str(s) => ArrValue::chars(s.chars()),271 Self::Arr(arr) => arr,272 }273 }274 275 276 277 278 279 280 281 pub fn slice(282 self,283 index: Option<i32>,284 end: Option<i32>,285 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,286 ) -> Result<Self> {287 match &self {288 Self::Str(s) => {289 let mut computed_len = None;290 let mut get_len = || {291 computed_len.unwrap_or_else(|| {292 let len = s.chars().count();293 let _ = computed_len.insert(len);294 len295 })296 };297 let mut get_idx = |pos: Option<i32>, default| {298 match pos {299 Some(v) if v < 0 => get_len().saturating_sub((-v) as usize),300 301 Some(v) => v as usize,302 None => default,303 }304 };305306 let index = get_idx(index, 0);307 let end = get_idx(end, usize::MAX);308 let step = step.as_deref().copied().unwrap_or(1);309310 if index >= end {311 return Ok(Self::Str("".into()));312 }313314 Ok(Self::Str(315 (s.chars()316 .skip(index)317 .take(end - index)318 .step_by(step)319 .collect::<String>())320 .into(),321 ))322 }323 Self::Arr(arr) => Ok(Self::Arr(arr.clone().slice(324 index,325 end,326 step.map(|v| NonZeroU32::new(v.value() as u32).expect("bounded != 0")),327 ))),328 }329 }330}331332#[derive(Debug, Clone, Acyclic)]333pub enum StrValue {334 Flat(IStr),335 Tree(Rc<(StrValue, StrValue, usize)>),336}337impl StrValue {338 pub fn concat(a: Self, b: Self) -> Self {339 340 const STRING_EXTEND_THRESHOLD: usize = 100;341342 if a.is_empty() {343 b344 } else if b.is_empty() {345 a346 } else if a.len() + b.len() < STRING_EXTEND_THRESHOLD {347 Self::Flat(format!("{a}{b}").into())348 } else {349 let len = a.len() + b.len();350 Self::Tree(Rc::new((a, b, len)))351 }352 }353 pub fn into_flat(self) -> IStr {354 #[cold]355 fn write_buf(s: &StrValue, out: &mut String) {356 match s {357 StrValue::Flat(f) => out.push_str(f),358 StrValue::Tree(t) => {359 write_buf(&t.0, out);360 write_buf(&t.1, out);361 }362 }363 }364 match self {365 Self::Flat(f) => f,366 Self::Tree(_) => {367 let mut buf = String::with_capacity(self.len());368 write_buf(&self, &mut buf);369 buf.into()370 }371 }372 }373 pub fn len(&self) -> usize {374 match self {375 Self::Flat(v) => v.len(),376 Self::Tree(t) => t.2,377 }378 }379 pub fn is_empty(&self) -> bool {380 match self {381 Self::Flat(v) => v.is_empty(),382 383 Self::Tree(_) => false,384 }385 }386}387impl<T> From<T> for StrValue388where389 IStr: From<T>,390{391 fn from(value: T) -> Self {392 Self::Flat(IStr::from(value))393 }394}395impl Display for StrValue {396 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {397 match self {398 Self::Flat(v) => write!(f, "{v}"),399 Self::Tree(t) => {400 write!(f, "{}", t.0)?;401 write!(f, "{}", t.1)402 }403 }404 }405}406impl PartialEq for StrValue {407 408 #[allow(clippy::unconditional_recursion)]409 fn eq(&self, other: &Self) -> bool {410 let a = self.clone().into_flat();411 let b = other.clone().into_flat();412 a == b413 }414}415impl Eq for StrValue {}416impl PartialOrd for StrValue {417 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {418 Some(self.cmp(other))419 }420}421impl Ord for StrValue {422 fn cmp(&self, other: &Self) -> Ordering {423 let a = self.clone().into_flat();424 let b = other.clone().into_flat();425 a.cmp(&b)426 }427}428429430431#[derive(Trace, Clone, Copy)]432#[repr(transparent)]433pub struct NumValue(f64);434impl NumValue {435 436 pub fn new(v: f64) -> Option<Self> {437 if !v.is_finite() {438 return None;439 }440 Some(Self(v))441 }442 #[inline]443 pub const fn get(&self) -> f64 {444 self.0445 }446 pub(crate) fn truncate_for_bitwise(self) -> Result<i64> {447 if self.0 < MIN_SAFE_INTEGER || self.0 > MAX_SAFE_INTEGER {448 bail!("numberic value outside of safe integer range for bitwise operation");449 }450 Ok(self.0 as i64)451 }452}453impl PartialEq for NumValue {454 fn eq(&self, other: &Self) -> bool {455 self.0 == other.0456 }457}458impl Eq for NumValue {}459impl Ord for NumValue {460 #[inline]461 fn cmp(&self, other: &Self) -> Ordering {462 463 464 unsafe { self.0.partial_cmp(&other.0).unwrap_unchecked() }465 }466}467impl PartialOrd for NumValue {468 #[inline]469 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {470 Some(self.cmp(other))471 }472}473impl Debug for NumValue {474 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {475 Debug::fmt(&self.0, f)476 }477}478impl Display for NumValue {479 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {480 Display::fmt(&self.0, f)481 }482}483impl Deref for NumValue {484 type Target = f64;485486 #[inline]487 fn deref(&self) -> &Self::Target {488 &self.0489 }490}491macro_rules! impl_num {492 ($($ty:ty),+) => {$(493 impl From<$ty> for NumValue {494 #[inline]495 fn from(value: $ty) -> Self {496 Self(value.into())497 }498 }499 )+};500}501impl_num!(i8, u8, i16, u16, i32, u32);502503#[derive(Clone, Copy, Debug, Error, Trace)]504pub enum ConvertNumValueError {505 #[error("overflow")]506 Overflow,507 #[error("underflow")]508 Underflow,509 #[error("non-finite")]510 NonFinite,511}512impl From<ConvertNumValueError> for Error {513 fn from(e: ConvertNumValueError) -> Self {514 Self::new(e.into())515 }516}517518macro_rules! impl_try_num {519 ($($ty:ty),+) => {$(520 impl TryFrom<$ty> for NumValue {521 type Error = ConvertNumValueError;522 #[inline]523 fn try_from(value: $ty) -> Result<Self, ConvertNumValueError> {524 let value = value as f64;525 if value < MIN_SAFE_INTEGER {526 return Err(ConvertNumValueError::Underflow)527 } else if value > MAX_SAFE_INTEGER {528 return Err(ConvertNumValueError::Overflow)529 }530 531 Ok(Self(value))532 }533 }534 )+};535}536impl_try_num!(usize, isize, i64, u64);537538impl TryFrom<f64> for NumValue {539 type Error = ConvertNumValueError;540541 #[inline]542 fn try_from(value: f64) -> Result<Self, Self::Error> {543 Self::new(value).ok_or(ConvertNumValueError::NonFinite)544 }545}546impl TryFrom<f32> for NumValue {547 type Error = ConvertNumValueError;548549 #[inline]550 fn try_from(value: f32) -> Result<Self, Self::Error> {551 Self::new(f64::from(value)).ok_or(ConvertNumValueError::NonFinite)552 }553}554555556#[derive(Debug, Clone, Trace, Default)]557pub enum Val {558 559 Bool(bool),560 561 #[default]562 Null,563 564 Str(StrValue),565 566 567 568 Num(NumValue),569 570 #[cfg(feature = "exp-bigint")]571 BigInt(#[trace(skip)] Box<num_bigint::BigInt>),572 573 Arr(ArrValue),574 575 Obj(ObjValue),576 577 Func(FuncVal),578}579580#[cfg(target_pointer_width = "64")]581static_assertions::assert_eq_size!(Val, [u8; 24]);582583impl From<IndexableVal> for Val {584 fn from(v: IndexableVal) -> Self {585 match v {586 IndexableVal::Str(s) => Self::string(s),587 IndexableVal::Arr(a) => Self::Arr(a),588 }589 }590}591592impl Val {593 pub const fn as_bool(&self) -> Option<bool> {594 match self {595 Self::Bool(v) => Some(*v),596 _ => None,597 }598 }599 pub const fn as_null(&self) -> Option<()> {600 match self {601 Self::Null => Some(()),602 _ => None,603 }604 }605 pub fn as_str(&self) -> Option<IStr> {606 match self {607 Self::Str(s) => Some(s.clone().into_flat()),608 _ => None,609 }610 }611 pub const fn as_num(&self) -> Option<f64> {612 match self {613 Self::Num(n) => Some(n.get()),614 _ => None,615 }616 }617 #[cfg(feature = "exp-bigint")]618 pub fn as_bigint(&self) -> Option<num_bigint::BigInt> {619 match self {620 Self::BigInt(n) => Some(*n.clone()),621 _ => None,622 }623 }624 pub fn as_arr(&self) -> Option<ArrValue> {625 match self {626 Self::Arr(a) => Some(a.clone()),627 _ => None,628 }629 }630 pub fn as_obj(&self) -> Option<ObjValue> {631 match self {632 Self::Obj(o) => Some(o.clone()),633 _ => None,634 }635 }636 pub fn as_func(&self) -> Option<FuncVal> {637 match self {638 Self::Func(f) => Some(f.clone()),639 _ => None,640 }641 }642643 pub const fn value_type(&self) -> ValType {644 match self {645 Self::Str(..) => ValType::Str,646 Self::Num(..) => ValType::Num,647 #[cfg(feature = "exp-bigint")]648 Self::BigInt(..) => ValType::BigInt,649 Self::Arr(..) => ValType::Arr,650 Self::Obj(..) => ValType::Obj,651 Self::Bool(_) => ValType::Bool,652 Self::Null => ValType::Null,653 Self::Func(..) => ValType::Func,654 }655 }656657 pub fn manifest(&self, format: impl ManifestFormat) -> Result<String> {658 fn manifest_dyn(val: &Val, manifest: &dyn ManifestFormat) -> Result<String> {659 manifest.manifest(val.clone())660 }661 manifest_dyn(self, &format)662 }663664 pub fn to_string(&self) -> Result<IStr> {665 Ok(match self {666 Self::Bool(true) => "true".into(),667 Self::Bool(false) => "false".into(),668 Self::Null => "null".into(),669 Self::Str(s) => s.clone().into_flat(),670 _ => self.manifest(ToStringFormat).map(IStr::from)?,671 })672 }673674 pub fn into_indexable(self) -> Result<IndexableVal> {675 Ok(match self {676 Self::Str(s) => IndexableVal::Str(s.into_flat()),677 Self::Arr(arr) => IndexableVal::Arr(arr),678 _ => bail!(ValueIsNotIndexable(self.value_type())),679 })680 }681682 pub fn function(function: impl Into<FuncVal>) -> Self {683 Self::Func(function.into())684 }685 pub fn string(string: impl Into<StrValue>) -> Self {686 Self::Str(string.into())687 }688 pub fn num(num: impl Into<NumValue>) -> Self {689 Self::Num(num.into())690 }691 pub fn try_num<V, E>(num: V) -> Result<Self, E>692 where693 NumValue: TryFrom<V, Error = E>,694 {695 Ok(Self::Num(num.try_into()?))696 }697}698699impl From<IStr> for Val {700 fn from(value: IStr) -> Self {701 Self::string(value)702 }703}704impl From<String> for Val {705 fn from(value: String) -> Self {706 Self::string(value)707 }708}709impl From<&str> for Val {710 fn from(value: &str) -> Self {711 Self::string(value)712 }713}714impl From<ObjValue> for Val {715 fn from(value: ObjValue) -> Self {716 Self::Obj(value)717 }718}719720const fn is_function_like(val: &Val) -> bool {721 matches!(val, Val::Func(_))722}723724725pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {726 Ok(match (val_a, val_b) {727 (Val::Bool(a), Val::Bool(b)) => a == b,728 (Val::Null, Val::Null) => true,729 (Val::Str(a), Val::Str(b)) => a == b,730 (Val::Num(a), Val::Num(b)) => (a.get() - b.get()).abs() <= f64::EPSILON,731 #[cfg(feature = "exp-bigint")]732 (Val::BigInt(a), Val::BigInt(b)) => a == b,733 (Val::Arr(_), Val::Arr(_)) => {734 bail!("primitiveEquals operates on primitive types, got array")735 }736 (Val::Obj(_), Val::Obj(_)) => {737 bail!("primitiveEquals operates on primitive types, got object")738 }739 (a, b) if is_function_like(a) && is_function_like(b) => {740 bail!("cannot test equality of functions")741 }742 (_, _) => false,743 })744}745746747pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {748 if val_a.value_type() != val_b.value_type() {749 return Ok(false);750 }751 match (val_a, val_b) {752 (Val::Arr(a), Val::Arr(b)) => {753 if ArrValue::ptr_eq(a, b) {754 return Ok(true);755 }756 if a.len() != b.len() {757 return Ok(false);758 }759 for (a, b) in a.iter().zip(b.iter()) {760 if !equals(&a?, &b?)? {761 return Ok(false);762 }763 }764 Ok(true)765 }766 (Val::Obj(a), Val::Obj(b)) => {767 if ObjValue::ptr_eq(a, b) {768 return Ok(true);769 }770 let fields = a.fields(771 #[cfg(feature = "exp-preserve-order")]772 false,773 );774 if fields775 != b.fields(776 #[cfg(feature = "exp-preserve-order")]777 false,778 ) {779 return Ok(false);780 }781 for field in fields {782 if !equals(783 &a.get(field.clone())?.expect("field exists"),784 &b.get(field)?.expect("field exists"),785 )? {786 return Ok(false);787 }788 }789 Ok(true)790 }791 (a, b) => Ok(primitive_equals(a, b)?),792 }793}