difftreelog
perf use weak objvalue as cache key
in: master
4 files changed
crates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-evaluator/Cargo.toml
+++ b/crates/jrsonnet-evaluator/Cargo.toml
@@ -15,9 +15,6 @@
# Allows library authors to throw custom errors
anyhow-error = ["anyhow"]
-# Unlocks extra features, but works only on unstable
-unstable = []
-
[dependencies]
jrsonnet-interner = { path = "../jrsonnet-interner", version = "0.4.2" }
jrsonnet-parser = { path = "../jrsonnet-parser", version = "0.4.2" }
crates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth1use crate::cc_ptr_eq;2use crate::gc::GcHashMap;3use crate::{4 error::Error::*, map::LayeredHashMap, FutureWrapper, LazyBinding, LazyVal, ObjValue, Result,5 Val,6};7use gcmodule::{Cc, Trace};8use jrsonnet_interner::IStr;9use std::fmt::Debug;1011#[derive(Clone, Trace)]12pub struct ContextCreator(pub Context, pub FutureWrapper<GcHashMap<IStr, LazyBinding>>);13impl ContextCreator {14 pub fn create(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<Context> {15 self.0.clone().extend_unbound(16 self.1.clone().unwrap(),17 self.0.dollar().clone().or_else(|| this.clone()),18 this,19 super_obj,20 )21 }22}2324#[derive(Trace)]25struct ContextInternals {26 dollar: Option<ObjValue>,27 this: Option<ObjValue>,28 super_obj: Option<ObjValue>,29 bindings: LayeredHashMap,30}31impl Debug for ContextInternals {32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {33 f.debug_struct("Context").finish()34 }35}3637#[derive(Debug, Clone, Trace)]38pub struct Context(Cc<ContextInternals>);39impl Context {40 pub fn new_future() -> FutureWrapper<Self> {41 FutureWrapper::new()42 }4344 pub fn dollar(&self) -> &Option<ObjValue> {45 &self.0.dollar46 }4748 pub fn this(&self) -> &Option<ObjValue> {49 &self.0.this50 }5152 pub fn super_obj(&self) -> &Option<ObjValue> {53 &self.0.super_obj54 }5556 pub fn new() -> Self {57 Self(Cc::new(ContextInternals {58 dollar: None,59 this: None,60 super_obj: None,61 bindings: LayeredHashMap::default(),62 }))63 }6465 pub fn binding(&self, name: IStr) -> Result<LazyVal> {66 Ok(self67 .068 .bindings69 .get(&name)70 .cloned()71 .ok_or(VariableIsNotDefined(name))?)72 }73 pub fn contains_binding(&self, name: IStr) -> bool {74 self.0.bindings.contains_key(&name)75 }76 pub fn into_future(self, ctx: FutureWrapper<Self>) -> Self {77 {78 ctx.0.borrow_mut().replace(self);79 }80 ctx.unwrap()81 }8283 pub fn with_var(self, name: IStr, value: Val) -> Self {84 let mut new_bindings = GcHashMap::with_capacity(1);85 new_bindings.insert(name, LazyVal::new_resolved(value));86 self.extend(new_bindings, None, None, None)87 }8889 pub fn with_this_super(self, new_this: ObjValue, new_super_obj: Option<ObjValue>) -> Self {90 self.extend(GcHashMap::new(), None, Some(new_this), new_super_obj)91 }9293 pub fn extend(94 self,95 new_bindings: GcHashMap<IStr, LazyVal>,96 new_dollar: Option<ObjValue>,97 new_this: Option<ObjValue>,98 new_super_obj: Option<ObjValue>,99 ) -> Self {100 let ctx = &self.0;101 let dollar = new_dollar.or_else(|| ctx.dollar.clone());102 let this = new_this.or_else(|| ctx.this.clone());103 let super_obj = new_super_obj.or_else(|| ctx.super_obj.clone());104 let bindings = if new_bindings.is_empty() {105 ctx.bindings.clone()106 } else {107 ctx.bindings.clone().extend(new_bindings)108 };109 Self(Cc::new(ContextInternals {110 dollar,111 this,112 super_obj,113 bindings,114 }))115 }116 pub fn extend_bound(self, new_bindings: GcHashMap<IStr, LazyVal>) -> Self {117 let new_this = self.0.this.clone();118 let new_super_obj = self.0.super_obj.clone();119 self.extend(new_bindings, None, new_this, new_super_obj)120 }121 pub fn extend_unbound(122 self,123 new_bindings: GcHashMap<IStr, LazyBinding>,124 new_dollar: Option<ObjValue>,125 new_this: Option<ObjValue>,126 new_super_obj: Option<ObjValue>,127 ) -> Result<Self> {128 let this = new_this.or_else(|| self.0.this.clone());129 let super_obj = new_super_obj.or_else(|| self.0.super_obj.clone());130 let mut new = GcHashMap::with_capacity(new_bindings.len());131 for (k, v) in new_bindings.0.into_iter() {132 new.insert(k, v.evaluate(this.clone(), super_obj.clone())?);133 }134 Ok(self.extend(new, new_dollar, this, super_obj))135 }136 #[cfg(feature = "unstable")]137 pub fn into_weak(self) -> WeakContext {138 WeakContext(Rc::downgrade(&self.0))139 }140}141142impl Default for Context {143 fn default() -> Self {144 Self::new()145 }146}147148impl PartialEq for Context {149 fn eq(&self, other: &Self) -> bool {150 cc_ptr_eq(&self.0, &other.0)151 }152}1use crate::cc_ptr_eq;2use crate::gc::GcHashMap;3use crate::{4 error::Error::*, map::LayeredHashMap, FutureWrapper, LazyBinding, LazyVal, ObjValue, Result,5 Val,6};7use gcmodule::{Cc, Trace};8use jrsonnet_interner::IStr;9use std::fmt::Debug;1011#[derive(Clone, Trace)]12pub struct ContextCreator(pub Context, pub FutureWrapper<GcHashMap<IStr, LazyBinding>>);13impl ContextCreator {14 pub fn create(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<Context> {15 self.0.clone().extend_unbound(16 self.1.clone().unwrap(),17 self.0.dollar().clone().or_else(|| this.clone()),18 this,19 super_obj,20 )21 }22}2324#[derive(Trace)]25struct ContextInternals {26 dollar: Option<ObjValue>,27 this: Option<ObjValue>,28 super_obj: Option<ObjValue>,29 bindings: LayeredHashMap,30}31impl Debug for ContextInternals {32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {33 f.debug_struct("Context").finish()34 }35}3637#[derive(Debug, Clone, Trace)]38pub struct Context(Cc<ContextInternals>);39impl Context {40 pub fn new_future() -> FutureWrapper<Self> {41 FutureWrapper::new()42 }4344 pub fn dollar(&self) -> &Option<ObjValue> {45 &self.0.dollar46 }4748 pub fn this(&self) -> &Option<ObjValue> {49 &self.0.this50 }5152 pub fn super_obj(&self) -> &Option<ObjValue> {53 &self.0.super_obj54 }5556 pub fn new() -> Self {57 Self(Cc::new(ContextInternals {58 dollar: None,59 this: None,60 super_obj: None,61 bindings: LayeredHashMap::default(),62 }))63 }6465 pub fn binding(&self, name: IStr) -> Result<LazyVal> {66 Ok(self67 .068 .bindings69 .get(&name)70 .cloned()71 .ok_or(VariableIsNotDefined(name))?)72 }73 pub fn contains_binding(&self, name: IStr) -> bool {74 self.0.bindings.contains_key(&name)75 }76 pub fn into_future(self, ctx: FutureWrapper<Self>) -> Self {77 {78 ctx.0.borrow_mut().replace(self);79 }80 ctx.unwrap()81 }8283 pub fn with_var(self, name: IStr, value: Val) -> Self {84 let mut new_bindings = GcHashMap::with_capacity(1);85 new_bindings.insert(name, LazyVal::new_resolved(value));86 self.extend(new_bindings, None, None, None)87 }8889 pub fn with_this_super(self, new_this: ObjValue, new_super_obj: Option<ObjValue>) -> Self {90 self.extend(GcHashMap::new(), None, Some(new_this), new_super_obj)91 }9293 pub fn extend(94 self,95 new_bindings: GcHashMap<IStr, LazyVal>,96 new_dollar: Option<ObjValue>,97 new_this: Option<ObjValue>,98 new_super_obj: Option<ObjValue>,99 ) -> Self {100 let ctx = &self.0;101 let dollar = new_dollar.or_else(|| ctx.dollar.clone());102 let this = new_this.or_else(|| ctx.this.clone());103 let super_obj = new_super_obj.or_else(|| ctx.super_obj.clone());104 let bindings = if new_bindings.is_empty() {105 ctx.bindings.clone()106 } else {107 ctx.bindings.clone().extend(new_bindings)108 };109 Self(Cc::new(ContextInternals {110 dollar,111 this,112 super_obj,113 bindings,114 }))115 }116 pub fn extend_bound(self, new_bindings: GcHashMap<IStr, LazyVal>) -> Self {117 let new_this = self.0.this.clone();118 let new_super_obj = self.0.super_obj.clone();119 self.extend(new_bindings, None, new_this, new_super_obj)120 }121 pub fn extend_unbound(122 self,123 new_bindings: GcHashMap<IStr, LazyBinding>,124 new_dollar: Option<ObjValue>,125 new_this: Option<ObjValue>,126 new_super_obj: Option<ObjValue>,127 ) -> Result<Self> {128 let this = new_this.or_else(|| self.0.this.clone());129 let super_obj = new_super_obj.or_else(|| self.0.super_obj.clone());130 let mut new = GcHashMap::with_capacity(new_bindings.len());131 for (k, v) in new_bindings.0.into_iter() {132 new.insert(k, v.evaluate(this.clone(), super_obj.clone())?);133 }134 Ok(self.extend(new, new_dollar, this, super_obj))135 }136}137138impl Default for Context {139 fn default() -> Self {140 Self::new()141 }142}143144impl PartialEq for Context {145 fn eq(&self, other: &Self) -> bool {146 cc_ptr_eq(&self.0, &other.0)147 }148}crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -1,4 +1,3 @@
-#![cfg_attr(feature = "unstable", feature(stmt_expr_attributes))]
#![warn(clippy::all, clippy::nursery)]
#![allow(
macro_expanded_macro_exports_accessed_by_absolute_paths,
@@ -31,7 +30,7 @@
pub use evaluate::*;
use function::{Builtin, TlaArg};
use gc::{GcHashMap, TraceBox};
-use gcmodule::{Cc, Trace};
+use gcmodule::{Cc, Trace, Weak};
pub use import::*;
pub use jrsonnet_interner::IStr;
use jrsonnet_parser::*;
@@ -653,6 +652,28 @@
std::ptr::eq(a, b)
}
+fn weak_raw<T>(a: Weak<T>) -> *const () {
+ unsafe { std::mem::transmute(a) }
+}
+fn weak_ptr_eq<T>(a: Weak<T>, b: Weak<T>) -> bool {
+ std::ptr::eq(weak_raw(a), weak_raw(b))
+}
+
+#[test]
+fn weak_unsafe() {
+ let a = Cc::new(1);
+ let b = Cc::new(2);
+
+ let aw1 = a.clone().downgrade();
+ let aw2 = a.clone().downgrade();
+ let aw3 = a.clone().downgrade();
+
+ let bw = b.clone().downgrade();
+
+ assert!(weak_ptr_eq(aw1, aw2));
+ assert!(!weak_ptr_eq(aw3, bw));
+}
+
#[cfg(test)]
pub mod tests {
use super::Val;
crates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/obj.rs
+++ b/crates/jrsonnet-evaluator/src/obj.rs
@@ -1,7 +1,7 @@
use crate::gc::{GcHashMap, GcHashSet, TraceBox};
use crate::operator::evaluate_add_op;
-use crate::{cc_ptr_eq, Bindable, LazyBinding, LazyVal, Result, Val};
-use gcmodule::{Cc, Trace};
+use crate::{cc_ptr_eq, weak_ptr_eq, weak_raw, Bindable, LazyBinding, LazyVal, Result, Val};
+use gcmodule::{Cc, Trace, Weak};
use jrsonnet_interner::IStr;
use jrsonnet_parser::{ExprLocation, Visibility};
use rustc_hash::FxHashMap;
@@ -22,7 +22,7 @@
}
// Field => This
-type CacheKey = (IStr, ObjValue);
+type CacheKey = (IStr, WeakObjValue);
#[derive(Trace)]
#[force_tracking]
pub struct ObjValueInternals {
@@ -35,6 +35,22 @@
}
#[derive(Clone, Trace)]
+pub struct WeakObjValue(#[skip_trace] pub(crate) Weak<ObjValueInternals>);
+
+impl PartialEq for WeakObjValue {
+ fn eq(&self, other: &Self) -> bool {
+ weak_ptr_eq(self.0.clone(), other.0.clone())
+ }
+}
+
+impl Eq for WeakObjValue {}
+impl Hash for WeakObjValue {
+ fn hash<H: Hasher>(&self, hasher: &mut H) {
+ hasher.write_usize(weak_raw(self.0.clone()) as usize)
+ }
+}
+
+#[derive(Clone, Trace)]
pub struct ObjValue(pub(crate) Cc<ObjValueInternals>);
impl Debug for ObjValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -50,14 +66,7 @@
for (name, member) in self.0.this_entries.iter() {
debug.field(name, member);
}
- #[cfg(feature = "unstable")]
- {
- debug.finish_non_exhaustive()
- }
- #[cfg(not(feature = "unstable"))]
- {
- debug.finish()
- }
+ debug.finish_non_exhaustive()
}
}
@@ -217,7 +226,7 @@
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(), real_this.clone());
+ let cache_key = (key.clone(), WeakObjValue(real_this.0.downgrade()));
if let Some(v) = self.0.value_cache.borrow().get(&cache_key) {
return Ok(v.clone());