1use std::{2 cell::RefCell,3 fmt::{self, Debug, Display},4 mem::replace,5 rc::Rc,6};78use jrsonnet_gcmodule::{Cc, Trace};9use jrsonnet_interner::IStr;10use jrsonnet_types::ValType;1112pub use crate::arr::ArrValue;13use crate::{14 error::{Error, ErrorKind::*},15 function::FuncVal,16 gc::{GcHashMap, TraceBox},17 manifest::{ManifestFormat, ToStringFormat},18 tb, throw,19 typed::BoundedUsize,20 ObjValue, Result, Unbound, WeakObjValue,21};2223pub trait ThunkValue: Trace {24 type Output;25 fn get(self: Box<Self>) -> Result<Self::Output>;26}2728#[derive(Trace)]29enum ThunkInner<T: Trace> {30 Computed(T),31 Errored(Error),32 Waiting(TraceBox<dyn ThunkValue<Output = T>>),33 Pending,34}353637#[allow(clippy::module_name_repetitions)]38#[derive(Clone, Trace)]39pub struct Thunk<T: Trace>(Cc<RefCell<ThunkInner<T>>>);4041impl<T: Trace> Thunk<T> {42 pub fn evaluated(val: T) -> Self {43 Self(Cc::new(RefCell::new(ThunkInner::Computed(val))))44 }45 pub fn new(f: impl ThunkValue<Output = T> + 'static) -> Self {46 Self(Cc::new(RefCell::new(ThunkInner::Waiting(tb!(f)))))47 }48 pub fn errored(e: Error) -> Self {49 Self(Cc::new(RefCell::new(ThunkInner::Errored(e))))50 }51}5253impl<T> Thunk<T>54where55 T: Clone + Trace,56{57 pub fn force(&self) -> Result<()> {58 self.evaluate()?;59 Ok(())60 }6162 63 64 65 66 67 68 pub fn evaluate(&self) -> Result<T> {69 match &*self.0.borrow() {70 ThunkInner::Computed(v) => return Ok(v.clone()),71 ThunkInner::Errored(e) => return Err(e.clone()),72 ThunkInner::Pending => return Err(InfiniteRecursionDetected.into()),73 ThunkInner::Waiting(..) => (),74 };75 let ThunkInner::Waiting(value) = replace(&mut *self.0.borrow_mut(), ThunkInner::Pending) else {76 unreachable!();77 };78 let new_value = match value.0.get() {79 Ok(v) => v,80 Err(e) => {81 *self.0.borrow_mut() = ThunkInner::Errored(e.clone());82 return Err(e);83 }84 };85 *self.0.borrow_mut() = ThunkInner::Computed(new_value.clone());86 Ok(new_value)87 }88}8990type CacheKey = (Option<WeakObjValue>, Option<WeakObjValue>);9192#[derive(Trace, Clone)]93pub struct CachedUnbound<I, T>94where95 I: Unbound<Bound = T>,96 T: Trace,97{98 cache: Cc<RefCell<GcHashMap<CacheKey, T>>>,99 value: I,100}101impl<I: Unbound<Bound = T>, T: Trace> CachedUnbound<I, T> {102 pub fn new(value: I) -> Self {103 Self {104 cache: Cc::new(RefCell::new(GcHashMap::new())),105 value,106 }107 }108}109impl<I: Unbound<Bound = T>, T: Clone + Trace> Unbound for CachedUnbound<I, T> {110 type Bound = T;111 fn bind(&self, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<T> {112 let cache_key = (113 sup.as_ref().map(|s| s.clone().downgrade()),114 this.as_ref().map(|t| t.clone().downgrade()),115 );116 {117 if let Some(t) = self.cache.borrow().get(&cache_key) {118 return Ok(t.clone());119 }120 }121 let bound = self.value.bind(sup, this)?;122123 {124 let mut cache = self.cache.borrow_mut();125 cache.insert(cache_key, bound.clone());126 }127128 Ok(bound)129 }130}131132impl<T: Debug + Trace> Debug for Thunk<T> {133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {134 write!(f, "Lazy")135 }136}137impl<T: Trace> PartialEq for Thunk<T> {138 fn eq(&self, other: &Self) -> bool {139 Cc::ptr_eq(&self.0, &other.0)140 }141}142143144#[allow(clippy::module_name_repetitions)]145pub enum IndexableVal {146 147 Str(IStr),148 149 Arr(ArrValue),150}151impl IndexableVal {152 pub fn to_array(self) -> ArrValue {153 match self {154 IndexableVal::Str(s) => ArrValue::chars(s.chars()),155 IndexableVal::Arr(arr) => arr,156 }157 }158 159 160 161 162 163 164 165 pub fn slice(166 self,167 index: Option<BoundedUsize<0, { i32::MAX as usize }>>,168 end: Option<BoundedUsize<0, { i32::MAX as usize }>>,169 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,170 ) -> Result<Self> {171 match &self {172 IndexableVal::Str(s) => {173 let index = index.as_deref().copied().unwrap_or(0);174 let end = end.as_deref().copied().unwrap_or(usize::MAX);175 let step = step.as_deref().copied().unwrap_or(1);176177 if index >= end {178 return Ok(Self::Str("".into()));179 }180181 Ok(Self::Str(182 (s.chars()183 .skip(index)184 .take(end - index)185 .step_by(step)186 .collect::<String>())187 .into(),188 ))189 }190 IndexableVal::Arr(arr) => {191 let index = index.as_deref().copied().unwrap_or(0);192 let end = end.as_deref().copied().unwrap_or(usize::MAX).min(arr.len());193 let step = step.as_deref().copied().unwrap_or(1);194195 if index >= end {196 return Ok(Self::Arr(ArrValue::empty()));197 }198199 Ok(Self::Arr(200 arr.clone()201 .slice(Some(index), Some(end), Some(step))202 .expect("arguments checked"),203 ))204 }205 }206 }207}208209#[derive(Debug, Clone, Trace)]210pub enum StrValue {211 Flat(IStr),212 Tree(Rc<(StrValue, StrValue, usize)>),213}214impl StrValue {215 pub fn concat(a: StrValue, b: StrValue) -> Self {216 217 const STRING_EXTEND_THRESHOLD: usize = 100;218219 if a.is_empty() {220 b221 } else if b.is_empty() {222 a223 } else if a.len() + b.len() < STRING_EXTEND_THRESHOLD {224 Self::Flat(format!("{a}{b}").into())225 } else {226 let len = a.len() + b.len();227 Self::Tree(Rc::new((a, b, len)))228 }229 }230 pub fn into_flat(self) -> IStr {231 #[cold]232 fn write_buf(s: &StrValue, out: &mut String) {233 match s {234 StrValue::Flat(f) => out.push_str(f),235 StrValue::Tree(t) => {236 write_buf(&t.0, out);237 write_buf(&t.1, out);238 }239 }240 }241 match self {242 StrValue::Flat(f) => f,243 StrValue::Tree(_) => {244 let mut buf = String::with_capacity(self.len());245 write_buf(&self, &mut buf);246 buf.into()247 }248 }249 }250 pub fn len(&self) -> usize {251 match self {252 StrValue::Flat(v) => v.len(),253 StrValue::Tree(t) => t.2,254 }255 }256 pub fn is_empty(&self) -> bool {257 match self {258 Self::Flat(v) => v.is_empty(),259 260 Self::Tree(_) => false,261 }262 }263}264impl From<&str> for StrValue {265 fn from(value: &str) -> Self {266 Self::Flat(value.into())267 }268}269impl From<String> for StrValue {270 fn from(value: String) -> Self {271 Self::Flat(value.into())272 }273}274impl Display for StrValue {275 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {276 match self {277 StrValue::Flat(v) => write!(f, "{v}"),278 StrValue::Tree(t) => {279 write!(f, "{}", t.0)?;280 write!(f, "{}", t.1)281 }282 }283 }284}285impl PartialEq for StrValue {286 fn eq(&self, other: &Self) -> bool {287 let a = self.clone().into_flat();288 let b = other.clone().into_flat();289 a == b290 }291}292impl Eq for StrValue {}293impl PartialOrd for StrValue {294 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {295 let a = self.clone().into_flat();296 let b = other.clone().into_flat();297 Some(a.cmp(&b))298 }299}300impl Ord for StrValue {301 fn cmp(&self, other: &Self) -> std::cmp::Ordering {302 self.partial_cmp(other)303 .expect("partial_cmp always returns Some")304 }305}306307308#[derive(Debug, Clone, Trace)]309pub enum Val {310 311 Bool(bool),312 313 Null,314 315 Str(StrValue),316 317 318 319 Num(f64),320 321 #[cfg(feature = "exp-bigint")]322 BigInt(#[trace(skip)] Box<num_bigint::BigInt>),323 324 Arr(ArrValue),325 326 Obj(ObjValue),327 328 Func(FuncVal),329}330331#[cfg(target_pointer_width = "64")]332static_assertions::assert_eq_size!(Val, [u8; 24]);333334impl From<IndexableVal> for Val {335 fn from(v: IndexableVal) -> Self {336 match v {337 IndexableVal::Str(s) => Self::Str(StrValue::Flat(s)),338 IndexableVal::Arr(a) => Self::Arr(a),339 }340 }341}342343impl Val {344 pub const fn as_bool(&self) -> Option<bool> {345 match self {346 Self::Bool(v) => Some(*v),347 _ => None,348 }349 }350 pub const fn as_null(&self) -> Option<()> {351 match self {352 Self::Null => Some(()),353 _ => None,354 }355 }356 pub fn as_str(&self) -> Option<IStr> {357 match self {358 Self::Str(s) => Some(s.clone().into_flat()),359 _ => None,360 }361 }362 pub const fn as_num(&self) -> Option<f64> {363 match self {364 Self::Num(n) => Some(*n),365 _ => None,366 }367 }368 pub fn as_arr(&self) -> Option<ArrValue> {369 match self {370 Self::Arr(a) => Some(a.clone()),371 _ => None,372 }373 }374 pub fn as_obj(&self) -> Option<ObjValue> {375 match self {376 Self::Obj(o) => Some(o.clone()),377 _ => None,378 }379 }380 pub fn as_func(&self) -> Option<FuncVal> {381 match self {382 Self::Func(f) => Some(f.clone()),383 _ => None,384 }385 }386387 388 389 pub fn new_checked_num(num: f64) -> Result<Self> {390 if num.is_finite() {391 Ok(Self::Num(num))392 } else {393 throw!("overflow")394 }395 }396397 pub const fn value_type(&self) -> ValType {398 match self {399 Self::Str(..) => ValType::Str,400 Self::Num(..) => ValType::Num,401 #[cfg(feature = "exp-bigint")]402 Self::BigInt(..) => ValType::BigInt,403 Self::Arr(..) => ValType::Arr,404 Self::Obj(..) => ValType::Obj,405 Self::Bool(_) => ValType::Bool,406 Self::Null => ValType::Null,407 Self::Func(..) => ValType::Func,408 }409 }410411 pub fn manifest(&self, format: impl ManifestFormat) -> Result<String> {412 fn manifest_dyn(val: &Val, manifest: &dyn ManifestFormat) -> Result<String> {413 manifest.manifest(val.clone())414 }415 manifest_dyn(self, &format)416 }417418 pub fn to_string(&self) -> Result<IStr> {419 Ok(match self {420 Self::Bool(true) => "true".into(),421 Self::Bool(false) => "false".into(),422 Self::Null => "null".into(),423 Self::Str(s) => s.clone().into_flat(),424 _ => self.manifest(ToStringFormat).map(IStr::from)?,425 })426 }427428 pub fn into_indexable(self) -> Result<IndexableVal> {429 Ok(match self {430 Val::Str(s) => IndexableVal::Str(s.into_flat()),431 Val::Arr(arr) => IndexableVal::Arr(arr),432 _ => throw!(ValueIsNotIndexable(self.value_type())),433 })434 }435}436437const fn is_function_like(val: &Val) -> bool {438 matches!(val, Val::Func(_))439}440441442pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {443 Ok(match (val_a, val_b) {444 (Val::Bool(a), Val::Bool(b)) => a == b,445 (Val::Null, Val::Null) => true,446 (Val::Str(a), Val::Str(b)) => a == b,447 (Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,448 #[cfg(feature = "exp-bigint")]449 (Val::BigInt(a), Val::BigInt(b)) => a == b,450 (Val::Arr(_), Val::Arr(_)) => {451 throw!("primitiveEquals operates on primitive types, got array")452 }453 (Val::Obj(_), Val::Obj(_)) => {454 throw!("primitiveEquals operates on primitive types, got object")455 }456 (a, b) if is_function_like(a) && is_function_like(b) => {457 throw!("cannot test equality of functions")458 }459 (_, _) => false,460 })461}462463464pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {465 if val_a.value_type() != val_b.value_type() {466 return Ok(false);467 }468 match (val_a, val_b) {469 (Val::Arr(a), Val::Arr(b)) => {470 if ArrValue::ptr_eq(a, b) {471 return Ok(true);472 }473 if a.len() != b.len() {474 return Ok(false);475 }476 for (a, b) in a.iter().zip(b.iter()) {477 if !equals(&a?, &b?)? {478 return Ok(false);479 }480 }481 Ok(true)482 }483 (Val::Obj(a), Val::Obj(b)) => {484 if ObjValue::ptr_eq(a, b) {485 return Ok(true);486 }487 let fields = a.fields(488 #[cfg(feature = "exp-preserve-order")]489 false,490 );491 if fields492 != b.fields(493 #[cfg(feature = "exp-preserve-order")]494 false,495 ) {496 return Ok(false);497 }498 for field in fields {499 if !equals(500 &a.get(field.clone())?.expect("field exists"),501 &b.get(field)?.expect("field exists"),502 )? {503 return Ok(false);504 }505 }506 Ok(true)507 }508 (a, b) => Ok(primitive_equals(a, b)?),509 }510}