difftreelog
refactor dyn ObjValue
in: master
5 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -219,6 +219,17 @@
]
[[package]]
+name = "derivative"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -346,6 +357,7 @@
"anyhow",
"async-trait",
"bincode",
+ "derivative",
"hashbrown 0.13.2",
"jrsonnet-gcmodule",
"jrsonnet-interner",
crates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-evaluator/Cargo.toml
+++ b/crates/jrsonnet-evaluator/Cargo.toml
@@ -58,3 +58,4 @@
async-trait = { version = "0.1.60", optional = true }
# Bigint
num-bigint = { version = "0.4.3", features = ["serde"], optional = true }
+derivative = "2.2.0"
crates/jrsonnet-evaluator/src/gc.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/gc.rs
+++ b/crates/jrsonnet-evaluator/src/gc.rs
@@ -116,7 +116,10 @@
}
}
-pub struct GcHashMap<K, V>(pub HashMap<K, V, BuildHasherDefault<FxHasher>>);
+#[derive(Debug)]
+pub struct GcHashMap<K, V>(
+ pub HashMap<K, V, BuildHasherDefault<FxHasher>>
+);
impl<K, V> GcHashMap<K, V> {
pub fn new() -> Self {
Self(HashMap::default())
crates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth1use std::{2 cell::RefCell,3 fmt::Debug,4 hash::{Hash, Hasher},5 ptr::addr_of,6};78use jrsonnet_gcmodule::{Cc, Trace, Weak};9use jrsonnet_interner::IStr;10use jrsonnet_parser::{ExprLocation, Visibility};11use rustc_hash::FxHashMap;1213use crate::{14 error::{Error, ErrorKind::*},15 function::CallLocation,16 gc::{GcHashMap, GcHashSet, TraceBox},17 operator::evaluate_add_op,18 tb, throw,19 val::ThunkValue,20 MaybeUnbound, Result, State, Thunk, Unbound, Val,21};2223#[cfg(not(feature = "exp-preserve-order"))]24mod ordering {25 #![allow(26 // This module works as stub for preserve-order feature27 clippy::unused_self,28 )]2930 use jrsonnet_gcmodule::Trace;3132 #[derive(Clone, Copy, Default, Debug, Trace)]33 pub struct FieldIndex(());34 impl FieldIndex {35 pub const fn next(self) -> Self {36 Self(())37 }38 }3940 #[derive(Clone, Copy, Default, Debug, Trace)]41 pub struct SuperDepth(());42 impl SuperDepth {43 pub const fn deeper(self) -> Self {44 Self(())45 }46 }4748 #[derive(Clone, Copy)]49 pub struct FieldSortKey(());50 impl FieldSortKey {51 pub const fn new(_: SuperDepth, _: FieldIndex) -> Self {52 Self(())53 }54 }55}5657#[cfg(feature = "exp-preserve-order")]58mod ordering {59 use std::cmp::{Ordering, Reverse};6061 use jrsonnet_gcmodule::Trace;6263 #[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]64 pub struct FieldIndex(u32);65 impl FieldIndex {66 pub fn next(self) -> Self {67 Self(self.0 + 1)68 }69 }7071 #[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Debug)]72 pub struct SuperDepth(u32);73 impl SuperDepth {74 pub fn deeper(self) -> Self {75 Self(self.0 + 1)76 }77 }7879 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]80 pub struct FieldSortKey(Reverse<SuperDepth>, FieldIndex);81 impl FieldSortKey {82 pub fn new(depth: SuperDepth, index: FieldIndex) -> Self {83 Self(Reverse(depth), index)84 }85 pub fn collide(self, other: Self) -> Self {86 match self.0 .0.cmp(&other.0 .0) {87 Ordering::Greater => self,88 Ordering::Less => other,89 Ordering::Equal => unreachable!("object can't have two fields with the same name"),90 }91 }92 }93}9495use ordering::*;9697#[allow(clippy::module_name_repetitions)]98#[derive(Debug, Trace)]99pub struct ObjMember {100 pub add: bool,101 pub visibility: Visibility,102 original_index: FieldIndex,103 pub invoke: MaybeUnbound,104 pub location: Option<ExprLocation>,105}106107pub trait ObjectAssertion: Trace {108 fn run(&self, super_obj: Option<ObjValue>, this: Option<ObjValue>) -> Result<()>;109}110111// Field => This112113#[derive(Trace)]114enum CacheValue {115 Cached(Val),116 NotFound,117 Pending,118 Errored(Error),119}120121#[allow(clippy::module_name_repetitions)]122#[derive(Trace)]123#[trace(tracking(force))]124pub struct ObjValueInternals {125 sup: Option<ObjValue>,126 this: Option<ObjValue>,127128 assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,129 assertions_ran: RefCell<GcHashSet<ObjValue>>,130 this_entries: Cc<GcHashMap<IStr, ObjMember>>,131 value_cache: RefCell<GcHashMap<(IStr, Option<WeakObjValue>), CacheValue>>,132}133134#[derive(Clone, Trace)]135pub struct WeakObjValue(#[trace(skip)] pub(crate) Weak<ObjValueInternals>);136137impl PartialEq for WeakObjValue {138 fn eq(&self, other: &Self) -> bool {139 Weak::ptr_eq(&self.0, &other.0)140 }141}142143impl Eq for WeakObjValue {}144impl Hash for WeakObjValue {145 fn hash<H: Hasher>(&self, hasher: &mut H) {146 // Safety: usize is POD147 let addr = unsafe { *std::ptr::addr_of!(self.0).cast() };148 hasher.write_usize(addr);149 }150}151152#[allow(clippy::module_name_repetitions)]153#[derive(Clone, Trace)]154pub struct ObjValue(pub(crate) Cc<ObjValueInternals>);155impl Debug for ObjValue {156 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {157 if let Some(super_obj) = self.0.sup.as_ref() {158 if f.alternate() {159 write!(f, "{super_obj:#?}")?;160 } else {161 write!(f, "{super_obj:?}")?;162 }163 write!(f, " + ")?;164 }165 let mut debug = f.debug_struct("ObjValue");166 for (name, member) in self.0.this_entries.iter() {167 debug.field(name, member);168 }169 debug.finish_non_exhaustive()170 }171}172173impl ObjValue {174 pub fn new(175 sup: Option<Self>,176 this_entries: Cc<GcHashMap<IStr, ObjMember>>,177 assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,178 ) -> Self {179 Self(Cc::new(ObjValueInternals {180 sup,181 this: None,182 assertions,183 assertions_ran: RefCell::new(GcHashSet::new()),184 this_entries,185 value_cache: RefCell::new(GcHashMap::new()),186 }))187 }188 pub fn new_empty() -> Self {189 Self::new(None, Cc::new(GcHashMap::new()), Cc::new(Vec::new()))190 }191 pub fn builder() -> ObjValueBuilder {192 ObjValueBuilder::new()193 }194 pub fn builder_with_capacity(capacity: usize) -> ObjValueBuilder {195 ObjValueBuilder::with_capacity(capacity)196 }197 #[must_use]198 pub fn extend_from(&self, sup: Self) -> Self {199 match &self.0.sup {200 None => Self::new(201 Some(sup),202 self.0.this_entries.clone(),203 self.0.assertions.clone(),204 ),205 Some(v) => Self::new(206 Some(v.extend_from(sup)),207 self.0.this_entries.clone(),208 self.0.assertions.clone(),209 ),210 }211 }212 pub(crate) fn extend_with_raw_member(self, key: IStr, value: ObjMember) -> Self {213 let mut new = GcHashMap::with_capacity(1);214 new.insert(key, value);215 Self::new(Some(self), Cc::new(new), Cc::new(Vec::new()))216 }217 pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder<'_>> {218 ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())219 }220221 #[must_use]222 pub fn with_this(&self, this: Self) -> Self {223 Self(Cc::new(ObjValueInternals {224 sup: self.0.sup.clone(),225 assertions: self.0.assertions.clone(),226 assertions_ran: RefCell::new(GcHashSet::new()),227 this: Some(this),228 this_entries: self.0.this_entries.clone(),229 value_cache: RefCell::new(GcHashMap::new()),230 }))231 }232233 pub fn len(&self) -> usize {234 self.fields_visibility()235 .into_iter()236 .filter(|(_, (visible, _))| *visible)237 .count()238 }239240 pub fn is_empty(&self) -> bool {241 if !self.0.this_entries.is_empty() {242 return false;243 }244 self.0.sup.as_ref().map_or(true, Self::is_empty)245 }246247 /// Run callback for every field found in object248 ///249 /// Returns true if ended prematurely250 pub(crate) fn enum_fields(251 &self,252 depth: SuperDepth,253 handler: &mut impl FnMut(SuperDepth, &IStr, &ObjMember) -> bool,254 ) -> bool {255 if let Some(s) = &self.0.sup {256 if s.enum_fields(depth.deeper(), handler) {257 return true;258 }259 }260 for (name, member) in self.0.this_entries.iter() {261 if handler(depth, name, member) {262 return true;263 }264 }265 false266 }267268 pub fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {269 let mut out = FxHashMap::default();270 self.enum_fields(SuperDepth::default(), &mut |depth, name, member| {271 let new_sort_key = FieldSortKey::new(depth, member.original_index);272 let entry = out.entry(name.clone());273 let (visible, _) = entry.or_insert((true, new_sort_key));274 match member.visibility {275 Visibility::Normal => {}276 Visibility::Hidden => {277 *visible = false;278 }279 Visibility::Unhide => {280 *visible = true;281 }282 };283 false284 });285 out286 }287 pub fn fields_ex(288 &self,289 include_hidden: bool,290 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,291 ) -> Vec<IStr> {292 #[cfg(feature = "exp-preserve-order")]293 if preserve_order {294 let (mut fields, mut keys): (Vec<_>, Vec<_>) = self295 .fields_visibility()296 .into_iter()297 .filter(|(_, (visible, _))| include_hidden || *visible)298 .enumerate()299 .map(|(idx, (k, (_, sk)))| (k, (sk, idx)))300 .unzip();301 keys.sort_unstable_by_key(|v| v.0);302 // Reorder in-place by resulting indexes303 for i in 0..fields.len() {304 let x = fields[i].clone();305 let mut j = i;306 loop {307 let k = keys[j].1;308 keys[j].1 = j;309 if k == i {310 break;311 }312 fields[j] = fields[k].clone();313 j = k;314 }315 fields[j] = x;316 }317 return fields;318 }319320 let mut fields: Vec<_> = self321 .fields_visibility()322 .into_iter()323 .filter(|(_, (visible, _))| include_hidden || *visible)324 .map(|(k, _)| k)325 .collect();326 fields.sort_unstable();327 fields328 }329 pub fn fields(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Vec<IStr> {330 self.fields_ex(331 false,332 #[cfg(feature = "exp-preserve-order")]333 preserve_order,334 )335 }336337 pub fn field_visibility(&self, name: IStr) -> Option<Visibility> {338 if let Some(m) = self.0.this_entries.get(&name) {339 Some(match &m.visibility {340 Visibility::Normal => self341 .0342 .sup343 .as_ref()344 .and_then(|super_obj| super_obj.field_visibility(name))345 .unwrap_or(Visibility::Normal),346 v => *v,347 })348 } else if let Some(super_obj) = &self.0.sup {349 super_obj.field_visibility(name)350 } else {351 None352 }353 }354355 fn has_field_include_hidden(&self, name: IStr) -> bool {356 if self.0.this_entries.contains_key(&name) {357 true358 } else if let Some(super_obj) = &self.0.sup {359 super_obj.has_field_include_hidden(name)360 } else {361 false362 }363 }364365 pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {366 if include_hidden {367 self.has_field_include_hidden(name)368 } else {369 self.has_field(name)370 }371 }372 pub fn has_field(&self, name: IStr) -> bool {373 self.field_visibility(name)374 .map_or(false, |v| v.is_visible())375 }376377 pub fn iter(378 &self,379 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,380 ) -> impl Iterator<Item = (IStr, Result<Val>)> + '_ {381 let fields = self.fields(382 #[cfg(feature = "exp-preserve-order")]383 preserve_order,384 );385 fields.into_iter().map(|field| {386 (387 field.clone(),388 self.get(field)389 .map(|opt| opt.expect("iterating over keys, field exists")),390 )391 })392 }393 pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {394 #[derive(Trace)]395 struct ThunkGet {396 obj: ObjValue,397 key: IStr,398 }399 impl ThunkValue for ThunkGet {400 type Output = Val;401402 fn get(self: Box<Self>) -> Result<Self::Output> {403 Ok(self.obj.get(self.key)?.expect("field exists"))404 }405 }406407 if !self.has_field_ex(key.clone(), true) {408 return None;409 }410 Some(Thunk::new(ThunkGet {411 obj: self.clone(),412 key,413 }))414 }415 pub fn get(&self, key: IStr) -> Result<Option<Val>> {416 self.get_for(key, self.0.this.clone().unwrap_or_else(|| self.clone()))417 }418 pub fn get_for(&self, key: IStr, this: Self) -> Result<Option<Val>> {419 self.run_assertions()?;420 let cache_key = (421 key.clone(),422 (!ObjValue::ptr_eq(&this, self)).then(|| this.clone().downgrade()),423 );424 if let Some(v) = self.0.value_cache.borrow().get(&cache_key) {425 return Ok(match v {426 CacheValue::Cached(v) => Some(v.clone()),427 CacheValue::NotFound => None,428 CacheValue::Pending => throw!(InfiniteRecursionDetected),429 CacheValue::Errored(e) => return Err(e.clone()),430 });431 }432 self.0433 .value_cache434 .borrow_mut()435 .insert(cache_key.clone(), CacheValue::Pending);436 let value = self.get_raw(key, this).map_err(|e| {437 self.0438 .value_cache439 .borrow_mut()440 .insert(cache_key.clone(), CacheValue::Errored(e.clone()));441 e442 })?;443 self.0.value_cache.borrow_mut().insert(444 cache_key,445 value446 .as_ref()447 .map_or(CacheValue::NotFound, |v| CacheValue::Cached(v.clone())),448 );449 Ok(value)450 }451452 fn get_raw(&self, key: IStr, real_this: Self) -> Result<Option<Val>> {453 match (self.0.this_entries.get(&key), &self.0.sup) {454 (Some(k), None) => Ok(Some(self.evaluate_this(k, real_this)?)),455 (Some(k), Some(super_obj)) => {456 let our = self.evaluate_this(k, real_this.clone())?;457 if k.add {458 super_obj459 .get_raw(key, real_this)?460 .map_or(Ok(Some(our.clone())), |v| {461 Ok(Some(evaluate_add_op(&v, &our)?))462 })463 } else {464 Ok(Some(our))465 }466 }467 (None, Some(super_obj)) => super_obj.get_raw(key, real_this),468 (None, None) => Ok(None),469 }470 }471 fn evaluate_this(&self, v: &ObjMember, real_this: Self) -> Result<Val> {472 v.invoke.evaluate(self.0.sup.clone(), Some(real_this))473 }474475 fn run_assertions_raw(&self, real_this: &Self) -> Result<()> {476 if self.0.assertions.is_empty() {477 if let Some(super_obj) = &self.0.sup {478 super_obj.run_assertions_raw(real_this)?;479 }480 return Ok(());481 }482 if self.0.assertions_ran.borrow_mut().insert(real_this.clone()) {483 for assertion in self.0.assertions.iter() {484 if let Err(e) = assertion.run(self.0.sup.clone(), Some(real_this.clone())) {485 self.0.assertions_ran.borrow_mut().remove(real_this);486 return Err(e);487 }488 }489 if let Some(super_obj) = &self.0.sup {490 super_obj.run_assertions_raw(real_this)?;491 }492 }493 Ok(())494 }495 pub fn run_assertions(&self) -> Result<()> {496 self.run_assertions_raw(self)497 }498499 pub fn ptr_eq(a: &Self, b: &Self) -> bool {500 Cc::ptr_eq(&a.0, &b.0)501 }502 pub fn downgrade(self) -> WeakObjValue {503 WeakObjValue(self.0.downgrade())504 }505}506507impl PartialEq for ObjValue {508 fn eq(&self, other: &Self) -> bool {509 Cc::ptr_eq(&self.0, &other.0)510 }511}512513impl Eq for ObjValue {}514impl Hash for ObjValue {515 fn hash<H: Hasher>(&self, hasher: &mut H) {516 hasher.write_usize(addr_of!(*self.0) as usize);517 }518}519520#[allow(clippy::module_name_repetitions)]521pub struct ObjValueBuilder {522 sup: Option<ObjValue>,523 map: GcHashMap<IStr, ObjMember>,524 assertions: Vec<TraceBox<dyn ObjectAssertion>>,525 next_field_index: FieldIndex,526}527impl ObjValueBuilder {528 pub fn new() -> Self {529 Self::with_capacity(0)530 }531 pub fn with_capacity(capacity: usize) -> Self {532 Self {533 sup: None,534 map: GcHashMap::with_capacity(capacity),535 assertions: Vec::new(),536 next_field_index: FieldIndex::default(),537 }538 }539 pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self {540 self.assertions.reserve_exact(capacity);541 self542 }543 pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {544 self.sup = Some(super_obj);545 self546 }547548 pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {549 self.assertions.push(tb!(assertion));550 self551 }552 pub fn member(&mut self, name: IStr) -> ObjMemberBuilder<ValueBuilder<'_>> {553 let field_index = self.next_field_index;554 self.next_field_index = self.next_field_index.next();555 ObjMemberBuilder::new(ValueBuilder(self), name, field_index)556 }557558 pub fn build(self) -> ObjValue {559 ObjValue::new(self.sup, Cc::new(self.map), Cc::new(self.assertions))560 }561}562impl Default for ObjValueBuilder {563 fn default() -> Self {564 Self::with_capacity(0)565 }566}567568#[allow(clippy::module_name_repetitions)]569#[must_use = "value not added unless binding() was called"]570pub struct ObjMemberBuilder<Kind> {571 kind: Kind,572 name: IStr,573 add: bool,574 visibility: Visibility,575 original_index: FieldIndex,576 location: Option<ExprLocation>,577}578579#[allow(clippy::missing_const_for_fn)]580impl<Kind> ObjMemberBuilder<Kind> {581 pub(crate) fn new(kind: Kind, name: IStr, original_index: FieldIndex) -> Self {582 Self {583 kind,584 name,585 original_index,586 add: false,587 visibility: Visibility::Normal,588 location: None,589 }590 }591592 pub const fn with_add(mut self, add: bool) -> Self {593 self.add = add;594 self595 }596 pub fn add(self) -> Self {597 self.with_add(true)598 }599 pub fn with_visibility(mut self, visibility: Visibility) -> Self {600 self.visibility = visibility;601 self602 }603 pub fn hide(self) -> Self {604 self.with_visibility(Visibility::Hidden)605 }606 pub fn with_location(mut self, location: ExprLocation) -> Self {607 self.location = Some(location);608 self609 }610 fn build_member(self, binding: MaybeUnbound) -> (Kind, IStr, ObjMember) {611 (612 self.kind,613 self.name,614 ObjMember {615 add: self.add,616 visibility: self.visibility,617 original_index: self.original_index,618 invoke: binding,619 location: self.location,620 },621 )622 }623}624625pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);626impl ObjMemberBuilder<ValueBuilder<'_>> {627 /// Inserts value, replacing if it is already defined628 pub fn value_unchecked(self, value: Val) {629 let (receiver, name, member) =630 self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value)));631 let entry = receiver.0.map.entry(name);632 entry.insert(member);633 }634635 pub fn value(self, value: Val) -> Result<()> {636 self.thunk(Thunk::evaluated(value))637 }638 pub fn thunk(self, value: Thunk<Val>) -> Result<()> {639 self.binding(MaybeUnbound::Bound(value))640 }641 pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {642 self.binding(MaybeUnbound::Unbound(Cc::new(tb!(bindable))))643 }644 pub fn binding(self, binding: MaybeUnbound) -> Result<()> {645 let (receiver, name, member) = self.build_member(binding);646 let location = member.location.clone();647 let old = receiver.0.map.insert(name.clone(), member);648 if old.is_some() {649 State::push(650 CallLocation(location.as_ref()),651 || format!("field <{}> initializtion", name.clone()),652 || throw!(DuplicateFieldName(name.clone())),653 )?;654 }655 Ok(())656 }657}658659pub struct ExtendBuilder<'v>(&'v mut ObjValue);660impl ObjMemberBuilder<ExtendBuilder<'_>> {661 pub fn value(self, value: Val) {662 self.binding(MaybeUnbound::Bound(Thunk::evaluated(value)));663 }664 pub fn bindable(self, bindable: TraceBox<dyn Unbound<Bound = Val>>) {665 self.binding(MaybeUnbound::Unbound(Cc::new(bindable)));666 }667 pub fn binding(self, binding: MaybeUnbound) {668 let (receiver, name, member) = self.build_member(binding);669 let new = receiver.0.clone();670 *receiver.0 = new.extend_with_raw_member(name, member);671 }672}1use std::{2 any::Any,3 cell::RefCell,4 fmt::Debug,5 hash::{Hash, Hasher},6 ptr::addr_of,7};89use jrsonnet_gcmodule::{Cc, Trace, Weak};10use jrsonnet_interner::IStr;11use jrsonnet_parser::{ExprLocation, Visibility};12use rustc_hash::FxHashMap;1314use crate::{15 error::{Error, ErrorKind::*},16 function::CallLocation,17 gc::{GcHashMap, GcHashSet, TraceBox},18 operator::evaluate_add_op,19 tb, throw,20 val::ThunkValue,21 MaybeUnbound, Result, State, Thunk, Unbound, Val,22};2324#[cfg(not(feature = "exp-preserve-order"))]25mod ordering {26 #![allow(27 // This module works as stub for preserve-order feature28 clippy::unused_self,29 )]3031 use jrsonnet_gcmodule::Trace;3233 #[derive(Clone, Copy, Default, Debug, Trace)]34 pub struct FieldIndex(());35 impl FieldIndex {36 pub const fn next(self) -> Self {37 Self(())38 }39 }4041 #[derive(Clone, Copy, Default, Debug, Trace)]42 pub struct SuperDepth(());43 impl SuperDepth {44 pub const fn deeper(self) -> Self {45 Self(())46 }47 }4849 #[derive(Clone, Copy)]50 pub struct FieldSortKey(());51 impl FieldSortKey {52 pub const fn new(_: SuperDepth, _: FieldIndex) -> Self {53 Self(())54 }55 }56}5758#[cfg(feature = "exp-preserve-order")]59mod ordering {60 use std::cmp::{Ordering, Reverse};6162 use jrsonnet_gcmodule::Trace;6364 #[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]65 pub struct FieldIndex(u32);66 impl FieldIndex {67 pub fn next(self) -> Self {68 Self(self.0 + 1)69 }70 }7172 #[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Debug)]73 pub struct SuperDepth(u32);74 impl SuperDepth {75 pub fn deeper(self) -> Self {76 Self(self.0 + 1)77 }78 }7980 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]81 pub struct FieldSortKey(Reverse<SuperDepth>, FieldIndex);82 impl FieldSortKey {83 pub fn new(depth: SuperDepth, index: FieldIndex) -> Self {84 Self(Reverse(depth), index)85 }86 pub fn collide(self, other: Self) -> Self {87 match self.0 .0.cmp(&other.0 .0) {88 Ordering::Greater => self,89 Ordering::Less => other,90 Ordering::Equal => unreachable!("object can't have two fields with the same name"),91 }92 }93 }94}9596use ordering::*;9798// 0 - add99// 12 - visibility100#[derive(Clone, Copy)]101pub struct ObjFieldFlags(u8);102impl ObjFieldFlags {103 fn new(add: bool, visibility: Visibility) -> Self {104 let mut v = 0;105 if add {106 v |= 1;107 }108 v |= match visibility {109 Visibility::Normal => 0b000,110 Visibility::Hidden => 0b010,111 Visibility::Unhide => 0b100,112 };113 Self(v)114 }115 pub fn add(&self) -> bool {116 self.0 & 1 != 0117 }118 pub fn visibility(&self) -> Visibility {119 match (self.0 & 0b110) >> 1 {120 0b00 => Visibility::Normal,121 0b01 => Visibility::Hidden,122 0b10 => Visibility::Unhide,123 _ => unreachable!(),124 }125 }126}127impl Debug for ObjFieldFlags {128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {129 f.debug_struct("ObjFieldFlags")130 .field("add", &self.add())131 .field("visibility", &self.visibility())132 .finish()133 }134}135136#[allow(clippy::module_name_repetitions)]137#[derive(Debug, Trace)]138pub struct ObjMember {139 #[trace(skip)]140 flags: ObjFieldFlags,141 original_index: FieldIndex,142 pub invoke: MaybeUnbound,143 pub location: Option<ExprLocation>,144}145146pub trait ObjectAssertion: Trace {147 fn run(&self, super_obj: Option<ObjValue>, this: Option<ObjValue>) -> Result<()>;148}149150// Field => This151152#[derive(Trace)]153enum CacheValue {154 Cached(Val),155 NotFound,156 Pending,157 Errored(Error),158}159160#[allow(clippy::module_name_repetitions)]161#[derive(Trace)]162#[trace(tracking(force))]163pub struct OopObject {164 sup: Option<ObjValue>,165 // this: Option<ObjValue>,166 assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,167 assertions_ran: RefCell<GcHashSet<ObjValue>>,168 this_entries: Cc<GcHashMap<IStr, ObjMember>>,169 value_cache: RefCell<GcHashMap<(IStr, Option<WeakObjValue>), CacheValue>>,170}171impl Debug for OopObject {172 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {173 f.debug_struct("OopObject")174 .field("sup", &self.sup)175 // .field("assertions", &self.assertions)176 // .field("assertions_ran", &self.assertions_ran)177 .field("this_entries", &self.this_entries)178 // .field("value_cache", &self.value_cache)179 .finish()180 }181}182183type EnumFieldsHandler<'a> = dyn FnMut(SuperDepth, FieldIndex, IStr, Visibility) -> bool + 'a;184185pub trait ObjectLike: Trace + Any + Debug {186 fn extend_from(&self, sup: ObjValue) -> ObjValue;187 /// When using standalone super in object, `this.super_obj.with_this(this)` is executed188 fn with_this(&self, me: ObjValue, this: ObjValue) -> ObjValue {189 ObjValue::new(ThisOverride { inner: me, this })190 }191 fn this(&self) -> Option<ObjValue> {192 None193 }194 fn len(&self) -> usize;195 fn is_empty(&self) -> bool;196 // If callback returns false, iteration stops197 fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool;198199 fn has_field_include_hidden(&self, name: IStr) -> bool;200 fn has_field(&self, name: IStr) -> bool;201202 fn get_for(&self, key: IStr, this: ObjValue) -> Result<Option<Val>>;203 fn get_for_uncached(&self, key: IStr, this: ObjValue) -> Result<Option<Val>>;204 fn field_visibility(&self, field: IStr) -> Option<Visibility>;205206 fn run_assertions_raw(&self, this: ObjValue) -> Result<()>;207}208209#[derive(Clone, Trace)]210pub struct WeakObjValue(#[trace(skip)] pub(crate) Weak<TraceBox<dyn ObjectLike>>);211212impl PartialEq for WeakObjValue {213 fn eq(&self, other: &Self) -> bool {214 Weak::ptr_eq(&self.0, &other.0)215 }216}217218impl Eq for WeakObjValue {}219impl Hash for WeakObjValue {220 fn hash<H: Hasher>(&self, hasher: &mut H) {221 // Safety: usize is POD222 let addr = unsafe { *std::ptr::addr_of!(self.0).cast() };223 hasher.write_usize(addr);224 }225}226227#[allow(clippy::module_name_repetitions)]228#[derive(Clone, Trace, Debug)]229pub struct ObjValue(pub(crate) Cc<TraceBox<dyn ObjectLike>>);230231#[derive(Debug, Trace)]232struct EmptyObject;233impl ObjectLike for EmptyObject {234 fn extend_from(&self, sup: ObjValue) -> ObjValue {235 // obj + {} == obj236 sup237 }238239 fn this(&self) -> Option<ObjValue> {240 None241 }242243 fn len(&self) -> usize {244 0245 }246247 fn is_empty(&self) -> bool {248 true249 }250251 fn enum_fields(&self, _depth: SuperDepth, _handler: &mut EnumFieldsHandler<'_>) -> bool {252 false253 }254255 fn has_field_include_hidden(&self, _name: IStr) -> bool {256 false257 }258259 fn has_field(&self, _name: IStr) -> bool {260 false261 }262263 fn get_for(&self, _key: IStr, _this: ObjValue) -> Result<Option<Val>> {264 Ok(None)265 }266 fn get_for_uncached(&self, _key: IStr, _this: ObjValue) -> Result<Option<Val>> {267 Ok(None)268 }269270 fn run_assertions_raw(&self, _this: ObjValue) -> Result<()> {271 Ok(())272 }273274 fn field_visibility(&self, _field: IStr) -> Option<Visibility> {275 None276 }277}278279#[derive(Trace, Debug)]280struct ThisOverride {281 inner: ObjValue,282 this: ObjValue,283}284impl ObjectLike for ThisOverride {285 fn with_this(&self, _me: ObjValue, this: ObjValue) -> ObjValue {286 ObjValue::new(ThisOverride {287 inner: self.inner.clone(),288 this,289 })290 }291292 fn extend_from(&self, sup: ObjValue) -> ObjValue {293 self.inner.extend_from(sup).with_this(self.this.clone())294 }295296 fn this(&self) -> Option<ObjValue> {297 Some(self.this.clone())298 }299300 fn len(&self) -> usize {301 self.inner.len()302 }303304 fn is_empty(&self) -> bool {305 self.inner.is_empty()306 }307308 fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool {309 self.inner.enum_fields(depth, handler)310 }311312 fn has_field_include_hidden(&self, name: IStr) -> bool {313 self.inner.has_field_include_hidden(name)314 }315316 fn has_field(&self, name: IStr) -> bool {317 self.inner.has_field(name)318 }319320 fn get_for(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {321 self.inner.get_for(key, this)322 }323324 fn get_for_uncached(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {325 self.inner.get_raw(key, this)326 }327328 fn field_visibility(&self, field: IStr) -> Option<Visibility> {329 self.inner.field_visibility(field)330 }331332 fn run_assertions_raw(&self, this: ObjValue) -> Result<()> {333 self.inner.run_assertions_raw(this)334 }335}336337impl ObjValue {338 pub fn new(v: impl ObjectLike) -> Self {339 Self(Cc::new(tb!(v)))340 }341 pub fn new_empty() -> Self {342 Self::new(EmptyObject)343 }344 pub fn builder() -> ObjValueBuilder {345 ObjValueBuilder::new()346 }347 pub fn builder_with_capacity(capacity: usize) -> ObjValueBuilder {348 ObjValueBuilder::with_capacity(capacity)349 }350 pub(crate) fn extend_with_raw_member(self, key: IStr, value: ObjMember) -> Self {351 // let mut new = GcHashMap::with_capacity(1);352 // new.insert(key, value);353 // Self::new(Some(self), Cc::new(new), Cc::new(Vec::new()))354 todo!()355 }356 pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder<'_>> {357 ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())358 }359360 #[must_use]361 pub fn extend_from(&self, sup: Self) -> Self {362 self.0.extend_from(sup)363 }364 #[must_use]365 pub fn with_this(&self, this: Self) -> Self {366 self.0.with_this(self.clone(), this)367 }368 pub fn len(&self) -> usize {369 self.0.len()370 }371 pub fn is_empty(&self) -> bool {372 self.0.is_empty()373 }374 pub fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool {375 self.0.enum_fields(depth, handler)376 }377378 pub fn has_field_include_hidden(&self, name: IStr) -> bool {379 self.0.has_field_include_hidden(name)380 }381 pub fn has_field(&self, name: IStr) -> bool {382 self.0.has_field(name)383 }384 pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {385 if include_hidden {386 self.has_field_include_hidden(name)387 } else {388 self.has_field(name)389 }390 }391392 pub fn get(&self, key: IStr) -> Result<Option<Val>> {393 self.run_assertions()?;394 self.get_for(key, self.0.this().unwrap_or(self.clone()))395 }396397 pub fn get_for(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {398 self.0.get_for(key, this)399 }400401 fn get_raw(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {402 self.0.get_for_uncached(key, this)403 }404405 fn field_visibility(&self, field: IStr) -> Option<Visibility> {406 self.0.field_visibility(field)407 }408409 pub fn run_assertions(&self) -> Result<()> {410 // FIXME: Should it use `self.0.this()` in case of standalone super?411 self.run_assertions_raw(self.clone())412 }413 fn run_assertions_raw(&self, this: ObjValue) -> Result<()> {414 self.0.run_assertions_raw(this)415 }416417 pub fn iter(418 &self,419 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,420 ) -> impl Iterator<Item = (IStr, Result<Val>)> + '_ {421 let fields = self.fields(422 #[cfg(feature = "exp-preserve-order")]423 preserve_order,424 );425 fields.into_iter().map(|field| {426 (427 field.clone(),428 self.get(field)429 .map(|opt| opt.expect("iterating over keys, field exists")),430 )431 })432 }433 pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {434 #[derive(Trace)]435 struct ThunkGet {436 obj: ObjValue,437 key: IStr,438 }439 impl ThunkValue for ThunkGet {440 type Output = Val;441442 fn get(self: Box<Self>) -> Result<Self::Output> {443 Ok(self.obj.get(self.key)?.expect("field exists"))444 }445 }446447 if !self.has_field_ex(key.clone(), true) {448 return None;449 }450 Some(Thunk::new(ThunkGet {451 obj: self.clone(),452 key,453 }))454 }455 pub fn ptr_eq(a: &Self, b: &Self) -> bool {456 Cc::ptr_eq(&a.0, &b.0)457 }458 pub fn downgrade(self) -> WeakObjValue {459 WeakObjValue(self.0.downgrade())460 }461 fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {462 let mut out = FxHashMap::default();463 self.enum_fields(464 SuperDepth::default(),465 &mut |depth, index, name, visibility| {466 let new_sort_key = FieldSortKey::new(depth, index);467 let entry = out.entry(name.clone());468 let (visible, _) = entry.or_insert((true, new_sort_key));469 match visibility {470 Visibility::Normal => {}471 Visibility::Hidden => {472 *visible = false;473 }474 Visibility::Unhide => {475 *visible = true;476 }477 };478 false479 },480 );481 out482 }483 pub fn fields_ex(484 &self,485 include_hidden: bool,486 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,487 ) -> Vec<IStr> {488 #[cfg(feature = "exp-preserve-order")]489 if preserve_order {490 let (mut fields, mut keys): (Vec<_>, Vec<_>) = self491 .fields_visibility()492 .into_iter()493 .filter(|(_, (visible, _))| include_hidden || *visible)494 .enumerate()495 .map(|(idx, (k, (_, sk)))| (k, (sk, idx)))496 .unzip();497 keys.sort_unstable_by_key(|v| v.0);498 // Reorder in-place by resulting indexes499 for i in 0..fields.len() {500 let x = fields[i].clone();501 let mut j = i;502 loop {503 let k = keys[j].1;504 keys[j].1 = j;505 if k == i {506 break;507 }508 fields[j] = fields[k].clone();509 j = k;510 }511 fields[j] = x;512 }513 return fields;514 }515516 let mut fields: Vec<_> = self517 .fields_visibility()518 .into_iter()519 .filter(|(_, (visible, _))| include_hidden || *visible)520 .map(|(k, _)| k)521 .collect();522 fields.sort_unstable();523 fields524 }525 pub fn fields(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Vec<IStr> {526 self.fields_ex(527 false,528 #[cfg(feature = "exp-preserve-order")]529 preserve_order,530 )531 }532}533534impl OopObject {535 pub fn new(536 sup: Option<ObjValue>,537 this_entries: Cc<GcHashMap<IStr, ObjMember>>,538 assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,539 ) -> Self {540 Self {541 sup,542 // this: None,543 assertions,544 assertions_ran: RefCell::new(GcHashSet::new()),545 this_entries,546 value_cache: RefCell::new(GcHashMap::new()),547 }548 }549550 fn evaluate_this(&self, v: &ObjMember, real_this: ObjValue) -> Result<Val> {551 v.invoke.evaluate(self.sup.clone(), Some(real_this))552 }553554 // FIXME: Duplication between ObjValue and OopObject555 fn fields_visibility(&self) -> FxHashMap<IStr, (bool, FieldSortKey)> {556 let mut out = FxHashMap::default();557 self.enum_fields(558 SuperDepth::default(),559 &mut |depth, index, name, visibility| {560 let new_sort_key = FieldSortKey::new(depth, index);561 let entry = out.entry(name.clone());562 let (visible, _) = entry.or_insert((true, new_sort_key));563 match visibility {564 Visibility::Normal => {}565 Visibility::Hidden => {566 *visible = false;567 }568 Visibility::Unhide => {569 *visible = true;570 }571 };572 false573 },574 );575 out576 }577}578579impl ObjectLike for OopObject {580 fn extend_from(&self, sup: ObjValue) -> ObjValue {581 ObjValue::new(match &self.sup {582 None => Self::new(583 Some(sup),584 self.this_entries.clone(),585 self.assertions.clone(),586 ),587 Some(v) => Self::new(588 Some(v.extend_from(sup)),589 self.this_entries.clone(),590 self.assertions.clone(),591 ),592 })593 }594595 fn len(&self) -> usize {596 self.fields_visibility()597 .into_iter()598 .filter(|(_, (visible, _))| *visible)599 .count()600 }601602 fn is_empty(&self) -> bool {603 if !self.this_entries.is_empty() {604 return false;605 }606 self.sup.as_ref().map_or(true, ObjValue::is_empty)607 }608609 /// Run callback for every field found in object610 ///611 /// Returns true if ended prematurely612 fn enum_fields(&self, depth: SuperDepth, handler: &mut EnumFieldsHandler<'_>) -> bool {613 if let Some(s) = &self.sup {614 if s.enum_fields(depth.deeper(), handler) {615 return true;616 }617 }618 for (name, member) in self.this_entries.iter() {619 if handler(620 depth,621 member.original_index,622 name.clone(),623 member.flags.visibility(),624 ) {625 return true;626 }627 }628 false629 }630631 fn has_field_include_hidden(&self, name: IStr) -> bool {632 if self.this_entries.contains_key(&name) {633 true634 } else if let Some(super_obj) = &self.sup {635 super_obj.has_field_include_hidden(name)636 } else {637 false638 }639 }640 fn has_field(&self, name: IStr) -> bool {641 self.field_visibility(name)642 .map_or(false, |v| v.is_visible())643 }644645 fn get_for(&self, key: IStr, this: ObjValue) -> Result<Option<Val>> {646 let cache_key = (key.clone(), Some(this.clone().downgrade()));647 if let Some(v) = self.value_cache.borrow().get(&cache_key) {648 return Ok(match v {649 CacheValue::Cached(v) => Some(v.clone()),650 CacheValue::NotFound => None,651 CacheValue::Pending => throw!(InfiniteRecursionDetected),652 CacheValue::Errored(e) => return Err(e.clone()),653 });654 }655 self.value_cache656 .borrow_mut()657 .insert(cache_key.clone(), CacheValue::Pending);658 let value = self.get_for_uncached(key, this).map_err(|e| {659 self.value_cache660 .borrow_mut()661 .insert(cache_key.clone(), CacheValue::Errored(e.clone()));662 e663 })?;664 self.value_cache.borrow_mut().insert(665 cache_key,666 value667 .as_ref()668 .map_or(CacheValue::NotFound, |v| CacheValue::Cached(v.clone())),669 );670 Ok(value)671 }672 fn get_for_uncached(&self, key: IStr, real_this: ObjValue) -> Result<Option<Val>> {673 match (self.this_entries.get(&key), &self.sup) {674 (Some(k), None) => Ok(Some(self.evaluate_this(k, real_this)?)),675 (Some(k), Some(super_obj)) => {676 let our = self.evaluate_this(k, real_this.clone())?;677 if k.flags.add() {678 super_obj679 .get_raw(key, real_this)?680 .map_or(Ok(Some(our.clone())), |v| {681 Ok(Some(evaluate_add_op(&v, &our)?))682 })683 } else {684 Ok(Some(our))685 }686 }687 (None, Some(super_obj)) => super_obj.get_raw(key, real_this),688 (None, None) => Ok(None),689 }690 }691 fn field_visibility(&self, name: IStr) -> Option<Visibility> {692 if let Some(m) = self.this_entries.get(&name) {693 Some(match &m.flags.visibility() {694 Visibility::Normal => self695 .sup696 .as_ref()697 .and_then(|super_obj| super_obj.field_visibility(name))698 .unwrap_or(Visibility::Normal),699 v => *v,700 })701 } else if let Some(super_obj) = &self.sup {702 super_obj.field_visibility(name)703 } else {704 None705 }706 }707708 fn run_assertions_raw(&self, real_this: ObjValue) -> Result<()> {709 if self.assertions.is_empty() {710 if let Some(super_obj) = &self.sup {711 super_obj.run_assertions_raw(real_this)?;712 }713 return Ok(());714 }715 if self.assertions_ran.borrow_mut().insert(real_this.clone()) {716 for assertion in self.assertions.iter() {717 if let Err(e) = assertion.run(self.sup.clone(), Some(real_this.clone())) {718 self.assertions_ran.borrow_mut().remove(&real_this);719 return Err(e);720 }721 }722 if let Some(super_obj) = &self.sup {723 super_obj.run_assertions_raw(real_this)?;724 }725 }726 Ok(())727 }728}729730impl PartialEq for ObjValue {731 fn eq(&self, other: &Self) -> bool {732 Cc::ptr_eq(&self.0, &other.0)733 }734}735736impl Eq for ObjValue {}737impl Hash for ObjValue {738 fn hash<H: Hasher>(&self, hasher: &mut H) {739 hasher.write_usize(addr_of!(*self.0) as usize);740 }741}742743#[allow(clippy::module_name_repetitions)]744pub struct ObjValueBuilder {745 sup: Option<ObjValue>,746 map: GcHashMap<IStr, ObjMember>,747 assertions: Vec<TraceBox<dyn ObjectAssertion>>,748 next_field_index: FieldIndex,749}750impl ObjValueBuilder {751 pub fn new() -> Self {752 Self::with_capacity(0)753 }754 pub fn with_capacity(capacity: usize) -> Self {755 Self {756 sup: None,757 map: GcHashMap::with_capacity(capacity),758 assertions: Vec::new(),759 next_field_index: FieldIndex::default(),760 }761 }762 pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self {763 self.assertions.reserve_exact(capacity);764 self765 }766 pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {767 self.sup = Some(super_obj);768 self769 }770771 pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {772 self.assertions.push(tb!(assertion));773 self774 }775 pub fn member(&mut self, name: IStr) -> ObjMemberBuilder<ValueBuilder<'_>> {776 let field_index = self.next_field_index;777 self.next_field_index = self.next_field_index.next();778 ObjMemberBuilder::new(ValueBuilder(self), name, field_index)779 }780781 pub fn build(self) -> ObjValue {782 if self.sup.is_none() && self.map.is_empty() && self.assertions.is_empty() {783 return ObjValue::new_empty();784 }785 ObjValue::new(OopObject::new(786 self.sup,787 Cc::new(self.map),788 Cc::new(self.assertions),789 ))790 }791}792impl Default for ObjValueBuilder {793 fn default() -> Self {794 Self::with_capacity(0)795 }796}797798#[allow(clippy::module_name_repetitions)]799#[must_use = "value not added unless binding() was called"]800pub struct ObjMemberBuilder<Kind> {801 kind: Kind,802 name: IStr,803 add: bool,804 visibility: Visibility,805 original_index: FieldIndex,806 location: Option<ExprLocation>,807}808809#[allow(clippy::missing_const_for_fn)]810impl<Kind> ObjMemberBuilder<Kind> {811 pub(crate) fn new(kind: Kind, name: IStr, original_index: FieldIndex) -> Self {812 Self {813 kind,814 name,815 original_index,816 add: false,817 visibility: Visibility::Normal,818 location: None,819 }820 }821822 pub const fn with_add(mut self, add: bool) -> Self {823 self.add = add;824 self825 }826 pub fn add(self) -> Self {827 self.with_add(true)828 }829 pub fn with_visibility(mut self, visibility: Visibility) -> Self {830 self.visibility = visibility;831 self832 }833 pub fn hide(self) -> Self {834 self.with_visibility(Visibility::Hidden)835 }836 pub fn with_location(mut self, location: ExprLocation) -> Self {837 self.location = Some(location);838 self839 }840 fn build_member(self, binding: MaybeUnbound) -> (Kind, IStr, ObjMember) {841 (842 self.kind,843 self.name,844 ObjMember {845 flags: ObjFieldFlags::new(self.add, self.visibility),846 original_index: self.original_index,847 invoke: binding,848 location: self.location,849 },850 )851 }852}853854pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);855impl ObjMemberBuilder<ValueBuilder<'_>> {856 /// Inserts value, replacing if it is already defined857 pub fn value_unchecked(self, value: Val) {858 let (receiver, name, member) =859 self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value)));860 let entry = receiver.0.map.entry(name);861 entry.insert(member);862 }863864 pub fn value(self, value: Val) -> Result<()> {865 self.thunk(Thunk::evaluated(value))866 }867 pub fn thunk(self, value: Thunk<Val>) -> Result<()> {868 self.binding(MaybeUnbound::Bound(value))869 }870 pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {871 self.binding(MaybeUnbound::Unbound(Cc::new(tb!(bindable))))872 }873 pub fn binding(self, binding: MaybeUnbound) -> Result<()> {874 let (receiver, name, member) = self.build_member(binding);875 let location = member.location.clone();876 let old = receiver.0.map.insert(name.clone(), member);877 if old.is_some() {878 State::push(879 CallLocation(location.as_ref()),880 || format!("field <{}> initializtion", name.clone()),881 || throw!(DuplicateFieldName(name.clone())),882 )?;883 }884 Ok(())885 }886}887888pub struct ExtendBuilder<'v>(&'v mut ObjValue);889impl ObjMemberBuilder<ExtendBuilder<'_>> {890 pub fn value(self, value: Val) {891 self.binding(MaybeUnbound::Bound(Thunk::evaluated(value)));892 }893 pub fn bindable(self, bindable: TraceBox<dyn Unbound<Bound = Val>>) {894 self.binding(MaybeUnbound::Unbound(Cc::new(bindable)));895 }896 pub fn binding(self, binding: MaybeUnbound) {897 let (receiver, name, member) = self.build_member(binding);898 let new = receiver.0.clone();899 *receiver.0 = new.extend_with_raw_member(name, member);900 }901}crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -1,6 +1,7 @@
use std::{
cell::RefCell,
fmt::{self, Debug, Display},
+ hash::Hasher,
mem::replace,
rc::Rc,
};
@@ -8,6 +9,7 @@
use jrsonnet_gcmodule::{Cc, Trace};
use jrsonnet_interner::IStr;
use jrsonnet_types::ValType;
+use rustc_hash::FxHasher;
pub use crate::arr::{ArrValue, ArrayLike};
use crate::{