git.delta.rocks / jrsonnet / refs/commits / 9d2a45fdc434

difftreelog

perf cleanup ObjValue

Yaroslav Bolyukin2021-02-20parent: #67899df.patch.diff
in: master

2 files changed

modifiedcrates/jrsonnet-evaluator/src/builtin/manifest.rsdiffbeforeafterboth
81 }81 }
82 Val::Obj(obj) => {82 Val::Obj(obj) => {
83 buf.push('{');83 buf.push('{');
84 let fields = obj.visible_fields();84 let fields = obj.fields();
85 if !fields.is_empty() {85 if !fields.is_empty() {
86 if mtype != ManifestType::ToString && mtype != ManifestType::Minify {86 if mtype != ManifestType::ToString && mtype != ManifestType::Minify {
87 buf.push('\n');87 buf.push('\n');
modifiedcrates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/obj.rs
+++ b/crates/jrsonnet-evaluator/src/obj.rs
@@ -1,8 +1,9 @@
 use crate::{evaluate_add_op, LazyBinding, Result, Val};
-use indexmap::IndexMap;
 use jrsonnet_interner::IStr;
 use jrsonnet_parser::{ExprLocation, Visibility};
-use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
+use rustc_hash::FxHashMap;
+use std::hash::{Hash, Hasher};
+use std::{cell::RefCell, fmt::Debug, rc::Rc};
 
 #[derive(Debug)]
 pub struct ObjMember {
@@ -13,13 +14,15 @@
 }
 
 // Field => This
-type CacheKey = (IStr, usize);
+type CacheKey = (IStr, ObjValue);
 #[derive(Debug)]
 pub struct ObjValueInternals {
 	super_obj: Option<ObjValue>,
-	this_entries: Rc<HashMap<IStr, ObjMember>>,
-	value_cache: RefCell<HashMap<CacheKey, Option<Val>>>,
+	this_obj: Option<ObjValue>,
+	this_entries: Rc<FxHashMap<IStr, ObjMember>>,
+	value_cache: RefCell<FxHashMap<CacheKey, Option<Val>>>,
 }
+
 #[derive(Clone)]
 pub struct ObjValue(pub(crate) Rc<ObjValueInternals>);
 impl Debug for ObjValue {
@@ -48,15 +51,16 @@
 }
 
 impl ObjValue {
-	pub fn new(super_obj: Option<Self>, this_entries: Rc<HashMap<IStr, ObjMember>>) -> Self {
+	pub fn new(super_obj: Option<Self>, this_entries: Rc<FxHashMap<IStr, ObjMember>>) -> Self {
 		Self(Rc::new(ObjValueInternals {
 			super_obj,
+			this_obj: None,
 			this_entries,
-			value_cache: RefCell::new(HashMap::new()),
+			value_cache: RefCell::new(FxHashMap::default()),
 		}))
 	}
 	pub fn new_empty() -> Self {
-		Self::new(None, Rc::new(HashMap::new()))
+		Self::new(None, Rc::new(FxHashMap::default()))
 	}
 	pub fn with_super(&self, super_obj: Self) -> Self {
 		match &self.0.super_obj {
@@ -64,23 +68,37 @@
 			Some(v) => Self::new(Some(v.with_super(super_obj)), self.0.this_entries.clone()),
 		}
 	}
-	pub fn enum_fields(&self, handler: &impl Fn(&IStr, &Visibility)) {
+	pub fn with_this(&self, this_obj: Self) -> Self {
+		Self(Rc::new(ObjValueInternals {
+			super_obj: self.0.super_obj.clone(),
+			this_obj: Some(this_obj),
+			this_entries: self.0.this_entries.clone(),
+			value_cache: RefCell::new(FxHashMap::default()),
+		}))
+	}
+
+	/// Run callback for every field found in object
+	pub(crate) fn enum_fields(&self, handler: &mut impl FnMut(&IStr, &Visibility) -> bool) -> bool {
 		if let Some(s) = &self.0.super_obj {
-			s.enum_fields(handler);
+			if s.enum_fields(handler) {
+				return true;
+			}
 		}
 		for (name, member) in self.0.this_entries.iter() {
-			handler(name, &member.visibility);
+			if handler(name, &member.visibility) {
+				return true;
+			}
 		}
+		false
 	}
-	pub fn fields_visibility(&self) -> IndexMap<IStr, bool> {
-		let out = Rc::new(RefCell::new(IndexMap::new()));
-		self.enum_fields(&|name, visibility| {
-			let mut out = out.borrow_mut();
+
+	pub fn fields_visibility(&self) -> FxHashMap<IStr, bool> {
+		let mut out = FxHashMap::default();
+		self.enum_fields(&mut |name, visibility| {
 			match visibility {
 				Visibility::Normal => {
-					if !out.contains_key(name) {
-						out.insert(name.to_owned(), true);
-					}
+					let entry = out.entry(name.to_owned());
+					entry.or_insert(true);
 				}
 				Visibility::Hidden => {
 					out.insert(name.to_owned(), false);
@@ -89,25 +107,71 @@
 					out.insert(name.to_owned(), true);
 				}
 			};
+			false
 		});
-		Rc::try_unwrap(out).unwrap().into_inner()
+		out
 	}
-	pub fn visible_fields(&self) -> Vec<IStr> {
-		let mut visible_fields: Vec<_> = self
+	pub fn fields_ex(&self, include_hidden: bool) -> Vec<IStr> {
+		let mut fields: Vec<_> = self
 			.fields_visibility()
 			.into_iter()
-			.filter(|(_k, v)| *v)
+			.filter(|(_k, v)| include_hidden || *v)
 			.map(|(k, _)| k)
 			.collect();
-		visible_fields.sort();
-		visible_fields
+		fields.sort_unstable();
+		fields
+	}
+	pub fn fields(&self) -> Vec<IStr> {
+		self.fields_ex(false)
+	}
+
+	pub fn field_visibility(&self, name: IStr) -> Option<Visibility> {
+		if let Some(m) = self.0.this_entries.get(&name) {
+			Some(match &m.visibility {
+				Visibility::Normal => self
+					.0
+					.super_obj
+					.as_ref()
+					.and_then(|super_obj| super_obj.field_visibility(name))
+					.unwrap_or(Visibility::Normal),
+				v => *v,
+			})
+		} else if let Some(super_obj) = &self.0.super_obj {
+			super_obj.field_visibility(name)
+		} else {
+			None
+		}
+	}
+
+	fn has_field_include_hidden(&self, name: IStr) -> bool {
+		if self.0.this_entries.contains_key(&name) {
+			true
+		} else if let Some(super_obj) = &self.0.super_obj {
+			super_obj.has_field_include_hidden(name)
+		} else {
+			false
+		}
+	}
+
+	pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {
+		if include_hidden {
+			self.has_field_include_hidden(name)
+		} else {
+			self.has_field(name)
+		}
+	}
+	pub fn has_field(&self, name: IStr) -> bool {
+		self.field_visibility(name)
+			.map(|v| v.is_visible())
+			.unwrap_or(false)
 	}
+
 	pub fn get(&self, key: IStr) -> Result<Option<Val>> {
-		Ok(self.get_raw(key, None)?)
+		self.get_raw(key, self.0.this_obj.as_ref())
 	}
 	pub(crate) fn get_raw(&self, key: IStr, real_this: Option<&Self>) -> Result<Option<Val>> {
 		let real_this = real_this.unwrap_or(self);
-		let cache_key = (key.clone(), Rc::as_ptr(&real_this.0) as usize);
+		let cache_key = (key.clone(), real_this.clone());
 
 		if let Some(v) = self.0.value_cache.borrow().get(&cache_key) {
 			return Ok(v.clone());
@@ -135,17 +199,25 @@
 		Ok(value)
 	}
 	fn evaluate_this(&self, v: &ObjMember, real_this: &Self) -> Result<Val> {
-		Ok(v.invoke
+		v.invoke
 			.evaluate(Some(real_this.clone()), self.0.super_obj.clone())?
-			.evaluate()?)
+			.evaluate()
 	}
 
 	pub fn ptr_eq(a: &Self, b: &Self) -> bool {
 		Rc::ptr_eq(&a.0, &b.0)
 	}
 }
+
 impl PartialEq for ObjValue {
 	fn eq(&self, other: &Self) -> bool {
 		Rc::ptr_eq(&self.0, &other.0)
 	}
 }
+
+impl Eq for ObjValue {}
+impl Hash for ObjValue {
+	fn hash<H: Hasher>(&self, state: &mut H) {
+		state.write_usize(Rc::as_ptr(&self.0) as usize)
+	}
+}