1use std::{2 cell::RefCell,3 cmp::Ordering,4 fmt::{self, Debug, Display},5 mem::replace,6 num::NonZeroU32,7 ops::Deref,8 rc::Rc,9};1011use derivative::Derivative;12use jrsonnet_gcmodule::{Cc, Trace};13use jrsonnet_interner::IStr;14use jrsonnet_types::ValType;15use thiserror::Error;1617pub use crate::arr::{ArrValue, ArrayLike};18use crate::{19 bail,20 error::{Error, ErrorKind::*},21 function::FuncVal,22 gc::{GcHashMap, TraceBox},23 manifest::{ManifestFormat, ToStringFormat},24 tb,25 typed::BoundedUsize,26 ObjValue, Result, Unbound, WeakObjValue,27};2829pub trait ThunkValue: Trace {30 type Output;31 fn get(self: Box<Self>) -> Result<Self::Output>;32}3334#[derive(Trace)]35enum ThunkInner<T: Trace> {36 Computed(T),37 Errored(Error),38 Waiting(TraceBox<dyn ThunkValue<Output = T>>),39 Pending,40}414243#[allow(clippy::module_name_repetitions)]44#[derive(Clone, Trace)]45pub struct Thunk<T: Trace>(Cc<RefCell<ThunkInner<T>>>);4647impl<T: Trace> Thunk<T> {48 pub fn evaluated(val: T) -> Self {49 Self(Cc::new(RefCell::new(ThunkInner::Computed(val))))50 }51 pub fn new(f: impl ThunkValue<Output = T> + 'static) -> Self {52 Self(Cc::new(RefCell::new(ThunkInner::Waiting(tb!(f)))))53 }54 pub fn errored(e: Error) -> Self {55 Self(Cc::new(RefCell::new(ThunkInner::Errored(e))))56 }57 pub fn result(res: Result<T, Error>) -> Self {58 match res {59 Ok(o) => Self::evaluated(o),60 Err(e) => Self::errored(e),61 }62 }63}6465impl<T> Thunk<T>66where67 T: Clone + Trace,68{69 pub fn force(&self) -> Result<()> {70 self.evaluate()?;71 Ok(())72 }7374 75 76 77 78 79 80 pub fn evaluate(&self) -> Result<T> {81 match &*self.0.borrow() {82 ThunkInner::Computed(v) => return Ok(v.clone()),83 ThunkInner::Errored(e) => return Err(e.clone()),84 ThunkInner::Pending => return Err(InfiniteRecursionDetected.into()),85 ThunkInner::Waiting(..) => (),86 };87 let ThunkInner::Waiting(value) = replace(&mut *self.0.borrow_mut(), ThunkInner::Pending)88 else {89 unreachable!();90 };91 let new_value = match value.0.get() {92 Ok(v) => v,93 Err(e) => {94 *self.0.borrow_mut() = ThunkInner::Errored(e.clone());95 return Err(e);96 }97 };98 *self.0.borrow_mut() = ThunkInner::Computed(new_value.clone());99 Ok(new_value)100 }101}102103pub trait ThunkMapper<Input>: Trace {104 type Output;105 fn map(self, from: Input) -> Result<Self::Output>;106}107impl<Input> Thunk<Input>108where109 Input: Trace + Clone,110{111 pub fn map<M>(self, mapper: M) -> Thunk<M::Output>112 where113 M: ThunkMapper<Input>,114 M::Output: Trace,115 {116 #[derive(Trace)]117 struct Mapped<Input: Trace, Mapper: Trace> {118 inner: Thunk<Input>,119 mapper: Mapper,120 }121 impl<Input, Mapper> ThunkValue for Mapped<Input, Mapper>122 where123 Input: Trace + Clone,124 Mapper: ThunkMapper<Input>,125 {126 type Output = Mapper::Output;127128 fn get(self: Box<Self>) -> Result<Self::Output> {129 let value = self.inner.evaluate()?;130 let mapped = self.mapper.map(value)?;131 Ok(mapped)132 }133 }134135 Thunk::new(Mapped::<Input, M> {136 inner: self,137 mapper,138 })139 }140}141142impl<T: Trace> From<Result<T>> for Thunk<T> {143 fn from(value: Result<T>) -> Self {144 match value {145 Ok(o) => Self::evaluated(o),146 Err(e) => Self::errored(e),147 }148 }149}150impl<T, V: Trace> From<T> for Thunk<V>151where152 T: ThunkValue<Output = V>,153{154 fn from(value: T) -> Self {155 Self::new(value)156 }157}158159impl<T: Trace + Default> Default for Thunk<T> {160 fn default() -> Self {161 Self::evaluated(T::default())162 }163}164165type CacheKey = (Option<WeakObjValue>, Option<WeakObjValue>);166167#[derive(Trace, Clone)]168pub struct CachedUnbound<I, T>169where170 I: Unbound<Bound = T>,171 T: Trace,172{173 cache: Cc<RefCell<GcHashMap<CacheKey, T>>>,174 value: I,175}176impl<I: Unbound<Bound = T>, T: Trace> CachedUnbound<I, T> {177 pub fn new(value: I) -> Self {178 Self {179 cache: Cc::new(RefCell::new(GcHashMap::new())),180 value,181 }182 }183}184impl<I: Unbound<Bound = T>, T: Clone + Trace> Unbound for CachedUnbound<I, T> {185 type Bound = T;186 fn bind(&self, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<T> {187 let cache_key = (188 sup.as_ref().map(|s| s.clone().downgrade()),189 this.as_ref().map(|t| t.clone().downgrade()),190 );191 {192 if let Some(t) = self.cache.borrow().get(&cache_key) {193 return Ok(t.clone());194 }195 }196 let bound = self.value.bind(sup, this)?;197198 {199 let mut cache = self.cache.borrow_mut();200 cache.insert(cache_key, bound.clone());201 }202203 Ok(bound)204 }205}206207impl<T: Debug + Trace> Debug for Thunk<T> {208 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {209 write!(f, "Lazy")210 }211}212impl<T: Trace> PartialEq for Thunk<T> {213 fn eq(&self, other: &Self) -> bool {214 Cc::ptr_eq(&self.0, &other.0)215 }216}217218219#[allow(clippy::module_name_repetitions)]220pub enum IndexableVal {221 222 Str(IStr),223 224 Arr(ArrValue),225}226impl IndexableVal {227 pub fn is_empty(&self) -> bool {228 match self {229 Self::Str(s) => s.is_empty(),230 Self::Arr(s) => s.is_empty(),231 }232 }233234 pub fn to_array(self) -> ArrValue {235 match self {236 Self::Str(s) => ArrValue::chars(s.chars()),237 Self::Arr(arr) => arr,238 }239 }240 241 242 243 244 245 246 247 pub fn slice(248 self,249 index: Option<i32>,250 end: Option<i32>,251 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,252 ) -> Result<Self> {253 match &self {254 Self::Str(s) => {255 let mut computed_len = None;256 let mut get_len = || {257 computed_len.map_or_else(258 || {259 let len = s.chars().count();260 let _ = computed_len.insert(len);261 len262 },263 |len| len,264 )265 };266 let mut get_idx = |pos: Option<i32>, default| {267 match pos {268 Some(v) if v < 0 => get_len().saturating_sub((-v) as usize),269 270 Some(v) => v as usize,271 None => default,272 }273 };274275 let index = get_idx(index, 0);276 let end = get_idx(end, usize::MAX);277 let step = step.as_deref().copied().unwrap_or(1);278279 if index >= end {280 return Ok(Self::Str("".into()));281 }282283 Ok(Self::Str(284 (s.chars()285 .skip(index)286 .take(end - index)287 .step_by(step)288 .collect::<String>())289 .into(),290 ))291 }292 Self::Arr(arr) => Ok(Self::Arr(arr.clone().slice(293 index,294 end,295 step.map(|v| NonZeroU32::new(v.value() as u32).expect("bounded != 0")),296 ))),297 }298 }299}300301#[derive(Debug, Clone, Trace)]302pub enum StrValue {303 Flat(IStr),304 Tree(Rc<(StrValue, StrValue, usize)>),305}306impl StrValue {307 pub fn concat(a: Self, b: Self) -> Self {308 309 const STRING_EXTEND_THRESHOLD: usize = 100;310311 if a.is_empty() {312 b313 } else if b.is_empty() {314 a315 } else if a.len() + b.len() < STRING_EXTEND_THRESHOLD {316 Self::Flat(format!("{a}{b}").into())317 } else {318 let len = a.len() + b.len();319 Self::Tree(Rc::new((a, b, len)))320 }321 }322 pub fn into_flat(self) -> IStr {323 #[cold]324 fn write_buf(s: &StrValue, out: &mut String) {325 match s {326 StrValue::Flat(f) => out.push_str(f),327 StrValue::Tree(t) => {328 write_buf(&t.0, out);329 write_buf(&t.1, out);330 }331 }332 }333 match self {334 Self::Flat(f) => f,335 Self::Tree(_) => {336 let mut buf = String::with_capacity(self.len());337 write_buf(&self, &mut buf);338 buf.into()339 }340 }341 }342 pub fn len(&self) -> usize {343 match self {344 Self::Flat(v) => v.len(),345 Self::Tree(t) => t.2,346 }347 }348 pub fn is_empty(&self) -> bool {349 match self {350 Self::Flat(v) => v.is_empty(),351 352 Self::Tree(_) => false,353 }354 }355}356impl<T> From<T> for StrValue357where358 IStr: From<T>,359{360 fn from(value: T) -> Self {361 Self::Flat(IStr::from(value))362 }363}364impl Display for StrValue {365 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {366 match self {367 Self::Flat(v) => write!(f, "{v}"),368 Self::Tree(t) => {369 write!(f, "{}", t.0)?;370 write!(f, "{}", t.1)371 }372 }373 }374}375impl PartialEq for StrValue {376 377 #[allow(clippy::unconditional_recursion)]378 fn eq(&self, other: &Self) -> bool {379 let a = self.clone().into_flat();380 let b = other.clone().into_flat();381 a == b382 }383}384impl Eq for StrValue {}385impl PartialOrd for StrValue {386 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {387 Some(self.cmp(other))388 }389}390impl Ord for StrValue {391 fn cmp(&self, other: &Self) -> Ordering {392 let a = self.clone().into_flat();393 let b = other.clone().into_flat();394 a.cmp(&b)395 }396}397398399400#[derive(Trace, Clone, Copy, Derivative)]401#[derivative(Debug = "transparent")]402#[repr(transparent)]403pub struct NumValue(f64);404impl NumValue {405 406 pub fn new(v: f64) -> Option<Self> {407 if !v.is_finite() {408 return None;409 }410 Some(Self(v))411 }412 #[inline]413 pub const fn get(&self) -> f64 {414 self.0415 }416}417impl PartialEq for NumValue {418 fn eq(&self, other: &Self) -> bool {419 self.0 == other.0420 }421}422impl Eq for NumValue {}423impl Ord for NumValue {424 #[inline]425 fn cmp(&self, other: &Self) -> Ordering {426 427 428 unsafe { self.0.partial_cmp(&other.0).unwrap_unchecked() }429 }430}431impl PartialOrd for NumValue {432 #[inline]433 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {434 Some(self.cmp(other))435 }436}437impl Display for NumValue {438 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {439 Display::fmt(&self.0, f)440 }441}442impl Deref for NumValue {443 type Target = f64;444445 #[inline]446 fn deref(&self) -> &Self::Target {447 &self.0448 }449}450macro_rules! impl_num {451 ($($ty:ty),+) => {$(452 impl From<$ty> for NumValue {453 #[inline]454 fn from(value: $ty) -> Self {455 Self(value.into())456 }457 }458 )+};459}460impl_num!(i8, u8, i16, u16, i32, u32);461462#[derive(Clone, Copy, Debug, Error, Trace)]463pub enum ConvertNumValueError {464 #[error("overflow")]465 Overflow,466 #[error("underflow")]467 Underflow,468 #[error("non-finite")]469 NonFinite,470}471impl From<ConvertNumValueError> for Error {472 fn from(e: ConvertNumValueError) -> Self {473 Self::new(e.into())474 }475}476477macro_rules! impl_try_num {478 ($($ty:ty),+) => {$(479 impl TryFrom<$ty> for NumValue {480 type Error = ConvertNumValueError;481 #[inline]482 fn try_from(value: $ty) -> Result<Self, ConvertNumValueError> {483 use crate::typed::conversions::{MIN_SAFE_INTEGER, MAX_SAFE_INTEGER};484 let value = value as f64;485 if value < MIN_SAFE_INTEGER {486 return Err(ConvertNumValueError::Underflow)487 } else if value > MAX_SAFE_INTEGER {488 return Err(ConvertNumValueError::Overflow)489 }490 491 Ok(Self(value))492 }493 }494 )+};495}496impl_try_num!(usize, isize, i64, u64);497498impl TryFrom<f64> for NumValue {499 type Error = ConvertNumValueError;500501 #[inline]502 fn try_from(value: f64) -> Result<Self, Self::Error> {503 Self::new(value).ok_or(ConvertNumValueError::NonFinite)504 }505}506impl TryFrom<f32> for NumValue {507 type Error = ConvertNumValueError;508509 #[inline]510 fn try_from(value: f32) -> Result<Self, Self::Error> {511 Self::new(f64::from(value)).ok_or(ConvertNumValueError::NonFinite)512 }513}514515516#[derive(Debug, Clone, Trace, Default)]517pub enum Val {518 519 Bool(bool),520 521 #[default]522 Null,523 524 Str(StrValue),525 526 527 528 Num(NumValue),529 530 #[cfg(feature = "exp-bigint")]531 BigInt(#[trace(skip)] Box<num_bigint::BigInt>),532 533 Arr(ArrValue),534 535 Obj(ObjValue),536 537 Func(FuncVal),538}539540#[cfg(target_pointer_width = "64")]541static_assertions::assert_eq_size!(Val, [u8; 24]);542543impl From<IndexableVal> for Val {544 fn from(v: IndexableVal) -> Self {545 match v {546 IndexableVal::Str(s) => Self::string(s),547 IndexableVal::Arr(a) => Self::Arr(a),548 }549 }550}551552impl Val {553 pub const fn as_bool(&self) -> Option<bool> {554 match self {555 Self::Bool(v) => Some(*v),556 _ => None,557 }558 }559 pub const fn as_null(&self) -> Option<()> {560 match self {561 Self::Null => Some(()),562 _ => None,563 }564 }565 pub fn as_str(&self) -> Option<IStr> {566 match self {567 Self::Str(s) => Some(s.clone().into_flat()),568 _ => None,569 }570 }571 pub const fn as_num(&self) -> Option<f64> {572 match self {573 Self::Num(n) => Some(n.get()),574 _ => None,575 }576 }577 pub fn as_arr(&self) -> Option<ArrValue> {578 match self {579 Self::Arr(a) => Some(a.clone()),580 _ => None,581 }582 }583 pub fn as_obj(&self) -> Option<ObjValue> {584 match self {585 Self::Obj(o) => Some(o.clone()),586 _ => None,587 }588 }589 pub fn as_func(&self) -> Option<FuncVal> {590 match self {591 Self::Func(f) => Some(f.clone()),592 _ => None,593 }594 }595596 pub const fn value_type(&self) -> ValType {597 match self {598 Self::Str(..) => ValType::Str,599 Self::Num(..) => ValType::Num,600 #[cfg(feature = "exp-bigint")]601 Self::BigInt(..) => ValType::BigInt,602 Self::Arr(..) => ValType::Arr,603 Self::Obj(..) => ValType::Obj,604 Self::Bool(_) => ValType::Bool,605 Self::Null => ValType::Null,606 Self::Func(..) => ValType::Func,607 }608 }609610 pub fn manifest(&self, format: impl ManifestFormat) -> Result<String> {611 fn manifest_dyn(val: &Val, manifest: &dyn ManifestFormat) -> Result<String> {612 manifest.manifest(val.clone())613 }614 manifest_dyn(self, &format)615 }616617 pub fn to_string(&self) -> Result<IStr> {618 Ok(match self {619 Self::Bool(true) => "true".into(),620 Self::Bool(false) => "false".into(),621 Self::Null => "null".into(),622 Self::Str(s) => s.clone().into_flat(),623 _ => self.manifest(ToStringFormat).map(IStr::from)?,624 })625 }626627 pub fn into_indexable(self) -> Result<IndexableVal> {628 Ok(match self {629 Self::Str(s) => IndexableVal::Str(s.into_flat()),630 Self::Arr(arr) => IndexableVal::Arr(arr),631 _ => bail!(ValueIsNotIndexable(self.value_type())),632 })633 }634635 pub fn function(function: impl Into<FuncVal>) -> Self {636 Self::Func(function.into())637 }638 pub fn string(string: impl Into<StrValue>) -> Self {639 Self::Str(string.into())640 }641 pub fn num(num: impl Into<NumValue>) -> Self {642 Self::Num(num.into())643 }644 pub fn try_num<V, E>(num: V) -> Result<Self, E>645 where646 NumValue: TryFrom<V, Error = E>,647 {648 Ok(Self::Num(num.try_into()?))649 }650}651652impl From<IStr> for Val {653 fn from(value: IStr) -> Self {654 Self::string(value)655 }656}657impl From<String> for Val {658 fn from(value: String) -> Self {659 Self::string(value)660 }661}662impl From<&str> for Val {663 fn from(value: &str) -> Self {664 Self::string(value)665 }666}667impl From<ObjValue> for Val {668 fn from(value: ObjValue) -> Self {669 Self::Obj(value)670 }671}672673const fn is_function_like(val: &Val) -> bool {674 matches!(val, Val::Func(_))675}676677678pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {679 Ok(match (val_a, val_b) {680 (Val::Bool(a), Val::Bool(b)) => a == b,681 (Val::Null, Val::Null) => true,682 (Val::Str(a), Val::Str(b)) => a == b,683 (Val::Num(a), Val::Num(b)) => (a.get() - b.get()).abs() <= f64::EPSILON,684 #[cfg(feature = "exp-bigint")]685 (Val::BigInt(a), Val::BigInt(b)) => a == b,686 (Val::Arr(_), Val::Arr(_)) => {687 bail!("primitiveEquals operates on primitive types, got array")688 }689 (Val::Obj(_), Val::Obj(_)) => {690 bail!("primitiveEquals operates on primitive types, got object")691 }692 (a, b) if is_function_like(a) && is_function_like(b) => {693 bail!("cannot test equality of functions")694 }695 (_, _) => false,696 })697}698699700pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {701 if val_a.value_type() != val_b.value_type() {702 return Ok(false);703 }704 match (val_a, val_b) {705 (Val::Arr(a), Val::Arr(b)) => {706 if ArrValue::ptr_eq(a, b) {707 return Ok(true);708 }709 if a.len() != b.len() {710 return Ok(false);711 }712 for (a, b) in a.iter().zip(b.iter()) {713 if !equals(&a?, &b?)? {714 return Ok(false);715 }716 }717 Ok(true)718 }719 (Val::Obj(a), Val::Obj(b)) => {720 if ObjValue::ptr_eq(a, b) {721 return Ok(true);722 }723 let fields = a.fields(724 #[cfg(feature = "exp-preserve-order")]725 false,726 );727 if fields728 != b.fields(729 #[cfg(feature = "exp-preserve-order")]730 false,731 ) {732 return Ok(false);733 }734 for field in fields {735 if !equals(736 &a.get(field.clone())?.expect("field exists"),737 &b.get(field)?.expect("field exists"),738 )? {739 return Ok(false);740 }741 }742 Ok(true)743 }744 (a, b) => Ok(primitive_equals(a, b)?),745 }746}