From 9d2a45fdc4343a6a332eb8002fe5e0feb8c3d3a9 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sat, 20 Feb 2021 18:25:47 +0000 Subject: [PATCH] perf: cleanup ObjValue --- --- a/crates/jrsonnet-evaluator/src/builtin/manifest.rs +++ b/crates/jrsonnet-evaluator/src/builtin/manifest.rs @@ -81,7 +81,7 @@ } Val::Obj(obj) => { buf.push('{'); - let fields = obj.visible_fields(); + let fields = obj.fields(); if !fields.is_empty() { if mtype != ManifestType::ToString && mtype != ManifestType::Minify { buf.push('\n'); --- 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, - this_entries: Rc>, - value_cache: RefCell>>, + this_obj: Option, + this_entries: Rc>, + value_cache: RefCell>>, } + #[derive(Clone)] pub struct ObjValue(pub(crate) Rc); impl Debug for ObjValue { @@ -48,15 +51,16 @@ } impl ObjValue { - pub fn new(super_obj: Option, this_entries: Rc>) -> Self { + pub fn new(super_obj: Option, this_entries: Rc>) -> 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 { - 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 { + 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 { - let mut visible_fields: Vec<_> = self + pub fn fields_ex(&self, include_hidden: bool) -> Vec { + 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 { + self.fields_ex(false) + } + + pub fn field_visibility(&self, name: IStr) -> Option { + 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> { - 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> { 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 { - 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(&self, state: &mut H) { + state.write_usize(Rc::as_ptr(&self.0) as usize) + } +} -- gitstuff