difftreelog
perf mutate context if operating on only strong ref
in: master
4 files changed
crates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/ctx.rs
+++ b/crates/jrsonnet-evaluator/src/ctx.rs
@@ -75,36 +75,56 @@
ctx.unwrap()
}
- pub fn with_var(&self, name: Rc<str>, value: Val) -> Result<Context> {
+ pub fn with_var(self, name: Rc<str>, value: Val) -> Context {
let mut new_bindings = HashMap::with_capacity(1);
new_bindings.insert(name, resolved_lazy_val!(value));
self.extend(new_bindings, None, None, None)
}
pub fn extend(
- &self,
+ self,
new_bindings: HashMap<Rc<str>, LazyVal>,
new_dollar: Option<ObjValue>,
new_this: Option<ObjValue>,
new_super_obj: Option<ObjValue>,
- ) -> Result<Context> {
- let dollar = new_dollar.or_else(|| self.0.dollar.clone());
- let this = new_this.or_else(|| self.0.this.clone());
- let super_obj = new_super_obj.or_else(|| self.0.super_obj.clone());
- let bindings = if new_bindings.is_empty() {
- self.0.bindings.clone()
- } else {
- self.0.bindings.extend(new_bindings)
- };
- Ok(Context(Rc::new(ContextInternals {
- dollar,
- this,
- super_obj,
- bindings,
- })))
+ ) -> Context {
+ match Rc::try_unwrap(self.0) {
+ Ok(mut ctx) => {
+ // Extended context aren't used by anything else, we can freely mutate it without cloning
+ if let Some(dollar) = new_dollar {
+ ctx.dollar = Some(dollar);
+ }
+ if let Some(this) = new_this {
+ ctx.this = Some(this);
+ }
+ if let Some(super_obj) = new_super_obj {
+ ctx.super_obj = Some(super_obj);
+ }
+ if !new_bindings.is_empty() {
+ ctx.bindings = ctx.bindings.extend(new_bindings);
+ }
+ Context(Rc::new(ctx))
+ }
+ Err(ctx) => {
+ let dollar = new_dollar.or_else(|| ctx.dollar.clone());
+ let this = new_this.or_else(|| ctx.this.clone());
+ let super_obj = new_super_obj.or_else(|| ctx.super_obj.clone());
+ let bindings = if new_bindings.is_empty() {
+ ctx.bindings.clone()
+ } else {
+ ctx.bindings.clone().extend(new_bindings)
+ };
+ Context(Rc::new(ContextInternals {
+ dollar,
+ this,
+ super_obj,
+ bindings,
+ }))
+ }
+ }
}
pub fn extend_unbound(
- &self,
+ self,
new_bindings: HashMap<Rc<str>, LazyBinding>,
new_dollar: Option<ObjValue>,
new_this: Option<ObjValue>,
@@ -116,7 +136,7 @@
for (k, v) in new_bindings.into_iter() {
new.insert(k, v.evaluate(this.clone(), super_obj.clone())?);
}
- self.extend(new, new_dollar, this, super_obj)
+ Ok(self.extend(new, new_dollar, this, super_obj))
}
pub fn into_weak(self) -> WeakContext {
WeakContext(Rc::downgrade(&self.0))
crates/jrsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate.rs
@@ -209,7 +209,7 @@
for item in list.iter() {
let item = item.unwrap_if_lazy()?;
out.push(evaluate_comp(
- context.with_var(var.clone(), item.clone())?,
+ context.clone().with_var(var.clone(), item.clone()),
value,
&specs[1..],
)?);
@@ -227,7 +227,7 @@
let future_this = FutureObjValue::new();
let context_creator = context_creator!(
closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {
- Ok(context.extend_unbound(
+ Ok(context.clone().extend_unbound(
new_bindings.clone().unwrap(),
context.dollar().clone().or_else(||this.clone()),
Some(this.unwrap()),
@@ -332,7 +332,7 @@
let new_bindings = FutureNewBindings::new();
let context_creator = context_creator!(
closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {
- Ok(context.extend_unbound(
+ Ok(context.clone().extend_unbound(
new_bindings.clone().unwrap(),
context.dollar().clone().or_else(||this.clone()),
None,
@@ -354,7 +354,7 @@
let key = evaluate(ctx.clone(), &obj.key)?;
let value = LazyBinding::Bindable(Rc::new(
closure!(clone ctx, clone obj.value, |this, _super_obj| {
- Ok(LazyVal::new_resolved(evaluate(ctx.extend(HashMap::new(), None, this, None)?, &value)?))
+ Ok(LazyVal::new_resolved(evaluate(ctx.clone().extend(HashMap::new(), None, this, None), &value)?))
}),
));
crates/jrsonnet-evaluator/src/function.rsdiffbeforeafterboth1use crate::{error::Error::*, evaluate, lazy_val, resolved_lazy_val, throw, Context, Result, Val};2use closure::closure;3use jrsonnet_parser::{ArgsDesc, ParamsDesc};4use std::{collections::HashMap, rc::Rc};56const NO_DEFAULT_CONTEXT: &str =7 "no default context set for call with defined default parameter value";89/// Creates correct [context](Context) for function body evaluation, returning error on invalid call10///11/// * `ctx` used for passed argument expressions execution, and for body execution (if `body_ctx` is not set)12/// * `body_ctx` used for default parameter values execution, and for body execution (if set)13/// * `params` function parameters definition14/// * `args` passed function arguments15/// * `tailstruct` if true - function arguments is eager executed, otherwise - lazy16pub fn parse_function_call(17 ctx: Context,18 body_ctx: Option<Context>,19 params: &ParamsDesc,20 args: &ArgsDesc,21 tailstrict: bool,22) -> Result<Context> {23 let mut out = HashMap::new();24 let mut positioned_args = vec![None; params.0.len()];25 for (id, arg) in args.iter().enumerate() {26 let idx = if let Some(name) = &arg.0 {27 params28 .iter()29 .position(|p| *p.0 == *name)30 .ok_or_else(|| UnknownFunctionParameter(name.clone()))?31 } else {32 id33 };3435 if idx >= params.len() {36 throw!(TooManyArgsFunctionHas(params.len()));37 }38 if positioned_args[idx].is_some() {39 throw!(BindingParameterASecondTime(params[idx].0.clone()));40 }41 positioned_args[idx] = Some(arg.1.clone());42 }43 // Fill defaults44 for (id, p) in params.iter().enumerate() {45 let (ctx, expr) = if let Some(arg) = &positioned_args[id] {46 (ctx.clone(), arg)47 } else if let Some(default) = &p.1 {48 (body_ctx.clone().expect(NO_DEFAULT_CONTEXT), default)49 } else {50 throw!(FunctionParameterNotBoundInCall(p.0.clone()));51 };52 let val = if tailstrict {53 resolved_lazy_val!(evaluate(ctx, expr)?)54 } else {55 lazy_val!(closure!(clone ctx, clone expr, ||evaluate(ctx.clone(), &expr)))56 };57 out.insert(p.0.clone(), val);58 }5960 Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None)?)61}6263pub fn parse_function_call_map(64 ctx: Context,65 body_ctx: Option<Context>,66 params: &ParamsDesc,67 args: &HashMap<Rc<str>, Val>,68 tailstrict: bool,69) -> Result<Context> {70 let mut out = HashMap::new();71 let mut positioned_args = vec![None; params.0.len()];72 for (name, val) in args.iter() {73 let idx = params74 .iter()75 .position(|p| *p.0 == **name)76 .ok_or_else(|| UnknownFunctionParameter((&name as &str).to_owned()))?;7778 if idx >= params.len() {79 throw!(TooManyArgsFunctionHas(params.len()));80 }81 if positioned_args[idx].is_some() {82 throw!(BindingParameterASecondTime(params[idx].0.clone()));83 }84 positioned_args[idx] = Some(val.clone());85 }86 // Fill defaults87 for (id, p) in params.iter().enumerate() {88 let val = if let Some(arg) = positioned_args[id].take() {89 resolved_lazy_val!(arg)90 } else if let Some(default) = &p.1 {91 if tailstrict {92 resolved_lazy_val!(evaluate(93 body_ctx.clone().expect(NO_DEFAULT_CONTEXT),94 default95 )?)96 } else {97 let body_ctx = body_ctx.clone();98 let default = default.clone();99 lazy_val!(move || {100 evaluate(body_ctx.clone().expect(NO_DEFAULT_CONTEXT), &default)101 })102 }103 } else {104 throw!(FunctionParameterNotBoundInCall(p.0.clone()));105 };106 out.insert(p.0.clone(), val);107 }108109 Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None)?)110}111112pub(crate) fn place_args(113 ctx: Context,114 body_ctx: Option<Context>,115 params: &ParamsDesc,116 args: &[Val],117) -> Result<Context> {118 let mut out = HashMap::new();119 let mut positioned_args = vec![None; params.0.len()];120 for (id, arg) in args.iter().enumerate() {121 if id >= params.len() {122 throw!(TooManyArgsFunctionHas(params.len()));123 }124 positioned_args[id] = Some(arg);125 }126 // Fill defaults127 for (id, p) in params.iter().enumerate() {128 let val = if let Some(arg) = &positioned_args[id] {129 (*arg).clone()130 } else if let Some(default) = &p.1 {131 evaluate(ctx.clone(), default)?132 } else {133 throw!(FunctionParameterNotBoundInCall(p.0.clone()));134 };135 out.insert(p.0.clone(), resolved_lazy_val!(val));136 }137138 Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None)?)139}140141#[macro_export]142macro_rules! parse_args {143 ($ctx: expr, $fn_name: expr, $args: expr, $total_args: expr, [144 $($id: expr, $name: ident $(: [$($p: path)|+] $(!! $a: path)?)?, $nt: expr);+ $(;)?145 ], $handler:block) => {{146 use crate::{throw, error::Error::*};147 let args = $args;148 if args.len() > $total_args {149 throw!(TooManyArgsFunctionHas($total_args));150 }151 $(152 if args.len() <= $id {153 throw!(FunctionParameterNotBoundInCall(stringify!($name).into()));154 }155 let $name = &args[$id];156 if $name.0.is_some() {157 if $name.0.as_ref().unwrap() != stringify!($name) {158 throw!(IntristicArgumentReorderingIsNotSupportedYet);159 }160 }161 let $name = evaluate($ctx.clone(), &$name.1)?;162 $(163 match $name {164 $($p(_))|+ => {},165 _ => throw!(TypeMismatch(166 concat!($fn_name, " ", stringify!($id), "nd (", stringify!($name), ") argument"),167 $nt, $name.value_type()?168 )),169 };170 $(171 let $name = match $name {172 $a(v) => v,173 _ =>throw!(TypeMismatch(concat!($fn_name, " ", stringify!($id), "nd (", stringify!($name), ") argument"), $nt, $name.value_type()?)),174 };175 )*176 )*177 )+178 ($handler as crate::Result<_>)179 }};180}181182#[test]183fn test() -> Result<()> {184 use crate::val::ValType;185 use jrsonnet_parser::*;186 let state = crate::EvaluationState::default();187 let evaluator = state.with_stdlib();188 let ctx = evaluator.create_default_context()?;189 evaluator.run_in_state(|| {190 parse_args!(ctx, "test", ArgsDesc(vec![191 Arg(None, el!(Expr::Num(2.0))),192 Arg(Some("b".into()), el!(Expr::Num(1.0))),193 ]), 2, [194 0, a: [Val::Num]!!Val::Num, vec![ValType::Num];195 1, b: [Val::Num]!!Val::Num, vec![ValType::Num];196 ], {197 assert!((a - 2.0).abs() <= f64::EPSILON);198 assert!((b - 1.0).abs() <= f64::EPSILON);199 Ok(())200 })201 .unwrap();202 Ok(())203 })204}1use crate::{error::Error::*, evaluate, lazy_val, resolved_lazy_val, throw, Context, Result, Val};2use closure::closure;3use jrsonnet_parser::{ArgsDesc, ParamsDesc};4use std::{collections::HashMap, rc::Rc};56const NO_DEFAULT_CONTEXT: &str =7 "no default context set for call with defined default parameter value";89/// Creates correct [context](Context) for function body evaluation, returning error on invalid call10///11/// * `ctx` used for passed argument expressions execution, and for body execution (if `body_ctx` is not set)12/// * `body_ctx` used for default parameter values execution, and for body execution (if set)13/// * `params` function parameters definition14/// * `args` passed function arguments15/// * `tailstruct` if true - function arguments is eager executed, otherwise - lazy16pub fn parse_function_call(17 ctx: Context,18 body_ctx: Option<Context>,19 params: &ParamsDesc,20 args: &ArgsDesc,21 tailstrict: bool,22) -> Result<Context> {23 let mut out = HashMap::new();24 let mut positioned_args = vec![None; params.0.len()];25 for (id, arg) in args.iter().enumerate() {26 let idx = if let Some(name) = &arg.0 {27 params28 .iter()29 .position(|p| *p.0 == *name)30 .ok_or_else(|| UnknownFunctionParameter(name.clone()))?31 } else {32 id33 };3435 if idx >= params.len() {36 throw!(TooManyArgsFunctionHas(params.len()));37 }38 if positioned_args[idx].is_some() {39 throw!(BindingParameterASecondTime(params[idx].0.clone()));40 }41 positioned_args[idx] = Some(arg.1.clone());42 }43 // Fill defaults44 for (id, p) in params.iter().enumerate() {45 let (ctx, expr) = if let Some(arg) = &positioned_args[id] {46 (ctx.clone(), arg)47 } else if let Some(default) = &p.1 {48 (body_ctx.clone().expect(NO_DEFAULT_CONTEXT), default)49 } else {50 throw!(FunctionParameterNotBoundInCall(p.0.clone()));51 };52 let val = if tailstrict {53 resolved_lazy_val!(evaluate(ctx, expr)?)54 } else {55 lazy_val!(closure!(clone ctx, clone expr, ||evaluate(ctx.clone(), &expr)))56 };57 out.insert(p.0.clone(), val);58 }5960 Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None))61}6263pub fn parse_function_call_map(64 ctx: Context,65 body_ctx: Option<Context>,66 params: &ParamsDesc,67 args: &HashMap<Rc<str>, Val>,68 tailstrict: bool,69) -> Result<Context> {70 let mut out = HashMap::new();71 let mut positioned_args = vec![None; params.0.len()];72 for (name, val) in args.iter() {73 let idx = params74 .iter()75 .position(|p| *p.0 == **name)76 .ok_or_else(|| UnknownFunctionParameter((&name as &str).to_owned()))?;7778 if idx >= params.len() {79 throw!(TooManyArgsFunctionHas(params.len()));80 }81 if positioned_args[idx].is_some() {82 throw!(BindingParameterASecondTime(params[idx].0.clone()));83 }84 positioned_args[idx] = Some(val.clone());85 }86 // Fill defaults87 for (id, p) in params.iter().enumerate() {88 let val = if let Some(arg) = positioned_args[id].take() {89 resolved_lazy_val!(arg)90 } else if let Some(default) = &p.1 {91 if tailstrict {92 resolved_lazy_val!(evaluate(93 body_ctx.clone().expect(NO_DEFAULT_CONTEXT),94 default95 )?)96 } else {97 let body_ctx = body_ctx.clone();98 let default = default.clone();99 lazy_val!(move || {100 evaluate(body_ctx.clone().expect(NO_DEFAULT_CONTEXT), &default)101 })102 }103 } else {104 throw!(FunctionParameterNotBoundInCall(p.0.clone()));105 };106 out.insert(p.0.clone(), val);107 }108109 Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None))110}111112pub(crate) fn place_args(113 ctx: Context,114 body_ctx: Option<Context>,115 params: &ParamsDesc,116 args: &[Val],117) -> Result<Context> {118 let mut out = HashMap::new();119 let mut positioned_args = vec![None; params.0.len()];120 for (id, arg) in args.iter().enumerate() {121 if id >= params.len() {122 throw!(TooManyArgsFunctionHas(params.len()));123 }124 positioned_args[id] = Some(arg);125 }126 // Fill defaults127 for (id, p) in params.iter().enumerate() {128 let val = if let Some(arg) = &positioned_args[id] {129 (*arg).clone()130 } else if let Some(default) = &p.1 {131 evaluate(ctx.clone(), default)?132 } else {133 throw!(FunctionParameterNotBoundInCall(p.0.clone()));134 };135 out.insert(p.0.clone(), resolved_lazy_val!(val));136 }137138 Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None))139}140141#[macro_export]142macro_rules! parse_args {143 ($ctx: expr, $fn_name: expr, $args: expr, $total_args: expr, [144 $($id: expr, $name: ident $(: [$($p: path)|+] $(!! $a: path)?)?, $nt: expr);+ $(;)?145 ], $handler:block) => {{146 use crate::{throw, error::Error::*};147 let args = $args;148 if args.len() > $total_args {149 throw!(TooManyArgsFunctionHas($total_args));150 }151 $(152 if args.len() <= $id {153 throw!(FunctionParameterNotBoundInCall(stringify!($name).into()));154 }155 let $name = &args[$id];156 if $name.0.is_some() {157 if $name.0.as_ref().unwrap() != stringify!($name) {158 throw!(IntristicArgumentReorderingIsNotSupportedYet);159 }160 }161 let $name = evaluate($ctx.clone(), &$name.1)?;162 $(163 match $name {164 $($p(_))|+ => {},165 _ => throw!(TypeMismatch(166 concat!($fn_name, " ", stringify!($id), "nd (", stringify!($name), ") argument"),167 $nt, $name.value_type()?168 )),169 };170 $(171 let $name = match $name {172 $a(v) => v,173 _ =>throw!(TypeMismatch(concat!($fn_name, " ", stringify!($id), "nd (", stringify!($name), ") argument"), $nt, $name.value_type()?)),174 };175 )*176 )*177 )+178 ($handler as crate::Result<_>)179 }};180}181182#[test]183fn test() -> Result<()> {184 use crate::val::ValType;185 use jrsonnet_parser::*;186 let state = crate::EvaluationState::default();187 let evaluator = state.with_stdlib();188 let ctx = evaluator.create_default_context()?;189 evaluator.run_in_state(|| {190 parse_args!(ctx, "test", ArgsDesc(vec![191 Arg(None, el!(Expr::Num(2.0))),192 Arg(Some("b".into()), el!(Expr::Num(1.0))),193 ]), 2, [194 0, a: [Val::Num]!!Val::Num, vec![ValType::Num];195 1, b: [Val::Num]!!Val::Num, vec![ValType::Num];196 ], {197 assert!((a - 2.0).abs() <= f64::EPSILON);198 assert!((b - 1.0).abs() <= f64::EPSILON);199 Ok(())200 })201 .unwrap();202 Ok(())203 })204}crates/jrsonnet-evaluator/src/map.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/map.rs
+++ b/crates/jrsonnet-evaluator/src/map.rs
@@ -10,12 +10,17 @@
pub struct LayeredHashMap<K: Hash, V>(Rc<LayeredHashMapInternals<K, V>>);
impl<K: Hash + Eq, V> LayeredHashMap<K, V> {
- pub fn extend(&self, new_layer: HashMap<K, V>) -> Self {
- let super_map = self.clone();
- LayeredHashMap(Rc::new(LayeredHashMapInternals {
- parent: Some(super_map),
- current: new_layer,
- }))
+ pub fn extend(self, new_layer: HashMap<K, V>) -> Self {
+ match Rc::try_unwrap(self.0) {
+ Ok(mut map) => {
+ map.current.extend(new_layer);
+ LayeredHashMap(Rc::new(map))
+ }
+ Err(this) => LayeredHashMap(Rc::new(LayeredHashMapInternals {
+ parent: Some(LayeredHashMap(this)),
+ current: new_layer,
+ })),
+ }
}
pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<&V>