From 30d381bece768bbd3f9a92f38062eacdc4c9048f Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Wed, 26 Oct 2022 19:35:22 +0000 Subject: [PATCH] refactor: move state to context --- --- a/Cargo.lock +++ b/Cargo.lock @@ -240,7 +240,6 @@ "pathdiff", "rustc-hash", "serde", - "serde_json", "static_assertions", "strsim", "thiserror", @@ -541,7 +540,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" dependencies = [ - "indexmap", "itoa", "ryu", "serde", --- a/bindings/jsonnet/src/import.rs +++ b/bindings/jsonnet/src/import.rs @@ -15,6 +15,7 @@ error::{Error::*, Result}, throw, FileImportResolver, ImportResolver, State, }; +use jrsonnet_gcmodule::Trace; use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath}; pub type JsonnetImportCallback = unsafe extern "C" fn( @@ -26,8 +27,11 @@ ) -> *mut c_char; /// Resolves imports using callback +#[derive(Trace)] pub struct CallbackImportResolver { + #[trace(skip)] cb: JsonnetImportCallback, + #[trace(skip)] ctx: *mut c_void, out: RefCell>>, } --- a/bindings/jsonnet/src/lib.rs +++ b/bindings/jsonnet/src/lib.rs @@ -17,7 +17,7 @@ }; use jrsonnet_evaluator::{ - trace::PathResolver, FileImportResolver, IStr, ManifestFormat, State, Val, + tb, trace::PathResolver, FileImportResolver, IStr, ManifestFormat, State, Val, }; /// WASM stub @@ -68,8 +68,8 @@ #[allow(clippy::box_default)] pub extern "C" fn jsonnet_make() -> *mut State { let state = State::default(); - state.settings_mut().import_resolver = Box::new(FileImportResolver::default()); - state.settings_mut().context_initializer = Box::new(jrsonnet_stdlib::ContextInitializer::new( + state.settings_mut().import_resolver = tb!(FileImportResolver::default()); + state.settings_mut().context_initializer = tb!(jrsonnet_stdlib::ContextInitializer::new( state.clone(), PathResolver::new_cwd_fallback(), )); --- a/bindings/jsonnet/src/native.rs +++ b/bindings/jsonnet/src/native.rs @@ -36,7 +36,7 @@ cb: JsonnetNativeCallback, } impl NativeCallbackHandler for JsonnetNativeCallbackHandler { - fn call(&self, s: State, args: &[Val]) -> Result { + fn call(&self, args: &[Val]) -> Result { let mut n_args = Vec::new(); for a in args { n_args.push(Some(Box::new(a.clone()))); @@ -54,7 +54,7 @@ if success == 1 { Ok(v) } else { - let e = IStr::from_untyped(v, s).expect("error msg should be a string"); + let e = IStr::from_untyped(v).expect("error msg should be a string"); Err(Error::RuntimeError(e).into()) } } --- a/crates/jrsonnet-cli/src/stdlib.rs +++ b/crates/jrsonnet-cli/src/stdlib.rs @@ -1,7 +1,7 @@ use std::{fs::read_to_string, str::FromStr}; use clap::Parser; -use jrsonnet_evaluator::{error::Result, trace::PathResolver, State}; +use jrsonnet_evaluator::{error::Result, tb, trace::PathResolver, State}; use crate::ConfigureState; @@ -125,7 +125,7 @@ for ext in self.ext_code_file.iter() { ctx.add_ext_code(&ext.name as &str, &ext.value as &str)?; } - s.settings_mut().context_initializer = Box::new(ctx); + s.settings_mut().context_initializer = tb!(ctx); Ok(()) } } --- a/crates/jrsonnet-cli/src/trace.rs +++ b/crates/jrsonnet-cli/src/trace.rs @@ -49,13 +49,11 @@ .as_ref() .unwrap_or(&TraceFormatName::Compact) { - TraceFormatName::Compact => s.set_trace_format(Box::new(CompactFormat { + TraceFormatName::Compact => s.set_trace_format(CompactFormat { resolver, padding: 4, - })), - TraceFormatName::Explaining => { - s.set_trace_format(Box::new(ExplainingFormat { resolver })) - } + }), + TraceFormatName::Explaining => s.set_trace_format(ExplainingFormat { resolver }), } s.set_max_trace(self.max_trace); Ok(()) --- a/crates/jrsonnet-evaluator/src/ctx.rs +++ b/crates/jrsonnet-evaluator/src/ctx.rs @@ -4,11 +4,13 @@ use jrsonnet_interner::IStr; use crate::{ - error::Error::*, gc::GcHashMap, map::LayeredHashMap, ObjValue, Pending, Result, Thunk, Val, + error::Error::*, gc::GcHashMap, map::LayeredHashMap, ObjValue, Pending, Result, State, Thunk, + Val, }; #[derive(Trace)] struct ContextInternals { + state: Option, dollar: Option, sup: Option, this: Option, @@ -30,6 +32,13 @@ Pending::new() } + pub fn state(&self) -> &State { + self.0 + .state + .as_ref() + .expect("used state from dummy context") + } + pub fn dollar(&self) -> &Option { &self.0.dollar } @@ -42,15 +51,6 @@ &self.0.sup } - pub fn new() -> Self { - Self(Cc::new(ContextInternals { - dollar: None, - this: None, - sup: None, - bindings: LayeredHashMap::default(), - })) - } - #[cfg(not(feature = "friendly-errors"))] pub fn binding(&self, name: IStr) -> Result> { Ok(self @@ -122,6 +122,7 @@ ctx.bindings.clone().extend(new_bindings) }; Self(Cc::new(ContextInternals { + state: ctx.state.clone(), dollar, sup, this, @@ -130,11 +131,11 @@ } } -impl Default for Context { - fn default() -> Self { - Self::new() - } -} +// impl Default for Context { +// fn default() -> Self { +// Self::new() +// } +// } impl PartialEq for Context { fn eq(&self, other: &Self) -> bool { @@ -143,22 +144,34 @@ } pub struct ContextBuilder { + state: Option, bindings: GcHashMap>, extend: Option, } impl ContextBuilder { - pub fn new() -> Self { - Self::with_capacity(0) + /// # 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: GcHashMap::new(), + extend: None, + } + } + pub fn new(state: State) -> Self { + Self::with_capacity(state, 0) } - pub fn with_capacity(capacity: usize) -> Self { + pub fn with_capacity(state: State, capacity: usize) -> Self { Self { + state: Some(state), bindings: GcHashMap::with_capacity(capacity), extend: None, } } pub fn extend(parent: Context) -> Self { Self { + state: parent.0.state.clone(), bindings: GcHashMap::new(), extend: Some(parent), } @@ -172,20 +185,16 @@ } pub fn build(self) -> Context { if let Some(parent) = self.extend { + // TODO: replace self.extend with Result, and remove `state` field parent.extend(self.bindings, None, None, None) } else { Context(Cc::new(ContextInternals { + state: self.state, bindings: LayeredHashMap::new(self.bindings), dollar: None, sup: None, this: None, })) } - } -} - -impl Default for ContextBuilder { - fn default() -> Self { - Self::new() } } --- a/crates/jrsonnet-evaluator/src/error.rs +++ b/crates/jrsonnet-evaluator/src/error.rs @@ -1,4 +1,7 @@ -use std::{fmt::Debug, path::PathBuf}; +use std::{ + fmt::{Debug, Display}, + path::PathBuf, +}; use jrsonnet_gcmodule::Trace; use jrsonnet_interner::IStr; @@ -252,15 +255,26 @@ &mut (self.0).1 } } -impl Debug for LocError { +impl Display for LocError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "{}", self.0 .0)?; for el in &self.0 .1 .0 { - writeln!(f, "\t{el:?}")?; + write!(f, "\t{}", el.desc)?; + if let Some(loc) = &el.location { + write!(f, "at {}", loc.0 .0 .0)?; + // loc.0 + loc.0.map_source_locations(&[loc.1, loc.2]); + } + writeln!(f)?; } Ok(()) } } +impl Debug for LocError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("LocError").field(&self.0).finish() + } +} pub trait ErrorSource { fn to_location(self) -> Option; --- a/crates/jrsonnet-evaluator/src/evaluate/destructure.rs +++ b/crates/jrsonnet-evaluator/src/evaluate/destructure.rs @@ -8,7 +8,7 @@ gc::GcHashMap, tb, throw, val::ThunkValue, - Context, Pending, State, Thunk, Val, + Context, Pending, Thunk, Val, }; #[allow(clippy::too_many_lines)] @@ -261,12 +261,11 @@ } impl ThunkValue for EvaluateThunkValue { type Output = Val; - fn get(self: Box, s: State) -> Result { - if let Some(name) = self.name { - evaluate_named(s, self.fctx.unwrap(), &self.expr, name) - } else { - evaluate(s, self.fctx.unwrap(), &self.expr) - } + fn get(self: Box) -> Result { + self.name.map_or_else( + || evaluate(self.fctx.unwrap(), &self.expr), + |name| evaluate_named(self.fctx.unwrap(), &self.expr, name), + ) } } let data = Thunk::new(tb!(EvaluateThunkValue { @@ -291,7 +290,7 @@ impl ThunkValue for MethodThunk { type Output = Val; - fn get(self: Box, _s: State) -> Result { + fn get(self: Box) -> Result { Ok(evaluate_method( self.fctx.unwrap(), self.name, --- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs +++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs @@ -31,18 +31,18 @@ }))) } -pub fn evaluate_field_name(s: State, ctx: Context, field_name: &FieldName) -> Result> { +pub fn evaluate_field_name(ctx: Context, field_name: &FieldName) -> Result> { Ok(match field_name { FieldName::Fixed(n) => Some(n.clone()), FieldName::Dyn(expr) => State::push( CallLocation::new(&expr.1), || "evaluating field name".to_string(), || { - let value = evaluate(s.clone(), ctx, expr)?; + let value = evaluate(ctx, expr)?; if matches!(value, Val::Null) { Ok(None) } else { - Ok(Some(IStr::from_untyped(value, s.clone())?)) + Ok(Some(IStr::from_untyped(value)?)) } }, )?, @@ -50,7 +50,6 @@ } pub fn evaluate_comp( - s: State, ctx: Context, specs: &[CompSpec], callback: &mut impl FnMut(Context) -> Result<()>, @@ -58,25 +57,22 @@ match specs.get(0) { None => callback(ctx)?, Some(CompSpec::IfSpec(IfSpecData(cond))) => { - if bool::from_untyped(evaluate(s.clone(), ctx.clone(), cond)?, s.clone())? { - evaluate_comp(s, ctx, &specs[1..], callback)?; + if bool::from_untyped(evaluate(ctx.clone(), cond)?)? { + evaluate_comp(ctx, &specs[1..], callback)?; } } - Some(CompSpec::ForSpec(ForSpecData(var, expr))) => { - match evaluate(s.clone(), ctx.clone(), expr)? { - Val::Arr(list) => { - for item in list.iter(s.clone()) { - evaluate_comp( - s.clone(), - ctx.clone().with_var(var.clone(), item?.clone()), - &specs[1..], - callback, - )?; - } + Some(CompSpec::ForSpec(ForSpecData(var, expr))) => match evaluate(ctx.clone(), expr)? { + Val::Arr(list) => { + for item in list.iter() { + evaluate_comp( + ctx.clone().with_var(var.clone(), item?.clone()), + &specs[1..], + callback, + )?; } - _ => throw!(InComprehensionCanOnlyIterateOverArray), } - } + _ => throw!(InComprehensionCanOnlyIterateOverArray), + }, } Ok(()) } @@ -96,12 +92,7 @@ impl Unbound for UnboundLocals { type Bound = Context; - fn bind( - &self, - _s: State, - sup: Option, - this: Option, - ) -> Result { + fn bind(&self, sup: Option, this: Option) -> Result { let fctx = Context::new_future(); let mut new_bindings = GcHashMap::new(); for b in self.locals.iter() { @@ -123,7 +114,7 @@ } #[allow(clippy::too_many_lines)] -pub fn evaluate_member_list_object(s: State, ctx: Context, members: &[Member]) -> Result { +pub fn evaluate_member_list_object(ctx: Context, members: &[Member]) -> Result { let mut builder = ObjValueBuilder::new(); let locals = Rc::new( members @@ -159,20 +150,18 @@ type Bound = Thunk; fn bind( &self, - s: State, sup: Option, this: Option, ) -> Result> { Ok(Thunk::evaluated(evaluate_named( - s.clone(), - self.uctx.bind(s, sup, this)?, + self.uctx.bind(sup, this)?, &self.value, self.name.clone(), )?)) } } - let name = evaluate_field_name(s.clone(), ctx.clone(), name)?; + let name = evaluate_field_name(ctx.clone(), name)?; let name = if let Some(name) = name { name } else { @@ -207,12 +196,11 @@ type Bound = Thunk; fn bind( &self, - s: State, sup: Option, this: Option, ) -> Result> { Ok(Thunk::evaluated(evaluate_method( - self.uctx.bind(s, sup, this)?, + self.uctx.bind(sup, this)?, self.name.clone(), self.params.clone(), self.value.clone(), @@ -220,7 +208,7 @@ } } - let name = if let Some(name) = evaluate_field_name(s.clone(), ctx.clone(), name)? { + let name = if let Some(name) = evaluate_field_name(ctx.clone(), name)? { name } else { continue; @@ -245,14 +233,9 @@ assert: AssertStmt, } impl> ObjectAssertion for ObjectAssert { - fn run( - &self, - s: State, - sup: Option, - this: Option, - ) -> Result<()> { - let ctx = self.uctx.bind(s.clone(), sup, this)?; - evaluate_assert(s, ctx, &self.assert) + fn run(&self, sup: Option, this: Option) -> Result<()> { + let ctx = self.uctx.bind(sup, this)?; + evaluate_assert(ctx, &self.assert) } } builder.assert(tb!(ObjectAssert { @@ -267,9 +250,9 @@ Ok(this) } -pub fn evaluate_object(s: State, ctx: Context, object: &ObjBody) -> Result { +pub fn evaluate_object(ctx: Context, object: &ObjBody) -> Result { Ok(match object { - ObjBody::MemberList(members) => evaluate_member_list_object(s, ctx, members)?, + ObjBody::MemberList(members) => evaluate_member_list_object(ctx, members)?, ObjBody::ObjComp(obj) => { let mut builder = ObjValueBuilder::new(); let locals = Rc::new( @@ -280,8 +263,8 @@ .collect::>(), ); let mut ctxs = vec![]; - evaluate_comp(s.clone(), ctx, &obj.compspecs, &mut |ctx| { - let key = evaluate(s.clone(), ctx.clone(), &obj.key)?; + evaluate_comp(ctx, &obj.compspecs, &mut |ctx| { + let key = evaluate(ctx.clone(), &obj.key)?; let fctx = Context::new_future(); ctxs.push((ctx, fctx.clone())); let uctx = evaluate_object_locals(fctx, locals.clone()); @@ -298,13 +281,11 @@ type Bound = Thunk; fn bind( &self, - s: State, sup: Option, this: Option, ) -> Result> { Ok(Thunk::evaluated(evaluate( - s.clone(), - self.uctx.bind(s, sup, this.clone())?.extend( + self.uctx.bind(sup, this.clone())?.extend( GcHashMap::new(), None, None, @@ -341,17 +322,16 @@ } pub fn evaluate_apply( - s: State, ctx: Context, value: &LocExpr, args: &ArgsDesc, loc: CallLocation<'_>, tailstrict: bool, ) -> Result { - let value = evaluate(s.clone(), ctx.clone(), value)?; + let value = evaluate(ctx.clone(), value)?; Ok(match value { Val::Func(f) => { - let body = || f.evaluate(s.clone(), ctx, loc, args, tailstrict); + let body = || f.evaluate(ctx, loc, args, tailstrict); if tailstrict { body()? } else { @@ -362,13 +342,13 @@ }) } -pub fn evaluate_assert(s: State, ctx: Context, assertion: &AssertStmt) -> Result<()> { +pub fn evaluate_assert(ctx: Context, assertion: &AssertStmt) -> Result<()> { let value = &assertion.0; let msg = &assertion.1; let assertion_result = State::push( CallLocation::new(&value.1), || "assertion condition".to_owned(), - || bool::from_untyped(evaluate(s.clone(), ctx.clone(), value)?, s.clone()), + || bool::from_untyped(evaluate(ctx.clone(), value)?), )?; if !assertion_result { State::push( @@ -376,28 +356,26 @@ || "assertion failure".to_owned(), || { if let Some(msg) = msg { - throw!(AssertionFailed( - evaluate(s.clone(), ctx, msg)?.to_string(s.clone())? - )); + throw!(AssertionFailed(evaluate(ctx, msg)?.to_string()?)); } - throw!(AssertionFailed(Val::Null.to_string(s.clone())?)); + throw!(AssertionFailed(Val::Null.to_string()?)); }, )?; } Ok(()) } -pub fn evaluate_named(s: State, ctx: Context, expr: &LocExpr, name: IStr) -> Result { +pub fn evaluate_named(ctx: Context, expr: &LocExpr, name: IStr) -> Result { use Expr::*; let LocExpr(raw_expr, _loc) = expr; Ok(match &**raw_expr { Function(params, body) => evaluate_method(ctx, name, params.clone(), body.clone()), - _ => evaluate(s, ctx, expr)?, + _ => evaluate(ctx, expr)?, }) } #[allow(clippy::too_many_lines)] -pub fn evaluate(s: State, ctx: Context, expr: &LocExpr) -> Result { +pub fn evaluate(ctx: Context, expr: &LocExpr) -> Result { use Expr::*; let LocExpr(expr, loc) = expr; // let bp = with_state(|s| s.0.stop_at.borrow().clone()); @@ -418,94 +396,89 @@ Literal(LiteralType::True) => Val::Bool(true), Literal(LiteralType::False) => Val::Bool(false), Literal(LiteralType::Null) => Val::Null, - Parened(e) => evaluate(s, ctx, e)?, + Parened(e) => evaluate(ctx, e)?, Str(v) => Val::Str(v.clone()), Num(v) => Val::new_checked_num(*v)?, - BinaryOp(v1, o, v2) => evaluate_binary_op_special(s, ctx, v1, *o, v2)?, - UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(s, ctx, v)?)?, + BinaryOp(v1, o, v2) => evaluate_binary_op_special(ctx, v1, *o, v2)?, + UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(ctx, v)?)?, Var(name) => State::push( CallLocation::new(loc), || format!("variable <{name}> access"), - || ctx.binding(name.clone())?.evaluate(s.clone()), + || ctx.binding(name.clone())?.evaluate(), )?, - Index(value, index) => { - match ( - evaluate(s.clone(), ctx.clone(), value)?, - evaluate(s.clone(), ctx, index)?, - ) { - (Val::Obj(v), Val::Str(key)) => State::push( - CallLocation::new(loc), - || format!("field <{key}> access"), - || match v.get(s.clone(), key.clone()) { - Ok(Some(v)) => Ok(v), - #[cfg(not(feature = "friendly-errors"))] - Ok(None) => throw!(NoSuchField(key.clone(), vec![])), - #[cfg(feature = "friendly-errors")] - Ok(None) => { - let mut heap = Vec::new(); - for field in v.fields_ex( - true, - #[cfg(feature = "exp-preserve-order")] - false, - ) { - let conf = strsim::jaro_winkler(&field as &str, &key as &str); - if conf < 0.8 { - continue; - } - heap.push((conf, field)); + Index(value, index) => match (evaluate(ctx.clone(), value)?, evaluate(ctx, index)?) { + (Val::Obj(v), Val::Str(key)) => State::push( + CallLocation::new(loc), + || format!("field <{key}> access"), + || match v.get(key.clone()) { + Ok(Some(v)) => Ok(v), + #[cfg(not(feature = "friendly-errors"))] + Ok(None) => throw!(NoSuchField(key.clone(), vec![])), + #[cfg(feature = "friendly-errors")] + Ok(None) => { + let mut heap = Vec::new(); + for field in v.fields_ex( + true, + #[cfg(feature = "exp-preserve-order")] + false, + ) { + let conf = strsim::jaro_winkler(&field as &str, &key as &str); + if conf < 0.8 { + continue; } - heap.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal)); - - throw!(NoSuchField( - key.clone(), - heap.into_iter().map(|(_, v)| v).collect() - )) + heap.push((conf, field)); } - Err(e) => Err(e), - }, - )?, - (Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot( - ValType::Obj, - ValType::Str, - n.value_type(), - )), + heap.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal)); - (Val::Arr(v), Val::Num(n)) => { - if n.fract() > f64::EPSILON { - throw!(FractionalIndex) + throw!(NoSuchField( + key.clone(), + heap.into_iter().map(|(_, v)| v).collect() + )) } - v.get(s, n as usize)? - .ok_or_else(|| ArrayBoundsError(n as usize, v.len()))? + Err(e) => Err(e), + }, + )?, + (Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot( + ValType::Obj, + ValType::Str, + n.value_type(), + )), + + (Val::Arr(v), Val::Num(n)) => { + if n.fract() > f64::EPSILON { + throw!(FractionalIndex) } - (Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)), - (Val::Arr(_), n) => throw!(ValueIndexMustBeTypeGot( - ValType::Arr, - ValType::Num, - n.value_type(), - )), + v.get(n as usize)? + .ok_or_else(|| ArrayBoundsError(n as usize, v.len()))? + } + (Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)), + (Val::Arr(_), n) => throw!(ValueIndexMustBeTypeGot( + ValType::Arr, + ValType::Num, + n.value_type(), + )), - (Val::Str(s), Val::Num(n)) => Val::Str({ - let v: IStr = s - .chars() - .skip(n as usize) - .take(1) - .collect::() - .into(); - if v.is_empty() { - let size = s.chars().count(); - throw!(StringBoundsError(n as usize, size)) - } - v - }), - (Val::Str(_), n) => throw!(ValueIndexMustBeTypeGot( - ValType::Str, - ValType::Num, - n.value_type(), - )), + (Val::Str(s), Val::Num(n)) => Val::Str({ + let v: IStr = s + .chars() + .skip(n as usize) + .take(1) + .collect::() + .into(); + if v.is_empty() { + let size = s.chars().count(); + throw!(StringBoundsError(n as usize, size)) + } + v + }), + (Val::Str(_), n) => throw!(ValueIndexMustBeTypeGot( + ValType::Str, + ValType::Num, + n.value_type(), + )), - (v, _) => throw!(CantIndexInto(v.value_type())), - } - } + (v, _) => throw!(CantIndexInto(v.value_type())), + }, LocalExpr(bindings, returned) => { let mut new_bindings: GcHashMap> = GcHashMap::with_capacity(bindings.len()); @@ -514,7 +487,7 @@ evaluate_dest(b, fctx.clone(), &mut new_bindings)?; } let ctx = ctx.extend(new_bindings, None, None, None).into_future(fctx); - evaluate(s, ctx, &returned.clone())? + evaluate(ctx, &returned.clone())? } Arr(items) => { let mut out = Vec::with_capacity(items.len()); @@ -527,8 +500,8 @@ } impl ThunkValue for ArrayElement { type Output = Val; - fn get(self: Box, s: State) -> Result { - evaluate(s, self.ctx, &self.item) + fn get(self: Box) -> Result { + evaluate(self.ctx, &self.item) } } out.push(Thunk::new(tb!(ArrayElement { @@ -540,36 +513,31 @@ } ArrComp(expr, comp_specs) => { let mut out = Vec::new(); - evaluate_comp(s.clone(), ctx, comp_specs, &mut |ctx| { - out.push(evaluate(s.clone(), ctx, expr)?); + evaluate_comp(ctx, comp_specs, &mut |ctx| { + out.push(evaluate(ctx, expr)?); Ok(()) })?; Val::Arr(ArrValue::Eager(Cc::new(out))) } - Obj(body) => Val::Obj(evaluate_object(s, ctx, body)?), + Obj(body) => Val::Obj(evaluate_object(ctx, body)?), ObjExtend(a, b) => evaluate_add_op( - s.clone(), - &evaluate(s.clone(), ctx.clone(), a)?, - &Val::Obj(evaluate_object(s, ctx, b)?), + &evaluate(ctx.clone(), a)?, + &Val::Obj(evaluate_object(ctx, b)?), )?, Apply(value, args, tailstrict) => { - evaluate_apply(s, ctx, value, args, CallLocation::new(loc), *tailstrict)? + evaluate_apply(ctx, value, args, CallLocation::new(loc), *tailstrict)? } Function(params, body) => { evaluate_method(ctx, "anonymous".into(), params.clone(), body.clone()) } AssertExpr(assert, returned) => { - evaluate_assert(s.clone(), ctx.clone(), assert)?; - evaluate(s, ctx, returned)? + evaluate_assert(ctx.clone(), assert)?; + evaluate(ctx, returned)? } ErrorStmt(e) => State::push( CallLocation::new(loc), || "error statement".to_owned(), - || { - throw!(RuntimeError( - evaluate(s.clone(), ctx, e)?.to_string(s.clone())?, - )) - }, + || throw!(RuntimeError(evaluate(ctx, e)?.to_string()?,)), )?, IfElse { cond, @@ -579,12 +547,12 @@ if State::push( CallLocation::new(loc), || "if condition".to_owned(), - || bool::from_untyped(evaluate(s.clone(), ctx.clone(), &cond.0)?, s.clone()), + || bool::from_untyped(evaluate(ctx.clone(), &cond.0)?), )? { - evaluate(s, ctx, cond_then)? + evaluate(ctx, cond_then)? } else { match cond_else { - Some(v) => evaluate(s, ctx, v)?, + Some(v) => evaluate(ctx, v)?, None => Val::Null, } } @@ -592,7 +560,6 @@ Slice(value, desc) => { fn parse_idx( loc: CallLocation<'_>, - s: State, ctx: &Context, expr: &Option, desc: &'static str, @@ -601,24 +568,25 @@ Ok(Some(State::push( loc, || format!("slice {desc}"), - || T::from_untyped(evaluate(s.clone(), ctx.clone(), value)?, s.clone()), + || T::from_untyped(evaluate(ctx.clone(), value)?), )?)) } else { Ok(None) } } - let indexable = evaluate(s.clone(), ctx.clone(), value)?; + let indexable = evaluate(ctx.clone(), value)?; let loc = CallLocation::new(loc); - let start = parse_idx(loc, s.clone(), &ctx, &desc.start, "start")?; - let end = parse_idx(loc, s.clone(), &ctx, &desc.end, "end")?; - let step = parse_idx(loc, s.clone(), &ctx, &desc.step, "step")?; + let start = parse_idx(loc, &ctx, &desc.start, "start")?; + let end = parse_idx(loc, &ctx, &desc.end, "end")?; + let step = parse_idx(loc, &ctx, &desc.step, "step")?; - IndexableVal::into_untyped(indexable.into_indexable()?.slice(start, end, step)?, s)? + IndexableVal::into_untyped(indexable.into_indexable()?.slice(start, end, step)?)? } i @ (Import(path) | ImportStr(path) | ImportBin(path)) => { let tmp = loc.clone().0; + let s = ctx.state(); let resolved_path = s.resolve_from(tmp.source_path(), path as &str)?; match i { Import(_) => State::push( --- a/crates/jrsonnet-evaluator/src/evaluate/operator.rs +++ b/crates/jrsonnet-evaluator/src/evaluate/operator.rs @@ -4,7 +4,7 @@ use crate::{ error::Error::*, evaluate, stdlib::std_format, throw, typed::Typed, val::equals, Context, - Result, State, Val, + Result, Val, }; pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Result { @@ -18,7 +18,7 @@ }) } -pub fn evaluate_add_op(s: State, a: &Val, b: &Val) -> Result { +pub fn evaluate_add_op(a: &Val, b: &Val) -> Result { use Val::*; Ok(match (a, b) { (Str(a), Str(b)) if a.is_empty() => Val::Str(b.clone()), @@ -29,9 +29,9 @@ (Num(a), Str(b)) => Str(format!("{a}{b}").into()), (Str(a), Num(b)) => Str(format!("{a}{b}").into()), - (Str(a), o) | (o, Str(a)) if a.is_empty() => Val::Str(o.clone().to_string(s)?), - (Str(a), o) => Str(format!("{a}{}", o.clone().to_string(s)?).into()), - (o, Str(a)) => Str(format!("{}{a}", o.clone().to_string(s)?).into()), + (Str(a), o) | (o, Str(a)) if a.is_empty() => Val::Str(o.clone().to_string()?), + (Str(a), o) => Str(format!("{a}{}", o.clone().to_string()?).into()), + (o, Str(a)) => Str(format!("{}{a}", o.clone().to_string()?).into()), (Obj(v1), Obj(v2)) => Obj(v2.extend_from(v1.clone())), (Arr(a), Arr(b)) => { @@ -49,7 +49,7 @@ }) } -pub fn evaluate_mod_op(s: State, a: &Val, b: &Val) -> Result { +pub fn evaluate_mod_op(a: &Val, b: &Val) -> Result { use Val::*; match (a, b) { (Num(a), Num(b)) => { @@ -58,9 +58,7 @@ } Ok(Num(a % b)) } - (Str(str), vals) => { - String::into_untyped(std_format(s.clone(), str.clone(), vals.clone())?, s) - } + (Str(str), vals) => String::into_untyped(std_format(str.clone(), vals.clone())?), (a, b) => throw!(BinaryOperatorDoesNotOperateOnValues( BinaryOpType::Mod, a.value_type(), @@ -70,7 +68,6 @@ } pub fn evaluate_binary_op_special( - s: State, ctx: Context, a: &LocExpr, op: BinaryOpType, @@ -78,24 +75,24 @@ ) -> Result { use BinaryOpType::*; use Val::*; - Ok(match (evaluate(s.clone(), ctx.clone(), a)?, op, b) { + Ok(match (evaluate(ctx.clone(), a)?, op, b) { (Bool(true), Or, _o) => Val::Bool(true), (Bool(false), And, _o) => Val::Bool(false), - (a, op, eb) => evaluate_binary_op_normal(s.clone(), &a, op, &evaluate(s, ctx, eb)?)?, + (a, op, eb) => evaluate_binary_op_normal(&a, op, &evaluate(ctx, eb)?)?, }) } -pub fn evaluate_compare_op(s: State, a: &Val, op: BinaryOpType, b: &Val) -> Result { +pub fn evaluate_compare_op(a: &Val, op: BinaryOpType, b: &Val) -> Result { use Val::*; Ok(match (a, b) { (Str(a), Str(b)) => a.cmp(b), (Num(a), Num(b)) => a.partial_cmp(b).expect("jsonnet numbers are non NaN"), (Arr(a), Arr(b)) => { - let ai = a.iter(s.clone()); - let bi = b.iter(s.clone()); + let ai = a.iter(); + let bi = b.iter(); for (a, b) in ai.zip(bi) { - let ord = evaluate_compare_op(s.clone(), &a?, op, &b?)?; + let ord = evaluate_compare_op(&a?, op, &b?)?; if !ord.is_eq() { return Ok(ord); } @@ -111,22 +108,22 @@ }) } -pub fn evaluate_binary_op_normal(s: State, a: &Val, op: BinaryOpType, b: &Val) -> Result { +pub fn evaluate_binary_op_normal(a: &Val, op: BinaryOpType, b: &Val) -> Result { use BinaryOpType::*; use Val::*; Ok(match (a, op, b) { - (a, Add, b) => evaluate_add_op(s, a, b)?, + (a, Add, b) => evaluate_add_op(a, b)?, - (a, Eq, b) => Bool(equals(s, a, b)?), - (a, Neq, b) => Bool(!equals(s, a, b)?), + (a, Eq, b) => Bool(equals(a, b)?), + (a, Neq, b) => Bool(!equals(a, b)?), - (a, Lt, b) => Bool(evaluate_compare_op(s, a, Lt, b)?.is_lt()), - (a, Gt, b) => Bool(evaluate_compare_op(s, a, Gt, b)?.is_gt()), - (a, Lte, b) => Bool(evaluate_compare_op(s, a, Lte, b)?.is_le()), - (a, Gte, b) => Bool(evaluate_compare_op(s, a, Gte, b)?.is_ge()), + (a, Lt, b) => Bool(evaluate_compare_op(a, Lt, b)?.is_lt()), + (a, Gt, b) => Bool(evaluate_compare_op(a, Gt, b)?.is_gt()), + (a, Lte, b) => Bool(evaluate_compare_op(a, Lte, b)?.is_le()), + (a, Gte, b) => Bool(evaluate_compare_op(a, Gte, b)?.is_ge()), (Str(a), In, Obj(obj)) => Bool(obj.has_field_ex(a.clone(), true)), - (a, Mod, b) => evaluate_mod_op(s, a, b)?, + (a, Mod, b) => evaluate_mod_op(a, b)?, (Str(v1), Mul, Num(v2)) => Str(v1.repeat(*v2 as usize).into()), --- a/crates/jrsonnet-evaluator/src/function/arglike.rs +++ b/crates/jrsonnet-evaluator/src/function/arglike.rs @@ -4,9 +4,10 @@ use jrsonnet_interner::IStr; use jrsonnet_parser::{ArgsDesc, LocExpr}; -use crate::{ - error::Result, evaluate, tb, typed::Typed, val::ThunkValue, Context, State, Thunk, Val, -}; +use crate::{error::Result, evaluate, tb, typed::Typed, val::ThunkValue, Context, Thunk, Val}; + +/// Marker for arguments, which can be evaluated with context set to None +pub trait OptionalContext {} #[derive(Trace)] struct EvaluateThunk { @@ -15,19 +16,19 @@ } impl ThunkValue for EvaluateThunk { type Output = Val; - fn get(self: Box, s: State) -> Result { - evaluate(s, self.ctx, &self.expr) + fn get(self: Box) -> Result { + evaluate(self.ctx, &self.expr) } } pub trait ArgLike { - fn evaluate_arg(&self, s: State, ctx: Context, tailstrict: bool) -> Result>; + fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result>; } impl ArgLike for &LocExpr { - fn evaluate_arg(&self, s: State, ctx: Context, tailstrict: bool) -> Result> { + fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result> { Ok(if tailstrict { - Thunk::evaluated(evaluate(s, ctx, self)?) + Thunk::evaluated(evaluate(ctx, self)?) } else { Thunk::new(tb!(EvaluateThunk { ctx, @@ -41,24 +42,25 @@ where T: Typed + Clone, { - fn evaluate_arg(&self, s: State, _ctx: Context, _tailstrict: bool) -> Result> { - let val = T::into_untyped(self.clone(), s)?; + fn evaluate_arg(&self, _ctx: Context, _tailstrict: bool) -> Result> { + let val = T::into_untyped(self.clone())?; Ok(Thunk::evaluated(val)) } } +impl OptionalContext for T where T: Typed + Clone {} -#[derive(Clone)] +#[derive(Clone, Trace)] pub enum TlaArg { String(IStr), Code(LocExpr), Val(Val), } impl ArgLike for TlaArg { - fn evaluate_arg(&self, s: State, ctx: Context, tailstrict: bool) -> Result> { + fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result> { match self { TlaArg::String(s) => Ok(Thunk::evaluated(Val::Str(s.clone()))), TlaArg::Code(code) => Ok(if tailstrict { - Thunk::evaluated(evaluate(s, ctx, code)?) + Thunk::evaluated(evaluate(ctx, code)?) } else { Thunk::new(tb!(EvaluateThunk { ctx, @@ -81,14 +83,12 @@ fn unnamed_len(&self) -> usize; fn unnamed_iter( &self, - s: State, ctx: Context, tailstrict: bool, handler: &mut dyn FnMut(usize, Thunk) -> Result<()>, ) -> Result<()>; fn named_iter( &self, - s: State, ctx: Context, tailstrict: bool, handler: &mut dyn FnMut(&IStr, Thunk) -> Result<()>, @@ -102,7 +102,6 @@ } fn unnamed_iter( &self, - _s: State, _ctx: Context, _tailstrict: bool, handler: &mut dyn FnMut(usize, Thunk) -> Result<()>, @@ -114,7 +113,6 @@ } fn named_iter( &self, - _s: State, _ctx: Context, _tailstrict: bool, _handler: &mut dyn FnMut(&IStr, Thunk) -> Result<()>, @@ -123,6 +121,7 @@ } fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {} } +impl OptionalContext for Vec {} impl ArgsLike for ArgsDesc { fn unnamed_len(&self) -> usize { @@ -131,7 +130,6 @@ fn unnamed_iter( &self, - s: State, ctx: Context, tailstrict: bool, handler: &mut dyn FnMut(usize, Thunk) -> Result<()>, @@ -140,7 +138,7 @@ handler( id, if tailstrict { - Thunk::evaluated(evaluate(s.clone(), ctx.clone(), arg)?) + Thunk::evaluated(evaluate(ctx.clone(), arg)?) } else { Thunk::new(tb!(EvaluateThunk { ctx: ctx.clone(), @@ -154,7 +152,6 @@ fn named_iter( &self, - s: State, ctx: Context, tailstrict: bool, handler: &mut dyn FnMut(&IStr, Thunk) -> Result<()>, @@ -163,7 +160,7 @@ handler( name, if tailstrict { - Thunk::evaluated(evaluate(s.clone(), ctx.clone(), arg)?) + Thunk::evaluated(evaluate(ctx.clone(), arg)?) } else { Thunk::new(tb!(EvaluateThunk { ctx: ctx.clone(), @@ -190,7 +187,6 @@ fn unnamed_iter( &self, - _s: State, _ctx: Context, _tailstrict: bool, _handler: &mut dyn FnMut(usize, Thunk) -> Result<()>, @@ -200,16 +196,12 @@ fn named_iter( &self, - s: State, ctx: Context, tailstrict: bool, handler: &mut dyn FnMut(&IStr, Thunk) -> Result<()>, ) -> Result<()> { for (name, value) in self.iter() { - handler( - name, - value.evaluate_arg(s.clone(), ctx.clone(), tailstrict)?, - )?; + handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?; } Ok(()) } @@ -220,6 +212,7 @@ } } } +impl OptionalContext for HashMap where A: ArgLike + OptionalContext {} macro_rules! impl_args_like { ($count:expr; $($gen:ident)*) => { @@ -231,7 +224,6 @@ #[allow(non_snake_case, unused_assignments)] fn unnamed_iter( &self, - s: State, ctx: Context, tailstrict: bool, handler: &mut dyn FnMut(usize, Thunk) -> Result<()>, @@ -239,14 +231,13 @@ let mut i = 0usize; let ($($gen,)*) = self; $( - handler(i, $gen.evaluate_arg(s.clone(), ctx.clone(), tailstrict)?)?; + handler(i, $gen.evaluate_arg(ctx.clone(), tailstrict)?)?; i+=1; )* Ok(()) } fn named_iter( &self, - _s: State, _ctx: Context, _tailstrict: bool, _handler: &mut dyn FnMut(&IStr, Thunk) -> Result<()>, @@ -255,6 +246,8 @@ } fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {} } + impl<$($gen: ArgLike,)*> OptionalContext for ($($gen,)*) where $($gen: OptionalContext),* {} + impl<$($gen: ArgLike,)*> sealed::Named for ($((IStr, $gen),)*) {} impl<$($gen: ArgLike,)*> ArgsLike for ($((IStr, $gen),)*) { fn unnamed_len(&self) -> usize { @@ -262,7 +255,6 @@ } fn unnamed_iter( &self, - _s: State, _ctx: Context, _tailstrict: bool, _handler: &mut dyn FnMut(usize, Thunk) -> Result<()>, @@ -272,14 +264,13 @@ #[allow(non_snake_case)] fn named_iter( &self, - s: State, ctx: Context, tailstrict: bool, handler: &mut dyn FnMut(&IStr, Thunk) -> Result<()>, ) -> Result<()> { let ($($gen,)*) = self; $( - handler(&$gen.0, $gen.1.evaluate_arg(s.clone(), ctx.clone(), tailstrict)?)?; + handler(&$gen.0, $gen.1.evaluate_arg(ctx.clone(), tailstrict)?)?; )* Ok(()) } @@ -291,6 +282,7 @@ )* } } + impl<$($gen: ArgLike,)*> OptionalContext for ($((IStr, $gen),)*) where $($gen: OptionalContext),* {} }; ($count:expr; $($cur:ident)* @ $c:ident $($rest:ident)*) => { impl_args_like!($count; $($cur)*); @@ -312,7 +304,6 @@ fn unnamed_iter( &self, - _s: State, _ctx: Context, _tailstrict: bool, _handler: &mut dyn FnMut(usize, Thunk) -> Result<()>, @@ -322,7 +313,6 @@ fn named_iter( &self, - _s: State, _ctx: Context, _tailstrict: bool, _handler: &mut dyn FnMut(&IStr, Thunk) -> Result<()>, @@ -332,3 +322,4 @@ fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {} } +impl OptionalContext for () {} --- a/crates/jrsonnet-evaluator/src/function/builtin.rs +++ b/crates/jrsonnet-evaluator/src/function/builtin.rs @@ -3,7 +3,7 @@ use jrsonnet_gcmodule::Trace; use super::{arglike::ArgsLike, parse::parse_builtin_call, CallLocation}; -use crate::{error::Result, gc::TraceBox, Context, State, Val}; +use crate::{error::Result, gc::TraceBox, Context, Val}; pub type BuiltinParamName = Cow<'static, str>; @@ -24,13 +24,7 @@ /// Parameter names for named calls fn params(&self) -> &[BuiltinParam]; /// Call the builtin - fn call( - &self, - s: State, - ctx: Context, - loc: CallLocation<'_>, - args: &dyn ArgsLike, - ) -> Result; + fn call(&self, ctx: Context, loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result; } pub trait StaticBuiltin: Builtin + Send + Sync @@ -76,23 +70,17 @@ &self.params } - fn call( - &self, - s: State, - ctx: Context, - _loc: CallLocation<'_>, - args: &dyn ArgsLike, - ) -> Result { - let args = parse_builtin_call(s.clone(), ctx, &self.params, args, true)?; + fn call(&self, ctx: Context, _loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result { + let args = parse_builtin_call(ctx, &self.params, args, true)?; let args = args .into_iter() .map(|a| a.expect("legacy natives have no default params")) - .map(|a| a.evaluate(s.clone())) + .map(|a| a.evaluate()) .collect::>>()?; - self.handler.call(s, &args) + self.handler.call(&args) } } pub trait NativeCallbackHandler: Trace { - fn call(&self, s: State, args: &[Val]) -> Result; + fn call(&self, args: &[Val]) -> Result; } --- a/crates/jrsonnet-evaluator/src/function/mod.rs +++ b/crates/jrsonnet-evaluator/src/function/mod.rs @@ -7,11 +7,12 @@ use jrsonnet_parser::{Destruct, Expr, ExprLocation, LocExpr, ParamsDesc}; use self::{ + arglike::OptionalContext, builtin::{Builtin, StaticBuiltin}, native::NativeDesc, parse::{parse_default_function_call, parse_function_call}, }; -use crate::{evaluate, gc::TraceBox, typed::Any, Context, Result, State, Val}; +use crate::{evaluate, gc::TraceBox, typed::Any, Context, ContextBuilder, Result, Val}; pub mod arglike; pub mod builtin; @@ -73,19 +74,11 @@ /// Create context, with which body code will run pub fn call_body_context( &self, - s: State, call_ctx: Context, args: &dyn ArgsLike, tailstrict: bool, ) -> Result { - parse_function_call( - s, - call_ctx, - self.ctx.clone(), - &self.params, - args, - tailstrict, - ) + parse_function_call(call_ctx, self.ctx.clone(), &self.params, args, tailstrict) } } @@ -140,7 +133,6 @@ /// If `tailstrict` is specified - then arguments will be evaluated before being passed to function body. pub fn evaluate( &self, - s: State, call_ctx: Context, loc: CallLocation<'_>, args: &dyn ArgsLike, @@ -155,19 +147,23 @@ } static ID: &builtin_id = &builtin_id {}; - ID.call(s, call_ctx, loc, args) + ID.call(call_ctx, loc, args) } Self::Normal(func) => { - let body_ctx = func.call_body_context(s.clone(), call_ctx, args, tailstrict)?; - evaluate(s, body_ctx, &func.body) + let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?; + evaluate(body_ctx, &func.body) } - Self::StaticBuiltin(b) => b.call(s, call_ctx, loc, args), - Self::Builtin(b) => b.call(s, call_ctx, loc, args), + Self::StaticBuiltin(b) => b.call(call_ctx, loc, args), + Self::Builtin(b) => b.call(call_ctx, loc, args), } } - /// Helper method, which calls [`Self::evaluate`] with sensible defaults for native code. - pub fn evaluate_simple(&self, s: State, args: &dyn ArgsLike) -> Result { - self.evaluate(s, Context::default(), CallLocation::native(), args, true) + pub fn evaluate_simple(&self, args: &A) -> Result { + self.evaluate( + ContextBuilder::dangerous_empty_state().build(), + CallLocation::native(), + args, + true, + ) } /// Convert jsonnet function to plain `Fn` value. pub fn into_native(self) -> D::Value { @@ -180,10 +176,6 @@ /// /// This function should only be used for optimization, not for the conditional logic, i.e code should work with syntetic identity function too pub fn is_identity(&self) -> bool { - if matches!(self, Self::Id) { - return true; - } - match self { Self::Id => true, Self::Normal(desc) => { --- a/crates/jrsonnet-evaluator/src/function/native.rs +++ b/crates/jrsonnet-evaluator/src/function/native.rs @@ -1,5 +1,8 @@ -use super::{arglike::ArgLike, CallLocation, FuncVal}; -use crate::{error::Result, typed::Typed, Context, State}; +use super::{ + arglike::{ArgLike, OptionalContext}, + FuncVal, +}; +use crate::{error::Result, typed::Typed}; pub trait NativeDesc { type Value; @@ -9,23 +12,18 @@ ($($gen:ident)*) => { impl<$($gen,)* O> NativeDesc for (($($gen,)*), O) where - $($gen: ArgLike,)* + $($gen: ArgLike + OptionalContext,)* O: Typed, { - type Value = Box Result>; + type Value = Box Result>; #[allow(non_snake_case)] fn into_native(val: FuncVal) -> Self::Value { - Box::new(move |s: State, $($gen),*| { - let val = val.evaluate( - s.clone(), - // This isn't intended to be used with ArgsDesc - Context::default(), - CallLocation::native(), + Box::new(move |$($gen),*| { + let val = val.evaluate_simple( &($($gen,)*), - true )?; - O::from_untyped(val, s.clone()) + O::from_untyped(val) }) } } --- a/crates/jrsonnet-evaluator/src/function/parse.rs +++ b/crates/jrsonnet-evaluator/src/function/parse.rs @@ -12,7 +12,7 @@ gc::GcHashMap, tb, throw, val::ThunkValue, - Context, Pending, State, Thunk, Val, + Context, Pending, Thunk, Val, }; #[derive(Trace)] @@ -24,8 +24,8 @@ impl ThunkValue for EvaluateNamedThunk { type Output = Val; - fn get(self: Box, s: State) -> Result { - evaluate_named(s, self.ctx.unwrap(), &self.value, self.name) + fn get(self: Box) -> Result { + evaluate_named(self.ctx.unwrap(), &self.value, self.name) } } @@ -38,7 +38,6 @@ /// * `args`: passed function arguments /// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily pub fn parse_function_call( - s: State, ctx: Context, body_ctx: Context, params: &ParamsDesc, @@ -56,7 +55,7 @@ let mut filled_named = 0; let mut filled_positionals = 0; - args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| { + args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| { let name = params[id].0.clone(); destruct( &name, @@ -68,7 +67,7 @@ Ok(()) })?; - args.named_iter(s, ctx, tailstrict, &mut |name, value| { + args.named_iter(ctx, tailstrict, &mut |name, value| { // FIXME: O(n) for arg existence check if !params.iter().any(|p| p.0.name().as_ref() == Some(name)) { throw!(UnknownFunctionParameter((name as &str).to_owned())); @@ -150,7 +149,6 @@ /// * `args`: passed function arguments /// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily pub fn parse_builtin_call( - s: State, ctx: Context, params: &[BuiltinParam], args: &dyn ArgsLike, @@ -169,13 +167,13 @@ let mut filled_args = 0; - args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| { + args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| { passed_args[id] = Some(arg); filled_args += 1; Ok(()) })?; - args.named_iter(s, ctx, tailstrict, &mut |name, arg| { + args.named_iter(ctx, tailstrict, &mut |name, arg| { // FIXME: O(n) for arg existence check let id = params .iter() @@ -232,7 +230,7 @@ struct DependsOnUnbound(IStr, ParamsDesc); impl ThunkValue for DependsOnUnbound { type Output = Val; - fn get(self: Box, _: State) -> Result { + fn get(self: Box) -> Result { Err(FunctionParameterNotBoundInCall( Some(self.0.clone()), self.1.iter().map(|p| (p.0.name(), p.1.is_some())).collect(), --- a/crates/jrsonnet-evaluator/src/import.rs +++ b/crates/jrsonnet-evaluator/src/import.rs @@ -8,6 +8,7 @@ }; use fs::File; +use jrsonnet_gcmodule::Trace; use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath}; use crate::{ @@ -19,7 +20,7 @@ }; /// Implements file resolution logic for `import` and `importStr` -pub trait ImportResolver { +pub trait ImportResolver: Trace { /// Resolves file path, e.g. `(/home/user/manifests, b.libjsonnet)` can correspond /// both to `/home/user/manifests/b.libjsonnet` and to `/home/user/${vendor}/b.libjsonnet` /// where `${vendor}` is a library path. @@ -47,6 +48,7 @@ } /// Dummy resolver, can't resolve/load any file +#[derive(Trace)] pub struct DummyImportResolver; impl ImportResolver for DummyImportResolver { fn load_file_contents(&self, _resolved: &SourcePath) -> Result> { @@ -65,7 +67,7 @@ } /// File resolver, can load file from both FS and library paths -#[derive(Default)] +#[derive(Default, Trace)] pub struct FileImportResolver { /// Library directories to search for file. /// Referred to as `jpath` in original jsonnet implementation. @@ -82,6 +84,7 @@ self.library_paths.borrow_mut().push(path); } } + impl ImportResolver for FileImportResolver { fn resolve_from(&self, from: &SourcePath, path: &str) -> Result { let mut direct = if let Some(f) = from.downcast_ref::() { --- a/crates/jrsonnet-evaluator/src/integrations/serde.rs +++ b/crates/jrsonnet-evaluator/src/integrations/serde.rs @@ -1,7 +1,11 @@ use std::borrow::Cow; use jrsonnet_gcmodule::Cc; -use serde::{de::Visitor, ser::Error, Deserialize, Serialize}; +use serde::{ + de::Visitor, + ser::{Error, SerializeMap, SerializeSeq}, + Deserialize, Serialize, +}; use crate::{error::Result, val::ArrValue, ObjValueBuilder, State, Val}; @@ -152,12 +156,48 @@ Val::Num(n) => serializer.serialize_f64(*n), Val::Arr(arr) => { let mut seq = serializer.serialize_seq(Some(arr.len()))?; - for element in arr.iter(State::default()) { - // seq.serialize_element() + for (i, element) in arr.iter().enumerate() { + let mut serde_error = None; + // TODO: rewrite using try{} after stabilization + State::push_description( + || format!("array index [{i}]"), + || { + let e = element?; + if let Err(e) = seq.serialize_element(&e) { + serde_error = Some(e); + } + Ok(()) + }, + ) + .map_err(|e| S::Error::custom(e.to_string()))?; + if let Some(e) = serde_error { + return Err(e); + } + } + seq.end() + } + Val::Obj(obj) => { + let mut map = serializer.serialize_map(Some(obj.len()))?; + for (field, value) in obj.iter() { + let mut serde_error = None; + // TODO: rewrite using try{} after stabilization + State::push_description( + || format!("object field {field:?}"), + || { + let v = value?; + if let Err(e) = map.serialize_entry(field.as_str(), &v) { + serde_error = Some(e); + } + Ok(()) + }, + ) + .map_err(|e| S::Error::custom(e.to_string()))?; + if let Some(e) = serde_error { + return Err(e); + } } - todo!() + map.end() } - Val::Obj(_) => todo!(), Val::Func(_) => Err(S::Error::custom("tried to manifest function")), } } --- a/crates/jrsonnet-evaluator/src/lib.rs +++ b/crates/jrsonnet-evaluator/src/lib.rs @@ -36,6 +36,8 @@ clippy::use_self, // https://github.com/rust-lang/rust-clippy/issues/8539 clippy::iter_with_drain, + // ci is being run with nightly, but library should work on stable + clippy::missing_const_for_fn, )] // For jrsonnet-macros @@ -63,7 +65,6 @@ collections::HashMap, fmt::{self, Debug}, path::Path, - rc::Rc, }; pub use ctx::*; @@ -89,7 +90,7 @@ /// Type of value after object context is bound type Bound; /// Create value bound to specified object context - fn bind(&self, s: State, sup: Option, this: Option) -> Result; + fn bind(&self, sup: Option, this: Option) -> Result; } /// Object fields may, or may not depend on `this`/`super`, this enum allows cheaper reuse of object-independent fields for native code @@ -109,14 +110,9 @@ } impl MaybeUnbound { /// Attach object context to value, if required - pub fn evaluate( - &self, - s: State, - sup: Option, - this: Option, - ) -> Result> { + pub fn evaluate(&self, sup: Option, this: Option) -> Result> { match self { - Self::Unbound(v) => v.bind(s, sup, this), + Self::Unbound(v) => v.bind(sup, this), Self::Bound(v) => Ok(v.clone()), } } @@ -124,7 +120,7 @@ /// During import, this trait will be called to create initial context for file. /// It may initialize global variables, stdlib for example. -pub trait ContextInitializer { +pub trait ContextInitializer: Trace { /// Initialize default file context. fn initialize(&self, state: State, for_file: Source) -> Context; /// Allows upcasting from abstract to concrete context initializer. @@ -133,10 +129,11 @@ } /// Context initializer which adds nothing. +#[derive(Trace)] pub struct DummyContextInitializer; impl ContextInitializer for DummyContextInitializer { - fn initialize(&self, _state: State, _for_file: Source) -> Context { - Context::default() + fn initialize(&self, state: State, _for_file: Source) -> Context { + ContextBuilder::new(state).build() } fn as_any(&self) -> &dyn Any { self @@ -144,6 +141,7 @@ } /// Dynamically reconfigurable evaluation settings +#[derive(Trace)] pub struct EvaluationSettings { /// Limits amount of stack trace items preserved pub max_trace: usize, @@ -151,27 +149,27 @@ pub tla_vars: HashMap, /// Context initializer, which will be used for imports and everything /// [`NoopContextInitializer`] is used by default, most likely you want to have `jrsonnet-stdlib` - pub context_initializer: Box, + pub context_initializer: TraceBox, /// Used to resolve file locations/contents - pub import_resolver: Box, + pub import_resolver: TraceBox, /// Used in manifestification functions pub manifest_format: ManifestFormat, /// Used for bindings - pub trace_format: Box, + pub trace_format: TraceBox, } impl Default for EvaluationSettings { fn default() -> Self { Self { max_trace: 20, - context_initializer: Box::new(DummyContextInitializer), + context_initializer: tb!(DummyContextInitializer), tla_vars: HashMap::default(), - import_resolver: Box::new(DummyImportResolver), + import_resolver: tb!(DummyImportResolver), manifest_format: ManifestFormat::Json { padding: 4, #[cfg(feature = "exp-preserve-order")] preserve_order: false, }, - trace_format: Box::new(CompactFormat { + trace_format: tb!(CompactFormat { padding: 4, resolver: trace::PathResolver::Absolute, }), @@ -179,6 +177,7 @@ } } +#[derive(Trace)] struct FileData { string: Option, bytes: Option, @@ -208,7 +207,7 @@ } } -#[derive(Default)] +#[derive(Default, Trace)] pub struct EvaluationStateInternals { /// Internal state file_cache: RefCell>, @@ -217,8 +216,8 @@ } /// Maintains stack trace and import resolution -#[derive(Default, Clone)] -pub struct State(Rc); +#[derive(Default, Clone, Trace)] +pub struct State(Cc); impl State { /// Should only be called with path retrieved from [`resolve_path`], may panic otherwise @@ -340,11 +339,7 @@ file.evaluating = true; // Dropping file cache guard here, as evaluation may use this map too drop(file_cache); - let res = evaluate( - self.clone(), - self.create_default_context(file_name), - &parsed, - ); + let res = evaluate(self.create_default_context(file_name), &parsed); let mut file_cache = self.file_cache(); let mut file = file_cache.raw_entry_mut().from_key(&path); @@ -425,14 +420,14 @@ pub fn manifest(&self, val: Val) -> Result { Self::push_description( || "manifestification".to_string(), - || val.manifest(self.clone(), &self.manifest_format()), + || val.manifest(&self.manifest_format()), ) } pub fn manifest_multi(&self, val: Val) -> Result> { - val.manifest_multi(self.clone(), &self.manifest_format()) + val.manifest_multi(&self.manifest_format()) } pub fn manifest_stream(&self, val: Val) -> Result> { - val.manifest_stream(self.clone(), &self.manifest_format()) + val.manifest_stream(&self.manifest_format()) } /// If passed value is function then call with set TLA @@ -442,7 +437,6 @@ || "during TLA call".to_owned(), || { func.evaluate( - self.clone(), self.create_default_context(Source::new_virtual( "".into(), IStr::empty(), @@ -487,7 +481,7 @@ path: source.clone(), error: Box::new(e), })?; - evaluate(self.clone(), self.create_default_context(source), &parsed) + evaluate(self.create_default_context(source), &parsed) } } @@ -537,7 +531,7 @@ Ref::map(self.settings(), |s| &*s.import_resolver) } pub fn set_import_resolver(&self, resolver: Box) { - self.settings_mut().import_resolver = resolver; + self.settings_mut().import_resolver = TraceBox(resolver); } pub fn context_initializer(&self) -> Ref<'_, dyn ContextInitializer> { Ref::map(self.settings(), |s| &*s.context_initializer) @@ -553,8 +547,8 @@ pub fn trace_format(&self) -> Ref<'_, dyn TraceFormat> { Ref::map(self.settings(), |s| &*s.trace_format) } - pub fn set_trace_format(&self, format: Box) { - self.settings_mut().trace_format = format; + pub fn set_trace_format(&self, format: impl TraceFormat) { + self.settings_mut().trace_format = tb!(format); } pub fn max_trace(&self) -> usize { --- a/crates/jrsonnet-evaluator/src/obj.rs +++ b/crates/jrsonnet-evaluator/src/obj.rs @@ -105,7 +105,7 @@ } pub trait ObjectAssertion: Trace { - fn run(&self, s: State, super_obj: Option, this: Option) -> Result<()>; + fn run(&self, super_obj: Option, this: Option) -> Result<()>; } // Field => This @@ -368,8 +368,19 @@ .map_or(false, |v| v.is_visible()) } - pub fn get(&self, s: State, key: IStr) -> Result> { - self.run_assertions(s.clone())?; + pub fn iter(&self) -> impl Iterator)> + '_ { + let fields = self.fields(); + fields.into_iter().map(|field| { + ( + field.clone(), + self.get(field) + .map(|opt| opt.expect("iterating over keys, field exists")), + ) + }) + } + + pub fn get(&self, key: IStr) -> Result> { + self.run_assertions()?; if let Some(v) = self.0.value_cache.borrow().get(&key) { return Ok(match v { CacheValue::Cached(v) => Some(v.clone()), @@ -384,7 +395,6 @@ .insert(key.clone(), CacheValue::Pending); let value = self .get_raw( - s, key.clone(), self.0.this.clone().unwrap_or_else(|| self.clone()), ) @@ -404,49 +414,47 @@ Ok(value) } - fn get_raw(&self, s: State, key: IStr, real_this: Self) -> Result> { + fn get_raw(&self, key: IStr, real_this: Self) -> Result> { match (self.0.this_entries.get(&key), &self.0.sup) { - (Some(k), None) => Ok(Some(self.evaluate_this(s, k, real_this)?)), + (Some(k), None) => Ok(Some(self.evaluate_this(k, real_this)?)), (Some(k), Some(super_obj)) => { - let our = self.evaluate_this(s.clone(), k, real_this.clone())?; + let our = self.evaluate_this(k, real_this.clone())?; if k.add { super_obj - .get_raw(s.clone(), key, real_this)? + .get_raw(key, real_this)? .map_or(Ok(Some(our.clone())), |v| { - Ok(Some(evaluate_add_op(s.clone(), &v, &our)?)) + Ok(Some(evaluate_add_op(&v, &our)?)) }) } else { Ok(Some(our)) } } - (None, Some(super_obj)) => super_obj.get_raw(s, key, real_this), + (None, Some(super_obj)) => super_obj.get_raw(key, real_this), (None, None) => Ok(None), } } - fn evaluate_this(&self, s: State, v: &ObjMember, real_this: Self) -> Result { + fn evaluate_this(&self, v: &ObjMember, real_this: Self) -> Result { v.invoke - .evaluate(s.clone(), self.0.sup.clone(), Some(real_this))? - .evaluate(s) + .evaluate(self.0.sup.clone(), Some(real_this))? + .evaluate() } - fn run_assertions_raw(&self, s: State, real_this: &Self) -> Result<()> { + fn run_assertions_raw(&self, real_this: &Self) -> Result<()> { if self.0.assertions_ran.borrow_mut().insert(real_this.clone()) { for assertion in self.0.assertions.iter() { - if let Err(e) = - assertion.run(s.clone(), self.0.sup.clone(), Some(real_this.clone())) - { + if let Err(e) = assertion.run(self.0.sup.clone(), Some(real_this.clone())) { self.0.assertions_ran.borrow_mut().remove(real_this); return Err(e); } } if let Some(super_obj) = &self.0.sup { - super_obj.run_assertions_raw(s, real_this)?; + super_obj.run_assertions_raw(real_this)?; } } Ok(()) } - pub fn run_assertions(&self, s: State) -> Result<()> { - self.run_assertions_raw(s, self) + pub fn run_assertions(&self) -> Result<()> { + self.run_assertions_raw(self) } pub fn ptr_eq(a: &Self, b: &Self) -> bool { --- a/crates/jrsonnet-evaluator/src/stdlib/format.rs +++ b/crates/jrsonnet-evaluator/src/stdlib/format.rs @@ -6,7 +6,7 @@ use jrsonnet_types::ValType; use thiserror::Error; -use crate::{error::Error::*, throw, typed::Typed, LocError, ObjValue, Result, State, Val}; +use crate::{error::Error::*, throw, typed::Typed, LocError, ObjValue, Result, Val}; #[derive(Debug, Clone, Error, Trace)] pub enum FormatError { @@ -472,7 +472,6 @@ #[allow(clippy::too_many_lines)] pub fn format_code( - s: State, out: &mut String, value: &Val, code: &Code<'_>, @@ -491,9 +490,9 @@ let mut tmp_out = String::new(); match code.convtype { - ConvTypeV::String => tmp_out.push_str(&value.clone().to_string(s)?), + ConvTypeV::String => tmp_out.push_str(&value.clone().to_string()?), ConvTypeV::Decimal => { - let value = f64::from_untyped(value.clone(), s)?; + let value = f64::from_untyped(value.clone())?; render_decimal( &mut tmp_out, value, @@ -504,7 +503,7 @@ ); } ConvTypeV::Octal => { - let value = f64::from_untyped(value.clone(), s)?; + let value = f64::from_untyped(value.clone())?; render_octal( &mut tmp_out, value, @@ -516,7 +515,7 @@ ); } ConvTypeV::Hexadecimal => { - let value = f64::from_untyped(value.clone(), s)?; + let value = f64::from_untyped(value.clone())?; render_hexadecimal( &mut tmp_out, value, @@ -529,7 +528,7 @@ ); } ConvTypeV::Scientific => { - let value = f64::from_untyped(value.clone(), s)?; + let value = f64::from_untyped(value.clone())?; render_float_sci( &mut tmp_out, value, @@ -543,7 +542,7 @@ ); } ConvTypeV::Float => { - let value = f64::from_untyped(value.clone(), s)?; + let value = f64::from_untyped(value.clone())?; render_float( &mut tmp_out, value, @@ -556,7 +555,7 @@ ); } ConvTypeV::Shorter => { - let value = f64::from_untyped(value.clone(), s)?; + let value = f64::from_untyped(value.clone())?; let exponent = value.log10().floor(); if exponent < -4.0 || exponent >= fpprec as f64 { render_float_sci( @@ -623,7 +622,7 @@ Ok(()) } -pub fn format_arr(s: State, str: &str, mut values: &[Val]) -> Result { +pub fn format_arr(str: &str, mut values: &[Val]) -> Result { let codes = parse_codes(str)?; let mut out = String::new(); @@ -640,7 +639,7 @@ } let value = &values[0]; values = &values[1..]; - usize::from_untyped(value.clone(), s.clone())? + usize::from_untyped(value.clone())? } Width::Fixed(n) => n, }; @@ -651,7 +650,7 @@ } let value = &values[0]; values = &values[1..]; - Some(usize::from_untyped(value.clone(), s.clone())?) + Some(usize::from_untyped(value.clone())?) } Some(Width::Fixed(n)) => Some(n), None => None, @@ -669,7 +668,7 @@ value }; - format_code(s.clone(), &mut out, value, &c, width, precision)?; + format_code(&mut out, value, &c, width, precision)?; } } } @@ -677,7 +676,7 @@ Ok(out) } -pub fn format_obj(s: State, str: &str, values: &ObjValue) -> Result { +pub fn format_obj(str: &str, values: &ObjValue) -> Result { let codes = parse_codes(str)?; let mut out = String::new(); @@ -709,14 +708,14 @@ if f.is_empty() { throw!(MappingKeysRequired); } - if let Some(v) = values.get(s.clone(), f.clone())? { + if let Some(v) = values.get(f.clone())? { v } else { throw!(NoSuchFormatField(f)); } }; - format_code(s.clone(), &mut out, &value, &c, width, precision)?; + format_code(&mut out, &value, &c, width, precision)?; } } } @@ -742,48 +741,21 @@ #[test] fn octals() { - let s = State::default(); - assert_eq!( - format_arr(s.clone(), "%#o", &[Val::Num(8.0)]).unwrap(), - "010" - ); - assert_eq!( - format_arr(s.clone(), "%#4o", &[Val::Num(8.0)]).unwrap(), - " 010" - ); - assert_eq!( - format_arr(s.clone(), "%4o", &[Val::Num(8.0)]).unwrap(), - " 10" - ); - assert_eq!( - format_arr(s.clone(), "%04o", &[Val::Num(8.0)]).unwrap(), - "0010" - ); - assert_eq!( - format_arr(s.clone(), "%+4o", &[Val::Num(8.0)]).unwrap(), - " +10" - ); - assert_eq!( - format_arr(s.clone(), "%+04o", &[Val::Num(8.0)]).unwrap(), - "+010" - ); - assert_eq!( - format_arr(s.clone(), "%-4o", &[Val::Num(8.0)]).unwrap(), - "10 " - ); - assert_eq!( - format_arr(s.clone(), "%+-4o", &[Val::Num(8.0)]).unwrap(), - "+10 " - ); - assert_eq!(format_arr(s, "%+-04o", &[Val::Num(8.0)]).unwrap(), "+10 "); + assert_eq!(format_arr("%#o", &[Val::Num(8.0)]).unwrap(), "010"); + assert_eq!(format_arr("%#4o", &[Val::Num(8.0)]).unwrap(), " 010"); + assert_eq!(format_arr("%4o", &[Val::Num(8.0)]).unwrap(), " 10"); + assert_eq!(format_arr("%04o", &[Val::Num(8.0)]).unwrap(), "0010"); + assert_eq!(format_arr("%+4o", &[Val::Num(8.0)]).unwrap(), " +10"); + assert_eq!(format_arr("%+04o", &[Val::Num(8.0)]).unwrap(), "+010"); + assert_eq!(format_arr("%-4o", &[Val::Num(8.0)]).unwrap(), "10 "); + assert_eq!(format_arr("%+-4o", &[Val::Num(8.0)]).unwrap(), "+10 "); + assert_eq!(format_arr("%+-04o", &[Val::Num(8.0)]).unwrap(), "+10 "); } #[test] fn percent_doesnt_consumes_values() { - let s = State::default(); assert_eq!( format_arr( - s, "How much error budget is left looking at our %.3f%% availability gurantees?", &[Val::Num(4.0)] ) --- a/crates/jrsonnet-evaluator/src/stdlib/manifest.rs +++ b/crates/jrsonnet-evaluator/src/stdlib/manifest.rs @@ -25,13 +25,12 @@ pub preserve_order: bool, } -pub fn manifest_json_ex(s: State, val: &Val, options: &ManifestJsonOptions<'_>) -> Result { +pub fn manifest_json_ex(val: &Val, options: &ManifestJsonOptions<'_>) -> Result { let mut out = String::new(); - manifest_json_ex_buf(s, val, &mut out, &mut String::new(), options)?; + manifest_json_ex_buf(val, &mut out, &mut String::new(), options)?; Ok(out) } fn manifest_json_ex_buf( - s: State, val: &Val, buf: &mut String, cur_padding: &mut String, @@ -59,7 +58,7 @@ let old_len = cur_padding.len(); cur_padding.push_str(options.padding); - for (i, item) in items.iter(s.clone()).enumerate() { + for (i, item) in items.iter().enumerate() { if i != 0 { buf.push(','); if mtype == ManifestType::ToString { @@ -69,7 +68,7 @@ } } buf.push_str(cur_padding); - manifest_json_ex_buf(s.clone(), &item?, buf, cur_padding, options)?; + manifest_json_ex_buf(&item?, buf, cur_padding, options)?; } cur_padding.truncate(old_len); @@ -86,7 +85,7 @@ buf.push(']'); } Val::Obj(obj) => { - obj.run_assertions(s.clone())?; + obj.run_assertions()?; buf.push('{'); let fields = obj.fields( #[cfg(feature = "exp-preserve-order")] @@ -114,8 +113,8 @@ State::push_description( || format!("field <{}> manifestification", field.clone()), || { - let value = obj.get(s.clone(), field.clone())?.unwrap(); - manifest_json_ex_buf(s.clone(), &value, buf, cur_padding, options)?; + let value = obj.get(field.clone())?.unwrap(); + manifest_json_ex_buf(&value, buf, cur_padding, options)?; Ok(()) }, )?; @@ -222,15 +221,14 @@ || string.parse::().is_ok() } -pub fn manifest_yaml_ex(s: State, val: &Val, options: &ManifestYamlOptions<'_>) -> Result { +pub fn manifest_yaml_ex(val: &Val, options: &ManifestYamlOptions<'_>) -> Result { let mut out = String::new(); - manifest_yaml_ex_buf(s, val, &mut out, &mut String::new(), options)?; + manifest_yaml_ex_buf(val, &mut out, &mut String::new(), options)?; Ok(out) } #[allow(clippy::too_many_lines)] fn manifest_yaml_ex_buf( - s: State, val: &Val, buf: &mut String, cur_padding: &mut String, @@ -268,7 +266,7 @@ if a.is_empty() { buf.push_str("[]"); } else { - for (i, item) in a.iter(s.clone()).enumerate() { + for (i, item) in a.iter().enumerate() { if i != 0 { buf.push('\n'); buf.push_str(cur_padding); @@ -292,7 +290,7 @@ if extra_padding { cur_padding.push_str(options.padding); } - manifest_yaml_ex_buf(s.clone(), &item, buf, cur_padding, options)?; + manifest_yaml_ex_buf(&item, buf, cur_padding, options)?; cur_padding.truncate(prev_len); } } @@ -320,7 +318,7 @@ } buf.push(':'); let prev_len = cur_padding.len(); - let item = o.get(s.clone(), key.clone())?.expect("field exists"); + let item = o.get(key.clone())?.expect("field exists"); match &item { Val::Arr(a) if !a.is_empty() => { buf.push('\n'); @@ -336,7 +334,7 @@ } _ => buf.push(' '), } - manifest_yaml_ex_buf(s.clone(), &item, buf, cur_padding, options)?; + manifest_yaml_ex_buf(&item, buf, cur_padding, options)?; cur_padding.truncate(prev_len); } } --- a/crates/jrsonnet-evaluator/src/stdlib/mod.rs +++ b/crates/jrsonnet-evaluator/src/stdlib/mod.rs @@ -9,15 +9,15 @@ pub mod format; pub mod manifest; -pub fn std_format(s: State, str: IStr, vals: Val) -> Result { +pub fn std_format(str: IStr, vals: Val) -> Result { State::push( CallLocation::native(), || format!("std.format of {str}"), || { Ok(match vals { - Val::Arr(vals) => format_arr(s.clone(), &str, &vals.evaluated(s.clone())?)?, - Val::Obj(obj) => format_obj(s.clone(), &str, &obj)?, - o => format_arr(s.clone(), &str, &[o])?, + Val::Arr(vals) => format_arr(&str, &vals.evaluated()?)?, + Val::Obj(obj) => format_obj(&str, &obj)?, + o => format_arr(&str, &[o])?, }) }, ) --- a/crates/jrsonnet-evaluator/src/trace/mod.rs +++ b/crates/jrsonnet-evaluator/src/trace/mod.rs @@ -1,11 +1,12 @@ use std::path::{Path, PathBuf}; +use jrsonnet_gcmodule::Trace; use jrsonnet_parser::{CodeLocation, Source}; use crate::{error::Error, LocError, State}; /// The way paths should be displayed -#[derive(Clone)] +#[derive(Clone, Trace)] pub enum PathResolver { /// Only filename FileName, @@ -43,7 +44,7 @@ /// Implements pretty-printing of traces #[allow(clippy::module_name_repetitions)] -pub trait TraceFormat { +pub trait TraceFormat: Trace { fn write_trace( &self, out: &mut dyn std::fmt::Write, @@ -77,6 +78,7 @@ } /// vanilla-like jsonnet formatting +#[derive(Trace)] pub struct CompactFormat { pub resolver: PathResolver, pub padding: usize, @@ -168,6 +170,7 @@ } } +#[derive(Trace)] pub struct JsFormat; impl TraceFormat for JsFormat { fn write_trace( @@ -202,6 +205,7 @@ /// rustc-like trace displaying #[cfg(feature = "explaining-traces")] +#[derive(Trace)] pub struct ExplainingFormat { pub resolver: PathResolver, } @@ -222,7 +226,7 @@ .into_iter() .next() .unwrap(); - let mut end_location = location.clone(); + let mut end_location = location; end_location.offset += 1; self.print_snippet( --- a/crates/jrsonnet-evaluator/src/typed/conversions.rs +++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs @@ -7,27 +7,27 @@ use crate::{ error::Result, - function::{FuncDesc, FuncVal}, + function::{native::NativeDesc, FuncDesc, FuncVal}, throw, typed::CheckType, val::{ArrValue, IndexableVal}, - ObjValue, ObjValueBuilder, State, Val, + ObjValue, ObjValueBuilder, Val, }; pub trait TypedObj: Typed { - fn serialize(self, s: State, out: &mut ObjValueBuilder) -> Result<()>; - fn parse(obj: &ObjValue, s: State) -> Result; - fn into_object(self, s: State) -> Result { + fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>; + fn parse(obj: &ObjValue) -> Result; + fn into_object(self) -> Result { let mut builder = ObjValueBuilder::new(); - self.serialize(s, &mut builder)?; + self.serialize(&mut builder)?; Ok(builder.build()) } } pub trait Typed: Sized { const TYPE: &'static ComplexValType; - fn into_untyped(typed: Self, s: State) -> Result; - fn from_untyped(untyped: Val, s: State) -> Result; + fn into_untyped(typed: Self) -> Result; + fn from_untyped(untyped: Val) -> Result; } macro_rules! impl_int { @@ -35,8 +35,8 @@ impl Typed for $ty { const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(Self::MIN as f64), Some(Self::MAX as f64)); - fn from_untyped(value: Val, s: State) -> Result { - ::TYPE.check(s, &value)?; + fn from_untyped(value: Val) -> Result { + ::TYPE.check(&value)?; match value { Val::Num(n) => { #[allow(clippy::float_cmp)] @@ -51,7 +51,7 @@ _ => unreachable!(), } } - fn into_untyped(value: Self, _: State) -> Result { + fn into_untyped(value: Self) -> Result { Ok(Val::Num(value as f64)) } } @@ -90,8 +90,8 @@ Some(MAX as f64), ); - fn from_untyped(value: Val, s: State) -> Result { - ::TYPE.check(s, &value)?; + fn from_untyped(value: Val) -> Result { + ::TYPE.check(&value)?; match value { Val::Num(n) => { #[allow(clippy::float_cmp)] @@ -107,7 +107,7 @@ } } - fn into_untyped(value: Self, _: State) -> Result { + fn into_untyped(value: Self) -> Result { Ok(Val::Num(value.0 as f64)) } } @@ -125,12 +125,12 @@ impl Typed for f64 { const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Num); - fn into_untyped(value: Self, _: State) -> Result { + fn into_untyped(value: Self) -> Result { Ok(Val::Num(value)) } - fn from_untyped(value: Val, s: State) -> Result { - ::TYPE.check(s, &value)?; + fn from_untyped(value: Val) -> Result { + ::TYPE.check(&value)?; match value { Val::Num(n) => Ok(n), _ => unreachable!(), @@ -142,12 +142,12 @@ impl Typed for PositiveF64 { const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(0.0), None); - fn into_untyped(value: Self, _: State) -> Result { + fn into_untyped(value: Self) -> Result { Ok(Val::Num(value.0)) } - fn from_untyped(value: Val, s: State) -> Result { - ::TYPE.check(s, &value)?; + fn from_untyped(value: Val) -> Result { + ::TYPE.check(&value)?; match value { Val::Num(n) => Ok(Self(n)), _ => unreachable!(), @@ -159,15 +159,15 @@ const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(0.0), Some(u32::MAX as f64)); - fn into_untyped(value: Self, _: State) -> Result { + fn into_untyped(value: Self) -> Result { if value > u32::MAX as Self { throw!("number is too large") } Ok(Val::Num(value as f64)) } - fn from_untyped(value: Val, s: State) -> Result { - ::TYPE.check(s, &value)?; + fn from_untyped(value: Val) -> Result { + ::TYPE.check(&value)?; match value { Val::Num(n) => { #[allow(clippy::float_cmp)] @@ -184,12 +184,12 @@ impl Typed for IStr { const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str); - fn into_untyped(value: Self, _: State) -> Result { + fn into_untyped(value: Self) -> Result { Ok(Val::Str(value)) } - fn from_untyped(value: Val, s: State) -> Result { - ::TYPE.check(s, &value)?; + fn from_untyped(value: Val) -> Result { + ::TYPE.check(&value)?; match value { Val::Str(s) => Ok(s), _ => unreachable!(), @@ -200,12 +200,12 @@ impl Typed for String { const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str); - fn into_untyped(value: Self, _: State) -> Result { + fn into_untyped(value: Self) -> Result { Ok(Val::Str(value.into())) } - fn from_untyped(value: Val, s: State) -> Result { - ::TYPE.check(s, &value)?; + fn from_untyped(value: Val) -> Result { + ::TYPE.check(&value)?; match value { Val::Str(s) => Ok(s.to_string()), _ => unreachable!(), @@ -216,12 +216,12 @@ impl Typed for char { const TYPE: &'static ComplexValType = &ComplexValType::Char; - fn into_untyped(value: Self, _: State) -> Result { + fn into_untyped(value: Self) -> Result { Ok(Val::Str(value.to_string().into())) } - fn from_untyped(value: Val, s: State) -> Result { - ::TYPE.check(s, &value)?; + fn from_untyped(value: Val) -> Result { + ::TYPE.check(&value)?; match value { Val::Str(s) => Ok(s.chars().next().unwrap()), _ => unreachable!(), @@ -235,21 +235,21 @@ { const TYPE: &'static ComplexValType = &ComplexValType::ArrayRef(T::TYPE); - fn into_untyped(value: Self, s: State) -> Result { + fn into_untyped(value: Self) -> Result { let mut o = Vec::with_capacity(value.len()); for i in value { - o.push(T::into_untyped(i, s.clone())?); + o.push(T::into_untyped(i)?); } Ok(Val::Arr(o.into())) } - fn from_untyped(value: Val, s: State) -> Result { - ::TYPE.check(s.clone(), &value)?; + fn from_untyped(value: Val) -> Result { + ::TYPE.check(&value)?; match value { Val::Arr(a) => { let mut o = Self::with_capacity(a.len()); - for i in a.iter(s.clone()) { - o.push(T::from_untyped(i?, s.clone())?); + for i in a.iter() { + o.push(T::from_untyped(i?)?); } Ok(o) } @@ -266,11 +266,11 @@ impl Typed for Any { const TYPE: &'static ComplexValType = &ComplexValType::Any; - fn into_untyped(value: Self, _: State) -> Result { + fn into_untyped(value: Self) -> Result { Ok(value.0) } - fn from_untyped(value: Val, _: State) -> Result { + fn from_untyped(value: Val) -> Result { Ok(Self(value)) } } @@ -281,14 +281,14 @@ impl Typed for VecVal { const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr); - fn into_untyped(value: Self, _: State) -> Result { + fn into_untyped(value: Self) -> Result { Ok(Val::Arr(ArrValue::Eager(value.0))) } - fn from_untyped(value: Val, s: State) -> Result { - ::TYPE.check(s.clone(), &value)?; + fn from_untyped(value: Val) -> Result { + ::TYPE.check(&value)?; match value { - Val::Arr(a) => Ok(Self(a.evaluated(s)?)), + Val::Arr(a) => Ok(Self(a.evaluated()?)), _ => unreachable!(), } } @@ -299,21 +299,21 @@ const TYPE: &'static ComplexValType = &ComplexValType::ArrayRef(&ComplexValType::BoundedNumber(Some(0.0), Some(255.0))); - fn into_untyped(value: Self, _: State) -> Result { + fn into_untyped(value: Self) -> Result { Ok(Val::Arr(ArrValue::Bytes(value))) } - fn from_untyped(value: Val, s: State) -> Result { + fn from_untyped(value: Val) -> Result { if let Val::Arr(ArrValue::Bytes(bytes)) = value { return Ok(bytes); } - ::TYPE.check(s.clone(), &value)?; + ::TYPE.check(&value)?; match value { Val::Arr(a) => { let mut out = Vec::with_capacity(a.len()); - for e in a.iter(s.clone()) { + for e in a.iter() { let r = e?; - out.push(u8::from_untyped(r, s.clone())?); + out.push(u8::from_untyped(r)?); } Ok(out.as_slice().into()) } @@ -326,18 +326,19 @@ impl Typed for M1 { const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(-1.0), Some(-1.0)); - fn into_untyped(_: Self, _: State) -> Result { + fn into_untyped(_: Self) -> Result { Ok(Val::Num(-1.0)) } - fn from_untyped(value: Val, s: State) -> Result { - ::TYPE.check(s, &value)?; + fn from_untyped(value: Val) -> Result { + ::TYPE.check(&value)?; Ok(Self) } } macro_rules! decl_either { ($($name: ident, $($id: ident)*);*) => {$( + #[derive(Clone)] pub enum $name<$($id),*> { $($id($id)),* } @@ -347,19 +348,19 @@ { const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[$($id::TYPE),*]); - fn into_untyped(value: Self, s: State) -> Result { + fn into_untyped(value: Self) -> Result { match value {$( - $name::$id(v) => $id::into_untyped(v, s) + $name::$id(v) => $id::into_untyped(v) ),*} } - fn from_untyped(value: Val, s: State) -> Result { + fn from_untyped(value: Val) -> Result { $( - if $id::TYPE.check(s.clone(), &value).is_ok() { - $id::from_untyped(value, s.clone()).map(Self::$id) + if $id::TYPE.check(&value).is_ok() { + $id::from_untyped(value).map(Self::$id) } else )* { - ::TYPE.check(s, &value)?; + ::TYPE.check(&value)?; unreachable!() } } @@ -392,12 +393,12 @@ impl Typed for ArrValue { const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr); - fn into_untyped(value: Self, _: State) -> Result { + fn into_untyped(value: Self) -> Result { Ok(Val::Arr(value)) } - fn from_untyped(value: Val, s: State) -> Result { - ::TYPE.check(s, &value)?; + fn from_untyped(value: Val) -> Result { + ::TYPE.check(&value)?; match value { Val::Arr(a) => Ok(a), _ => unreachable!(), @@ -408,12 +409,12 @@ impl Typed for FuncVal { const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func); - fn into_untyped(value: Self, _: State) -> Result { + fn into_untyped(value: Self) -> Result { Ok(Val::Func(value)) } - fn from_untyped(value: Val, s: State) -> Result { - ::TYPE.check(s, &value)?; + fn from_untyped(value: Val) -> Result { + ::TYPE.check(&value)?; match value { Val::Func(a) => Ok(a), _ => unreachable!(), @@ -424,12 +425,12 @@ impl Typed for Cc { const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func); - fn into_untyped(value: Self, _: State) -> Result { + fn into_untyped(value: Self) -> Result { Ok(Val::Func(FuncVal::Normal(value))) } - fn from_untyped(value: Val, s: State) -> Result { - ::TYPE.check(s, &value)?; + fn from_untyped(value: Val) -> Result { + ::TYPE.check(&value)?; match value { Val::Func(FuncVal::Normal(desc)) => Ok(desc), Val::Func(_) => throw!("expected normal function, not builtin"), @@ -441,12 +442,12 @@ impl Typed for ObjValue { const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Obj); - fn into_untyped(value: Self, _: State) -> Result { + fn into_untyped(value: Self) -> Result { Ok(Val::Obj(value)) } - fn from_untyped(value: Val, s: State) -> Result { - ::TYPE.check(s, &value)?; + fn from_untyped(value: Val) -> Result { + ::TYPE.check(&value)?; match value { Val::Obj(a) => Ok(a), _ => unreachable!(), @@ -457,12 +458,12 @@ impl Typed for bool { const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Bool); - fn into_untyped(value: Self, _: State) -> Result { + fn into_untyped(value: Self) -> Result { Ok(Val::Bool(value)) } - fn from_untyped(value: Val, s: State) -> Result { - ::TYPE.check(s, &value)?; + fn from_untyped(value: Val) -> Result { + ::TYPE.check(&value)?; match value { Val::Bool(a) => Ok(a), _ => unreachable!(), @@ -475,15 +476,15 @@ &ComplexValType::Simple(ValType::Str), ]); - fn into_untyped(value: Self, _: State) -> Result { + fn into_untyped(value: Self) -> Result { match value { IndexableVal::Str(s) => Ok(Val::Str(s)), IndexableVal::Arr(a) => Ok(Val::Arr(a)), } } - fn from_untyped(value: Val, s: State) -> Result { - ::TYPE.check(s, &value)?; + fn from_untyped(value: Val) -> Result { + ::TYPE.check(&value)?; value.into_indexable() } } @@ -492,12 +493,37 @@ impl Typed for Null { const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Null); - fn into_untyped(_: Self, _: State) -> Result { + fn into_untyped(_: Self) -> Result { Ok(Val::Null) } - fn from_untyped(value: Val, s: State) -> Result { - ::TYPE.check(s, &value)?; + fn from_untyped(value: Val) -> Result { + ::TYPE.check(&value)?; Ok(Self) } } + +pub struct NativeFn(D::Value); +impl Deref for NativeFn { + type Target = D::Value; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Typed for NativeFn { + const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func); + + fn into_untyped(_typed: Self) -> Result { + throw!("can only convert functions from jsonnet to native") + } + + fn from_untyped(untyped: Val) -> Result { + Ok(Self( + untyped + .as_func() + .expect("shape is checked") + .into_native::(), + )) + } +} --- a/crates/jrsonnet-evaluator/src/typed/mod.rs +++ b/crates/jrsonnet-evaluator/src/typed/mod.rs @@ -102,11 +102,11 @@ // TODO: check_fast for fast path of union type checking pub trait CheckType { - fn check(&self, s: State, value: &Val) -> Result<()>; + fn check(&self, value: &Val) -> Result<()>; } impl CheckType for ValType { - fn check(&self, _: State, value: &Val) -> Result<()> { + fn check(&self, value: &Val) -> Result<()> { let got = value.value_type(); if got != *self { let loc_error: TypeLocError = TypeError::ExpectedGot((*self).into(), got).into(); @@ -145,10 +145,10 @@ impl CheckType for ComplexValType { #[allow(clippy::too_many_lines)] - fn check(&self, s: State, value: &Val) -> Result<()> { + fn check(&self, value: &Val) -> Result<()> { match self { Self::Any => Ok(()), - Self::Simple(t) => t.check(s, value), + Self::Simple(t) => t.check(value), Self::Char => match value { Val::Str(s) if s.len() == 1 || s.chars().count() == 1 => Ok(()), v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()), @@ -167,11 +167,11 @@ } Self::Array(elem_type) => match value { Val::Arr(a) => { - for (i, item) in a.iter(s.clone()).enumerate() { + for (i, item) in a.iter().enumerate() { push_type_description( || format!("array index {i}"), || ValuePathItem::Index(i as u64), - || elem_type.check(s.clone(), &item.clone()?), + || elem_type.check(&item.clone()?), )?; } Ok(()) @@ -180,11 +180,11 @@ }, Self::ArrayRef(elem_type) => match value { Val::Arr(a) => { - for (i, item) in a.iter(s.clone()).enumerate() { + for (i, item) in a.iter().enumerate() { push_type_description( || format!("array index {i}"), || ValuePathItem::Index(i as u64), - || elem_type.check(s.clone(), &item.clone()?), + || elem_type.check(&item.clone()?), )?; } Ok(()) @@ -194,11 +194,11 @@ Self::ObjectRef(elems) => match value { Val::Obj(obj) => { for (k, v) in elems.iter() { - if let Some(got_v) = obj.get(s.clone(), (*k).into())? { + if let Some(got_v) = obj.get((*k).into())? { push_type_description( || format!("property {k}"), || ValuePathItem::Field((*k).into()), - || v.check(s.clone(), &got_v), + || v.check(&got_v), )?; } else { return Err( @@ -213,7 +213,7 @@ Self::Union(types) => { let mut errors = Vec::new(); for ty in types.iter() { - match ty.check(s.clone(), value) { + match ty.check(value) { Ok(()) => { return Ok(()); } @@ -228,7 +228,7 @@ Self::UnionRef(types) => { let mut errors = Vec::new(); for ty in types.iter() { - match ty.check(s.clone(), value) { + match ty.check(value) { Ok(()) => { return Ok(()); } @@ -242,13 +242,13 @@ } Self::Sum(types) => { for ty in types.iter() { - ty.check(s.clone(), value)?; + ty.check(value)?; } Ok(()) } Self::SumRef(types) => { for ty in types.iter() { - ty.check(s.clone(), value)?; + ty.check(value)?; } Ok(()) } --- a/crates/jrsonnet-evaluator/src/val.rs +++ b/crates/jrsonnet-evaluator/src/val.rs @@ -13,12 +13,12 @@ }, throw, typed::BoundedUsize, - ObjValue, Result, State, Unbound, WeakObjValue, + ObjValue, Result, Unbound, WeakObjValue, }; pub trait ThunkValue: Trace { type Output; - fn get(self: Box, s: State) -> Result; + fn get(self: Box) -> Result; } #[derive(Trace)] @@ -43,11 +43,11 @@ pub fn evaluated(val: T) -> Self { Self(Cc::new(RefCell::new(ThunkInner::Computed(val)))) } - pub fn force(&self, s: State) -> Result<()> { - self.evaluate(s)?; + pub fn force(&self) -> Result<()> { + self.evaluate()?; Ok(()) } - pub fn evaluate(&self, s: State) -> Result { + pub fn evaluate(&self) -> Result { match &*self.0.borrow() { ThunkInner::Computed(v) => return Ok(v.clone()), ThunkInner::Errored(e) => return Err(e.clone()), @@ -61,7 +61,7 @@ } else { unreachable!() }; - let new_value = match value.0.get(s) { + let new_value = match value.0.get() { Ok(v) => v, Err(e) => { *self.0.borrow_mut() = ThunkInner::Errored(e.clone()); @@ -94,7 +94,7 @@ } impl, T: Clone + Trace> Unbound for CachedUnbound { type Bound = T; - fn bind(&self, s: State, sup: Option, this: Option) -> Result { + fn bind(&self, sup: Option, this: Option) -> Result { let cache_key = ( sup.as_ref().map(|s| s.clone().downgrade()), this.as_ref().map(|t| t.clone().downgrade()), @@ -104,7 +104,7 @@ return Ok(t.clone()); } } - let bound = self.value.bind(s, sup, this)?; + let bound = self.value.bind(sup, this)?; { let mut cache = self.cache.borrow_mut(); @@ -126,7 +126,7 @@ } } -#[derive(Clone)] +#[derive(Clone, Trace)] pub enum ManifestFormat { YamlStream(Box), Yaml { @@ -268,14 +268,14 @@ /// Get array element by index, evaluating it, if it is lazy. /// /// Returns `None` on out-of-bounds condition. - pub fn get(&self, s: State, index: usize) -> Result> { + pub fn get(&self, index: usize) -> Result> { match self { Self::Bytes(i) => i .get(index) .map_or(Ok(None), |v| Ok(Some(Val::Num(f64::from(*v))))), Self::Lazy(vec) => { if let Some(v) = vec.get(index) { - Ok(Some(v.evaluate(s)?)) + Ok(Some(v.evaluate()?)) } else { Ok(None) } @@ -284,9 +284,9 @@ Self::Extended(v) => { let a_len = v.0.len(); if a_len > index { - v.0.get(s, index) + v.0.get(index) } else { - v.1.get(s, index - a_len) + v.1.get(index - a_len) } } Self::Range(a, _) => { @@ -300,14 +300,14 @@ if index >= len { return Ok(None); } - v.get(s, len - index - 1) + v.get(len - index - 1) } Self::Slice(v) => { let index = v.from() + index * v.step(); if index >= v.to() { return Ok(None); } - v.inner.get(s, index) + v.inner.get(index) } } } @@ -356,7 +356,7 @@ } /// Evaluate all array elements, returning new array. - pub fn evaluated(&self, s: State) -> Result>> { + pub fn evaluated(&self) -> Result>> { Ok(match self { Self::Bytes(i) => { let mut out = Vec::with_capacity(i.len()); @@ -368,14 +368,14 @@ Self::Lazy(vec) => { let mut out = Vec::with_capacity(vec.len()); for item in vec.iter() { - out.push(item.evaluate(s.clone())?); + out.push(item.evaluate()?); } Cc::new(out) } Self::Eager(vec) => vec.clone(), Self::Extended(_v) => { let mut out = Vec::with_capacity(self.len()); - for item in self.iter(s) { + for item in self.iter() { out.push(item?); } Cc::new(out) @@ -388,7 +388,7 @@ Cc::new(out) } Self::Reversed(r) => { - let mut r = r.evaluated(s)?; + let mut r = r.evaluated()?; Cc::update_with(&mut r, |v| v.reverse()); r } @@ -401,7 +401,7 @@ .take(v.to() - v.from()) .step_by(v.step()) { - out.push(v.evaluate(s.clone())?); + out.push(v.evaluate()?); } Cc::new(out) } @@ -409,13 +409,13 @@ } /// Iterate over elements, evaluating them. - pub fn iter(&self, s: State) -> impl DoubleEndedIterator> + '_ { + pub fn iter(&self) -> impl DoubleEndedIterator> + '_ { (0..self.len()).map(move |idx| match self { Self::Bytes(b) => Ok(Val::Num(f64::from(b[idx]))), - Self::Lazy(l) => l[idx].evaluate(s.clone()), + Self::Lazy(l) => l[idx].evaluate(), Self::Eager(e) => Ok(e[idx].clone()), Self::Extended(..) | Self::Range(..) | Self::Reversed(..) | Self::Slice(..) => { - self.get(s.clone(), idx).map(|e| e.expect("idx < len")) + self.get(idx).map(|e| e.expect("idx < len")) } }) } @@ -439,10 +439,10 @@ } /// Return a new array, produced by passing every element of current array to specified callback function. - pub fn map(self, s: State, mapper: impl Fn(Val) -> Result) -> Result { + pub fn map(self, mapper: impl Fn(Val) -> Result) -> Result { let mut out = Vec::with_capacity(self.len()); - for value in self.iter(s) { + for value in self.iter() { out.push(mapper(value?)?); } @@ -450,10 +450,10 @@ } /// Return a new array, produced from current array by removing every value, for which specified callback function returns false. - pub fn filter(self, s: State, filter: impl Fn(&Val) -> Result) -> Result { + pub fn filter(self, filter: impl Fn(&Val) -> Result) -> Result { let mut out = Vec::with_capacity(self.len()); - for value in self.iter(s) { + for value in self.iter() { let value = value?; if filter(&value)? { out.push(value); @@ -645,14 +645,13 @@ } } - pub fn to_string(&self, s: State) -> Result { + pub fn to_string(&self) -> Result { Ok(match self { Self::Bool(true) => "true".into(), Self::Bool(false) => "false".into(), Self::Null => "null".into(), Self::Str(s) => s.clone(), v => manifest_json_ex( - s, v, &ManifestJsonOptions { padding: "", @@ -668,7 +667,7 @@ } /// Expects value to be object, outputs (key, manifested value) pairs - pub fn manifest_multi(&self, s: State, ty: &ManifestFormat) -> Result> { + pub fn manifest_multi(&self, ty: &ManifestFormat) -> Result> { let obj = match self { Self::Obj(obj) => obj, _ => throw!(MultiManifestOutputIsNotAObject), @@ -680,28 +679,28 @@ let mut out = Vec::with_capacity(keys.len()); for key in keys { let value = obj - .get(s.clone(), key.clone())? + .get(key.clone())? .expect("item in object") - .manifest(s.clone(), ty)?; + .manifest(ty)?; out.push((key, value)); } Ok(out) } /// Expects value to be array, outputs manifested values - pub fn manifest_stream(&self, s: State, ty: &ManifestFormat) -> Result> { + pub fn manifest_stream(&self, ty: &ManifestFormat) -> Result> { let arr = match self { Self::Arr(a) => a, _ => throw!(StreamManifestOutputIsNotAArray), }; let mut out = Vec::with_capacity(arr.len()); - for i in arr.iter(s.clone()) { - out.push(i?.manifest(s.clone(), ty)?); + for i in arr.iter() { + out.push(i?.manifest(ty)?); } Ok(out) } - pub fn manifest(&self, s: State, ty: &ManifestFormat) -> Result { + pub fn manifest(&self, ty: &ManifestFormat) -> Result { Ok(match ty { ManifestFormat::YamlStream(format) => { let arr = match self { @@ -717,9 +716,9 @@ }; if !arr.is_empty() { - for v in arr.iter(s.clone()) { + for v in arr.iter() { out.push_str("---\n"); - out.push_str(&v?.manifest(s.clone(), format)?); + out.push_str(&v?.manifest(format)?); out.push('\n'); } out.push_str("..."); @@ -732,7 +731,6 @@ #[cfg(feature = "exp-preserve-order")] preserve_order, } => self.to_yaml( - s, *padding, #[cfg(feature = "exp-preserve-order")] *preserve_order, @@ -742,12 +740,11 @@ #[cfg(feature = "exp-preserve-order")] preserve_order, } => self.to_json( - s, *padding, #[cfg(feature = "exp-preserve-order")] *preserve_order, )?, - ManifestFormat::ToString => self.to_string(s)?, + ManifestFormat::ToString => self.to_string()?, ManifestFormat::String => match self { Self::Str(s) => s.clone(), _ => throw!(StringManifestOutputIsNotAString), @@ -758,12 +755,10 @@ /// For manifestification pub fn to_json( &self, - s: State, padding: usize, #[cfg(feature = "exp-preserve-order")] preserve_order: bool, ) -> Result { manifest_json_ex( - s, self, &ManifestJsonOptions { padding: &" ".repeat(padding), @@ -784,12 +779,10 @@ /// Calls `std.manifestJson` pub fn to_std_json( &self, - s: State, padding: usize, #[cfg(feature = "exp-preserve-order")] preserve_order: bool, ) -> Result> { manifest_json_ex( - s, self, &ManifestJsonOptions { padding: &" ".repeat(padding), @@ -805,13 +798,11 @@ pub fn to_yaml( &self, - s: State, padding: usize, #[cfg(feature = "exp-preserve-order")] preserve_order: bool, ) -> Result { let padding = &" ".repeat(padding); manifest_yaml_ex( - s, self, &ManifestYamlOptions { padding, @@ -857,7 +848,7 @@ } /// Native implementation of `std.equals` -pub fn equals(s: State, val_a: &Val, val_b: &Val) -> Result { +pub fn equals(val_a: &Val, val_b: &Val) -> Result { if val_a.value_type() != val_b.value_type() { return Ok(false); } @@ -869,8 +860,8 @@ if a.len() != b.len() { return Ok(false); } - for (a, b) in a.iter(s.clone()).zip(b.iter(s.clone())) { - if !equals(s.clone(), &a?, &b?)? { + for (a, b) in a.iter().zip(b.iter()) { + if !equals(&a?, &b?)? { return Ok(false); } } @@ -893,9 +884,8 @@ } for field in fields { if !equals( - s.clone(), - &a.get(s.clone(), field.clone())?.expect("field exists"), - &b.get(s.clone(), field)?.expect("field exists"), + &a.get(field.clone())?.expect("field exists"), + &b.get(field)?.expect("field exists"), )? { return Ok(false); } --- a/crates/jrsonnet-interner/src/lib.rs +++ b/crates/jrsonnet-interner/src/lib.rs @@ -4,6 +4,7 @@ clippy::undocumented_unsafe_blocks )] #![warn(clippy::pedantic, clippy::nursery)] +#![allow(clippy::missing_const_for_fn)] use std::{ borrow::Cow, cell::RefCell, --- a/crates/jrsonnet-macros/src/lib.rs +++ b/crates/jrsonnet-macros/src/lib.rs @@ -130,7 +130,7 @@ is_option: bool, name: Option, }, - State, + Context, Location, This, } @@ -146,8 +146,8 @@ _ => None, }; let ty = &arg.ty; - if type_is_path(ty, "State").is_some() { - return Ok(Self::State); + if type_is_path(ty, "Context").is_some() { + return Ok(Self::Context); } else if type_is_path(ty, "CallLocation").is_some() { return Ok(Self::Location); } else if type_is_path(ty, "Thunk").is_some() { @@ -273,7 +273,7 @@ }, }) } - ArgInfo::State => None, + ArgInfo::Context => None, ArgInfo::Location => None, ArgInfo::This => None, }); @@ -287,7 +287,7 @@ id += 1; (quote! {#cid}, a) } - ArgInfo::State | ArgInfo::Location | ArgInfo::This => { + ArgInfo::Context | ArgInfo::Location | ArgInfo::This => { (quote! {compile_error!("should not use id")}, a) } }) @@ -301,7 +301,7 @@ let name = name.as_ref().map(|v| v.as_str()).unwrap_or(""); let eval = quote! {jrsonnet_evaluator::State::push_description( || format!("argument <{}> evaluation", #name), - || <#ty>::from_untyped(value.evaluate(s.clone())?, s.clone()), + || <#ty>::from_untyped(value.evaluate()?), )?}; let value = if *is_option { quote! {if let Some(value) = &parsed[#id] { @@ -333,7 +333,7 @@ } } } - ArgInfo::State => quote! {s.clone(),}, + ArgInfo::Context => quote! {ctx.clone(),}, ArgInfo::Location => quote! {location,}, ArgInfo::This => quote! {self,}, }); @@ -394,12 +394,12 @@ fn params(&self) -> &[BuiltinParam] { PARAMS } - fn call(&self, s: State, ctx: Context, location: CallLocation, args: &dyn ArgsLike) -> Result { - let parsed = parse_builtin_call(s.clone(), ctx, &PARAMS, args, false)?; + fn call(&self, ctx: Context, location: CallLocation, args: &dyn ArgsLike) -> Result { + let parsed = parse_builtin_call(ctx.clone(), &PARAMS, args, false)?; let result: #result = #name(#(#pass)*); let result = result?; - <#result_inner>::into_untyped(result, s) + <#result_inner>::into_untyped(result) } } }; @@ -535,11 +535,11 @@ // optional flatten is handled in same way as serde return if self.is_option { quote! { - #ident: <#ty>::parse(&obj, s.clone()).ok(), + #ident: <#ty>::parse(&obj).ok(), } } else { quote! { - #ident: <#ty>::parse(&obj, s.clone())?, + #ident: <#ty>::parse(&obj)?, } }; }; @@ -547,15 +547,15 @@ let name = self.name().unwrap(); let value = if self.is_option { quote! { - if let Some(value) = obj.get(s.clone(), #name.into())? { - Some(<#ty>::from_untyped(value, s.clone())?) + if let Some(value) = obj.get(#name.into())? { + Some(<#ty>::from_untyped(value)?) } else { None } } } else { quote! { - <#ty>::from_untyped(obj.get(s.clone(), #name.into())?.ok_or_else(|| Error::NoSuchField(#name.into(), vec![]))?, s.clone())? + <#ty>::from_untyped(obj.get(#name.into())?.ok_or_else(|| Error::NoSuchField(#name.into(), vec![]))?)? } }; @@ -570,23 +570,23 @@ if self.is_option { quote! { if let Some(value) = self.#ident { - out.member(#name.into()).value(s.clone(), <#ty>::into_untyped(value, s.clone())?)?; + out.member(#name.into()).value(<#ty>::into_untyped(value)?)?; } } } else { quote! { - out.member(#name.into()).value(s.clone(), <#ty>::into_untyped(self.#ident, s.clone())?)?; + out.member(#name.into()).value(<#ty>::into_untyped(self.#ident)?)?; } } } else if self.is_option { quote! { if let Some(value) = self.#ident { - value.serialize(s.clone(), out)?; + value.serialize(out)?; } } } else { quote! { - self.#ident.serialize(s.clone(), out)?; + self.#ident.serialize(out)?; } }) } @@ -628,14 +628,14 @@ impl Typed for #ident { const TYPE: &'static ComplexValType = &ComplexValType::ObjectRef(&ITEMS); - fn from_untyped(value: Val, s: State) -> Result { + fn from_untyped(value: Val) -> Result { let obj = value.as_obj().expect("shape is correct"); - Self::parse(&obj, s) + Self::parse(&obj) } - fn into_untyped(value: Self, s: State) -> Result { + fn into_untyped(value: Self) -> Result { let mut out = ObjValueBuilder::new(); - value.serialize(s, &mut out)?; + value.serialize(&mut out)?; Ok(Val::Obj(out.build())) } @@ -661,12 +661,12 @@ #typed impl TypedObj for #ident { - fn serialize(self, s: State, out: &mut ObjValueBuilder) -> Result<(), LocError> { + fn serialize(self, out: &mut ObjValueBuilder) -> Result<(), LocError> { #(#fields_serialize)* Ok(()) } - fn parse(obj: &ObjValue, s: State) -> Result { + fn parse(obj: &ObjValue) -> Result { Ok(Self { #(#fields_parse)* }) --- a/crates/jrsonnet-parser/src/location.rs +++ b/crates/jrsonnet-parser/src/location.rs @@ -1,5 +1,5 @@ #[allow(clippy::module_name_repetitions)] -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] pub struct CodeLocation { pub offset: usize, @@ -24,9 +24,9 @@ } #[allow(clippy::module_name_repetitions)] -pub fn offset_to_location(file: &str, offsets: &[u32]) -> Vec { +pub fn offset_to_location(file: &str, offsets: &[u32; S]) -> [CodeLocation; S] { if offsets.is_empty() { - return vec![]; + return [CodeLocation::default(); S]; } let mut line = 1; let mut column = 1; @@ -40,16 +40,7 @@ offset_map.sort_by_key(|v| v.0); offset_map.reverse(); - let mut out = vec![ - CodeLocation { - offset: 0, - column: 0, - line: 0, - line_start_offset: 0, - line_end_offset: 0 - }; - offsets.len() - ]; + let mut out = [CodeLocation::default(); S]; let mut with_no_known_line_ending = vec![]; let mut this_line_offset = 0; for (pos, ch) in file @@ -103,7 +94,7 @@ "hello world\n_______________________________________________________", &[0, 14] ), - vec![ + [ CodeLocation { offset: 0, line: 1, --- a/crates/jrsonnet-parser/src/source.rs +++ b/crates/jrsonnet-parser/src/source.rs @@ -289,7 +289,7 @@ &self.0 .0 } - pub fn map_source_locations(&self, locs: &[u32]) -> Vec { + pub fn map_source_locations(&self, locs: &[u32; S]) -> [CodeLocation; S] { offset_to_location(&self.0 .1, locs) } pub fn map_from_source_location(&self, line: usize, column: usize) -> Option { --- a/crates/jrsonnet-stdlib/src/arrays.rs +++ b/crates/jrsonnet-stdlib/src/arrays.rs @@ -2,17 +2,17 @@ error::Result, function::{builtin, FuncVal}, throw, - typed::{Any, BoundedUsize, Typed, VecVal}, + typed::{Any, BoundedUsize, Either2, NativeFn, Typed, VecVal}, val::{equals, ArrValue, IndexableVal}, - IStr, State, Val, + Either, IStr, Val, }; use jrsonnet_gcmodule::Cc; #[builtin] -pub fn builtin_make_array(s: State, sz: usize, func: FuncVal) -> Result { +pub fn builtin_make_array(sz: usize, func: NativeFn<((f64,), Any)>) -> Result { let mut out = Vec::with_capacity(sz); for i in 0..sz { - out.push(func.evaluate_simple(s.clone(), &(i as f64,))?); + out.push(func(i as f64)?.0); } Ok(VecVal(Cc::new(out))) } @@ -28,19 +28,20 @@ } #[builtin] -pub fn builtin_map(s: State, func: FuncVal, arr: ArrValue) -> Result { - arr.map(s.clone(), |val| { - func.evaluate_simple(s.clone(), &(Any(val),)) - }) +pub fn builtin_map(func: NativeFn<((Any,), Any)>, arr: ArrValue) -> Result { + arr.map(|val| Ok(func(Any(val))?.0)) } #[builtin] -pub fn builtin_flatmap(s: State, func: FuncVal, arr: IndexableVal) -> Result { +pub fn builtin_flatmap( + func: NativeFn<((Either![String, Any],), Any)>, + arr: IndexableVal, +) -> Result { match arr { IndexableVal::Str(str) => { let mut out = String::new(); for c in str.chars() { - match func.evaluate_simple(s.clone(), &(c.to_string(),))? { + match func(Either2::A(c.to_string()))?.0 { Val::Str(o) => out.push_str(&o), Val::Null => continue, _ => throw!("in std.join all items should be strings"), @@ -50,11 +51,11 @@ } IndexableVal::Arr(a) => { let mut out = Vec::new(); - for el in a.iter(s.clone()) { + for el in a.iter() { let el = el?; - match func.evaluate_simple(s.clone(), &(Any(el),))? { + match func(Either2::B(Any(el)))?.0 { Val::Arr(o) => { - for oe in o.iter(s.clone()) { + for oe in o.iter() { out.push(oe?); } } @@ -68,29 +69,24 @@ } #[builtin] -pub fn builtin_filter(s: State, func: FuncVal, arr: ArrValue) -> Result { - arr.filter(s.clone(), |val| { - bool::from_untyped( - func.evaluate_simple(s.clone(), &(Any(val.clone()),))?, - s.clone(), - ) - }) +pub fn builtin_filter(func: FuncVal, arr: ArrValue) -> Result { + arr.filter(|val| bool::from_untyped(func.evaluate_simple(&(Any(val.clone()),))?)) } #[builtin] -pub fn builtin_foldl(s: State, func: FuncVal, arr: ArrValue, init: Any) -> Result { +pub fn builtin_foldl(func: FuncVal, arr: ArrValue, init: Any) -> Result { let mut acc = init.0; - for i in arr.iter(s.clone()) { - acc = func.evaluate_simple(s.clone(), &(Any(acc), Any(i?)))?; + for i in arr.iter() { + acc = func.evaluate_simple(&(Any(acc), Any(i?)))?; } Ok(Any(acc)) } #[builtin] -pub fn builtin_foldr(s: State, func: FuncVal, arr: ArrValue, init: Any) -> Result { +pub fn builtin_foldr(func: FuncVal, arr: ArrValue, init: Any) -> Result { let mut acc = init.0; - for i in arr.iter(s.clone()).rev() { - acc = func.evaluate_simple(s.clone(), &(Any(i?), Any(acc)))?; + for i in arr.iter().rev() { + acc = func.evaluate_simple(&(Any(i?), Any(acc)))?; } Ok(Any(acc)) } @@ -104,25 +100,25 @@ } #[builtin] -pub fn builtin_join(s: State, sep: IndexableVal, arr: ArrValue) -> Result { +pub fn builtin_join(sep: IndexableVal, arr: ArrValue) -> Result { Ok(match sep { IndexableVal::Arr(joiner_items) => { let mut out = Vec::new(); let mut first = true; - for item in arr.iter(s.clone()) { + for item in arr.iter() { let item = item?.clone(); if let Val::Arr(items) = item { if !first { out.reserve(joiner_items.len()); // TODO: extend - for item in joiner_items.iter(s.clone()) { + for item in joiner_items.iter() { out.push(item?); } } first = false; out.reserve(items.len()); - for item in items.iter(s.clone()) { + for item in items.iter() { out.push(item?); } } else if matches!(item, Val::Null) { @@ -138,7 +134,7 @@ let mut out = String::new(); let mut first = true; - for item in arr.iter(s) { + for item in arr.iter() { let item = item?.clone(); if let Val::Str(item) = item { if !first { @@ -164,9 +160,9 @@ } #[builtin] -pub fn builtin_any(s: State, arr: ArrValue) -> Result { - for v in arr.iter(s.clone()) { - let v = bool::from_untyped(v?, s.clone())?; +pub fn builtin_any(arr: ArrValue) -> Result { + for v in arr.iter() { + let v = bool::from_untyped(v?)?; if v { return Ok(true); } @@ -175,9 +171,9 @@ } #[builtin] -pub fn builtin_all(s: State, arr: ArrValue) -> Result { - for v in arr.iter(s.clone()) { - let v = bool::from_untyped(v?, s.clone())?; +pub fn builtin_all(arr: ArrValue) -> Result { + for v in arr.iter() { + let v = bool::from_untyped(v?)?; if !v { return Ok(false); } @@ -186,16 +182,16 @@ } #[builtin] -pub fn builtin_member(s: State, arr: IndexableVal, x: Any) -> Result { +pub fn builtin_member(arr: IndexableVal, x: Any) -> Result { match arr { IndexableVal::Str(str) => { - let x: IStr = IStr::from_untyped(x.0, s)?; + let x: IStr = IStr::from_untyped(x.0)?; Ok(!x.is_empty() && str.contains(&*x)) } IndexableVal::Arr(a) => { - for item in a.iter(s.clone()) { + for item in a.iter() { let item = item?; - if equals(s.clone(), &item, &x.0)? { + if equals(&item, &x.0)? { return Ok(true); } } @@ -205,10 +201,10 @@ } #[builtin] -pub fn builtin_count(s: State, arr: Vec, v: Any) -> Result { +pub fn builtin_count(arr: Vec, v: Any) -> Result { let mut count = 0; for item in &arr { - if equals(s.clone(), &item.0, &v.0)? { + if equals(&item.0, &v.0)? { count += 1; } } --- a/crates/jrsonnet-stdlib/src/lib.rs +++ b/crates/jrsonnet-stdlib/src/lib.rs @@ -12,7 +12,7 @@ trace::PathResolver, Context, ContextBuilder, IStr, ObjValue, ObjValueBuilder, State, Thunk, Val, }; -use jrsonnet_gcmodule::Cc; +use jrsonnet_gcmodule::{Cc, Trace}; use jrsonnet_parser::Source; mod expr; @@ -41,11 +41,11 @@ mod misc; pub use misc::*; -pub fn stdlib_uncached(s: State, settings: Rc>) -> ObjValue { +pub fn stdlib_uncached(settings: Rc>) -> ObjValue { let mut builder = ObjValueBuilder::new(); let expr = expr::stdlib_expr(); - let eval = jrsonnet_evaluator::evaluate(s.clone(), Context::default(), &expr) + let eval = jrsonnet_evaluator::evaluate(ContextBuilder::dangerous_empty_state().build(), &expr) .expect("stdlib.jsonnet should have no errors") .as_obj() .expect("stdlib.jsonnet should evaluate to object"); @@ -173,7 +173,7 @@ } pub trait TracePrinter { - fn print_trace(&self, s: State, loc: CallLocation, value: IStr); + fn print_trace(&self, loc: CallLocation, value: IStr); } pub struct StdTracePrinter { @@ -185,7 +185,7 @@ } } impl TracePrinter for StdTracePrinter { - fn print_trace(&self, _s: State, loc: CallLocation, value: IStr) { + fn print_trace(&self, loc: CallLocation, value: IStr) { eprint!("TRACE:"); if let Some(loc) = loc.0 { let locs = loc.0.map_source_locations(&[loc.1]); @@ -220,6 +220,7 @@ Source::new_virtual(source_name.into(), code.into()) } +#[derive(Trace)] pub struct ContextInitializer { // When we don't need to support legacy-this-file, we can reuse same context for all files #[cfg(not(feature = "legacy-this-file"))] @@ -242,10 +243,10 @@ Self { #[cfg(not(feature = "legacy-this-file"))] context: { - let mut context = ContextBuilder::with_capacity(1); + let mut context = ContextBuilder::with_capacity(s, 1); context.bind( "std".into(), - Thunk::evaluated(Val::Obj(stdlib_uncached(s, settings.clone()))), + Thunk::evaluated(Val::Obj(stdlib_uncached(settings.clone()))), ); context.build() }, @@ -349,7 +350,7 @@ impl StateExt for State { fn with_stdlib(&self) { let initializer = ContextInitializer::new(self.clone(), PathResolver::new_cwd_fallback()); - self.settings_mut().context_initializer = Box::new(initializer) + self.settings_mut().context_initializer = tb!(initializer) } fn add_global(&self, name: IStr, value: Thunk) { self.settings() --- a/crates/jrsonnet-stdlib/src/manifest.rs +++ b/crates/jrsonnet-stdlib/src/manifest.rs @@ -6,7 +6,7 @@ ManifestYamlOptions, }, typed::Any, - IStr, State, + IStr, }; #[builtin] @@ -16,7 +16,6 @@ #[builtin] pub fn builtin_manifest_json_ex( - s: State, value: Any, indent: IStr, newline: Option, @@ -26,7 +25,6 @@ let newline = newline.as_deref().unwrap_or("\n"); let key_val_sep = key_val_sep.as_deref().unwrap_or(": "); manifest_json_ex( - s, &value.0, &ManifestJsonOptions { padding: &indent, @@ -41,14 +39,12 @@ #[builtin] pub fn builtin_manifest_yaml_doc( - s: State, value: Any, indent_array_in_object: Option, quote_keys: Option, #[cfg(feature = "exp-preserve-order")] preserve_order: Option, ) -> Result { manifest_yaml_ex( - s, &value.0, &ManifestYamlOptions { padding: " ", --- a/crates/jrsonnet-stdlib/src/misc.rs +++ b/crates/jrsonnet-stdlib/src/misc.rs @@ -6,7 +6,7 @@ throw, typed::{Any, Either2, Either4}, val::{equals, ArrValue}, - Either, IStr, ObjValue, State, Thunk, Val, + Context, Either, IStr, ObjValue, Thunk, Val, }; use crate::{extvar_source, Settings}; @@ -25,8 +25,8 @@ #[builtin(fields( settings: Rc>, ))] -pub fn builtin_ext_var(this: &builtin_ext_var, s: State, x: IStr) -> Result { - let ctx = s.create_default_context(extvar_source(&x, "")); +pub fn builtin_ext_var(this: &builtin_ext_var, ctx: Context, x: IStr) -> Result { + let ctx = ctx.state().create_default_context(extvar_source(&x, "")); Ok(Any(this .settings .borrow() @@ -34,8 +34,8 @@ .get(&x) .cloned() .ok_or_else(|| UndefinedExternalVariable(x))? - .evaluate_arg(s.clone(), ctx, true)? - .evaluate(s)?)) + .evaluate_arg(ctx, true)? + .evaluate()?)) } #[builtin(fields( @@ -58,41 +58,29 @@ ))] pub fn builtin_trace( this: &builtin_trace, - s: State, loc: CallLocation, str: IStr, rest: Thunk, ) -> Result { - this.settings - .borrow() - .trace_printer - .print_trace(s.clone(), loc, str); - Ok(Any(rest.evaluate(s)?)) + this.settings.borrow().trace_printer.print_trace(loc, str); + Ok(Any(rest.evaluate()?)) } #[allow(clippy::comparison_chain)] #[builtin] -pub fn builtin_starts_with( - s: State, - a: Either![IStr, ArrValue], - b: Either![IStr, ArrValue], -) -> Result { +pub fn builtin_starts_with(a: Either![IStr, ArrValue], b: Either![IStr, ArrValue]) -> Result { Ok(match (a, b) { (Either2::A(a), Either2::A(b)) => a.starts_with(b.as_str()), (Either2::B(a), Either2::B(b)) => { if b.len() > a.len() { return Ok(false); } else if b.len() == a.len() { - return equals(s, &Val::Arr(a), &Val::Arr(b)); + return equals(&Val::Arr(a), &Val::Arr(b)); } else { - for (a, b) in a - .slice(None, Some(b.len()), None) - .iter(s.clone()) - .zip(b.iter(s.clone())) - { + for (a, b) in a.slice(None, Some(b.len()), None).iter().zip(b.iter()) { let a = a?; let b = b?; - if !equals(s.clone(), &a, &b)? { + if !equals(&a, &b)? { return Ok(false); } } @@ -105,28 +93,24 @@ #[allow(clippy::comparison_chain)] #[builtin] -pub fn builtin_ends_with( - s: State, - a: Either![IStr, ArrValue], - b: Either![IStr, ArrValue], -) -> Result { +pub fn builtin_ends_with(a: Either![IStr, ArrValue], b: Either![IStr, ArrValue]) -> Result { Ok(match (a, b) { (Either2::A(a), Either2::A(b)) => a.ends_with(b.as_str()), (Either2::B(a), Either2::B(b)) => { if b.len() > a.len() { return Ok(false); } else if b.len() == a.len() { - return equals(s, &Val::Arr(a), &Val::Arr(b)); + return equals(&Val::Arr(a), &Val::Arr(b)); } else { let a_len = a.len(); for (a, b) in a .slice(Some(a_len - b.len()), None, None) - .iter(s.clone()) - .zip(b.iter(s.clone())) + .iter() + .zip(b.iter()) { let a = a?; let b = b?; - if !equals(s.clone(), &a, &b)? { + if !equals(&a, &b)? { return Ok(false); } } --- a/crates/jrsonnet-stdlib/src/operator.rs +++ b/crates/jrsonnet-stdlib/src/operator.rs @@ -8,14 +8,13 @@ stdlib::std_format, typed::{Any, Either, Either2}, val::{equals, primitive_equals}, - IStr, State, Val, + IStr, Val, }; #[builtin] -pub fn builtin_mod(s: State, a: Either![f64, IStr], b: Any) -> Result { +pub fn builtin_mod(a: Either![f64, IStr], b: Any) -> Result { use Either2::*; Ok(Any(evaluate_mod_op( - s, &match a { A(v) => Val::Num(v), B(s) => Val::Str(s), @@ -30,11 +29,11 @@ } #[builtin] -pub fn builtin_equals(s: State, a: Any, b: Any) -> Result { - equals(s, &a.0, &b.0) +pub fn builtin_equals(a: Any, b: Any) -> Result { + equals(&a.0, &b.0) } #[builtin] -pub fn builtin_format(s: State, str: IStr, vals: Any) -> Result { - std_format(s, str, vals.0) +pub fn builtin_format(str: IStr, vals: Any) -> Result { + std_format(str, vals.0) } --- a/crates/jrsonnet-stdlib/src/sort.rs +++ b/crates/jrsonnet-stdlib/src/sort.rs @@ -1,10 +1,10 @@ use jrsonnet_evaluator::{ error::Result, - function::{builtin, FuncVal}, + function::{builtin, CallLocation, FuncVal}, throw, typed::Any, val::ArrValue, - State, Val, + Context, Val, }; use jrsonnet_gcmodule::Cc; @@ -50,7 +50,7 @@ } /// * `key_getter` - None, if identity sort required -pub fn sort(s: State, values: Cc>, key_getter: FuncVal) -> Result>> { +pub fn sort(ctx: Context, values: Cc>, key_getter: FuncVal) -> Result>> { if values.len() <= 1 { return Ok(values); } @@ -76,7 +76,12 @@ for value in values.iter() { vk.push(( value.clone(), - key_getter.evaluate_simple(s.clone(), &(Any(value.clone()),))?, + key_getter.evaluate( + ctx.clone(), + CallLocation::native(), + &(Any(value.clone()),), + true, + )?, )); } let sort_type = get_sort_type(&mut vk, |v| &mut v.1)?; @@ -97,13 +102,13 @@ #[builtin] #[allow(non_snake_case)] -pub fn builtin_sort(s: State, arr: ArrValue, keyF: Option) -> Result { +pub fn builtin_sort(ctx: Context, arr: ArrValue, keyF: Option) -> Result { if arr.len() <= 1 { return Ok(arr); } Ok(ArrValue::Eager(super::sort::sort( - s.clone(), - arr.evaluated(s)?, + ctx, + arr.evaluated()?, keyF.unwrap_or_else(FuncVal::identity), )?)) } --- /dev/null +++ b/flake.lock @@ -0,0 +1,93 @@ +{ + "nodes": { + "flake-utils": { + "locked": { + "lastModified": 1659877975, + "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "locked": { + "lastModified": 1659877975, + "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1666714275, + "narHash": "sha256-5Ba4rKMqjQ9e7K6eqdwfWuhQU4HO+8an90WQ3BaiFQM=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "b410bee3cdea9365afcd9f342839d5b6e51c2df4", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1665296151, + "narHash": "sha256-uOB0oxqxN9K7XGF1hcnY+PQnlQJ+3bP2vCn/+Ru/bbc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "14ccaaedd95a488dd7ae142757884d8e125b3363", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": "flake-utils_2", + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1666667027, + "narHash": "sha256-MUJa0T8j5cy3eE70hoL1KW52VfTcXm4VbwvdF5scs1g=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "79b6e66bb76537c96707703f08630765e46148d1", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} --- a/flake.nix +++ b/flake.nix @@ -21,6 +21,8 @@ nativeBuildInputs = with pkgs;[ rust cargo-edit + hyperfine + go-jsonnet ]; }; } --- a/tests/tests/as_native.rs +++ b/tests/tests/as_native.rs @@ -13,8 +13,8 @@ let native = func.into_native::<((u32, u32), u32)>(); - ensure_eq!(native(s.clone(), 1, 2)?, 3); - ensure_eq!(native(s, 3, 4)?, 7); + ensure_eq!(native(1, 2)?, 3); + ensure_eq!(native(3, 4)?, 7); Ok(()) } --- a/tests/tests/builtin.rs +++ b/tests/tests/builtin.rs @@ -5,7 +5,7 @@ function::{builtin, builtin::Builtin, CallLocation, FuncVal}, tb, typed::Typed, - Context, State, Thunk, Val, + ContextBuilder, State, Thunk, Val, }; use jrsonnet_gcmodule::Cc; use jrsonnet_stdlib::StateExt; @@ -17,12 +17,12 @@ #[test] fn basic_function() -> Result<()> { - let s = State::default(); let a: a = a {}; - let v = u32::from_untyped( - a.call(s.clone(), Context::new(), CallLocation::native(), &())?, - s, - )?; + let v = u32::from_untyped(a.call( + ContextBuilder::dangerous_empty_state().build(), + CallLocation::native(), + &(), + )?)?; ensure_eq!(v, 1); Ok(()) @@ -50,7 +50,7 @@ null ", )?; - ensure_val_eq!(s, v, Val::Null); + ensure_val_eq!(v, Val::Null); Ok(()) } @@ -89,6 +89,6 @@ null ", )?; - ensure_val_eq!(s, v, Val::Null); + ensure_val_eq!(v, Val::Null); Ok(()) } --- a/tests/tests/common.rs +++ b/tests/tests/common.rs @@ -27,18 +27,16 @@ #[macro_export] macro_rules! ensure_val_eq { - ($s:expr, $a:expr, $b:expr) => {{ - if !::jrsonnet_evaluator::val::equals($s.clone(), &$a.clone(), &$b.clone())? { + ($a:expr, $b:expr) => {{ + if !::jrsonnet_evaluator::val::equals(&$a.clone(), &$b.clone())? { ::jrsonnet_evaluator::throw!( "assertion failed: a != b\na={:#?}\nb={:#?}", $a.to_json( - $s.clone(), 2, #[cfg(feature = "exp-preserve-order")] false )?, $b.to_json( - $s.clone(), 2, #[cfg(feature = "exp-preserve-order")] false @@ -49,8 +47,8 @@ } #[builtin] -fn assert_throw(s: State, lazy: Thunk, message: String) -> Result { - match lazy.evaluate(s) { +fn assert_throw(lazy: Thunk, message: String) -> Result { + match lazy.evaluate() { Ok(_) => { throw!("expected argument to throw on evaluation, but it returned instead") } @@ -67,10 +65,7 @@ let mut bobj = ObjValueBuilder::new(); bobj.member("assertThrow".into()) .hide() - .value( - s.clone(), - Val::Func(FuncVal::StaticBuiltin(assert_throw::INST)), - ) + .value(Val::Func(FuncVal::StaticBuiltin(assert_throw::INST))) .expect("no error"); s.add_global("test".into(), Thunk::evaluated(Val::Obj(bobj.build()))) --- a/tests/tests/golden.rs +++ b/tests/tests/golden.rs @@ -13,10 +13,10 @@ fn run(root: &Path, file: &Path) -> String { let s = State::default(); - s.set_trace_format(Box::new(CompactFormat { + s.set_trace_format(CompactFormat { resolver: PathResolver::Relative(root.to_owned()), padding: 3, - })); + }); s.with_stdlib(); common::with_test(&s); s.set_import_resolver(Box::new(FileImportResolver::default())); @@ -26,7 +26,6 @@ Err(e) => return s.stringify_err(&e), }; match v.to_json( - s.clone(), 3, #[cfg(feature = "exp-preserve-order")] false, --- a/tests/tests/sanity.rs +++ b/tests/tests/sanity.rs @@ -9,9 +9,9 @@ s.with_stdlib(); let v = s.evaluate_snippet("snip".to_owned(), "assert 1 == 1: 'fail'; null")?; - ensure_val_eq!(s, v, Val::Null); + ensure_val_eq!(v, Val::Null); let v = s.evaluate_snippet("snip".to_owned(), "std.assertEqual(1, 1)")?; - ensure_val_eq!(s, v, Val::Bool(true)); + ensure_val_eq!(v, Val::Bool(true)); Ok(()) } --- a/tests/tests/suite.rs +++ b/tests/tests/suite.rs @@ -13,10 +13,10 @@ fn run(root: &Path, file: &Path) { let s = State::default(); - s.set_trace_format(Box::new(CompactFormat { + s.set_trace_format(CompactFormat { resolver: PathResolver::Relative(root.to_owned()), padding: 3, - })); + }); s.with_stdlib(); common::with_test(&s); s.set_import_resolver(Box::new(FileImportResolver::default())); --- a/tests/tests/typed_obj.rs +++ b/tests/tests/typed_obj.rs @@ -11,12 +11,12 @@ b: u16, } -fn test_roundtrip(value: T, s: State) -> Result<()> { - let untyped = T::into_untyped(value.clone(), s.clone())?; - let value2 = T::from_untyped(untyped.clone(), s.clone())?; +fn test_roundtrip(value: T) -> Result<()> { + let untyped = T::into_untyped(value.clone())?; + let value2 = T::from_untyped(untyped.clone())?; ensure_eq!(value, value2); - let untyped2 = T::into_untyped(value2, s.clone())?; - ensure_val_eq!(s, untyped, untyped2); + let untyped2 = T::into_untyped(value2)?; + ensure_val_eq!(untyped, untyped2); Ok(()) } @@ -25,12 +25,9 @@ fn simple_object() -> Result<()> { let s = State::default(); s.with_stdlib(); - let a = A::from_untyped( - s.evaluate_snippet("snip".to_owned(), "{a: 1, b: 2}")?, - s.clone(), - )?; + let a = A::from_untyped(s.evaluate_snippet("snip".to_owned(), "{a: 1, b: 2}")?)?; ensure_eq!(a, A { a: 1, b: 2 }); - test_roundtrip(a, s)?; + test_roundtrip(a)?; Ok(()) } @@ -45,16 +42,13 @@ fn renamed_field() -> Result<()> { let s = State::default(); s.with_stdlib(); - let b = B::from_untyped( - s.evaluate_snippet("snip".to_owned(), "{a: 1, c: 2}")?, - s.clone(), - )?; + let b = B::from_untyped(s.evaluate_snippet("snip".to_owned(), "{a: 1, c: 2}")?)?; ensure_eq!(b, B { a: 1, b: 2 }); ensure_eq!( - &B::into_untyped(b.clone(), s.clone())?.to_string(s.clone())? as &str, + &B::into_untyped(b.clone())?.to_string()? as &str, r#"{"a": 1, "c": 2}"#, ); - test_roundtrip(b, s)?; + test_roundtrip(b)?; Ok(()) } @@ -79,7 +73,6 @@ s.with_stdlib(); let obj = Object::from_untyped( s.evaluate_snippet("snip".to_owned(), "{apiVersion: 'ver', kind: 'kind', b: 2}")?, - s.clone(), )?; ensure_eq!( obj, @@ -92,10 +85,10 @@ } ); ensure_eq!( - &Object::into_untyped(obj.clone(), s.clone())?.to_string(s.clone())? as &str, + &Object::into_untyped(obj.clone())?.to_string()? as &str, r#"{"apiVersion": "ver", "b": 2, "kind": "kind"}"#, ); - test_roundtrip(obj, s)?; + test_roundtrip(obj)?; Ok(()) } @@ -109,16 +102,13 @@ fn optional_field_some() -> Result<()> { let s = State::default(); s.with_stdlib(); - let c = C::from_untyped( - s.evaluate_snippet("snip".to_owned(), "{a: 1, b: 2}")?, - s.clone(), - )?; + let c = C::from_untyped(s.evaluate_snippet("snip".to_owned(), "{a: 1, b: 2}")?)?; ensure_eq!(c, C { a: Some(1), b: 2 }); ensure_eq!( - &C::into_untyped(c.clone(), s.clone())?.to_string(s.clone())? as &str, + &C::into_untyped(c.clone())?.to_string()? as &str, r#"{"a": 1, "b": 2}"#, ); - test_roundtrip(c, s)?; + test_roundtrip(c)?; Ok(()) } @@ -126,13 +116,13 @@ fn optional_field_none() -> Result<()> { let s = State::default(); s.with_stdlib(); - let c = C::from_untyped(s.evaluate_snippet("snip".to_owned(), "{b: 2}")?, s.clone())?; + let c = C::from_untyped(s.evaluate_snippet("snip".to_owned(), "{b: 2}")?)?; ensure_eq!(c, C { a: None, b: 2 }); ensure_eq!( - &C::into_untyped(c.clone(), s.clone())?.to_string(s.clone())? as &str, + &C::into_untyped(c.clone())?.to_string()? as &str, r#"{"b": 2}"#, ); - test_roundtrip(c, s)?; + test_roundtrip(c)?; Ok(()) } @@ -152,10 +142,7 @@ fn flatten_optional_some() -> Result<()> { let s = State::default(); s.with_stdlib(); - let d = D::from_untyped( - s.evaluate_snippet("snip".to_owned(), "{b: 2, v:1}")?, - s.clone(), - )?; + let d = D::from_untyped(s.evaluate_snippet("snip".to_owned(), "{b: 2, v:1}")?)?; ensure_eq!( d, D { @@ -164,10 +151,10 @@ } ); ensure_eq!( - &D::into_untyped(d.clone(), s.clone())?.to_string(s.clone())? as &str, + &D::into_untyped(d.clone())?.to_string()? as &str, r#"{"b": 2, "v": 1}"#, ); - test_roundtrip(d, s)?; + test_roundtrip(d)?; Ok(()) } @@ -175,15 +162,12 @@ fn flatten_optional_none() -> Result<()> { let s = State::default(); s.with_stdlib(); - let d = D::from_untyped( - s.evaluate_snippet("snip".to_owned(), "{b: 2, v: '1'}")?, - s.clone(), - )?; + let d = D::from_untyped(s.evaluate_snippet("snip".to_owned(), "{b: 2, v: '1'}")?)?; ensure_eq!(d, D { e: None, b: 2 }); ensure_eq!( - &D::into_untyped(d.clone(), s.clone())?.to_string(s.clone())? as &str, + &D::into_untyped(d.clone())?.to_string()? as &str, r#"{"b": 2}"#, ); - test_roundtrip(d, s)?; + test_roundtrip(d)?; Ok(()) } -- gitstuff