1use std::{2 cell::RefCell,3 fmt::{self, Debug, Display},4 mem::replace,5 num::NonZeroU32,6 rc::Rc,7};89use jrsonnet_gcmodule::{Cc, Trace};10use jrsonnet_interner::IStr;11use jrsonnet_types::ValType;1213pub use crate::arr::{ArrValue, ArrayLike};14use crate::{15 bail,16 error::{Error, ErrorKind::*},17 function::FuncVal,18 gc::{GcHashMap, TraceBox},19 manifest::{ManifestFormat, ToStringFormat},20 tb,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 pub fn result(res: Result<T, Error>) -> Self {54 match res {55 Ok(o) => Self::evaluated(o),56 Err(e) => Self::errored(e),57 }58 }59}6061impl<T> Thunk<T>62where63 T: Clone + Trace,64{65 pub fn force(&self) -> Result<()> {66 self.evaluate()?;67 Ok(())68 }6970 71 72 73 74 75 76 pub fn evaluate(&self) -> Result<T> {77 match &*self.0.borrow() {78 ThunkInner::Computed(v) => return Ok(v.clone()),79 ThunkInner::Errored(e) => return Err(e.clone()),80 ThunkInner::Pending => return Err(InfiniteRecursionDetected.into()),81 ThunkInner::Waiting(..) => (),82 };83 let ThunkInner::Waiting(value) = replace(&mut *self.0.borrow_mut(), ThunkInner::Pending)84 else {85 unreachable!();86 };87 let new_value = match value.0.get() {88 Ok(v) => v,89 Err(e) => {90 *self.0.borrow_mut() = ThunkInner::Errored(e.clone());91 return Err(e);92 }93 };94 *self.0.borrow_mut() = ThunkInner::Computed(new_value.clone());95 Ok(new_value)96 }97}9899pub trait ThunkMapper<Input>: Trace {100 type Output;101 fn map(self, from: Input) -> Result<Self::Output>;102}103impl<Input> Thunk<Input>104where105 Input: Trace + Clone,106{107 pub fn map<M>(self, mapper: M) -> Thunk<M::Output>108 where109 M: ThunkMapper<Input>,110 M::Output: Trace,111 {112 #[derive(Trace)]113 struct Mapped<Input: Trace, Mapper: Trace> {114 inner: Thunk<Input>,115 mapper: Mapper,116 }117 impl<Input, Mapper> ThunkValue for Mapped<Input, Mapper>118 where119 Input: Trace + Clone,120 Mapper: ThunkMapper<Input>,121 {122 type Output = Mapper::Output;123124 fn get(self: Box<Self>) -> Result<Self::Output> {125 let value = self.inner.evaluate()?;126 let mapped = self.mapper.map(value)?;127 Ok(mapped)128 }129 }130131 Thunk::new(Mapped::<Input, M> {132 inner: self,133 mapper,134 })135 }136}137138impl<T: Trace> From<Result<T>> for Thunk<T> {139 fn from(value: Result<T>) -> Self {140 match value {141 Ok(o) => Self::evaluated(o),142 Err(e) => Self::errored(e),143 }144 }145}146impl<T, V: Trace> From<T> for Thunk<V>147where148 T: ThunkValue<Output = V>,149{150 fn from(value: T) -> Self {151 Self::new(value)152 }153}154155impl<T: Trace + Default> Default for Thunk<T> {156 fn default() -> Self {157 Self::evaluated(T::default())158 }159}160161type CacheKey = (Option<WeakObjValue>, Option<WeakObjValue>);162163#[derive(Trace, Clone)]164pub struct CachedUnbound<I, T>165where166 I: Unbound<Bound = T>,167 T: Trace,168{169 cache: Cc<RefCell<GcHashMap<CacheKey, T>>>,170 value: I,171}172impl<I: Unbound<Bound = T>, T: Trace> CachedUnbound<I, T> {173 pub fn new(value: I) -> Self {174 Self {175 cache: Cc::new(RefCell::new(GcHashMap::new())),176 value,177 }178 }179}180impl<I: Unbound<Bound = T>, T: Clone + Trace> Unbound for CachedUnbound<I, T> {181 type Bound = T;182 fn bind(&self, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<T> {183 let cache_key = (184 sup.as_ref().map(|s| s.clone().downgrade()),185 this.as_ref().map(|t| t.clone().downgrade()),186 );187 {188 if let Some(t) = self.cache.borrow().get(&cache_key) {189 return Ok(t.clone());190 }191 }192 let bound = self.value.bind(sup, this)?;193194 {195 let mut cache = self.cache.borrow_mut();196 cache.insert(cache_key, bound.clone());197 }198199 Ok(bound)200 }201}202203impl<T: Debug + Trace> Debug for Thunk<T> {204 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {205 write!(f, "Lazy")206 }207}208impl<T: Trace> PartialEq for Thunk<T> {209 fn eq(&self, other: &Self) -> bool {210 Cc::ptr_eq(&self.0, &other.0)211 }212}213214215#[allow(clippy::module_name_repetitions)]216pub enum IndexableVal {217 218 Str(IStr),219 220 Arr(ArrValue),221}222impl IndexableVal {223 pub fn is_empty(&self) -> bool {224 match self {225 Self::Str(s) => s.is_empty(),226 Self::Arr(s) => s.is_empty(),227 }228 }229230 pub fn to_array(self) -> ArrValue {231 match self {232 Self::Str(s) => ArrValue::chars(s.chars()),233 Self::Arr(arr) => arr,234 }235 }236 237 238 239 240 241 242 243 pub fn slice(244 self,245 index: Option<i32>,246 end: Option<i32>,247 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,248 ) -> Result<Self> {249 match &self {250 Self::Str(s) => {251 let mut computed_len = None;252 let mut get_len = || {253 computed_len.map_or_else(254 || {255 let len = s.chars().count();256 let _ = computed_len.insert(len);257 len258 },259 |len| len,260 )261 };262 let mut get_idx = |pos: Option<i32>, default| {263 match pos {264 Some(v) if v < 0 => get_len().saturating_sub((-v) as usize),265 266 Some(v) => v as usize,267 None => default,268 }269 };270271 let index = get_idx(index, 0);272 let end = get_idx(end, usize::MAX);273 let step = step.as_deref().copied().unwrap_or(1);274275 if index >= end {276 return Ok(Self::Str("".into()));277 }278279 Ok(Self::Str(280 (s.chars()281 .skip(index)282 .take(end - index)283 .step_by(step)284 .collect::<String>())285 .into(),286 ))287 }288 Self::Arr(arr) => Ok(Self::Arr(arr.clone().slice(289 index,290 end,291 step.map(|v| NonZeroU32::new(v.value() as u32).expect("bounded != 0")),292 ))),293 }294 }295}296297#[derive(Debug, Clone, Trace)]298pub enum StrValue {299 Flat(IStr),300 Tree(Rc<(StrValue, StrValue, usize)>),301}302impl StrValue {303 pub fn concat(a: Self, b: Self) -> Self {304 305 const STRING_EXTEND_THRESHOLD: usize = 100;306307 if a.is_empty() {308 b309 } else if b.is_empty() {310 a311 } else if a.len() + b.len() < STRING_EXTEND_THRESHOLD {312 Self::Flat(format!("{a}{b}").into())313 } else {314 let len = a.len() + b.len();315 Self::Tree(Rc::new((a, b, len)))316 }317 }318 pub fn into_flat(self) -> IStr {319 #[cold]320 fn write_buf(s: &StrValue, out: &mut String) {321 match s {322 StrValue::Flat(f) => out.push_str(f),323 StrValue::Tree(t) => {324 write_buf(&t.0, out);325 write_buf(&t.1, out);326 }327 }328 }329 match self {330 Self::Flat(f) => f,331 Self::Tree(_) => {332 let mut buf = String::with_capacity(self.len());333 write_buf(&self, &mut buf);334 buf.into()335 }336 }337 }338 pub fn len(&self) -> usize {339 match self {340 Self::Flat(v) => v.len(),341 Self::Tree(t) => t.2,342 }343 }344 pub fn is_empty(&self) -> bool {345 match self {346 Self::Flat(v) => v.is_empty(),347 348 Self::Tree(_) => false,349 }350 }351}352impl<T> From<T> for StrValue353where354 IStr: From<T>,355{356 fn from(value: T) -> Self {357 Self::Flat(IStr::from(value))358 }359}360impl Display for StrValue {361 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {362 match self {363 Self::Flat(v) => write!(f, "{v}"),364 Self::Tree(t) => {365 write!(f, "{}", t.0)?;366 write!(f, "{}", t.1)367 }368 }369 }370}371impl PartialEq for StrValue {372 373 #[allow(clippy::unconditional_recursion)]374 fn eq(&self, other: &Self) -> bool {375 let a = self.clone().into_flat();376 let b = other.clone().into_flat();377 a == b378 }379}380impl Eq for StrValue {}381impl PartialOrd for StrValue {382 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {383 Some(self.cmp(other))384 }385}386impl Ord for StrValue {387 fn cmp(&self, other: &Self) -> std::cmp::Ordering {388 let a = self.clone().into_flat();389 let b = other.clone().into_flat();390 a.cmp(&b)391 }392}393394395#[derive(Debug, Clone, Trace, Default)]396pub enum Val {397 398 Bool(bool),399 400 #[default]401 Null,402 403 Str(StrValue),404 405 406 407 Num(f64),408 409 #[cfg(feature = "exp-bigint")]410 BigInt(#[trace(skip)] Box<num_bigint::BigInt>),411 412 Arr(ArrValue),413 414 Obj(ObjValue),415 416 Func(FuncVal),417}418419#[cfg(target_pointer_width = "64")]420static_assertions::assert_eq_size!(Val, [u8; 24]);421422impl From<IndexableVal> for Val {423 fn from(v: IndexableVal) -> Self {424 match v {425 IndexableVal::Str(s) => Self::string(s),426 IndexableVal::Arr(a) => Self::Arr(a),427 }428 }429}430431impl Val {432 pub const fn as_bool(&self) -> Option<bool> {433 match self {434 Self::Bool(v) => Some(*v),435 _ => None,436 }437 }438 pub const fn as_null(&self) -> Option<()> {439 match self {440 Self::Null => Some(()),441 _ => None,442 }443 }444 pub fn as_str(&self) -> Option<IStr> {445 match self {446 Self::Str(s) => Some(s.clone().into_flat()),447 _ => None,448 }449 }450 pub const fn as_num(&self) -> Option<f64> {451 match self {452 Self::Num(n) => Some(*n),453 _ => None,454 }455 }456 pub fn as_arr(&self) -> Option<ArrValue> {457 match self {458 Self::Arr(a) => Some(a.clone()),459 _ => None,460 }461 }462 pub fn as_obj(&self) -> Option<ObjValue> {463 match self {464 Self::Obj(o) => Some(o.clone()),465 _ => None,466 }467 }468 pub fn as_func(&self) -> Option<FuncVal> {469 match self {470 Self::Func(f) => Some(f.clone()),471 _ => None,472 }473 }474475 476 477 pub fn new_checked_num(num: f64) -> Result<Self> {478 if num.is_finite() {479 Ok(Self::Num(num))480 } else {481 bail!("overflow")482 }483 }484485 pub const fn value_type(&self) -> ValType {486 match self {487 Self::Str(..) => ValType::Str,488 Self::Num(..) => ValType::Num,489 #[cfg(feature = "exp-bigint")]490 Self::BigInt(..) => ValType::BigInt,491 Self::Arr(..) => ValType::Arr,492 Self::Obj(..) => ValType::Obj,493 Self::Bool(_) => ValType::Bool,494 Self::Null => ValType::Null,495 Self::Func(..) => ValType::Func,496 }497 }498499 pub fn manifest(&self, format: impl ManifestFormat) -> Result<String> {500 fn manifest_dyn(val: &Val, manifest: &dyn ManifestFormat) -> Result<String> {501 manifest.manifest(val.clone())502 }503 manifest_dyn(self, &format)504 }505506 pub fn to_string(&self) -> Result<IStr> {507 Ok(match self {508 Self::Bool(true) => "true".into(),509 Self::Bool(false) => "false".into(),510 Self::Null => "null".into(),511 Self::Str(s) => s.clone().into_flat(),512 _ => self.manifest(ToStringFormat).map(IStr::from)?,513 })514 }515516 pub fn into_indexable(self) -> Result<IndexableVal> {517 Ok(match self {518 Self::Str(s) => IndexableVal::Str(s.into_flat()),519 Self::Arr(arr) => IndexableVal::Arr(arr),520 _ => bail!(ValueIsNotIndexable(self.value_type())),521 })522 }523524 pub fn function(function: impl Into<FuncVal>) -> Self {525 Self::Func(function.into())526 }527 pub fn string(string: impl Into<StrValue>) -> Self {528 Self::Str(string.into())529 }530}531532impl From<IStr> for Val {533 fn from(value: IStr) -> Self {534 Self::string(value)535 }536}537impl From<String> for Val {538 fn from(value: String) -> Self {539 Self::string(value)540 }541}542impl From<&str> for Val {543 fn from(value: &str) -> Self {544 Self::string(value)545 }546}547impl From<ObjValue> for Val {548 fn from(value: ObjValue) -> Self {549 Self::Obj(value)550 }551}552553const fn is_function_like(val: &Val) -> bool {554 matches!(val, Val::Func(_))555}556557558pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {559 Ok(match (val_a, val_b) {560 (Val::Bool(a), Val::Bool(b)) => a == b,561 (Val::Null, Val::Null) => true,562 (Val::Str(a), Val::Str(b)) => a == b,563 (Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,564 #[cfg(feature = "exp-bigint")]565 (Val::BigInt(a), Val::BigInt(b)) => a == b,566 (Val::Arr(_), Val::Arr(_)) => {567 bail!("primitiveEquals operates on primitive types, got array")568 }569 (Val::Obj(_), Val::Obj(_)) => {570 bail!("primitiveEquals operates on primitive types, got object")571 }572 (a, b) if is_function_like(a) && is_function_like(b) => {573 bail!("cannot test equality of functions")574 }575 (_, _) => false,576 })577}578579580pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {581 if val_a.value_type() != val_b.value_type() {582 return Ok(false);583 }584 match (val_a, val_b) {585 (Val::Arr(a), Val::Arr(b)) => {586 if ArrValue::ptr_eq(a, b) {587 return Ok(true);588 }589 if a.len() != b.len() {590 return Ok(false);591 }592 for (a, b) in a.iter().zip(b.iter()) {593 if !equals(&a?, &b?)? {594 return Ok(false);595 }596 }597 Ok(true)598 }599 (Val::Obj(a), Val::Obj(b)) => {600 if ObjValue::ptr_eq(a, b) {601 return Ok(true);602 }603 let fields = a.fields(604 #[cfg(feature = "exp-preserve-order")]605 false,606 );607 if fields608 != b.fields(609 #[cfg(feature = "exp-preserve-order")]610 false,611 ) {612 return Ok(false);613 }614 for field in fields {615 if !equals(616 &a.get(field.clone())?.expect("field exists"),617 &b.get(field)?.expect("field exists"),618 )? {619 return Ok(false);620 }621 }622 Ok(true)623 }624 (a, b) => Ok(primitive_equals(a, b)?),625 }626}