1use std::{2 cell::RefCell,3 fmt::{self, Debug, Display},4 hash::Hasher,5 mem::replace,6 rc::Rc,7};89use jrsonnet_gcmodule::{Cc, Trace};10use jrsonnet_interner::IStr;11use jrsonnet_types::ValType;12use rustc_hash::FxHasher;1314pub use crate::arr::{ArrValue, ArrayLike};15use crate::{16 error::{Error, ErrorKind::*},17 function::FuncVal,18 gc::{GcHashMap, TraceBox},19 manifest::{ManifestFormat, ToStringFormat},20 tb, throw,21 typed::BoundedUsize,22 ObjValue, Result, Unbound, WeakObjValue,23};2425pub trait ThunkValue: Trace {26 type Output;27 fn get(self: Box<Self>) -> Result<Self::Output>;28}2930#[derive(Trace)]31enum ThunkInner<T: Trace> {32 Computed(T),33 Errored(Error),34 Waiting(TraceBox<dyn ThunkValue<Output = T>>),35 Pending,36}373839#[allow(clippy::module_name_repetitions)]40#[derive(Clone, Trace)]41pub struct Thunk<T: Trace>(Cc<RefCell<ThunkInner<T>>>);4243impl<T: Trace> Thunk<T> {44 pub fn evaluated(val: T) -> Self {45 Self(Cc::new(RefCell::new(ThunkInner::Computed(val))))46 }47 pub fn new(f: impl ThunkValue<Output = T> + 'static) -> Self {48 Self(Cc::new(RefCell::new(ThunkInner::Waiting(tb!(f)))))49 }50 pub fn errored(e: Error) -> Self {51 Self(Cc::new(RefCell::new(ThunkInner::Errored(e))))52 }53}5455impl<T> Thunk<T>56where57 T: Clone + Trace,58{59 pub fn force(&self) -> Result<()> {60 self.evaluate()?;61 Ok(())62 }6364 65 66 67 68 69 70 pub fn evaluate(&self) -> Result<T> {71 match &*self.0.borrow() {72 ThunkInner::Computed(v) => return Ok(v.clone()),73 ThunkInner::Errored(e) => return Err(e.clone()),74 ThunkInner::Pending => return Err(InfiniteRecursionDetected.into()),75 ThunkInner::Waiting(..) => (),76 };77 let ThunkInner::Waiting(value) = replace(&mut *self.0.borrow_mut(), ThunkInner::Pending)78 else {79 unreachable!();80 };81 let new_value = match value.0.get() {82 Ok(v) => v,83 Err(e) => {84 *self.0.borrow_mut() = ThunkInner::Errored(e.clone());85 return Err(e);86 }87 };88 *self.0.borrow_mut() = ThunkInner::Computed(new_value.clone());89 Ok(new_value)90 }91}9293pub trait ThunkMapper<Input>: Trace {94 type Output;95 fn map(self, from: Input) -> Result<Self::Output>;96}97impl<Input> Thunk<Input>98where99 Input: Trace + Clone,100{101 pub fn map<M>(self, mapper: M) -> Thunk<M::Output>102 where103 M: ThunkMapper<Input>,104 M::Output: Trace,105 {106 #[derive(Trace)]107 struct Mapped<Input: Trace, Mapper: Trace> {108 inner: Thunk<Input>,109 mapper: Mapper,110 }111 impl<Input, Mapper> ThunkValue for Mapped<Input, Mapper>112 where113 Input: Trace + Clone,114 Mapper: ThunkMapper<Input>,115 {116 type Output = Mapper::Output;117118 fn get(self: Box<Self>) -> Result<Self::Output> {119 let value = self.inner.evaluate()?;120 let mapped = self.mapper.map(value)?;121 Ok(mapped)122 }123 }124125 Thunk::new(Mapped::<Input, M> {126 inner: self,127 mapper,128 })129 }130}131132impl<T: Trace> From<Result<T>> for Thunk<T> {133 fn from(value: Result<T>) -> Self {134 match value {135 Ok(o) => Self::evaluated(o),136 Err(e) => Self::errored(e),137 }138 }139}140141impl<T: Trace + Default> Default for Thunk<T> {142 fn default() -> Self {143 Self::evaluated(T::default())144 }145}146147type CacheKey = (Option<WeakObjValue>, Option<WeakObjValue>);148149#[derive(Trace, Clone)]150pub struct CachedUnbound<I, T>151where152 I: Unbound<Bound = T>,153 T: Trace,154{155 cache: Cc<RefCell<GcHashMap<CacheKey, T>>>,156 value: I,157}158impl<I: Unbound<Bound = T>, T: Trace> CachedUnbound<I, T> {159 pub fn new(value: I) -> Self {160 Self {161 cache: Cc::new(RefCell::new(GcHashMap::new())),162 value,163 }164 }165}166impl<I: Unbound<Bound = T>, T: Clone + Trace> Unbound for CachedUnbound<I, T> {167 type Bound = T;168 fn bind(&self, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<T> {169 let cache_key = (170 sup.as_ref().map(|s| s.clone().downgrade()),171 this.as_ref().map(|t| t.clone().downgrade()),172 );173 {174 if let Some(t) = self.cache.borrow().get(&cache_key) {175 return Ok(t.clone());176 }177 }178 let bound = self.value.bind(sup, this)?;179180 {181 let mut cache = self.cache.borrow_mut();182 cache.insert(cache_key, bound.clone());183 }184185 Ok(bound)186 }187}188189impl<T: Debug + Trace> Debug for Thunk<T> {190 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {191 write!(f, "Lazy")192 }193}194impl<T: Trace> PartialEq for Thunk<T> {195 fn eq(&self, other: &Self) -> bool {196 Cc::ptr_eq(&self.0, &other.0)197 }198}199200201#[allow(clippy::module_name_repetitions)]202pub enum IndexableVal {203 204 Str(IStr),205 206 Arr(ArrValue),207}208impl IndexableVal {209 pub fn to_array(self) -> ArrValue {210 match self {211 IndexableVal::Str(s) => ArrValue::chars(s.chars()),212 IndexableVal::Arr(arr) => arr,213 }214 }215 216 217 218 219 220 221 222 pub fn slice(223 self,224 index: Option<BoundedUsize<0, { i32::MAX as usize }>>,225 end: Option<BoundedUsize<0, { i32::MAX as usize }>>,226 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,227 ) -> Result<Self> {228 match &self {229 IndexableVal::Str(s) => {230 let index = index.as_deref().copied().unwrap_or(0);231 let end = end.as_deref().copied().unwrap_or(usize::MAX);232 let step = step.as_deref().copied().unwrap_or(1);233234 if index >= end {235 return Ok(Self::Str("".into()));236 }237238 Ok(Self::Str(239 (s.chars()240 .skip(index)241 .take(end - index)242 .step_by(step)243 .collect::<String>())244 .into(),245 ))246 }247 IndexableVal::Arr(arr) => {248 let index = index.as_deref().copied().unwrap_or(0);249 let end = end.as_deref().copied().unwrap_or(usize::MAX).min(arr.len());250 let step = step.as_deref().copied().unwrap_or(1);251252 if index >= end {253 return Ok(Self::Arr(ArrValue::empty()));254 }255256 Ok(Self::Arr(257 arr.clone()258 .slice(Some(index), Some(end), Some(step))259 .expect("arguments checked"),260 ))261 }262 }263 }264}265266#[derive(Debug, Clone, Trace)]267pub enum StrValue {268 Flat(IStr),269 Tree(Rc<(StrValue, StrValue, usize)>),270}271impl StrValue {272 pub fn concat(a: StrValue, b: StrValue) -> Self {273 274 const STRING_EXTEND_THRESHOLD: usize = 100;275276 if a.is_empty() {277 b278 } else if b.is_empty() {279 a280 } else if a.len() + b.len() < STRING_EXTEND_THRESHOLD {281 Self::Flat(format!("{a}{b}").into())282 } else {283 let len = a.len() + b.len();284 Self::Tree(Rc::new((a, b, len)))285 }286 }287 pub fn into_flat(self) -> IStr {288 #[cold]289 fn write_buf(s: &StrValue, out: &mut String) {290 match s {291 StrValue::Flat(f) => out.push_str(f),292 StrValue::Tree(t) => {293 write_buf(&t.0, out);294 write_buf(&t.1, out);295 }296 }297 }298 match self {299 StrValue::Flat(f) => f,300 StrValue::Tree(_) => {301 let mut buf = String::with_capacity(self.len());302 write_buf(&self, &mut buf);303 buf.into()304 }305 }306 }307 pub fn len(&self) -> usize {308 match self {309 StrValue::Flat(v) => v.len(),310 StrValue::Tree(t) => t.2,311 }312 }313 pub fn is_empty(&self) -> bool {314 match self {315 Self::Flat(v) => v.is_empty(),316 317 Self::Tree(_) => false,318 }319 }320}321impl From<&str> for StrValue {322 fn from(value: &str) -> Self {323 Self::Flat(value.into())324 }325}326impl From<String> for StrValue {327 fn from(value: String) -> Self {328 Self::Flat(value.into())329 }330}331impl From<IStr> for StrValue {332 fn from(value: IStr) -> Self {333 Self::Flat(value)334 }335}336impl Display for StrValue {337 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {338 match self {339 StrValue::Flat(v) => write!(f, "{v}"),340 StrValue::Tree(t) => {341 write!(f, "{}", t.0)?;342 write!(f, "{}", t.1)343 }344 }345 }346}347impl PartialEq for StrValue {348 fn eq(&self, other: &Self) -> bool {349 let a = self.clone().into_flat();350 let b = other.clone().into_flat();351 a == b352 }353}354impl Eq for StrValue {}355impl PartialOrd for StrValue {356 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {357 Some(self.cmp(other))358 }359}360impl Ord for StrValue {361 fn cmp(&self, other: &Self) -> std::cmp::Ordering {362 let a = self.clone().into_flat();363 let b = other.clone().into_flat();364 a.cmp(&b)365 }366}367368369#[derive(Debug, Clone, Trace, Default)]370pub enum Val {371 372 Bool(bool),373 374 #[default]375 Null,376 377 Str(StrValue),378 379 380 381 Num(f64),382 383 #[cfg(feature = "exp-bigint")]384 BigInt(#[trace(skip)] Box<num_bigint::BigInt>),385 386 Arr(ArrValue),387 388 Obj(ObjValue),389 390 Func(FuncVal),391}392393#[cfg(target_pointer_width = "64")]394static_assertions::assert_eq_size!(Val, [u8; 24]);395396impl From<IndexableVal> for Val {397 fn from(v: IndexableVal) -> Self {398 match v {399 IndexableVal::Str(s) => Self::Str(StrValue::Flat(s)),400 IndexableVal::Arr(a) => Self::Arr(a),401 }402 }403}404405impl Val {406 pub const fn as_bool(&self) -> Option<bool> {407 match self {408 Self::Bool(v) => Some(*v),409 _ => None,410 }411 }412 pub const fn as_null(&self) -> Option<()> {413 match self {414 Self::Null => Some(()),415 _ => None,416 }417 }418 pub fn as_str(&self) -> Option<IStr> {419 match self {420 Self::Str(s) => Some(s.clone().into_flat()),421 _ => None,422 }423 }424 pub const fn as_num(&self) -> Option<f64> {425 match self {426 Self::Num(n) => Some(*n),427 _ => None,428 }429 }430 pub fn as_arr(&self) -> Option<ArrValue> {431 match self {432 Self::Arr(a) => Some(a.clone()),433 _ => None,434 }435 }436 pub fn as_obj(&self) -> Option<ObjValue> {437 match self {438 Self::Obj(o) => Some(o.clone()),439 _ => None,440 }441 }442 pub fn as_func(&self) -> Option<FuncVal> {443 match self {444 Self::Func(f) => Some(f.clone()),445 _ => None,446 }447 }448449 450 451 pub fn new_checked_num(num: f64) -> Result<Self> {452 if num.is_finite() {453 Ok(Self::Num(num))454 } else {455 throw!("overflow")456 }457 }458459 pub const fn value_type(&self) -> ValType {460 match self {461 Self::Str(..) => ValType::Str,462 Self::Num(..) => ValType::Num,463 #[cfg(feature = "exp-bigint")]464 Self::BigInt(..) => ValType::BigInt,465 Self::Arr(..) => ValType::Arr,466 Self::Obj(..) => ValType::Obj,467 Self::Bool(_) => ValType::Bool,468 Self::Null => ValType::Null,469 Self::Func(..) => ValType::Func,470 }471 }472473 pub fn manifest(&self, format: impl ManifestFormat) -> Result<String> {474 fn manifest_dyn(val: &Val, manifest: &dyn ManifestFormat) -> Result<String> {475 manifest.manifest(val.clone())476 }477 manifest_dyn(self, &format)478 }479480 pub fn to_string(&self) -> Result<IStr> {481 Ok(match self {482 Self::Bool(true) => "true".into(),483 Self::Bool(false) => "false".into(),484 Self::Null => "null".into(),485 Self::Str(s) => s.clone().into_flat(),486 _ => self.manifest(ToStringFormat).map(IStr::from)?,487 })488 }489490 pub fn into_indexable(self) -> Result<IndexableVal> {491 Ok(match self {492 Val::Str(s) => IndexableVal::Str(s.into_flat()),493 Val::Arr(arr) => IndexableVal::Arr(arr),494 _ => throw!(ValueIsNotIndexable(self.value_type())),495 })496 }497}498499const fn is_function_like(val: &Val) -> bool {500 matches!(val, Val::Func(_))501}502503504pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {505 Ok(match (val_a, val_b) {506 (Val::Bool(a), Val::Bool(b)) => a == b,507 (Val::Null, Val::Null) => true,508 (Val::Str(a), Val::Str(b)) => a == b,509 (Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,510 #[cfg(feature = "exp-bigint")]511 (Val::BigInt(a), Val::BigInt(b)) => a == b,512 (Val::Arr(_), Val::Arr(_)) => {513 throw!("primitiveEquals operates on primitive types, got array")514 }515 (Val::Obj(_), Val::Obj(_)) => {516 throw!("primitiveEquals operates on primitive types, got object")517 }518 (a, b) if is_function_like(a) && is_function_like(b) => {519 throw!("cannot test equality of functions")520 }521 (_, _) => false,522 })523}524525526pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {527 if val_a.value_type() != val_b.value_type() {528 return Ok(false);529 }530 match (val_a, val_b) {531 (Val::Arr(a), Val::Arr(b)) => {532 if ArrValue::ptr_eq(a, b) {533 return Ok(true);534 }535 if a.len() != b.len() {536 return Ok(false);537 }538 for (a, b) in a.iter().zip(b.iter()) {539 if !equals(&a?, &b?)? {540 return Ok(false);541 }542 }543 Ok(true)544 }545 (Val::Obj(a), Val::Obj(b)) => {546 if ObjValue::ptr_eq(a, b) {547 return Ok(true);548 }549 let fields = a.fields(550 #[cfg(feature = "exp-preserve-order")]551 false,552 );553 if fields554 != b.fields(555 #[cfg(feature = "exp-preserve-order")]556 false,557 ) {558 return Ok(false);559 }560 for field in fields {561 if !equals(562 &a.get(field.clone())?.expect("field exists"),563 &b.get(field)?.expect("field exists"),564 )? {565 return Ok(false);566 }567 }568 Ok(true)569 }570 (a, b) => Ok(primitive_equals(a, b)?),571 }572}