git.delta.rocks / jrsonnet / refs/commits / ec654c44d309

difftreelog

refactor get rid of LazyBinding variants

Yaroslav Bolyukin2021-02-22parent: #3154bc9.patch.diff
in: master

5 files changed

modifiedcrates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/ctx.rs
1use crate::{2	error::Error::*, map::LayeredHashMap, rc_fn_helper, resolved_lazy_val, FutureWrapper,3	LazyBinding, LazyVal, ObjValue, Result, Val,4};5use jrsonnet_interner::IStr;6use rustc_hash::FxHashMap;7use std::hash::BuildHasherDefault;8use std::{collections::HashMap, fmt::Debug, rc::Rc};910rc_fn_helper!(11	ContextCreator,12	context_creator,13	dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Result<Context>14);1516struct ContextInternals {17	dollar: Option<ObjValue>,18	this: Option<ObjValue>,19	super_obj: Option<ObjValue>,20	bindings: LayeredHashMap<LazyVal>,21}22impl Debug for ContextInternals {23	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {24		f.debug_struct("Context")25			.field("this", &self.this.as_ref().map(|e| Rc::as_ptr(&e.0)))26			.field("bindings", &self.bindings)27			.finish()28	}29}3031#[derive(Debug, Clone)]32pub struct Context(Rc<ContextInternals>);33impl Context {34	pub fn new_future() -> FutureWrapper<Context> {35		FutureWrapper::new()36	}3738	pub fn dollar(&self) -> &Option<ObjValue> {39		&self.0.dollar40	}4142	pub fn this(&self) -> &Option<ObjValue> {43		&self.0.this44	}4546	pub fn super_obj(&self) -> &Option<ObjValue> {47		&self.0.super_obj48	}4950	pub fn new() -> Self {51		Self(Rc::new(ContextInternals {52			dollar: None,53			this: None,54			super_obj: None,55			bindings: LayeredHashMap::default(),56		}))57	}5859	pub fn binding(&self, name: IStr) -> Result<LazyVal> {60		Ok(self61			.062			.bindings63			.get(&name)64			.cloned()65			.ok_or(VariableIsNotDefined(name))?)66	}67	pub fn into_future(self, ctx: FutureWrapper<Context>) -> Self {68		{69			ctx.0.borrow_mut().replace(self);70		}71		ctx.unwrap()72	}7374	pub fn with_var(self, name: IStr, value: Val) -> Self {75		let mut new_bindings =76			FxHashMap::with_capacity_and_hasher(1, BuildHasherDefault::default());77		new_bindings.insert(name, resolved_lazy_val!(value));78		self.extend(new_bindings, None, None, None)79	}8081	pub fn extend(82		self,83		new_bindings: FxHashMap<IStr, LazyVal>,84		new_dollar: Option<ObjValue>,85		new_this: Option<ObjValue>,86		new_super_obj: Option<ObjValue>,87	) -> Self {88		match Rc::try_unwrap(self.0) {89			Ok(mut ctx) => {90				// Extended context aren't used by anything else, we can freely mutate it without cloning91				if let Some(dollar) = new_dollar {92					ctx.dollar = Some(dollar);93				}94				if let Some(this) = new_this {95					ctx.this = Some(this);96				}97				if let Some(super_obj) = new_super_obj {98					ctx.super_obj = Some(super_obj);99				}100				if !new_bindings.is_empty() {101					ctx.bindings = ctx.bindings.extend(new_bindings);102				}103				Self(Rc::new(ctx))104			}105			Err(ctx) => {106				let dollar = new_dollar.or_else(|| ctx.dollar.clone());107				let this = new_this.or_else(|| ctx.this.clone());108				let super_obj = new_super_obj.or_else(|| ctx.super_obj.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(Rc::new(ContextInternals {115					dollar,116					this,117					super_obj,118					bindings,119				}))120			}121		}122	}123	pub fn extend_unbound(124		self,125		new_bindings: HashMap<IStr, LazyBinding>,126		new_dollar: Option<ObjValue>,127		new_this: Option<ObjValue>,128		new_super_obj: Option<ObjValue>,129	) -> Result<Self> {130		let this = new_this.or_else(|| self.0.this.clone());131		let super_obj = new_super_obj.or_else(|| self.0.super_obj.clone());132		let mut new =133			FxHashMap::with_capacity_and_hasher(new_bindings.len(), BuildHasherDefault::default());134		for (k, v) in new_bindings.into_iter() {135			new.insert(k, v.evaluate(this.clone(), super_obj.clone())?);136		}137		Ok(self.extend(new, new_dollar, this, super_obj))138	}139	#[cfg(feature = "unstable")]140	pub fn into_weak(self) -> WeakContext {141		WeakContext(Rc::downgrade(&self.0))142	}143}144145impl Default for Context {146	fn default() -> Self {147		Self::new()148	}149}150151impl PartialEq for Context {152	fn eq(&self, other: &Self) -> bool {153		Rc::ptr_eq(&self.0, &other.0)154	}155}156157#[cfg(feature = "unstable")]158#[derive(Debug, Clone)]159pub struct WeakContext(std::rc::Weak<ContextInternals>);160#[cfg(feature = "unstable")]161impl WeakContext {162	pub fn upgrade(&self) -> Context {163		Context(self.0.upgrade().expect("context is removed"))164	}165}166#[cfg(feature = "unstable")]167impl PartialEq for WeakContext {168	fn eq(&self, other: &Self) -> bool {169		self.0.ptr_eq(&other.0)170	}171}
after · crates/jrsonnet-evaluator/src/ctx.rs
1use crate::{2	error::Error::*, map::LayeredHashMap, resolved_lazy_val, FutureWrapper, LazyBinding, LazyVal,3	ObjValue, Result, Val,4};5use jrsonnet_interner::IStr;6use rustc_hash::FxHashMap;7use std::hash::BuildHasherDefault;8use std::{fmt::Debug, rc::Rc};910#[derive(Clone)]11pub struct ContextCreator(pub Context, pub FutureWrapper<FxHashMap<IStr, LazyBinding>>);12impl ContextCreator {13	pub fn create(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<Context> {14		self.0.clone().extend_unbound(15			self.1.clone().unwrap(),16			self.0.dollar().clone().or_else(|| this.clone()),17			this,18			super_obj,19		)20	}21}2223struct ContextInternals {24	dollar: Option<ObjValue>,25	this: Option<ObjValue>,26	super_obj: Option<ObjValue>,27	bindings: LayeredHashMap<LazyVal>,28}29impl Debug for ContextInternals {30	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {31		f.debug_struct("Context")32			.field("this", &self.this.as_ref().map(|e| Rc::as_ptr(&e.0)))33			.field("bindings", &self.bindings)34			.finish()35	}36}3738#[derive(Debug, Clone)]39pub struct Context(Rc<ContextInternals>);40impl Context {41	pub fn new_future() -> FutureWrapper<Context> {42		FutureWrapper::new()43	}4445	pub fn dollar(&self) -> &Option<ObjValue> {46		&self.0.dollar47	}4849	pub fn this(&self) -> &Option<ObjValue> {50		&self.0.this51	}5253	pub fn super_obj(&self) -> &Option<ObjValue> {54		&self.0.super_obj55	}5657	pub fn new() -> Self {58		Self(Rc::new(ContextInternals {59			dollar: None,60			this: None,61			super_obj: None,62			bindings: LayeredHashMap::default(),63		}))64	}6566	pub fn binding(&self, name: IStr) -> Result<LazyVal> {67		Ok(self68			.069			.bindings70			.get(&name)71			.cloned()72			.ok_or(VariableIsNotDefined(name))?)73	}74	pub fn into_future(self, ctx: FutureWrapper<Context>) -> Self {75		{76			ctx.0.borrow_mut().replace(self);77		}78		ctx.unwrap()79	}8081	pub fn with_var(self, name: IStr, value: Val) -> Self {82		let mut new_bindings =83			FxHashMap::with_capacity_and_hasher(1, BuildHasherDefault::default());84		new_bindings.insert(name, resolved_lazy_val!(value));85		self.extend(new_bindings, None, None, None)86	}8788	pub fn extend(89		self,90		new_bindings: FxHashMap<IStr, LazyVal>,91		new_dollar: Option<ObjValue>,92		new_this: Option<ObjValue>,93		new_super_obj: Option<ObjValue>,94	) -> Self {95		match Rc::try_unwrap(self.0) {96			Ok(mut ctx) => {97				// Extended context aren't used by anything else, we can freely mutate it without cloning98				if let Some(dollar) = new_dollar {99					ctx.dollar = Some(dollar);100				}101				if let Some(this) = new_this {102					ctx.this = Some(this);103				}104				if let Some(super_obj) = new_super_obj {105					ctx.super_obj = Some(super_obj);106				}107				if !new_bindings.is_empty() {108					ctx.bindings = ctx.bindings.extend(new_bindings);109				}110				Self(Rc::new(ctx))111			}112			Err(ctx) => {113				let dollar = new_dollar.or_else(|| ctx.dollar.clone());114				let this = new_this.or_else(|| ctx.this.clone());115				let super_obj = new_super_obj.or_else(|| ctx.super_obj.clone());116				let bindings = if new_bindings.is_empty() {117					ctx.bindings.clone()118				} else {119					ctx.bindings.clone().extend(new_bindings)120				};121				Self(Rc::new(ContextInternals {122					dollar,123					this,124					super_obj,125					bindings,126				}))127			}128		}129	}130	pub fn extend_bound(self, new_bindings: FxHashMap<IStr, LazyVal>) -> Self {131		let new_this = self.0.this.clone();132		let new_super_obj = self.0.super_obj.clone();133		self.extend(new_bindings, None, new_this, new_super_obj)134	}135	pub fn extend_unbound(136		self,137		new_bindings: FxHashMap<IStr, LazyBinding>,138		new_dollar: Option<ObjValue>,139		new_this: Option<ObjValue>,140		new_super_obj: Option<ObjValue>,141	) -> Result<Self> {142		let this = new_this.or_else(|| self.0.this.clone());143		let super_obj = new_super_obj.or_else(|| self.0.super_obj.clone());144		let mut new =145			FxHashMap::with_capacity_and_hasher(new_bindings.len(), BuildHasherDefault::default());146		for (k, v) in new_bindings.into_iter() {147			new.insert(k, v.evaluate(this.clone(), super_obj.clone())?);148		}149		Ok(self.extend(new, new_dollar, this, super_obj))150	}151	#[cfg(feature = "unstable")]152	pub fn into_weak(self) -> WeakContext {153		WeakContext(Rc::downgrade(&self.0))154	}155}156157impl Default for Context {158	fn default() -> Self {159		Self::new()160	}161}162163impl PartialEq for Context {164	fn eq(&self, other: &Self) -> bool {165		Rc::ptr_eq(&self.0, &other.0)166	}167}168169#[cfg(feature = "unstable")]170#[derive(Debug, Clone)]171pub struct WeakContext(std::rc::Weak<ContextInternals>);172#[cfg(feature = "unstable")]173impl WeakContext {174	pub fn upgrade(&self) -> Context {175		Context(self.0.upgrade().expect("context is removed"))176	}177}178#[cfg(feature = "unstable")]179impl PartialEq for WeakContext {180	fn eq(&self, other: &Self) -> bool {181		self.0.ptr_eq(&other.0)182	}183}
modifiedcrates/jrsonnet-evaluator/src/dynamic.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/dynamic.rs
+++ b/crates/jrsonnet-evaluator/src/dynamic.rs
@@ -12,7 +12,7 @@
 	}
 }
 impl<T: Clone> FutureWrapper<T> {
-	pub fn unwrap(self) -> T {
+	pub fn unwrap(&self) -> T {
 		self.0.borrow().as_ref().cloned().unwrap()
 	}
 }
@@ -21,30 +21,4 @@
 	fn default() -> Self {
 		Self::new()
 	}
-}
-
-#[macro_export]
-macro_rules! rc_fn_helper {
-	($name: ident, $macro_name: ident, $fn: ty) => {
-		#[derive(Clone)]
-		#[doc = "Function wrapper"]
-		pub struct $name(pub std::rc::Rc<$fn>);
-		impl std::fmt::Debug for $name {
-			fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-				f.debug_struct(std::stringify!($name)).finish()
-			}
-		}
-		impl std::cmp::PartialEq for $name {
-			fn eq(&self, other: &$name) -> bool {
-				std::ptr::eq(&self.0, &other.0)
-			}
-		}
-		#[doc = "Macro to ease wrapper creation"]
-		#[macro_export]
-		macro_rules! $macro_name {
-			($val: expr) => {
-				$crate::$name(std::rc::Rc::new($val))
-			};
-		}
-	};
 }
