difftreelog
refactor move state to global
in: master
9 files changed
bindings/jsonnet/src/lib.rsdiffbeforeafterboth--- a/bindings/jsonnet/src/lib.rs
+++ b/bindings/jsonnet/src/lib.rs
@@ -247,7 +247,7 @@
match vm
.state
.import(filename)
- .and_then(|val| apply_tla(vm.state.clone(), &vm.tla_args, val))
+ .and_then(|val| apply_tla(&vm.tla_args, val))
.and_then(|val| val.manifest(&vm.manifest_format))
{
Ok(v) => {
@@ -282,7 +282,7 @@
match vm
.state
.evaluate_snippet(filename.to_str().unwrap(), snippet.to_str().unwrap())
- .and_then(|val| apply_tla(vm.state.clone(), &vm.tla_args, val))
+ .and_then(|val| apply_tla(&vm.tla_args, val))
.and_then(|val| val.manifest(&vm.manifest_format))
{
Ok(v) => {
@@ -340,7 +340,7 @@
match vm
.state
.import(filename)
- .and_then(|val| apply_tla(vm.state.clone(), &vm.tla_args, val))
+ .and_then(|val| apply_tla(&vm.tla_args, val))
.and_then(|val| val_to_multi(val, &vm.manifest_format))
{
Ok(v) => {
@@ -369,7 +369,7 @@
match vm
.state
.evaluate_snippet(filename.to_str().unwrap(), snippet.to_str().unwrap())
- .and_then(|val| apply_tla(vm.state.clone(), &vm.tla_args, val))
+ .and_then(|val| apply_tla(&vm.tla_args, val))
.and_then(|val| val_to_multi(val, &vm.manifest_format))
{
Ok(v) => {
@@ -422,7 +422,7 @@
match vm
.state
.import(filename)
- .and_then(|val| apply_tla(vm.state.clone(), &vm.tla_args, val))
+ .and_then(|val| apply_tla(&vm.tla_args, val))
.and_then(|val| val_to_stream(val, &vm.manifest_format))
{
Ok(v) => {
@@ -451,7 +451,7 @@
match vm
.state
.evaluate_snippet(filename.to_str().unwrap(), snippet.to_str().unwrap())
- .and_then(|val| apply_tla(vm.state.clone(), &vm.tla_args, val))
+ .and_then(|val| apply_tla(&vm.tla_args, val))
.and_then(|val| val_to_stream(val, &vm.manifest_format))
{
Ok(v) => {
cmds/jrsonnet/src/main.rsdiffbeforeafterboth--- a/cmds/jrsonnet/src/main.rs
+++ b/cmds/jrsonnet/src/main.rs
@@ -173,6 +173,7 @@
let mut s = State::builder();
s.import_resolver(import_resolver).context_initializer(std);
let s = s.build();
+ let _s = s.enter();
let input = opts.input.input.ok_or(Error::MissingInputArgument)?;
let val = if opts.input.exec {
@@ -192,7 +193,7 @@
unused_mut,
clippy::redundant_clone,
)]
- let mut val = apply_tla(s.clone(), &tla, val)?;
+ let mut val = apply_tla(&tla, val)?;
#[cfg(feature = "exp-apply")]
for apply in opts.input.exp_apply {
crates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/ctx.rs
+++ b/crates/jrsonnet-evaluator/src/ctx.rs
@@ -6,12 +6,11 @@
use crate::{
error::ErrorKind::*, gc::WithCapacityExt as _, map::LayeredHashMap, ObjValue, Pending, Result,
- State, Thunk, Val,
+ Thunk, Val,
};
#[derive(Trace)]
struct ContextInternals {
- state: Option<State>,
dollar: Option<ObjValue>,
sup: Option<ObjValue>,
this: Option<ObjValue>,
@@ -33,13 +32,6 @@
Pending::new()
}
- pub fn state(&self) -> &State {
- self.0
- .state
- .as_ref()
- .expect("used state from dummy context")
- }
-
pub fn dollar(&self) -> Option<&ObjValue> {
self.0.dollar.as_ref()
}
@@ -112,7 +104,6 @@
ctx.bindings.clone().extend(new_bindings)
};
Self(Cc::new(ContextInternals {
- state: ctx.state.clone(),
dollar,
sup,
this,
@@ -128,34 +119,22 @@
}
pub struct ContextBuilder {
- state: Option<State>,
bindings: FxHashMap<IStr, Thunk<Val>>,
extend: Option<Context>,
}
impl ContextBuilder {
- /// # Panics
- /// Panics aren't directly caused by this function, but if state from resulting context is used
- pub fn dangerous_empty_state() -> Self {
- Self {
- state: None,
- bindings: FxHashMap::new(),
- extend: None,
- }
+ pub fn new() -> Self {
+ Self::with_capacity(0)
}
- pub fn new(state: State) -> Self {
- Self::with_capacity(state, 0)
- }
- pub fn with_capacity(state: State, capacity: usize) -> Self {
+ pub fn with_capacity(capacity: usize) -> Self {
Self {
- state: Some(state),
bindings: FxHashMap::with_capacity(capacity),
extend: None,
}
}
pub fn extend(parent: Context) -> Self {
Self {
- state: parent.0.state.clone(),
bindings: FxHashMap::new(),
extend: Some(parent),
}
@@ -173,7 +152,6 @@
parent.extend(self.bindings, None, None, None)
} else {
Context(Cc::new(ContextInternals {
- state: self.state,
bindings: LayeredHashMap::new(self.bindings),
dollar: None,
sup: None,
crates/jrsonnet-evaluator/src/function/arglike.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function/arglike.rs
+++ b/crates/jrsonnet-evaluator/src/function/arglike.rs
@@ -4,7 +4,7 @@
use jrsonnet_interner::IStr;
use jrsonnet_parser::{ArgsDesc, LocExpr, SourceFifo, SourcePath};
-use crate::{evaluate, typed::Typed, Context, Result, Thunk, Val};
+use crate::{evaluate, typed::Typed, with_state, Context, Result, Thunk, Val};
/// Marker for arguments, which can be evaluated with context set to None
pub trait OptionalContext {}
@@ -47,28 +47,59 @@
ImportStr(String),
InlineCode(String),
}
-impl ArgLike for TlaArg {
- fn evaluate_arg(&self, ctx: Context, _tailstrict: bool) -> Result<Thunk<Val>> {
+impl TlaArg {
+ pub fn evaluate_tailstrict(&self) -> Result<Val> {
match self {
+ Self::String(s) => Ok(Val::string(s.clone())),
+ Self::Val(val) => Ok(val.clone()),
+ Self::Lazy(lazy) => Ok(lazy.evaluate()?),
+ Self::Import(p) => with_state(|s| {
+ let resolved = s.resolve_from_default(&p.as_str())?;
+ s.import_resolved(resolved)
+ }),
+ Self::ImportStr(p) => with_state(|s| {
+ let resolved = s.resolve_from_default(&p.as_str())?;
+ s.import_resolved_str(resolved).map(Val::string)
+ }),
+ Self::InlineCode(p) => with_state(|s| {
+ let resolved =
+ SourcePath::new(SourceFifo("<inline code>".to_owned(), p.as_bytes().into()));
+ s.import_resolved(resolved)
+ }),
+ }
+ }
+ pub fn evaluate(&self) -> Result<Thunk<Val>> {
+ match self {
Self::String(s) => Ok(Thunk::evaluated(Val::string(s.clone()))),
Self::Val(val) => Ok(Thunk::evaluated(val.clone())),
Self::Lazy(lazy) => Ok(lazy.clone()),
- Self::Import(p) => {
- let resolved = ctx.state().resolve_from_default(&p.as_str())?;
- Ok(Thunk!(move || ctx.state().import_resolved(resolved)))
- }
- Self::ImportStr(p) => {
- let resolved = ctx.state().resolve_from_default(&p.as_str())?;
- Ok(Thunk!(move || ctx
- .state()
+ Self::Import(p) => with_state(|s| {
+ let resolved = s.resolve_from_default(&p.as_str())?;
+ Ok(Thunk!(move || s.import_resolved(resolved)))
+ }),
+ Self::ImportStr(p) => with_state(|s| {
+ let resolved = s.resolve_from_default(&p.as_str())?;
+ Ok(Thunk!(move || s
.import_resolved_str(resolved)
.map(Val::string)))
- }
- Self::InlineCode(p) => {
+ }),
+ Self::InlineCode(p) => with_state(|s| {
let resolved =
SourcePath::new(SourceFifo("<inline code>".to_owned(), p.as_bytes().into()));
- Ok(Thunk!(move || ctx.state().import_resolved(resolved)))
- }
+ Ok(Thunk!(move || s.import_resolved(resolved)))
+ }),
+ }
+ }
+}
+
+// TODO: Is this implementation really required, as there is no Context to use?
+// Maybe something a bit stricter is possible to add, especially with precompiled calls?
+impl ArgLike for TlaArg {
+ fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {
+ if tailstrict {
+ self.evaluate_tailstrict().map(Thunk::evaluated)
+ } else {
+ self.evaluate()
}
}
}
crates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function/mod.rs
+++ b/crates/jrsonnet-evaluator/src/function/mod.rs
@@ -207,7 +207,7 @@
tailstrict: bool,
) -> Result<Val> {
self.evaluate(
- ContextBuilder::dangerous_empty_state().build(),
+ ContextBuilder::new().build(),
CallLocation::native(),
args,
tailstrict,
crates/jrsonnet-evaluator/src/tla.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/tla.rs
+++ b/crates/jrsonnet-evaluator/src/tla.rs
@@ -1,21 +1,24 @@
use jrsonnet_interner::IStr;
use jrsonnet_parser::Source;
+use rustc_hash::FxHashMap;
use crate::{
- function::{ArgsLike, CallLocation},
- in_description_frame, Result, State, Val,
+ function::{CallLocation, TlaArg},
+ in_description_frame, with_state, Result, Val,
};
-pub fn apply_tla<A: ArgsLike>(s: State, args: &A, val: Val) -> Result<Val> {
+pub fn apply_tla(args: &FxHashMap<IStr, TlaArg>, val: Val) -> Result<Val> {
Ok(if let Val::Func(func) = val {
in_description_frame(
|| "during TLA call".to_owned(),
|| {
func.evaluate(
- s.create_default_context(Source::new_virtual(
- "<top-level-arg>".into(),
- IStr::empty(),
- )),
+ with_state(|s| {
+ s.create_default_context(Source::new_virtual(
+ "<top-level-arg>".into(),
+ IStr::empty(),
+ ))
+ }),
CallLocation::native(),
args,
false,
crates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/lib.rs
+++ b/crates/jrsonnet-stdlib/src/lib.rs
@@ -335,11 +335,6 @@
pub path_resolver: PathResolver,
}
-fn extvar_source(name: &str, code: impl Into<IStr>) -> Source {
- let source_name = format!("<extvar:{name}>");
- Source::new_virtual(source_name.into(), code.into())
-}
-
#[derive(Trace, Clone)]
pub struct ContextInitializer {
/// std without applied thisFile overlay
crates/jrsonnet-stdlib/src/misc.rsdiffbeforeafterboth1use std::{cell::RefCell, collections::BTreeSet};23use jrsonnet_evaluator::{4 bail,5 error::{ErrorKind::*, Result},6 function::{builtin, ArgLike, CallLocation, FuncVal},7 manifest::JsonFormat,8 typed::{Either2, Either4},9 val::{equals, ArrValue},10 Context, Either, IStr, ObjValue, ObjValueBuilder, ResultExt, Thunk, Val,11};12use jrsonnet_gcmodule::Cc;1314use crate::{extvar_source, Settings};1516#[builtin]17pub fn builtin_length(x: Either![IStr, ArrValue, ObjValue, FuncVal]) -> usize {18 use Either4::*;19 match x {20 A(x) => x.chars().count(),21 B(x) => x.len(),22 C(x) => x.len(),23 D(f) => f.params_len(),24 }25}2627#[builtin]28pub fn builtin_get(29 o: ObjValue,30 f: IStr,31 default: Option<Thunk<Val>>,32 #[default(true)] inc_hidden: bool,33) -> Result<Val> {34 let do_default = move || {35 let Some(default) = default else {36 return Ok(Val::Null);37 };38 default.evaluate()39 };40 // Happy path for invisible fields41 if !inc_hidden && !o.has_field_ex(f.clone(), false) {42 return do_default();43 }44 let Some(v) = o.get(f)? else {45 return do_default();46 };47 Ok(v)48}4950#[builtin(fields(51 settings: Cc<RefCell<Settings>>,52))]53pub fn builtin_ext_var(this: &builtin_ext_var, ctx: Context, x: IStr) -> Result<Val> {54 let ctx = ctx.state().create_default_context(extvar_source(&x, ""));55 this.settings56 .borrow()57 .ext_vars58 .get(&x)59 .cloned()60 .ok_or_else(|| UndefinedExternalVariable(x))?61 .evaluate_arg(ctx, true)?62 .evaluate()63}6465#[builtin(fields(66 settings: Cc<RefCell<Settings>>,67))]68pub fn builtin_native(this: &builtin_native, x: IStr) -> Val {69 this.settings70 .borrow()71 .ext_natives72 .get(&x)73 .cloned()74 .map_or(Val::Null, Val::Func)75}7677#[builtin(fields(78 settings: Cc<RefCell<Settings>>,79))]80pub fn builtin_trace(81 this: &builtin_trace,82 loc: CallLocation,83 str: Val,84 rest: Option<Thunk<Val>>,85) -> Result<Val> {86 this.settings.borrow().trace_printer.print_trace(87 loc,88 match &str {89 Val::Str(s) => s.clone().into_flat(),90 Val::Func(f) => format!("{f:?}").into(),91 v => v.manifest(JsonFormat::debug())?.into(),92 },93 );94 rest.map_or_else(|| Ok(str), |rest| rest.evaluate())95}9697#[allow(clippy::comparison_chain)]98#[builtin]99pub fn builtin_starts_with(a: Either![IStr, ArrValue], b: Either![IStr, ArrValue]) -> Result<bool> {100 Ok(match (a, b) {101 (Either2::A(a), Either2::A(b)) => a.starts_with(b.as_str()),102 (Either2::B(a), Either2::B(b)) => {103 if b.len() > a.len() {104 return Ok(false);105 } else if b.len() == a.len() {106 return equals(&Val::Arr(a), &Val::Arr(b));107 }108 for (a, b) in a.iter().take(b.len()).zip(b.iter()) {109 let a = a?;110 let b = b?;111 if !equals(&a, &b)? {112 return Ok(false);113 }114 }115 true116 }117 _ => bail!("both arguments should be of the same type"),118 })119}120121#[allow(clippy::comparison_chain)]122#[builtin]123pub fn builtin_ends_with(a: Either![IStr, ArrValue], b: Either![IStr, ArrValue]) -> Result<bool> {124 Ok(match (a, b) {125 (Either2::A(a), Either2::A(b)) => a.ends_with(b.as_str()),126 (Either2::B(a), Either2::B(b)) => {127 if b.len() > a.len() {128 return Ok(false);129 } else if b.len() == a.len() {130 return equals(&Val::Arr(a), &Val::Arr(b));131 }132 let a_len = a.len();133 for (a, b) in a.iter().skip(a_len - b.len()).zip(b.iter()) {134 let a = a?;135 let b = b?;136 if !equals(&a, &b)? {137 return Ok(false);138 }139 }140 true141 }142 _ => bail!("both arguments should be of the same type"),143 })144}145146#[builtin]147pub fn builtin_assert_equal(a: Val, b: Val) -> Result<bool> {148 if equals(&a, &b)? {149 return Ok(true);150 }151 // TODO: Use debug output format152 let format = JsonFormat::std_to_json(153 " ".to_owned(),154 "\n",155 ": ",156 #[cfg(feature = "exp-preserve-order")]157 true,158 );159 let a = if let Some(a) = a.as_str() {160 format!("<A>\n{a}\n</A>")161 } else {162 a.manifest(&format).description("<a> manifestification")?163 };164 let b = if let Some(b) = b.as_str() {165 format!("<B>\n{b}\n</B>")166 } else {167 b.manifest(&format).description("<b> manifestification")?168 };169 bail!("assertion failed: A != B\nA: {a}\nB: {b}")170}171172#[builtin]173pub fn builtin_merge_patch(target: Val, patch: Val) -> Result<Val> {174 let Some(patch) = patch.as_obj() else {175 return Ok(patch);176 };177 let target = target.as_obj().unwrap_or_else(|| ObjValue::new_empty());178 let target_fields = target179 .fields(180 // FIXME: Makes no sense to preserve order for BTreeSet, it would be better to use IndexSet here?181 // But IndexSet won't allow fast ordered union...182 // // Makes sense to preserve source ordering where possible.183 // // May affect evaluation order, but it is not specified by jsonnet spec.184 // #[cfg(feature = "exp-preserve-order")]185 // true,186 #[cfg(feature = "exp-preserve-order")]187 false,188 )189 .into_iter()190 .collect::<BTreeSet<IStr>>();191 let patch_fields = patch192 .fields(193 // No need to look at the patch field order, I think?194 // New fields (that will be appended at the end) will be alphabeticaly-ordered,195 // but it is fine for jsonpatch, I don't think people write jsonpatch in jsonnet,196 // when they can use mixins.197 #[cfg(feature = "exp-preserve-order")]198 false,199 )200 .into_iter()201 .collect::<BTreeSet<IStr>>();202203 let mut out = ObjValueBuilder::new();204 for field in target_fields.union(&patch_fields) {205 let Some(field_patch) = patch.get(field.clone())? else {206 out.field(field.clone()).value(target.get(field.clone())?.expect("we're iterating over fields union, if field is missing in patch - it exists in target"));207 continue;208 };209 if matches!(field_patch, Val::Null) {210 continue;211 }212 let field_target = target.get(field.clone())?.unwrap_or(Val::Null);213 out.field(field.clone())214 .value(builtin_merge_patch(field_target, field_patch)?);215 }216 Ok(out.build().into())217}1use std::{cell::RefCell, collections::BTreeSet};23use jrsonnet_evaluator::{4 bail,5 error::{ErrorKind::*, Result},6 function::{builtin, CallLocation, FuncVal},7 manifest::JsonFormat,8 typed::{Either2, Either4},9 val::{equals, ArrValue},10 Either, IStr, ObjValue, ObjValueBuilder, ResultExt, Thunk, Val,11};12use jrsonnet_gcmodule::Cc;1314use crate::Settings;1516#[builtin]17pub fn builtin_length(x: Either![IStr, ArrValue, ObjValue, FuncVal]) -> usize {18 use Either4::*;19 match x {20 A(x) => x.chars().count(),21 B(x) => x.len(),22 C(x) => x.len(),23 D(f) => f.params_len(),24 }25}2627#[builtin]28pub fn builtin_get(29 o: ObjValue,30 f: IStr,31 default: Option<Thunk<Val>>,32 #[default(true)] inc_hidden: bool,33) -> Result<Val> {34 let do_default = move || {35 let Some(default) = default else {36 return Ok(Val::Null);37 };38 default.evaluate()39 };40 // Happy path for invisible fields41 if !inc_hidden && !o.has_field_ex(f.clone(), false) {42 return do_default();43 }44 let Some(v) = o.get(f)? else {45 return do_default();46 };47 Ok(v)48}4950#[builtin(fields(51 settings: Cc<RefCell<Settings>>,52))]53pub fn builtin_ext_var(this: &builtin_ext_var, x: IStr) -> Result<Val> {54 this.settings55 .borrow()56 .ext_vars57 .get(&x)58 .cloned()59 .ok_or_else(|| UndefinedExternalVariable(x))?60 .evaluate_tailstrict()61}6263#[builtin(fields(64 settings: Cc<RefCell<Settings>>,65))]66pub fn builtin_native(this: &builtin_native, x: IStr) -> Val {67 this.settings68 .borrow()69 .ext_natives70 .get(&x)71 .cloned()72 .map_or(Val::Null, Val::Func)73}7475#[builtin(fields(76 settings: Cc<RefCell<Settings>>,77))]78pub fn builtin_trace(79 this: &builtin_trace,80 loc: CallLocation,81 str: Val,82 rest: Option<Thunk<Val>>,83) -> Result<Val> {84 this.settings.borrow().trace_printer.print_trace(85 loc,86 match &str {87 Val::Str(s) => s.clone().into_flat(),88 Val::Func(f) => format!("{f:?}").into(),89 v => v.manifest(JsonFormat::debug())?.into(),90 },91 );92 rest.map_or_else(|| Ok(str), |rest| rest.evaluate())93}9495#[allow(clippy::comparison_chain)]96#[builtin]97pub fn builtin_starts_with(a: Either![IStr, ArrValue], b: Either![IStr, ArrValue]) -> Result<bool> {98 Ok(match (a, b) {99 (Either2::A(a), Either2::A(b)) => a.starts_with(b.as_str()),100 (Either2::B(a), Either2::B(b)) => {101 if b.len() > a.len() {102 return Ok(false);103 } else if b.len() == a.len() {104 return equals(&Val::Arr(a), &Val::Arr(b));105 }106 for (a, b) in a.iter().take(b.len()).zip(b.iter()) {107 let a = a?;108 let b = b?;109 if !equals(&a, &b)? {110 return Ok(false);111 }112 }113 true114 }115 _ => bail!("both arguments should be of the same type"),116 })117}118119#[allow(clippy::comparison_chain)]120#[builtin]121pub fn builtin_ends_with(a: Either![IStr, ArrValue], b: Either![IStr, ArrValue]) -> Result<bool> {122 Ok(match (a, b) {123 (Either2::A(a), Either2::A(b)) => a.ends_with(b.as_str()),124 (Either2::B(a), Either2::B(b)) => {125 if b.len() > a.len() {126 return Ok(false);127 } else if b.len() == a.len() {128 return equals(&Val::Arr(a), &Val::Arr(b));129 }130 let a_len = a.len();131 for (a, b) in a.iter().skip(a_len - b.len()).zip(b.iter()) {132 let a = a?;133 let b = b?;134 if !equals(&a, &b)? {135 return Ok(false);136 }137 }138 true139 }140 _ => bail!("both arguments should be of the same type"),141 })142}143144#[builtin]145pub fn builtin_assert_equal(a: Val, b: Val) -> Result<bool> {146 if equals(&a, &b)? {147 return Ok(true);148 }149 // TODO: Use debug output format150 let format = JsonFormat::std_to_json(151 " ".to_owned(),152 "\n",153 ": ",154 #[cfg(feature = "exp-preserve-order")]155 true,156 );157 let a = if let Some(a) = a.as_str() {158 format!("<A>\n{a}\n</A>")159 } else {160 a.manifest(&format).description("<a> manifestification")?161 };162 let b = if let Some(b) = b.as_str() {163 format!("<B>\n{b}\n</B>")164 } else {165 b.manifest(&format).description("<b> manifestification")?166 };167 bail!("assertion failed: A != B\nA: {a}\nB: {b}")168}169170#[builtin]171pub fn builtin_merge_patch(target: Val, patch: Val) -> Result<Val> {172 let Some(patch) = patch.as_obj() else {173 return Ok(patch);174 };175 let target = target.as_obj().unwrap_or_else(|| ObjValue::new_empty());176 let target_fields = target177 .fields(178 // FIXME: Makes no sense to preserve order for BTreeSet, it would be better to use IndexSet here?179 // But IndexSet won't allow fast ordered union...180 // // Makes sense to preserve source ordering where possible.181 // // May affect evaluation order, but it is not specified by jsonnet spec.182 // #[cfg(feature = "exp-preserve-order")]183 // true,184 #[cfg(feature = "exp-preserve-order")]185 false,186 )187 .into_iter()188 .collect::<BTreeSet<IStr>>();189 let patch_fields = patch190 .fields(191 // No need to look at the patch field order, I think?192 // New fields (that will be appended at the end) will be alphabeticaly-ordered,193 // but it is fine for jsonpatch, I don't think people write jsonpatch in jsonnet,194 // when they can use mixins.195 #[cfg(feature = "exp-preserve-order")]196 false,197 )198 .into_iter()199 .collect::<BTreeSet<IStr>>();200201 let mut out = ObjValueBuilder::new();202 for field in target_fields.union(&patch_fields) {203 let Some(field_patch) = patch.get(field.clone())? else {204 out.field(field.clone()).value(target.get(field.clone())?.expect("we're iterating over fields union, if field is missing in patch - it exists in target"));205 continue;206 };207 if matches!(field_patch, Val::Null) {208 continue;209 }210 let field_target = target.get(field.clone())?.unwrap_or(Val::Null);211 out.field(field.clone())212 .value(builtin_merge_patch(field_target, field_patch)?);213 }214 Ok(out.build().into())215}tests/tests/builtin.rsdiffbeforeafterboth--- a/tests/tests/builtin.rs
+++ b/tests/tests/builtin.rs
@@ -19,7 +19,7 @@
fn basic_function() -> Result<()> {
let a: a = a {};
let v = u32::from_untyped(a.call(
- ContextBuilder::dangerous_empty_state().build(),
+ ContextBuilder::new().build(),
CallLocation::native(),
&(),
)?)?;