difftreelog
feat immutable obj core list
in: master
7 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -163,6 +163,15 @@
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
[[package]]
+name = "bitmaps"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2"
+dependencies = [
+ "typenum",
+]
+
+[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -682,6 +691,20 @@
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]]
+name = "im-rc"
+version = "15.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af1955a75fa080c677d3972822ec4bad316169ab1cfc6c257a942c2265dbe5fe"
+dependencies = [
+ "bitmaps",
+ "rand_core 0.6.4",
+ "rand_xoshiro",
+ "sized-chunks",
+ "typenum",
+ "version_check",
+]
+
+[[package]]
name = "indexmap"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -793,6 +816,7 @@
"anyhow",
"educe",
"hi-doc",
+ "im-rc",
"jrsonnet-gcmodule",
"jrsonnet-interner",
"jrsonnet-ir",
@@ -837,18 +861,19 @@
[[package]]
name = "jrsonnet-gcmodule"
-version = "0.4.2"
+version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f95b976a79e4000bb9e07ff0709dca0ea27bcf1952d4c17d91fb7364d6145683"
+checksum = "8a6a63a6e55ba82764e483d7f8a181f25db95a8f25da8ae6520e95a5fe39c6a6"
dependencies = [
+ "im-rc",
"jrsonnet-gcmodule-derive",
]
[[package]]
name = "jrsonnet-gcmodule-derive"
-version = "0.4.2"
+version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51d928626220a310ff0cec815e80cf7fe104697184352ca21c40534e0b0d72d9"
+checksum = "095fe3c4c0acf32de80205a8a479ef63c216b9efb0024dec9eb7fe1c5ef1f1a1"
dependencies = [
"proc-macro2",
"quote",
@@ -1319,7 +1344,7 @@
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_chacha",
- "rand_core",
+ "rand_core 0.9.5",
]
[[package]]
@@ -1329,11 +1354,17 @@
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
- "rand_core",
+ "rand_core 0.9.5",
]
[[package]]
name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+
+[[package]]
+name = "rand_core"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
@@ -1342,6 +1373,15 @@
]
[[package]]
+name = "rand_xoshiro"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
+dependencies = [
+ "rand_core 0.6.4",
+]
+
+[[package]]
name = "random_color"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1587,6 +1627,16 @@
checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
[[package]]
+name = "sized-chunks"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e"
+dependencies = [
+ "bitmaps",
+ "typenum",
+]
+
+[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
Cargo.tomldiffbeforeafterboth--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,7 +22,7 @@
jrsonnet-cli = { path = "./crates/jrsonnet-cli", version = "0.5.0-pre98" }
jrsonnet-types = { path = "./crates/jrsonnet-types", version = "0.5.0-pre98" }
jrsonnet-formatter = { path = "./crates/jrsonnet-formatter", version = "0.5.0-pre98" }
-jrsonnet-gcmodule = { version = "0.4.2" }
+jrsonnet-gcmodule = { version = "0.4.3", features = ["im-rc"] }
# Diagnostics.
# hi-doc is my library, which handles text formatting very well, but isn't polished enough yet
# Previous implementation was based on annotate-snippets, which I don't like for many reasons.
crates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-evaluator/Cargo.toml
+++ b/crates/jrsonnet-evaluator/Cargo.toml
@@ -76,6 +76,7 @@
"Hash",
"PartialEq",
] }
+im-rc = "15.1.0"
[build-dependencies]
rustversion = "1.0.22"
crates/jrsonnet-evaluator/src/evaluate/destructure.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/destructure.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/destructure.rs
@@ -144,9 +144,7 @@
Thunk!(move || {
let full = full.evaluate()?;
let mut builder = ObjValueBuilder::new();
- builder
- .reserve_cores(1)
- .extend_with_core(full.as_standalone());
+ builder.extend_with_core(full.as_standalone());
builder.with_fields_omitted(captured_fields);
Ok(Val::Obj(builder.build()))
}),
crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -410,7 +410,7 @@
let locals = obj.locals.clone();
evaluate_comp(ctx, &obj.compspecs, 0, &mut |ctx, reserve| {
let uctx = evaluate_object_locals(ctx.clone(), locals.clone());
- builder.reserve_cores(reserve);
+ builder.reserve_fields(reserve);
evaluate_field_member(&mut builder, ctx, uctx, &obj.field)
})?;
crates/jrsonnet-evaluator/src/obj/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/obj/mod.rs
+++ b/crates/jrsonnet-evaluator/src/obj/mod.rs
@@ -11,6 +11,7 @@
};
use educe::Educe;
+use im_rc::{Vector, vector};
use jrsonnet_gcmodule::{Acyclic, Cc, Trace, Weak, cc_dyn};
use jrsonnet_interner::IStr;
use jrsonnet_ir::Span;
@@ -235,10 +236,11 @@
CcObjectCore, ObjectCore,
pub fn new() {...}
);
+
#[derive(Trace, Educe)]
#[educe(Debug)]
struct ObjValueInner {
- cores: Vec<CcObjectCore>,
+ cores: Vector<CcObjectCore>,
assertions_ran: Cell<bool>,
has_assertions: bool,
value_cache: RefCell<FxHashMap<(IStr, CoreIdx), CacheValue>>,
@@ -266,7 +268,7 @@
thread_local! {
static EMPTY_OBJ: ObjValue = ObjValue(Cc::new(ObjValueInner {
- cores: vec![],
+ cores: vector![],
assertions_ran: Cell::new(true),
has_assertions: false,
value_cache: RefCell::default(),
@@ -428,7 +430,7 @@
bail!(NoSuperFound)
}
let mut out = ObjValue::builder();
- out.reserve_cores(1).extend_with_core(StandaloneSuperCore {
+ out.extend_with_core(StandaloneSuperCore {
sup: self.sup,
this: self.this.clone(),
});
@@ -484,9 +486,7 @@
#[must_use]
pub fn extend_from(&self, sup: Self) -> Self {
- let mut cores = Vec::with_capacity(sup.0.cores.len() + self.0.cores.len());
- cores.extend(sup.0.cores.iter().cloned());
- cores.extend(self.0.cores.iter().cloned());
+ let cores = sup.0.cores.clone() + self.0.cores.clone();
let has_assertions = sup.0.has_assertions || self.0.has_assertions;
ObjValue(Cc::new(ObjValueInner {
cores,
@@ -521,13 +521,27 @@
},
)
}
+
+ fn iter_cores(&self, idx: CoreIdx) -> impl Iterator<Item = &CcObjectCore> {
+ self.0.cores.iter().take(idx.idx).rev()
+ }
+ fn iter_cores_enumerate(&self, idx: CoreIdx) -> impl Iterator<Item = (CoreIdx, &CcObjectCore)> {
+ self.0
+ .cores
+ .iter()
+ .take(idx.idx)
+ .enumerate()
+ .rev()
+ .map(|(idx, o)| (CoreIdx { idx }, o))
+ }
+
fn enum_fields_idx(
&self,
super_depth: &mut SuperDepth,
handler: &mut EnumFieldsHandler<'_>,
idx: CoreIdx,
) -> bool {
- for core in self.0.cores[..idx.idx].iter().rev() {
+ for core in self.iter_cores(idx) {
if !core.0.enum_fields_core(super_depth, handler) {
return false;
}
@@ -546,7 +560,7 @@
}
fn has_field_include_hidden_idx(&self, name: IStr, core: CoreIdx) -> bool {
let mut skip = Saturating(0usize);
- for ele in self.0.cores[..core.idx].iter().rev() {
+ for ele in self.iter_cores(core) {
match ele.0.has_field_include_hidden_core(name.clone()) {
HasFieldIncludeHidden::Exists => {
if skip.0 == 0 {
@@ -616,9 +630,9 @@
let mut first_add = None;
let mut add_stack: Vec<Val> = Vec::new();
let mut skip = Saturating(0);
- for (sup, core) in self.0.cores[..core.idx].iter().enumerate().rev() {
+ for (sup, core) in self.iter_cores_enumerate(core) {
let sup_this = SupThis {
- sup: CoreIdx { idx: sup },
+ sup,
this: self.clone(),
};
match core.0.get_for_core(key.clone(), sup_this, skip.0 != 0)? {
@@ -686,7 +700,7 @@
fn field_visibility_idx(&self, field: IStr, core: CoreIdx) -> Option<Visibility> {
let mut exists = false;
let mut skip = Saturating(0usize);
- for ele in self.0.cores[..core.idx].iter().rev() {
+ for ele in self.iter_cores(core) {
let vis = ele.0.field_visibility_core(field.clone());
match vis {
FieldVisibility::Found(vis @ (Visibility::Unhide | Visibility::Hidden)) => {
crates/jrsonnet-evaluator/src/obj/oop.rsdiffbeforeafterboth1use std::{2 cell::{Cell, RefCell},3 fmt, mem,4 ops::ControlFlow,5};67use jrsonnet_gcmodule::{Cc, Trace};8use jrsonnet_ir::IStr;9use rustc_hash::{FxHashMap, FxHashSet};1011use super::{12 CcObjectAssertion, CcObjectCore, EnumFields, EnumFieldsHandler, FieldVisibility, GetFor,13 HasFieldIncludeHidden, ObjMember, ObjMemberBuilder, ObjValue, ObjValueInner, ObjectAssertion,14 ObjectCore, OmitFieldsCore, SupThis,15 ordering::{FieldIndex, SuperDepth},16};17use crate::{18 CcUnbound, MaybeUnbound, Result, Thunk, Unbound, Val, bail,19 error::ErrorKind::*,20 function::{CallLocation, FuncVal},21 gc::WithCapacityExt as _,22 in_frame,23};2425#[allow(clippy::module_name_repetitions)]26#[derive(Trace, Default)]27#[trace(tracking(force))]28pub struct OopObject {29 assertion: Option<CcObjectAssertion>,30 this_entries: FxHashMap<IStr, ObjMember>,31}32impl fmt::Debug for OopObject {33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {34 f.debug_struct("OopObject")35 .field("this_entries", &self.this_entries)36 .finish_non_exhaustive()37 }38}39impl OopObject {40 fn is_empty(&self) -> bool {41 self.assertion.is_none() && self.this_entries.is_empty()42 }43}44impl OopObject {45 pub fn new(46 this_entries: FxHashMap<IStr, ObjMember>,47 assertion: Option<CcObjectAssertion>,48 ) -> Self {49 Self {50 assertion,51 this_entries,52 }53 }54}5556impl ObjectCore for OopObject {57 fn enum_fields_core(58 &self,59 super_depth: &mut SuperDepth,60 handler: &mut EnumFieldsHandler<'_>,61 ) -> bool {62 for (name, member) in &self.this_entries {63 if matches!(64 handler(65 *super_depth,66 member.original_index,67 name.clone(),68 EnumFields::Normal(member.flags.visibility()),69 ),70 ControlFlow::Break(())71 ) {72 return false;73 }74 }75 true76 }7778 fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden {79 if self.this_entries.contains_key(&name) {80 HasFieldIncludeHidden::Exists81 } else {82 HasFieldIncludeHidden::NotFound83 }84 }8586 fn get_for_core(&self, key: IStr, sup_this: SupThis, omit_only: bool) -> Result<GetFor> {87 if omit_only {88 return Ok(GetFor::NotFound);89 }90 match self.this_entries.get(&key) {91 Some(k) => {92 let v = k.invoke.evaluate(sup_this)?;93 Ok(if k.flags.add() {94 GetFor::SuperPlus(v)95 } else {96 GetFor::Final(v)97 })98 }99 None => Ok(GetFor::NotFound),100 }101 }102 fn field_visibility_core(&self, name: IStr) -> FieldVisibility {103 self.this_entries104 .get(&name)105 .map_or(FieldVisibility::NotFound, |f| {106 FieldVisibility::Found(f.flags.visibility())107 })108 }109110 fn run_assertions_core(&self, sup_this: SupThis) -> Result<()> {111 if let Some(assertion) = &self.assertion {112 assertion.0.run(sup_this)?;113 }114 Ok(())115 }116}117118#[allow(clippy::module_name_repetitions)]119pub struct ObjValueBuilder {120 sup: Vec<CcObjectCore>,121 has_assertions: bool,122123 new: OopObject,124 next_field_index: FieldIndex,125}126impl ObjValueBuilder {127 pub fn new() -> Self {128 Self::with_capacity(0)129 }130 pub fn with_capacity(capacity: usize) -> Self {131 Self {132 sup: vec![],133 has_assertions: false,134 new: OopObject::new(FxHashMap::with_capacity(capacity), None),135 next_field_index: FieldIndex::default(),136 }137 }138 pub fn reserve_fields(&mut self, capacity: usize) {139 self.new.this_entries.reserve(capacity);140 }141 pub fn reserve_cores(&mut self, capacity: usize) -> &mut Self {142 self.sup.reserve_exact(capacity);143 self144 }145 pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {146 self.has_assertions |= super_obj.0.has_assertions;147 self.sup.clone_from(&super_obj.0.cores);148 self149 }150151 pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {152 assert!(153 self.new.assertion.is_none(),154 "one OopObject can only have one assertion"155 );156 self.has_assertions = true;157 self.new.assertion = Some(CcObjectAssertion::new(assertion));158 self159 }160 pub fn field(&mut self, name: impl Into<IStr>) -> ObjMemberBuilder<ValueBuilder<'_>> {161 let field_index = self.next_field_index;162 self.next_field_index = self.next_field_index.next();163 ObjMemberBuilder::new(ValueBuilder(self), name.into(), field_index)164 }165 /// Preset for common method definiton pattern:166 /// Create a hidden field with the function value.167 ///168 /// `.field(name).hide().value(Val::function(value))`169 pub fn method(&mut self, name: impl Into<IStr>, value: impl Into<FuncVal>) -> &mut Self {170 self.field(name).hide().value(Val::Func(value.into()));171 self172 }173 pub fn try_method(174 &mut self,175 name: impl Into<IStr>,176 value: impl Into<FuncVal>,177 ) -> Result<&mut Self> {178 self.field(name).hide().try_value(Val::Func(value.into()))?;179 Ok(self)180 }181182 pub fn extend_with_core(&mut self, core: impl ObjectCore) {183 self.commit();184 self.sup.push(CcObjectCore::new(core));185 }186187 fn commit(&mut self) {188 if !self.new.is_empty() {189 self.sup.push(CcObjectCore::new(mem::take(&mut self.new)));190 }191 self.next_field_index = FieldIndex::default();192 }193194 pub fn with_fields_omitted(&mut self, omit: FxHashSet<IStr>) {195 self.commit();196 self.sup.push(CcObjectCore::new(OmitFieldsCore {197 omit,198 prev_layers: self.sup.len(),199 }));200 }201202 pub fn build(mut self) -> ObjValue {203 self.commit();204 if self.sup.is_empty() {205 return ObjValue::empty();206 }207 let has_assertions = self.has_assertions;208 ObjValue(Cc::new(ObjValueInner {209 cores: self.sup,210 assertions_ran: Cell::new(!has_assertions),211 has_assertions,212 value_cache: RefCell::default(),213 }))214 }215}216impl Default for ObjValueBuilder {217 fn default() -> Self {218 Self::with_capacity(0)219 }220}221222pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);223impl ObjMemberBuilder<ValueBuilder<'_>> {224 /// Inserts value, replacing if it is already defined225 pub fn value(self, value: impl Into<Val>) {226 let (receiver, name, member) =227 self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value.into())));228 let entry = receiver.0.new.this_entries.entry(name);229 entry.insert_entry(member);230 }231 /// Inserts thunk, replacing if it is already defined232 pub fn thunk(self, value: impl Into<Thunk<Val>>) {233 let (receiver, name, member) = self.build_member(MaybeUnbound::Bound(value.into()));234 let entry = receiver.0.new.this_entries.entry(name);235 entry.insert_entry(member);236 }237238 /// Tries to insert value, returns an error if it was already defined239 pub fn try_value(self, value: impl Into<Val>) -> Result<()> {240 self.try_thunk(Thunk::evaluated(value.into()))241 }242 pub fn try_thunk(self, value: impl Into<Thunk<Val>>) -> Result<()> {243 self.binding(MaybeUnbound::Bound(value.into()))244 }245 pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {246 self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)))247 }248 pub fn binding(self, binding: MaybeUnbound) -> Result<()> {249 let (receiver, name, member) = self.build_member(binding);250 let location = member.location.clone();251 let old = receiver.0.new.this_entries.insert(name.clone(), member);252 if old.is_some() {253 in_frame(254 CallLocation(location.as_ref()),255 || format!("field <{}> initializtion", name.clone()),256 || bail!(DuplicateFieldName(name.clone())),257 )?;258 }259 Ok(())260 }261}1use std::{2 cell::{Cell, RefCell},3 fmt, mem,4 ops::ControlFlow,5};67use im_rc::Vector;8use jrsonnet_gcmodule::{Cc, Trace};9use jrsonnet_ir::IStr;10use rustc_hash::{FxHashMap, FxHashSet};1112use super::{13 CcObjectAssertion, CcObjectCore, EnumFields, EnumFieldsHandler, FieldVisibility, GetFor,14 HasFieldIncludeHidden, ObjMember, ObjMemberBuilder, ObjValue, ObjValueInner, ObjectAssertion,15 ObjectCore, OmitFieldsCore, SupThis,16 ordering::{FieldIndex, SuperDepth},17};18use crate::{19 CcUnbound, MaybeUnbound, Result, Thunk, Unbound, Val, bail,20 error::ErrorKind::*,21 function::{CallLocation, FuncVal},22 gc::WithCapacityExt as _,23 in_frame,24};2526#[allow(clippy::module_name_repetitions)]27#[derive(Trace, Default)]28#[trace(tracking(force))]29pub struct OopObject {30 assertion: Option<CcObjectAssertion>,31 this_entries: FxHashMap<IStr, ObjMember>,32}33impl fmt::Debug for OopObject {34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {35 f.debug_struct("OopObject")36 .field("this_entries", &self.this_entries)37 .finish_non_exhaustive()38 }39}40impl OopObject {41 fn is_empty(&self) -> bool {42 self.assertion.is_none() && self.this_entries.is_empty()43 }44}45impl OopObject {46 pub fn new(47 this_entries: FxHashMap<IStr, ObjMember>,48 assertion: Option<CcObjectAssertion>,49 ) -> Self {50 Self {51 assertion,52 this_entries,53 }54 }55}5657impl ObjectCore for OopObject {58 fn enum_fields_core(59 &self,60 super_depth: &mut SuperDepth,61 handler: &mut EnumFieldsHandler<'_>,62 ) -> bool {63 for (name, member) in &self.this_entries {64 if matches!(65 handler(66 *super_depth,67 member.original_index,68 name.clone(),69 EnumFields::Normal(member.flags.visibility()),70 ),71 ControlFlow::Break(())72 ) {73 return false;74 }75 }76 true77 }7879 fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden {80 if self.this_entries.contains_key(&name) {81 HasFieldIncludeHidden::Exists82 } else {83 HasFieldIncludeHidden::NotFound84 }85 }8687 fn get_for_core(&self, key: IStr, sup_this: SupThis, omit_only: bool) -> Result<GetFor> {88 if omit_only {89 return Ok(GetFor::NotFound);90 }91 match self.this_entries.get(&key) {92 Some(k) => {93 let v = k.invoke.evaluate(sup_this)?;94 Ok(if k.flags.add() {95 GetFor::SuperPlus(v)96 } else {97 GetFor::Final(v)98 })99 }100 None => Ok(GetFor::NotFound),101 }102 }103 fn field_visibility_core(&self, name: IStr) -> FieldVisibility {104 self.this_entries105 .get(&name)106 .map_or(FieldVisibility::NotFound, |f| {107 FieldVisibility::Found(f.flags.visibility())108 })109 }110111 fn run_assertions_core(&self, sup_this: SupThis) -> Result<()> {112 if let Some(assertion) = &self.assertion {113 assertion.0.run(sup_this)?;114 }115 Ok(())116 }117}118119#[allow(clippy::module_name_repetitions)]120pub struct ObjValueBuilder {121 sup: Vector<CcObjectCore>,122 has_assertions: bool,123124 new: OopObject,125 next_field_index: FieldIndex,126}127impl ObjValueBuilder {128 pub fn new() -> Self {129 Self::with_capacity(0)130 }131 pub fn with_capacity(capacity: usize) -> Self {132 Self {133 sup: Vector::new(),134 has_assertions: false,135 new: OopObject::new(FxHashMap::with_capacity(capacity), None),136 next_field_index: FieldIndex::default(),137 }138 }139 pub fn reserve_fields(&mut self, capacity: usize) {140 self.new.this_entries.reserve(capacity);141 }142 pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {143 self.has_assertions |= super_obj.0.has_assertions;144 self.sup = super_obj.0.cores.clone();145 self146 }147148 pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {149 assert!(150 self.new.assertion.is_none(),151 "one OopObject can only have one assertion"152 );153 self.has_assertions = true;154 self.new.assertion = Some(CcObjectAssertion::new(assertion));155 self156 }157 pub fn field(&mut self, name: impl Into<IStr>) -> ObjMemberBuilder<ValueBuilder<'_>> {158 let field_index = self.next_field_index;159 self.next_field_index = self.next_field_index.next();160 ObjMemberBuilder::new(ValueBuilder(self), name.into(), field_index)161 }162 /// Preset for common method definiton pattern:163 /// Create a hidden field with the function value.164 ///165 /// `.field(name).hide().value(Val::function(value))`166 pub fn method(&mut self, name: impl Into<IStr>, value: impl Into<FuncVal>) -> &mut Self {167 self.field(name).hide().value(Val::Func(value.into()));168 self169 }170 pub fn try_method(171 &mut self,172 name: impl Into<IStr>,173 value: impl Into<FuncVal>,174 ) -> Result<&mut Self> {175 self.field(name).hide().try_value(Val::Func(value.into()))?;176 Ok(self)177 }178179 pub fn extend_with_core(&mut self, core: impl ObjectCore) {180 self.commit();181 self.sup.push_back(CcObjectCore::new(core));182 }183184 fn commit(&mut self) {185 if !self.new.is_empty() {186 self.sup187 .push_back(CcObjectCore::new(mem::take(&mut self.new)));188 }189 self.next_field_index = FieldIndex::default();190 }191192 pub fn with_fields_omitted(&mut self, omit: FxHashSet<IStr>) {193 self.commit();194 self.sup.push_back(CcObjectCore::new(OmitFieldsCore {195 omit,196 prev_layers: self.sup.len(),197 }));198 }199200 pub fn build(mut self) -> ObjValue {201 self.commit();202 if self.sup.is_empty() {203 return ObjValue::empty();204 }205 let has_assertions = self.has_assertions;206 ObjValue(Cc::new(ObjValueInner {207 cores: self.sup,208 assertions_ran: Cell::new(!has_assertions),209 has_assertions,210 value_cache: RefCell::default(),211 }))212 }213}214impl Default for ObjValueBuilder {215 fn default() -> Self {216 Self::with_capacity(0)217 }218}219220pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);221impl ObjMemberBuilder<ValueBuilder<'_>> {222 /// Inserts value, replacing if it is already defined223 pub fn value(self, value: impl Into<Val>) {224 let (receiver, name, member) =225 self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value.into())));226 let entry = receiver.0.new.this_entries.entry(name);227 entry.insert_entry(member);228 }229 /// Inserts thunk, replacing if it is already defined230 pub fn thunk(self, value: impl Into<Thunk<Val>>) {231 let (receiver, name, member) = self.build_member(MaybeUnbound::Bound(value.into()));232 let entry = receiver.0.new.this_entries.entry(name);233 entry.insert_entry(member);234 }235236 /// Tries to insert value, returns an error if it was already defined237 pub fn try_value(self, value: impl Into<Val>) -> Result<()> {238 self.try_thunk(Thunk::evaluated(value.into()))239 }240 pub fn try_thunk(self, value: impl Into<Thunk<Val>>) -> Result<()> {241 self.binding(MaybeUnbound::Bound(value.into()))242 }243 pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {244 self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)))245 }246 pub fn binding(self, binding: MaybeUnbound) -> Result<()> {247 let (receiver, name, member) = self.build_member(binding);248 let location = member.location.clone();249 let old = receiver.0.new.this_entries.insert(name.clone(), member);250 if old.is_some() {251 in_frame(252 CallLocation(location.as_ref()),253 || format!("field <{}> initializtion", name.clone()),254 || bail!(DuplicateFieldName(name.clone())),255 )?;256 }257 Ok(())258 }259}