modifiedcrates/jrsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate.rs
@@ -1,6 +1,6 @@
 use crate::{
-	context_creator, error::Error::*, lazy_val, push, throw, with_state, Context, ContextCreator,
-	FuncDesc, FuncVal, FutureWrapper, LazyBinding, LazyVal, ObjMember, ObjValue, Result, Val,
+	error::Error::*, lazy_val, push, throw, with_state, Context, ContextCreator, FuncDesc, FuncVal,
+	FutureWrapper, LazyBinding, LazyVal, ObjMember, ObjValue, Result, Val,
 };
 use closure::closure;
 use jrsonnet_interner::IStr;
@@ -10,8 +10,30 @@
 	Visibility,
 };
 use jrsonnet_types::ValType;
-use rustc_hash::FxHashMap;
-use std::{collections::HashMap, rc::Rc};
+use rustc_hash::{FxHashMap, FxHasher};
+use std::{collections::HashMap, hash::BuildHasherDefault, rc::Rc};
+
+pub fn evaluate_binding_in_future(
+	b: &BindSpec,
+	context_creator: FutureWrapper<Context>,
+) -> LazyVal {
+	let b = b.clone();
+	if let Some(params) = &b.params {
+		let params = params.clone();
+		LazyVal::new(Box::new(move || {
+			Ok(evaluate_method(
+				context_creator.unwrap(),
+				b.name.clone(),
+				params.clone(),
+				b.value.clone(),
+			))
+		}))
+	} else {
+		LazyVal::new(Box::new(move || {
+			evaluate_named(context_creator.unwrap(), &b.value, b.name.clone())
+		}))
+	}
+}
 
 pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (IStr, LazyBinding) {
 	let b = b.clone();
@@ -22,7 +44,7 @@
 			LazyBinding::Bindable(Rc::new(move |this, super_obj| {
 				Ok(lazy_val!(
 					closure!(clone b, clone params, clone context_creator, || Ok(evaluate_method(
-						context_creator.0(this.clone(), super_obj.clone())?,
+						context_creator.create(this.clone(), super_obj.clone())?,
 						b.name.clone(),
 						params.clone(),
 						b.value.clone(),
@@ -35,11 +57,11 @@
 			b.name.clone(),
 			LazyBinding::Bindable(Rc::new(move |this, super_obj| {
 				Ok(lazy_val!(closure!(clone context_creator, clone b, ||
-						evaluate_named(
-							context_creator.0(this.clone(), super_obj.clone())?,
-							&b.value,
-							b.name.clone()
-						)
+					evaluate_named(
+						context_creator.create(this.clone(), super_obj.clone())?,
+						&b.value,
+						b.name.clone()
+					)
 				)))
 			})),
 		)
@@ -217,18 +239,10 @@
 pub fn evaluate_member_list_object(context: Context, members: &[Member]) -> Result<ObjValue> {
 	let new_bindings = FutureWrapper::new();
 	let future_this = FutureWrapper::new();
-	let context_creator = context_creator!(
-		closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {
-			context.clone().extend_unbound(
-				new_bindings.clone().unwrap(),
-				context.dollar().clone().or_else(||this.clone()),
-				Some(this.unwrap()),
-				super_obj
-			)
-		})
-	);
+	let context_creator = ContextCreator(context.clone(), new_bindings.clone());
 	{
-		let mut bindings: HashMap<IStr, LazyBinding> = HashMap::new();
+		let mut bindings: FxHashMap<IStr, LazyBinding> =
+			FxHashMap::with_capacity_and_hasher(members.len(), BuildHasherDefault::default());
 		for (n, b) in members
 			.iter()
 			.filter_map(|m| match m {
@@ -265,7 +279,7 @@
 						invoke: LazyBinding::Bindable(Rc::new(
 							closure!(clone name, clone value, clone context_creator, |this, super_obj| {
 								Ok(LazyVal::new_resolved(evaluate(
-									context_creator.0(this, super_obj)?,
+									context_creator.create(this, super_obj)?,
 									&value,
 								)?))
 							}),
@@ -294,7 +308,7 @@
 							closure!(clone value, clone context_creator, clone params, clone name, |this, super_obj| {
 								// TODO: Assert
 								Ok(LazyVal::new_resolved(evaluate_method(
-									context_creator.0(this, super_obj)?,
+									context_creator.create(this, super_obj)?,
 									name.clone(),
 									params.clone(),
 									value.clone(),
@@ -324,17 +338,8 @@
 				context.clone(),
 				&|ctx| {
 					let new_bindings = FutureWrapper::new();
-					let context_creator = context_creator!(
-						closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {
-							context.clone().extend_unbound(
-								new_bindings.clone().unwrap(),
-								context.dollar().clone().or_else(||this.clone()),
-								None,
-								super_obj
-							)
-						})
-					);
-					let mut bindings: HashMap<IStr, LazyBinding> = HashMap::new();
+					let context_creator = ContextCreator(context.clone(), new_bindings.clone());
+					let mut bindings: FxHashMap<IStr, LazyBinding> = FxHashMap::with_capacity_and_hasher(obj.pre_locals.len() + obj.post_locals.len(), BuildHasherDefault::default());
 					for (n, b) in obj
 						.pre_locals
 						.iter()
@@ -497,22 +502,19 @@
 			}
 		}
 		LocalExpr(bindings, returned) => {
-			let mut new_bindings: HashMap<IStr, LazyBinding> = HashMap::new();
+			let mut new_bindings: FxHashMap<IStr, LazyVal> = HashMap::with_capacity_and_hasher(
+				bindings.len(),
+				BuildHasherDefault::<FxHasher>::default(),
+			);
 			let future_context = Context::new_future();
-
-			let context_creator = context_creator!(
-				closure!(clone future_context, |_, _| Ok(future_context.clone().unwrap()))
-			);
-
-			for (k, v) in bindings
-				.iter()
-				.map(|b| evaluate_binding(b, context_creator.clone()))
-			{
-				new_bindings.insert(k, v);
+			for b in bindings {
+				new_bindings.insert(
+					b.name.clone(),
+					evaluate_binding_in_future(b, future_context.clone()),
+				);
 			}
-
 			let context = context
-				.extend_unbound(new_bindings, None, None, None)?
+				.extend_bound(new_bindings)
 				.into_future(future_context);
 			evaluate(context, &returned.clone())?
 		}
modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -27,10 +27,12 @@
 use jrsonnet_parser::*;
 use native::NativeCallback;
 pub use obj::*;
+use rustc_hash::FxHashMap;
 use std::{
 	cell::{Ref, RefCell, RefMut},
 	collections::HashMap,
 	fmt::Debug,
+	hash::BuildHasherDefault,
 	path::PathBuf,
 	rc::Rc,
 };
@@ -230,7 +232,7 @@
 			}
 			value.parsed.clone()
 		};
-		let value = evaluate(self.create_default_context()?, &expr)?;
+		let value = evaluate(self.create_default_context(), &expr)?;
 		{
 			self.data_mut()
 				.files
@@ -260,16 +262,14 @@
 	}
 
 	/// Creates context with all passed global variables
-	pub fn create_default_context(&self) -> Result<Context> {
+	pub fn create_default_context(&self) -> Context {
 		let globals = &self.settings().globals;
-		let mut new_bindings: HashMap<IStr, LazyBinding> = HashMap::new();
+		let mut new_bindings: FxHashMap<IStr, LazyVal> =
+			FxHashMap::with_capacity_and_hasher(globals.len(), BuildHasherDefault::default());
 		for (name, value) in globals.iter() {
-			new_bindings.insert(
-				name.clone(),
-				LazyBinding::Bound(resolved_lazy_val!(value.clone())),
-			);
+			new_bindings.insert(name.clone(), resolved_lazy_val!(value.clone()));
 		}
-		Context::new().extend_unbound(new_bindings, None, None, None)
+		Context::new().extend_bound(new_bindings)
 	}
 
 	/// Executes code creating a new stack frame
@@ -345,7 +345,7 @@
 					|| "during TLA call".to_owned(),
 					|| {
 						func.evaluate_map(
-							self.create_default_context()?,
+							self.create_default_context(),
 							&self.settings().tla_vars,
 							true,
 						)
@@ -396,7 +396,7 @@
 	}
 	/// Evaluates the parsed expression
 	pub fn evaluate_expr_raw(&self, code: LocExpr) -> Result<Val> {
-		self.run_in_state(|| evaluate(self.create_default_context()?, &code))
+		self.run_in_state(|| evaluate(self.create_default_context(), &code))
 	}
 }
 
@@ -950,6 +950,23 @@
 		Ok(())
 	}
 
+	#[test]
+	fn comp_self() -> crate::error::Result<()> {
+		assert_eval!(
+			r#"
+			std.objectFields({
+				a:{
+					[name]: name for name in std.objectFields(self)
+				},
+				b: 2,
+				c: 3,
+			}.a) == ['a', 'b', 'c']
+			"#
+		);
+
+		Ok(())
+	}
+
 	struct TestImportResolver(IStr);
 	impl crate::import::ImportResolver for TestImportResolver {
 		fn resolve_file(&self, _: &PathBuf, _: &PathBuf) -> crate::error::Result<Rc<PathBuf>> {
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -535,7 +535,7 @@
 	pub fn to_yaml(&self, padding: usize) -> Result<IStr> {
 		with_state(|s| {
 			let ctx = s
-				.create_default_context()?
+				.create_default_context()
 				.with_var("__tmp__to_json__".into(), self.clone());
 			evaluate(
 				ctx,