difftreelog
refactor move state to global
in: master
9 files changed
bindings/jsonnet/src/lib.rsdiffbeforeafterboth--- a/bindings/jsonnet/src/lib.rs
+++ b/bindings/jsonnet/src/lib.rs
@@ -247,7 +247,7 @@
match vm
.state
.import(filename)
- .and_then(|val| apply_tla(vm.state.clone(), &vm.tla_args, val))
+ .and_then(|val| apply_tla(&vm.tla_args, val))
.and_then(|val| val.manifest(&vm.manifest_format))
{
Ok(v) => {
@@ -282,7 +282,7 @@
match vm
.state
.evaluate_snippet(filename.to_str().unwrap(), snippet.to_str().unwrap())
- .and_then(|val| apply_tla(vm.state.clone(), &vm.tla_args, val))
+ .and_then(|val| apply_tla(&vm.tla_args, val))
.and_then(|val| val.manifest(&vm.manifest_format))
{
Ok(v) => {
@@ -340,7 +340,7 @@
match vm
.state
.import(filename)
- .and_then(|val| apply_tla(vm.state.clone(), &vm.tla_args, val))
+ .and_then(|val| apply_tla(&vm.tla_args, val))
.and_then(|val| val_to_multi(val, &vm.manifest_format))
{
Ok(v) => {
@@ -369,7 +369,7 @@
match vm
.state
.evaluate_snippet(filename.to_str().unwrap(), snippet.to_str().unwrap())
- .and_then(|val| apply_tla(vm.state.clone(), &vm.tla_args, val))
+ .and_then(|val| apply_tla(&vm.tla_args, val))
.and_then(|val| val_to_multi(val, &vm.manifest_format))
{
Ok(v) => {
@@ -422,7 +422,7 @@
match vm
.state
.import(filename)
- .and_then(|val| apply_tla(vm.state.clone(), &vm.tla_args, val))
+ .and_then(|val| apply_tla(&vm.tla_args, val))
.and_then(|val| val_to_stream(val, &vm.manifest_format))
{
Ok(v) => {
@@ -451,7 +451,7 @@
match vm
.state
.evaluate_snippet(filename.to_str().unwrap(), snippet.to_str().unwrap())
- .and_then(|val| apply_tla(vm.state.clone(), &vm.tla_args, val))
+ .and_then(|val| apply_tla(&vm.tla_args, val))
.and_then(|val| val_to_stream(val, &vm.manifest_format))
{
Ok(v) => {
cmds/jrsonnet/src/main.rsdiffbeforeafterboth--- a/cmds/jrsonnet/src/main.rs
+++ b/cmds/jrsonnet/src/main.rs
@@ -173,6 +173,7 @@
let mut s = State::builder();
s.import_resolver(import_resolver).context_initializer(std);
let s = s.build();
+ let _s = s.enter();
let input = opts.input.input.ok_or(Error::MissingInputArgument)?;
let val = if opts.input.exec {
@@ -192,7 +193,7 @@
unused_mut,
clippy::redundant_clone,
)]
- let mut val = apply_tla(s.clone(), &tla, val)?;
+ let mut val = apply_tla(&tla, val)?;
#[cfg(feature = "exp-apply")]
for apply in opts.input.exp_apply {
crates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth1use std::fmt::Debug;23use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::IStr;5use rustc_hash::FxHashMap;67use crate::{8 error::ErrorKind::*, gc::WithCapacityExt as _, map::LayeredHashMap, ObjValue, Pending, Result,9 State, Thunk, Val,10};1112#[derive(Trace)]13struct ContextInternals {14 state: Option<State>,15 dollar: Option<ObjValue>,16 sup: Option<ObjValue>,17 this: Option<ObjValue>,18 bindings: LayeredHashMap,19}20impl Debug for ContextInternals {21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {22 f.debug_struct("Context").finish()23 }24}2526/// Context keeps information about current lexical code location27///28/// This information includes local variables, top-level object (`$`), current object (`this`), and super object (`super`)29#[derive(Debug, Clone, Trace)]30pub struct Context(Cc<ContextInternals>);31impl Context {32 pub fn new_future() -> Pending<Self> {33 Pending::new()34 }3536 pub fn state(&self) -> &State {37 self.038 .state39 .as_ref()40 .expect("used state from dummy context")41 }4243 pub fn dollar(&self) -> Option<&ObjValue> {44 self.0.dollar.as_ref()45 }4647 pub fn this(&self) -> Option<&ObjValue> {48 self.0.this.as_ref()49 }5051 pub fn super_obj(&self) -> Option<&ObjValue> {52 self.0.sup.as_ref()53 }5455 pub fn binding(&self, name: IStr) -> Result<Thunk<Val>> {56 use std::cmp::Ordering;5758 use crate::bail;5960 if let Some(val) = self.0.bindings.get(&name).cloned() {61 return Ok(val);62 }6364 let mut heap = Vec::new();65 self.0.bindings.clone().iter_keys(|k| {66 let conf = strsim::jaro_winkler(&k as &str, &name as &str);67 if conf < 0.8 {68 return;69 }70 heap.push((conf, k));71 });72 heap.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));7374 bail!(VariableIsNotDefined(75 name,76 heap.into_iter().map(|(_, k)| k).collect()77 ))78 }79 pub fn contains_binding(&self, name: IStr) -> bool {80 self.0.bindings.contains_key(&name)81 }82 #[must_use]83 pub fn into_future(self, ctx: Pending<Self>) -> Self {84 {85 ctx.clone().fill(self);86 }87 ctx.unwrap()88 }8990 #[must_use]91 pub fn with_var(self, name: impl Into<IStr>, value: Val) -> Self {92 let mut new_bindings = FxHashMap::with_capacity(1);93 new_bindings.insert(name.into(), Thunk::evaluated(value));94 self.extend(new_bindings, None, None, None)95 }9697 #[must_use]98 pub fn extend(99 self,100 new_bindings: FxHashMap<IStr, Thunk<Val>>,101 new_dollar: Option<ObjValue>,102 new_sup: Option<ObjValue>,103 new_this: Option<ObjValue>,104 ) -> Self {105 let ctx = &self.0;106 let dollar = new_dollar.or_else(|| ctx.dollar.clone());107 let this = new_this.or_else(|| ctx.this.clone());108 let sup = new_sup.or_else(|| ctx.sup.clone());109 let bindings = if new_bindings.is_empty() {110 ctx.bindings.clone()111 } else {112 ctx.bindings.clone().extend(new_bindings)113 };114 Self(Cc::new(ContextInternals {115 state: ctx.state.clone(),116 dollar,117 sup,118 this,119 bindings,120 }))121 }122}123124impl PartialEq for Context {125 fn eq(&self, other: &Self) -> bool {126 Cc::ptr_eq(&self.0, &other.0)127 }128}129130pub struct ContextBuilder {131 state: Option<State>,132 bindings: FxHashMap<IStr, Thunk<Val>>,133 extend: Option<Context>,134}135136impl ContextBuilder {137 /// # Panics138 /// Panics aren't directly caused by this function, but if state from resulting context is used139 pub fn dangerous_empty_state() -> Self {140 Self {141 state: None,142 bindings: FxHashMap::new(),143 extend: None,144 }145 }146 pub fn new(state: State) -> Self {147 Self::with_capacity(state, 0)148 }149 pub fn with_capacity(state: State, capacity: usize) -> Self {150 Self {151 state: Some(state),152 bindings: FxHashMap::with_capacity(capacity),153 extend: None,154 }155 }156 pub fn extend(parent: Context) -> Self {157 Self {158 state: parent.0.state.clone(),159 bindings: FxHashMap::new(),160 extend: Some(parent),161 }162 }163 /// # Panics164 /// If `name` is already bound165 pub fn bind(&mut self, name: impl Into<IStr>, value: Thunk<Val>) -> &mut Self {166 let old = self.bindings.insert(name.into(), value);167 assert!(old.is_none(), "variable bound twice in single context call");168 self169 }170 pub fn build(self) -> Context {171 if let Some(parent) = self.extend {172 // TODO: replace self.extend with Result<Context, State>, and remove `state` field173 parent.extend(self.bindings, None, None, None)174 } else {175 Context(Cc::new(ContextInternals {176 state: self.state,177 bindings: LayeredHashMap::new(self.bindings),178 dollar: None,179 sup: None,180 this: None,181 }))182 }183 }184}1use std::fmt::Debug;23use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::IStr;5use rustc_hash::FxHashMap;67use crate::{8 error::ErrorKind::*, gc::WithCapacityExt as _, map::LayeredHashMap, ObjValue, Pending, Result,9 Thunk, Val,10};1112#[derive(Trace)]13struct ContextInternals {14 dollar: Option<ObjValue>,15 sup: Option<ObjValue>,16 this: Option<ObjValue>,17 bindings: LayeredHashMap,18}19impl Debug for ContextInternals {20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {21 f.debug_struct("Context").finish()22 }23}2425/// Context keeps information about current lexical code location26///27/// This information includes local variables, top-level object (`$`), current object (`this`), and super object (`super`)28#[derive(Debug, Clone, Trace)]29pub struct Context(Cc<ContextInternals>);30impl Context {31 pub fn new_future() -> Pending<Self> {32 Pending::new()33 }3435 pub fn dollar(&self) -> Option<&ObjValue> {36 self.0.dollar.as_ref()37 }3839 pub fn this(&self) -> Option<&ObjValue> {40 self.0.this.as_ref()41 }4243 pub fn super_obj(&self) -> Option<&ObjValue> {44 self.0.sup.as_ref()45 }4647 pub fn binding(&self, name: IStr) -> Result<Thunk<Val>> {48 use std::cmp::Ordering;4950 use crate::bail;5152 if let Some(val) = self.0.bindings.get(&name).cloned() {53 return Ok(val);54 }5556 let mut heap = Vec::new();57 self.0.bindings.clone().iter_keys(|k| {58 let conf = strsim::jaro_winkler(&k as &str, &name as &str);59 if conf < 0.8 {60 return;61 }62 heap.push((conf, k));63 });64 heap.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));6566 bail!(VariableIsNotDefined(67 name,68 heap.into_iter().map(|(_, k)| k).collect()69 ))70 }71 pub fn contains_binding(&self, name: IStr) -> bool {72 self.0.bindings.contains_key(&name)73 }74 #[must_use]75 pub fn into_future(self, ctx: Pending<Self>) -> Self {76 {77 ctx.clone().fill(self);78 }79 ctx.unwrap()80 }8182 #[must_use]83 pub fn with_var(self, name: impl Into<IStr>, value: Val) -> Self {84 let mut new_bindings = FxHashMap::with_capacity(1);85 new_bindings.insert(name.into(), Thunk::evaluated(value));86 self.extend(new_bindings, None, None, None)87 }8889 #[must_use]90 pub fn extend(91 self,92 new_bindings: FxHashMap<IStr, Thunk<Val>>,93 new_dollar: Option<ObjValue>,94 new_sup: Option<ObjValue>,95 new_this: Option<ObjValue>,96 ) -> Self {97 let ctx = &self.0;98 let dollar = new_dollar.or_else(|| ctx.dollar.clone());99 let this = new_this.or_else(|| ctx.this.clone());100 let sup = new_sup.or_else(|| ctx.sup.clone());101 let bindings = if new_bindings.is_empty() {102 ctx.bindings.clone()103 } else {104 ctx.bindings.clone().extend(new_bindings)105 };106 Self(Cc::new(ContextInternals {107 dollar,108 sup,109 this,110 bindings,111 }))112 }113}114115impl PartialEq for Context {116 fn eq(&self, other: &Self) -> bool {117 Cc::ptr_eq(&self.0, &other.0)118 }119}120121pub struct ContextBuilder {122 bindings: FxHashMap<IStr, Thunk<Val>>,123 extend: Option<Context>,124}125126impl ContextBuilder {127 pub fn new() -> Self {128 Self::with_capacity(0)129 }130 pub fn with_capacity(capacity: usize) -> Self {131 Self {132 bindings: FxHashMap::with_capacity(capacity),133 extend: None,134 }135 }136 pub fn extend(parent: Context) -> Self {137 Self {138 bindings: FxHashMap::new(),139 extend: Some(parent),140 }141 }142 /// # Panics143 /// If `name` is already bound144 pub fn bind(&mut self, name: impl Into<IStr>, value: Thunk<Val>) -> &mut Self {145 let old = self.bindings.insert(name.into(), value);146 assert!(old.is_none(), "variable bound twice in single context call");147 self148 }149 pub fn build(self) -> Context {150 if let Some(parent) = self.extend {151 // TODO: replace self.extend with Result<Context, State>, and remove `state` field152 parent.extend(self.bindings, None, None, None)153 } else {154 Context(Cc::new(ContextInternals {155 bindings: LayeredHashMap::new(self.bindings),156 dollar: None,157 sup: None,158 this: None,159 }))160 }161 }162}crates/jrsonnet-evaluator/src/function/arglike.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function/arglike.rs
+++ b/crates/jrsonnet-evaluator/src/function/arglike.rs
@@ -4,7 +4,7 @@
use jrsonnet_interner::IStr;
use jrsonnet_parser::{ArgsDesc, LocExpr, SourceFifo, SourcePath};
-use crate::{evaluate, typed::Typed, Context, Result, Thunk, Val};
+use crate::{evaluate, typed::Typed, with_state, Context, Result, Thunk, Val};
/// Marker for arguments, which can be evaluated with context set to None
pub trait OptionalContext {}
@@ -47,28 +47,59 @@
ImportStr(String),
InlineCode(String),
}
-impl ArgLike for TlaArg {
- fn evaluate_arg(&self, ctx: Context, _tailstrict: bool) -> Result<Thunk<Val>> {
+impl TlaArg {
+ pub fn evaluate_tailstrict(&self) -> Result<Val> {
match self {
+ Self::String(s) => Ok(Val::string(s.clone())),
+ Self::Val(val) => Ok(val.clone()),
+ Self::Lazy(lazy) => Ok(lazy.evaluate()?),
+ Self::Import(p) => with_state(|s| {
+ let resolved = s.resolve_from_default(&p.as_str())?;
+ s.import_resolved(resolved)
+ }),
+ Self::ImportStr(p) => with_state(|s| {
+ let resolved = s.resolve_from_default(&p.as_str())?;
+ s.import_resolved_str(resolved).map(Val::string)
+ }),
+ Self::InlineCode(p) => with_state(|s| {
+ let resolved =
+ SourcePath::new(SourceFifo("<inline code>".to_owned(), p.as_bytes().into()));
+ s.import_resolved(resolved)
+ }),
+ }
+ }
+ pub fn evaluate(&self) -> Result<Thunk<Val>> {
+ match self {
Self::String(s) => Ok(Thunk::evaluated(Val::string(s.clone()))),
Self::Val(val) => Ok(Thunk::evaluated(val.clone())),
Self::Lazy(lazy) => Ok(lazy.clone()),
- Self::Import(p) => {
- let resolved = ctx.state().resolve_from_default(&p.as_str())?;
- Ok(Thunk!(move || ctx.state().import_resolved(resolved)))
- }
- Self::ImportStr(p) => {
- let resolved = ctx.state().resolve_from_default(&p.as_str())?;
- Ok(Thunk!(move || ctx
- .state()
+ Self::Import(p) => with_state(|s| {
+ let resolved = s.resolve_from_default(&p.as_str())?;
+ Ok(Thunk!(move || s.import_resolved(resolved)))
+ }),
+ Self::ImportStr(p) => with_state(|s| {
+ let resolved = s.resolve_from_default(&p.as_str())?;
+ Ok(Thunk!(move || s
.import_resolved_str(resolved)
.map(Val::string)))
- }
- Self::InlineCode(p) => {
+ }),
+ Self::InlineCode(p) => with_state(|s| {
let resolved =
SourcePath::new(SourceFifo("<inline code>".to_owned(), p.as_bytes().into()));
- Ok(Thunk!(move || ctx.state().import_resolved(resolved)))
- }
+ Ok(Thunk!(move || s.import_resolved(resolved)))
+ }),
+ }
+ }
+}
+
+// TODO: Is this implementation really required, as there is no Context to use?
+// Maybe something a bit stricter is possible to add, especially with precompiled calls?
+impl ArgLike for TlaArg {
+ fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {
+ if tailstrict {
+ self.evaluate_tailstrict().map(Thunk::evaluated)
+ } else {
+ self.evaluate()
}
}
}
crates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function/mod.rs
+++ b/crates/jrsonnet-evaluator/src/function/mod.rs
@@ -207,7 +207,7 @@
tailstrict: bool,
) -> Result<Val> {
self.evaluate(
- ContextBuilder::dangerous_empty_state().build(),
+ ContextBuilder::new().build(),
CallLocation::native(),
args,
tailstrict,
crates/jrsonnet-evaluator/src/tla.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/tla.rs
+++ b/crates/jrsonnet-evaluator/src/tla.rs
@@ -1,21 +1,24 @@
use jrsonnet_interner::IStr;
use jrsonnet_parser::Source;
+use rustc_hash::FxHashMap;
use crate::{
- function::{ArgsLike, CallLocation},
- in_description_frame, Result, State, Val,
+ function::{CallLocation, TlaArg},
+ in_description_frame, with_state, Result, Val,
};
-pub fn apply_tla<A: ArgsLike>(s: State, args: &A, val: Val) -> Result<Val> {
+pub fn apply_tla(args: &FxHashMap<IStr, TlaArg>, val: Val) -> Result<Val> {
Ok(if let Val::Func(func) = val {
in_description_frame(
|| "during TLA call".to_owned(),
|| {
func.evaluate(
- s.create_default_context(Source::new_virtual(
- "<top-level-arg>".into(),
- IStr::empty(),
- )),
+ with_state(|s| {
+ s.create_default_context(Source::new_virtual(
+ "<top-level-arg>".into(),
+ IStr::empty(),
+ ))
+ }),
CallLocation::native(),
args,
false,
crates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/lib.rs
+++ b/crates/jrsonnet-stdlib/src/lib.rs
@@ -335,11 +335,6 @@
pub path_resolver: PathResolver,
}
-fn extvar_source(name: &str, code: impl Into<IStr>) -> Source {
- let source_name = format!("<extvar:{name}>");
- Source::new_virtual(source_name.into(), code.into())
-}
-
#[derive(Trace, Clone)]
pub struct ContextInitializer {
/// std without applied thisFile overlay
crates/jrsonnet-stdlib/src/misc.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/misc.rs
+++ b/crates/jrsonnet-stdlib/src/misc.rs
@@ -3,15 +3,15 @@
use jrsonnet_evaluator::{
bail,
error::{ErrorKind::*, Result},
- function::{builtin, ArgLike, CallLocation, FuncVal},
+ function::{builtin, CallLocation, FuncVal},
manifest::JsonFormat,
typed::{Either2, Either4},
val::{equals, ArrValue},
- Context, Either, IStr, ObjValue, ObjValueBuilder, ResultExt, Thunk, Val,
+ Either, IStr, ObjValue, ObjValueBuilder, ResultExt, Thunk, Val,
};
use jrsonnet_gcmodule::Cc;
-use crate::{extvar_source, Settings};
+use crate::Settings;
#[builtin]
pub fn builtin_length(x: Either![IStr, ArrValue, ObjValue, FuncVal]) -> usize {
@@ -50,16 +50,14 @@
#[builtin(fields(
settings: Cc<RefCell<Settings>>,
))]
-pub fn builtin_ext_var(this: &builtin_ext_var, ctx: Context, x: IStr) -> Result<Val> {
- let ctx = ctx.state().create_default_context(extvar_source(&x, ""));
+pub fn builtin_ext_var(this: &builtin_ext_var, x: IStr) -> Result<Val> {
this.settings
.borrow()
.ext_vars
.get(&x)
.cloned()
.ok_or_else(|| UndefinedExternalVariable(x))?
- .evaluate_arg(ctx, true)?
- .evaluate()
+ .evaluate_tailstrict()
}
#[builtin(fields(
tests/tests/builtin.rsdiffbeforeafterboth--- a/tests/tests/builtin.rs
+++ b/tests/tests/builtin.rs
@@ -19,7 +19,7 @@
fn basic_function() -> Result<()> {
let a: a = a {};
let v = u32::from_untyped(a.call(
- ContextBuilder::dangerous_empty_state().build(),
+ ContextBuilder::new().build(),
CallLocation::native(),
&(),
)?)?;