1use std::{2 cell::RefCell,3 fmt::{self, Debug, Display},4 mem::replace,5 num::{NonZeroU32, NonZeroUsize},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 to_array(self) -> ArrValue {224 match self {225 Self::Str(s) => ArrValue::chars(s.chars()),226 Self::Arr(arr) => arr,227 }228 }229 230 231 232 233 234 235 236 pub fn slice(237 self,238 index: Option<i32>,239 end: Option<i32>,240 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,241 ) -> Result<Self> {242 match &self {243 Self::Str(s) => {244 let mut computed_len = None;245 let mut get_len = || {246 computed_len.map_or_else(247 || {248 let len = s.chars().count();249 let _ = computed_len.insert(len);250 len251 },252 |len| len,253 )254 };255 let mut get_idx = |pos: Option<i32>, default| {256 match pos {257 Some(v) if v < 0 => get_len().saturating_sub((-v) as usize),258 259 Some(v) => v as usize,260 None => default,261 }262 };263264 let index = get_idx(index, 0);265 let end = get_idx(end, usize::MAX);266 let step = step.as_deref().copied().unwrap_or(1);267268 if index >= end {269 return Ok(Self::Str("".into()));270 }271272 Ok(Self::Str(273 (s.chars()274 .skip(index)275 .take(end - index)276 .step_by(step)277 .collect::<String>())278 .into(),279 ))280 }281 Self::Arr(arr) => Ok(Self::Arr(arr.clone().slice(282 index,283 end,284 step.map(|v| NonZeroU32::new(v.value() as u32).expect("bounded != 0")),285 ))),286 }287 }288}289290#[derive(Debug, Clone, Trace)]291pub enum StrValue {292 Flat(IStr),293 Tree(Rc<(StrValue, StrValue, usize)>),294}295impl StrValue {296 pub fn concat(a: Self, b: Self) -> Self {297 298 const STRING_EXTEND_THRESHOLD: usize = 100;299300 if a.is_empty() {301 b302 } else if b.is_empty() {303 a304 } else if a.len() + b.len() < STRING_EXTEND_THRESHOLD {305 Self::Flat(format!("{a}{b}").into())306 } else {307 let len = a.len() + b.len();308 Self::Tree(Rc::new((a, b, len)))309 }310 }311 pub fn into_flat(self) -> IStr {312 #[cold]313 fn write_buf(s: &StrValue, out: &mut String) {314 match s {315 StrValue::Flat(f) => out.push_str(f),316 StrValue::Tree(t) => {317 write_buf(&t.0, out);318 write_buf(&t.1, out);319 }320 }321 }322 match self {323 Self::Flat(f) => f,324 Self::Tree(_) => {325 let mut buf = String::with_capacity(self.len());326 write_buf(&self, &mut buf);327 buf.into()328 }329 }330 }331 pub fn len(&self) -> usize {332 match self {333 Self::Flat(v) => v.len(),334 Self::Tree(t) => t.2,335 }336 }337 pub fn is_empty(&self) -> bool {338 match self {339 Self::Flat(v) => v.is_empty(),340 341 Self::Tree(_) => false,342 }343 }344}345impl<T> From<T> for StrValue346where347 IStr: From<T>,348{349 fn from(value: T) -> Self {350 Self::Flat(IStr::from(value))351 }352}353impl Display for StrValue {354 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {355 match self {356 Self::Flat(v) => write!(f, "{v}"),357 Self::Tree(t) => {358 write!(f, "{}", t.0)?;359 write!(f, "{}", t.1)360 }361 }362 }363}364impl PartialEq for StrValue {365 366 #[allow(clippy::unconditional_recursion)]367 fn eq(&self, other: &Self) -> bool {368 let a = self.clone().into_flat();369 let b = other.clone().into_flat();370 a == b371 }372}373impl Eq for StrValue {}374impl PartialOrd for StrValue {375 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {376 Some(self.cmp(other))377 }378}379impl Ord for StrValue {380 fn cmp(&self, other: &Self) -> std::cmp::Ordering {381 let a = self.clone().into_flat();382 let b = other.clone().into_flat();383 a.cmp(&b)384 }385}386387388#[derive(Debug, Clone, Trace, Default)]389pub enum Val {390 391 Bool(bool),392 393 #[default]394 Null,395 396 Str(StrValue),397 398 399 400 Num(f64),401 402 #[cfg(feature = "exp-bigint")]403 BigInt(#[trace(skip)] Box<num_bigint::BigInt>),404 405 Arr(ArrValue),406 407 Obj(ObjValue),408 409 Func(FuncVal),410}411412#[cfg(target_pointer_width = "64")]413static_assertions::assert_eq_size!(Val, [u8; 24]);414415impl From<IndexableVal> for Val {416 fn from(v: IndexableVal) -> Self {417 match v {418 IndexableVal::Str(s) => Self::string(s),419 IndexableVal::Arr(a) => Self::Arr(a),420 }421 }422}423424impl Val {425 pub const fn as_bool(&self) -> Option<bool> {426 match self {427 Self::Bool(v) => Some(*v),428 _ => None,429 }430 }431 pub const fn as_null(&self) -> Option<()> {432 match self {433 Self::Null => Some(()),434 _ => None,435 }436 }437 pub fn as_str(&self) -> Option<IStr> {438 match self {439 Self::Str(s) => Some(s.clone().into_flat()),440 _ => None,441 }442 }443 pub const fn as_num(&self) -> Option<f64> {444 match self {445 Self::Num(n) => Some(*n),446 _ => None,447 }448 }449 pub fn as_arr(&self) -> Option<ArrValue> {450 match self {451 Self::Arr(a) => Some(a.clone()),452 _ => None,453 }454 }455 pub fn as_obj(&self) -> Option<ObjValue> {456 match self {457 Self::Obj(o) => Some(o.clone()),458 _ => None,459 }460 }461 pub fn as_func(&self) -> Option<FuncVal> {462 match self {463 Self::Func(f) => Some(f.clone()),464 _ => None,465 }466 }467468 469 470 pub fn new_checked_num(num: f64) -> Result<Self> {471 if num.is_finite() {472 Ok(Self::Num(num))473 } else {474 bail!("overflow")475 }476 }477478 pub const fn value_type(&self) -> ValType {479 match self {480 Self::Str(..) => ValType::Str,481 Self::Num(..) => ValType::Num,482 #[cfg(feature = "exp-bigint")]483 Self::BigInt(..) => ValType::BigInt,484 Self::Arr(..) => ValType::Arr,485 Self::Obj(..) => ValType::Obj,486 Self::Bool(_) => ValType::Bool,487 Self::Null => ValType::Null,488 Self::Func(..) => ValType::Func,489 }490 }491492 pub fn manifest(&self, format: impl ManifestFormat) -> Result<String> {493 fn manifest_dyn(val: &Val, manifest: &dyn ManifestFormat) -> Result<String> {494 manifest.manifest(val.clone())495 }496 manifest_dyn(self, &format)497 }498499 pub fn to_string(&self) -> Result<IStr> {500 Ok(match self {501 Self::Bool(true) => "true".into(),502 Self::Bool(false) => "false".into(),503 Self::Null => "null".into(),504 Self::Str(s) => s.clone().into_flat(),505 _ => self.manifest(ToStringFormat).map(IStr::from)?,506 })507 }508509 pub fn into_indexable(self) -> Result<IndexableVal> {510 Ok(match self {511 Self::Str(s) => IndexableVal::Str(s.into_flat()),512 Self::Arr(arr) => IndexableVal::Arr(arr),513 _ => bail!(ValueIsNotIndexable(self.value_type())),514 })515 }516517 pub fn function(function: impl Into<FuncVal>) -> Self {518 Self::Func(function.into())519 }520 pub fn string(string: impl Into<StrValue>) -> Self {521 Self::Str(string.into())522 }523}524525impl From<IStr> for Val {526 fn from(value: IStr) -> Self {527 Self::string(value)528 }529}530impl From<String> for Val {531 fn from(value: String) -> Self {532 Self::string(value)533 }534}535impl From<&str> for Val {536 fn from(value: &str) -> Self {537 Self::string(value)538 }539}540impl From<ObjValue> for Val {541 fn from(value: ObjValue) -> Self {542 Self::Obj(value)543 }544}545546const fn is_function_like(val: &Val) -> bool {547 matches!(val, Val::Func(_))548}549550551pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {552 Ok(match (val_a, val_b) {553 (Val::Bool(a), Val::Bool(b)) => a == b,554 (Val::Null, Val::Null) => true,555 (Val::Str(a), Val::Str(b)) => a == b,556 (Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,557 #[cfg(feature = "exp-bigint")]558 (Val::BigInt(a), Val::BigInt(b)) => a == b,559 (Val::Arr(_), Val::Arr(_)) => {560 bail!("primitiveEquals operates on primitive types, got array")561 }562 (Val::Obj(_), Val::Obj(_)) => {563 bail!("primitiveEquals operates on primitive types, got object")564 }565 (a, b) if is_function_like(a) && is_function_like(b) => {566 bail!("cannot test equality of functions")567 }568 (_, _) => false,569 })570}571572573pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {574 if val_a.value_type() != val_b.value_type() {575 return Ok(false);576 }577 match (val_a, val_b) {578 (Val::Arr(a), Val::Arr(b)) => {579 if ArrValue::ptr_eq(a, b) {580 return Ok(true);581 }582 if a.len() != b.len() {583 return Ok(false);584 }585 for (a, b) in a.iter().zip(b.iter()) {586 if !equals(&a?, &b?)? {587 return Ok(false);588 }589 }590 Ok(true)591 }592 (Val::Obj(a), Val::Obj(b)) => {593 if ObjValue::ptr_eq(a, b) {594 return Ok(true);595 }596 let fields = a.fields(597 #[cfg(feature = "exp-preserve-order")]598 false,599 );600 if fields601 != b.fields(602 #[cfg(feature = "exp-preserve-order")]603 false,604 ) {605 return Ok(false);606 }607 for field in fields {608 if !equals(609 &a.get(field.clone())?.expect("field exists"),610 &b.get(field)?.expect("field exists"),611 )? {612 return Ok(false);613 }614 }615 Ok(true)616 }617 (a, b) => Ok(primitive_equals(a, b)?),618 }619}