From 0a2dd7a14884bf962669889401ed321f18945de7 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sat, 5 Jun 2021 21:47:21 +0300 Subject: [PATCH 01/19] feat(jrsonnet-types): implement gc It is currently not possible to implement recursive type, so all tracing is noop here Signed-off-by: Yaroslav Bolyukin --- crates/jrsonnet-types/Cargo.toml | 1 + crates/jrsonnet-types/src/lib.rs | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/crates/jrsonnet-types/Cargo.toml b/crates/jrsonnet-types/Cargo.toml index 52a3155a..637759f6 100644 --- a/crates/jrsonnet-types/Cargo.toml +++ b/crates/jrsonnet-types/Cargo.toml @@ -8,3 +8,4 @@ edition = "2018" [dependencies] peg = "0.7.0" +gc = { version = "0.4.1", features = ["derive"] } diff --git a/crates/jrsonnet-types/src/lib.rs b/crates/jrsonnet-types/src/lib.rs index d3efe63a..40349739 100644 --- a/crates/jrsonnet-types/src/lib.rs +++ b/crates/jrsonnet-types/src/lib.rs @@ -1,5 +1,6 @@ #![allow(clippy::redundant_closure_call)] +use gc::{unsafe_empty_trace, Finalize, Trace}; use std::fmt::Display; #[macro_export] @@ -87,6 +88,10 @@ pub enum ValType { Obj, Func, } +impl Finalize for ValType {} +unsafe impl Trace for ValType { + unsafe_empty_trace!(); +} impl ValType { pub const fn name(&self) -> &'static str { @@ -123,6 +128,11 @@ pub enum ComplexValType { Sum(Vec), SumRef(&'static [ComplexValType]), } +impl Finalize for ComplexValType {} +unsafe impl Trace for ComplexValType { + unsafe_empty_trace!(); +} + impl From for ComplexValType { fn from(s: ValType) -> Self { Self::Simple(s) From 6766bfc4d87cad5de01af9f1b09fcd54f822429f Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sat, 5 Jun 2021 21:52:38 +0300 Subject: [PATCH 02/19] feat(jrsonnet-parser): implement gc AST can't have cycles, so tracing is noop here Signed-off-by: Yaroslav Bolyukin --- crates/jrsonnet-parser/Cargo.toml | 1 + crates/jrsonnet-parser/src/expr.rs | 95 ++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/crates/jrsonnet-parser/Cargo.toml b/crates/jrsonnet-parser/Cargo.toml index bdc00a45..110b1941 100644 --- a/crates/jrsonnet-parser/Cargo.toml +++ b/crates/jrsonnet-parser/Cargo.toml @@ -18,6 +18,7 @@ peg = "0.7.0" unescape = "0.1.0" serde = { version = "1.0", features = ["derive", "rc"], optional = true } +gc = { version = "0.4.1", features = ["derive"] } [dev-dependencies] jrsonnet-stdlib = { path = "../jrsonnet-stdlib", version = "0.3.7" } diff --git a/crates/jrsonnet-parser/src/expr.rs b/crates/jrsonnet-parser/src/expr.rs index f3662fe6..e4d4d42e 100644 --- a/crates/jrsonnet-parser/src/expr.rs +++ b/crates/jrsonnet-parser/src/expr.rs @@ -1,3 +1,4 @@ +use gc::{unsafe_empty_trace, Finalize, Trace}; use jrsonnet_interner::IStr; #[cfg(feature = "deserialize")] use serde::Deserialize; @@ -19,6 +20,10 @@ pub enum FieldName { /// {["dyn"+"amic"]: 3} Dyn(LocExpr), } +impl Finalize for FieldName {} +unsafe impl Trace for FieldName { + unsafe_empty_trace!(); +} #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] @@ -31,6 +36,10 @@ pub enum Visibility { /// ::: Unhide, } +impl Finalize for Visibility {} +unsafe impl Trace for Visibility { + unsafe_empty_trace!(); +} impl Visibility { pub fn is_visible(&self) -> bool { @@ -42,6 +51,10 @@ impl Visibility { #[cfg_attr(feature = "deserialize", derive(Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct AssertStmt(pub LocExpr, pub Option); +impl Finalize for AssertStmt {} +unsafe impl Trace for AssertStmt { + unsafe_empty_trace!(); +} #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] @@ -53,6 +66,10 @@ pub struct FieldMember { pub visibility: Visibility, pub value: LocExpr, } +impl Finalize for FieldMember {} +unsafe impl Trace for FieldMember { + unsafe_empty_trace!(); +} #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] @@ -62,6 +79,10 @@ pub enum Member { BindStmt(BindSpec), AssertStmt(AssertStmt), } +impl Finalize for Member {} +unsafe impl Trace for Member { + unsafe_empty_trace!(); +} #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] @@ -72,6 +93,11 @@ pub enum UnaryOpType { BitNot, Not, } +impl Finalize for UnaryOpType {} +unsafe impl Trace for UnaryOpType { + unsafe_empty_trace!(); +} + impl Display for UnaryOpType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use UnaryOpType::*; @@ -119,6 +145,11 @@ pub enum BinaryOpType { And, Or, } +impl Finalize for BinaryOpType {} +unsafe impl Trace for BinaryOpType { + unsafe_empty_trace!(); +} + impl Display for BinaryOpType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use BinaryOpType::*; @@ -154,12 +185,21 @@ impl Display for BinaryOpType { #[cfg_attr(feature = "deserialize", derive(Deserialize))] #[derive(Debug, PartialEq)] pub struct Param(pub IStr, pub Option); +impl Finalize for Param {} +unsafe impl Trace for Param { + unsafe_empty_trace!(); +} /// Defined function parameters #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] #[derive(Debug, Clone, PartialEq)] pub struct ParamsDesc(pub Rc>); +impl Finalize for ParamsDesc {} +unsafe impl Trace for ParamsDesc { + unsafe_empty_trace!(); +} + impl Deref for ParamsDesc { type Target = Vec; fn deref(&self) -> &Self::Target { @@ -171,11 +211,20 @@ impl Deref for ParamsDesc { #[cfg_attr(feature = "deserialize", derive(Deserialize))] #[derive(Debug, PartialEq)] pub struct Arg(pub Option, pub LocExpr); +impl Finalize for Arg {} +unsafe impl Trace for Arg { + unsafe_empty_trace!(); +} #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] #[derive(Debug, PartialEq)] pub struct ArgsDesc(pub Vec); +impl Finalize for ArgsDesc {} +unsafe impl Trace for ArgsDesc { + unsafe_empty_trace!(); +} + impl Deref for ArgsDesc { type Target = Vec; fn deref(&self) -> &Self::Target { @@ -191,16 +240,28 @@ pub struct BindSpec { pub params: Option, pub value: LocExpr, } +impl Finalize for BindSpec {} +unsafe impl Trace for BindSpec { + unsafe_empty_trace!(); +} #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] #[derive(Debug, PartialEq)] pub struct IfSpecData(pub LocExpr); +impl Finalize for IfSpecData {} +unsafe impl Trace for IfSpecData { + unsafe_empty_trace!(); +} #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] #[derive(Debug, PartialEq)] pub struct ForSpecData(pub IStr, pub LocExpr); +impl Finalize for ForSpecData {} +unsafe impl Trace for ForSpecData { + unsafe_empty_trace!(); +} #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] @@ -209,6 +270,10 @@ pub enum CompSpec { IfSpec(IfSpecData), ForSpec(ForSpecData), } +impl Finalize for CompSpec {} +unsafe impl Trace for CompSpec { + unsafe_empty_trace!(); +} #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] @@ -220,6 +285,10 @@ pub struct ObjComp { pub post_locals: Vec, pub compspecs: Vec, } +impl Finalize for ObjComp {} +unsafe impl Trace for ObjComp { + unsafe_empty_trace!(); +} #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] @@ -228,6 +297,10 @@ pub enum ObjBody { MemberList(Vec), ObjComp(ObjComp), } +impl Finalize for ObjBody {} +unsafe impl Trace for ObjBody { + unsafe_empty_trace!(); +} #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] @@ -240,6 +313,10 @@ pub enum LiteralType { True, False, } +impl Finalize for LiteralType {} +unsafe impl Trace for LiteralType { + unsafe_empty_trace!(); +} #[derive(Debug, PartialEq)] pub struct SliceDesc { @@ -247,6 +324,10 @@ pub struct SliceDesc { pub end: Option, pub step: Option, } +impl Finalize for SliceDesc {} +unsafe impl Trace for SliceDesc { + unsafe_empty_trace!(); +} /// Syntax base #[cfg_attr(feature = "serialize", derive(Serialize))] @@ -315,12 +396,21 @@ pub enum Expr { cond_else: Option, }, } +impl Finalize for Expr {} +unsafe impl Trace for Expr { + unsafe_empty_trace!(); +} /// file, begin offset, end offset #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] #[derive(Clone, PartialEq)] pub struct ExprLocation(pub Rc, pub usize, pub usize); +impl Finalize for ExprLocation {} +unsafe impl Trace for ExprLocation { + unsafe_empty_trace!(); +} + impl Debug for ExprLocation { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}:{:?}-{:?}", self.0, self.1, self.2) @@ -332,6 +422,11 @@ impl Debug for ExprLocation { #[cfg_attr(feature = "deserialize", derive(Deserialize))] #[derive(Clone, PartialEq)] pub struct LocExpr(pub Rc, pub Option); +impl Finalize for LocExpr {} +unsafe impl Trace for LocExpr { + unsafe_empty_trace!(); +} + impl Debug for LocExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if f.alternate() { From e2e1f7b086aac5b983adaa732881bdb233af086e Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sat, 5 Jun 2021 21:54:05 +0300 Subject: [PATCH 03/19] feat(jrsonnet-interner): implement gc Interned string is backed by Rc, there is nothing to trace here Signed-off-by: Yaroslav Bolyukin --- crates/jrsonnet-interner/Cargo.toml | 1 + crates/jrsonnet-interner/src/lib.rs | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/crates/jrsonnet-interner/Cargo.toml b/crates/jrsonnet-interner/Cargo.toml index c70bc5bf..fba704da 100644 --- a/crates/jrsonnet-interner/Cargo.toml +++ b/crates/jrsonnet-interner/Cargo.toml @@ -9,3 +9,4 @@ edition = "2018" [dependencies] serde = { version = "1.0" } rustc-hash = "1.1.0" +gc = { version = "0.4.1", features = ["derive"] } \ No newline at end of file diff --git a/crates/jrsonnet-interner/src/lib.rs b/crates/jrsonnet-interner/src/lib.rs index 51a0a0d2..9fc23cda 100644 --- a/crates/jrsonnet-interner/src/lib.rs +++ b/crates/jrsonnet-interner/src/lib.rs @@ -1,3 +1,4 @@ +use gc::{unsafe_empty_trace, Finalize, Trace}; use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; use std::{ @@ -10,6 +11,10 @@ use std::{ #[derive(Clone, PartialOrd, Ord, Eq)] pub struct IStr(Rc); +impl Finalize for IStr {} +unsafe impl Trace for IStr { + unsafe_empty_trace!(); +} impl Deref for IStr { type Target = str; From ab385dd4fde02fbc34179de3a7f593acbaf82d44 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sat, 5 Jun 2021 21:59:52 +0300 Subject: [PATCH 04/19] feat(jrsonnet-evaluator): implement gc Some manual Trace/Finalize implementations can be replaced with derives with https://github.com/Manishearth/rust-gc/pull/116 getting merged Signed-off-by: Yaroslav Bolyukin --- crates/jrsonnet-evaluator/Cargo.toml | 3 +- .../jrsonnet-evaluator/src/builtin/format.rs | 3 +- .../src/builtin/manifest.rs | 1 + crates/jrsonnet-evaluator/src/builtin/mod.rs | 31 +- crates/jrsonnet-evaluator/src/builtin/sort.rs | 14 +- crates/jrsonnet-evaluator/src/ctx.rs | 90 ++--- crates/jrsonnet-evaluator/src/dynamic.rs | 14 +- crates/jrsonnet-evaluator/src/error.rs | 12 +- crates/jrsonnet-evaluator/src/evaluate.rs | 372 ++++++++++++++---- crates/jrsonnet-evaluator/src/function.rs | 55 ++- .../src/integrations/serde.rs | 10 +- crates/jrsonnet-evaluator/src/lib.rs | 17 +- crates/jrsonnet-evaluator/src/map.rs | 39 +- crates/jrsonnet-evaluator/src/native.rs | 20 +- crates/jrsonnet-evaluator/src/obj.rs | 75 ++-- crates/jrsonnet-evaluator/src/typed.rs | 11 +- crates/jrsonnet-evaluator/src/val.rs | 193 +++++++-- 17 files changed, 656 insertions(+), 304 deletions(-) diff --git a/crates/jrsonnet-evaluator/Cargo.toml b/crates/jrsonnet-evaluator/Cargo.toml index 2217e8a5..63b1cd14 100644 --- a/crates/jrsonnet-evaluator/Cargo.toml +++ b/crates/jrsonnet-evaluator/Cargo.toml @@ -30,13 +30,12 @@ jrsonnet-stdlib = { path = "../jrsonnet-stdlib", version = "0.3.7" } jrsonnet-types = { path = "../jrsonnet-types", version = "0.3.7" } pathdiff = "0.2.0" -closure = "0.3.0" - md5 = "0.7.0" base64 = "0.13.0" rustc-hash = "1.1.0" thiserror = "1.0" +gc = { version = "0.4.1", features = ["derive"] } [dependencies.anyhow] version = "1.0" diff --git a/crates/jrsonnet-evaluator/src/builtin/format.rs b/crates/jrsonnet-evaluator/src/builtin/format.rs index f931805f..c326e616 100644 --- a/crates/jrsonnet-evaluator/src/builtin/format.rs +++ b/crates/jrsonnet-evaluator/src/builtin/format.rs @@ -2,11 +2,12 @@ #![allow(clippy::too_many_arguments)] use crate::{error::Error::*, throw, LocError, ObjValue, Result, Val}; +use gc::{Finalize, Trace}; use jrsonnet_interner::IStr; use jrsonnet_types::ValType; use thiserror::Error; -#[derive(Debug, Clone, Error)] +#[derive(Debug, Clone, Error, Trace, Finalize)] pub enum FormatError { #[error("truncated format code")] TruncatedFormatCode, diff --git a/crates/jrsonnet-evaluator/src/builtin/manifest.rs b/crates/jrsonnet-evaluator/src/builtin/manifest.rs index 1ca262d9..0353c58a 100644 --- a/crates/jrsonnet-evaluator/src/builtin/manifest.rs +++ b/crates/jrsonnet-evaluator/src/builtin/manifest.rs @@ -126,6 +126,7 @@ fn manifest_json_ex_buf( buf.push('}'); } Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())), + Val::DebugGcTraceValue(v) => manifest_json_ex_buf(&v.value, buf, cur_padding, options)?, }; Ok(()) } diff --git a/crates/jrsonnet-evaluator/src/builtin/mod.rs b/crates/jrsonnet-evaluator/src/builtin/mod.rs index 8112bceb..bf809b6a 100644 --- a/crates/jrsonnet-evaluator/src/builtin/mod.rs +++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs @@ -1,10 +1,11 @@ use crate::{ equals, error::{Error::*, Result}, - parse_args, primitive_equals, push, throw, with_state, ArrValue, Context, EvaluationState, - FuncVal, LazyVal, Val, + parse_args, primitive_equals, push, throw, with_state, ArrValue, Context, DebugGcTraceValue, + EvaluationState, FuncVal, LazyVal, Val, }; use format::{format_arr, format_obj}; +use gc::Gc; use jrsonnet_interner::IStr; use jrsonnet_parser::{ArgsDesc, BinaryOpType, ExprLocation}; use jrsonnet_types::ty; @@ -68,6 +69,8 @@ thread_local! { ("md5".into(), builtin_md5), ("base64".into(), builtin_base64), ("trace".into(), builtin_trace), + ("gc".into(), builtin_gc), + ("gcTrace".into(), builtin_gc_trace), ("join".into(), builtin_join), ("escapeStringJson".into(), builtin_escape_string_json), ("manifestJsonEx".into(), builtin_manifest_json_ex), @@ -301,7 +304,7 @@ fn builtin_native(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc parse_args!(context, "native", args, 1, [ 0, x: ty!(string) => Val::Str; ], { - Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::Func(Rc::new(FuncVal::NativeExt(x.clone(), v)))).ok_or(UndefinedExternalFunction(x))?) + Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::Func(Gc::new(FuncVal::NativeExt(x.clone(), v)))).ok_or(UndefinedExternalFunction(x))?) }) } @@ -446,6 +449,28 @@ fn builtin_trace(context: Context, loc: Option<&ExprLocation>, args: &ArgsDesc) }) } +fn builtin_gc(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result { + parse_args!(context, "gc", args, 1, [ + 0, rest: ty!(any); + ], { + println!("GC start"); + gc::force_collect(); + println!("GC done"); + + Ok(rest) + }) +} + +fn builtin_gc_trace(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result { + parse_args!(context, "gcTrace", args, 2, [ + 0, name: ty!(string) => Val::Str; + 1, rest: ty!(any); + ], { + + Ok(DebugGcTraceValue::new(name, rest)) + }) +} + fn builtin_base64(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result { parse_args!(context, "base64", args, 1, [ 0, input: ty!((string | (Array))); diff --git a/crates/jrsonnet-evaluator/src/builtin/sort.rs b/crates/jrsonnet-evaluator/src/builtin/sort.rs index dbe68902..af68772a 100644 --- a/crates/jrsonnet-evaluator/src/builtin/sort.rs +++ b/crates/jrsonnet-evaluator/src/builtin/sort.rs @@ -2,9 +2,9 @@ use crate::{ error::{Error, LocError, Result}, throw, Context, FuncVal, Val, }; -use std::rc::Rc; +use gc::{Finalize, Gc, Trace}; -#[derive(Debug, Clone, thiserror::Error)] +#[derive(Debug, Clone, thiserror::Error, Trace, Finalize)] pub enum SortError { #[error("sort key should be string or number")] SortKeyShouldBeStringOrNumber, @@ -59,13 +59,13 @@ fn get_sort_type( Ok(sort_type) } -pub fn sort(ctx: Context, mut values: Rc>, key_getter: &FuncVal) -> Result>> { +pub fn sort(ctx: Context, values: Gc>, key_getter: &FuncVal) -> Result>> { if values.len() <= 1 { return Ok(values); } if key_getter.is_ident() { - let mvalues = Rc::make_mut(&mut values); - let sort_type = get_sort_type(mvalues, |k| k)?; + let mut mvalues = (*values).clone(); + let sort_type = get_sort_type(&mut mvalues, |k| k)?; match sort_type { SortKeyType::Number => mvalues.sort_by_key(|v| match v { Val::Num(n) => NonNaNf64(*n), @@ -77,7 +77,7 @@ pub fn sort(ctx: Context, mut values: Rc>, key_getter: &FuncVal) -> Res }), SortKeyType::Unknown => unreachable!(), }; - Ok(values) + Ok(Gc::new(mvalues)) } else { let mut vk = Vec::with_capacity(values.len()); for value in values.iter() { @@ -98,6 +98,6 @@ pub fn sort(ctx: Context, mut values: Rc>, key_getter: &FuncVal) -> Res }), SortKeyType::Unknown => unreachable!(), }; - Ok(Rc::new(vk.into_iter().map(|v| v.0).collect())) + Ok(Gc::new(vk.into_iter().map(|v| v.0).collect())) } } diff --git a/crates/jrsonnet-evaluator/src/ctx.rs b/crates/jrsonnet-evaluator/src/ctx.rs index 16e27c2b..46313cdf 100644 --- a/crates/jrsonnet-evaluator/src/ctx.rs +++ b/crates/jrsonnet-evaluator/src/ctx.rs @@ -1,13 +1,14 @@ use crate::{ - error::Error::*, map::LayeredHashMap, resolved_lazy_val, FutureWrapper, LazyBinding, LazyVal, - ObjValue, Result, Val, + error::Error::*, map::LayeredHashMap, FutureWrapper, LazyBinding, LazyVal, ObjValue, Result, + Val, }; +use gc::{Finalize, Gc, Trace}; use jrsonnet_interner::IStr; use rustc_hash::FxHashMap; +use std::fmt::Debug; use std::hash::BuildHasherDefault; -use std::{fmt::Debug, rc::Rc}; -#[derive(Clone)] +#[derive(Clone, Trace, Finalize)] pub struct ContextCreator(pub Context, pub FutureWrapper>); impl ContextCreator { pub fn create(&self, this: Option, super_obj: Option) -> Result { @@ -20,6 +21,7 @@ impl ContextCreator { } } +#[derive(Trace, Finalize)] struct ContextInternals { dollar: Option, this: Option, @@ -28,15 +30,12 @@ struct ContextInternals { } impl Debug for ContextInternals { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Context") - .field("this", &self.this.as_ref().map(|e| Rc::as_ptr(&e.0))) - .field("bindings", &self.bindings) - .finish() + f.debug_struct("Context").finish() } } -#[derive(Debug, Clone)] -pub struct Context(Rc); +#[derive(Debug, Clone, Trace, Finalize)] +pub struct Context(Gc); impl Context { pub fn new_future() -> FutureWrapper { FutureWrapper::new() @@ -55,7 +54,7 @@ impl Context { } pub fn new() -> Self { - Self(Rc::new(ContextInternals { + Self(Gc::new(ContextInternals { dollar: None, this: None, super_obj: None, @@ -81,7 +80,7 @@ impl Context { pub fn with_var(self, name: IStr, value: Val) -> Self { let mut new_bindings = FxHashMap::with_capacity_and_hasher(1, BuildHasherDefault::default()); - new_bindings.insert(name, resolved_lazy_val!(value)); + new_bindings.insert(name, LazyVal::new_resolved(value)); self.extend(new_bindings, None, None, None) } @@ -96,40 +95,21 @@ impl Context { new_this: Option, new_super_obj: Option, ) -> Self { - 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); - } - Self(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) - }; - Self(Rc::new(ContextInternals { - dollar, - this, - super_obj, - bindings, - })) - } - } + let ctx = &self.0; + 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) + }; + Self(Gc::new(ContextInternals { + dollar, + this, + super_obj, + bindings, + })) } pub fn extend_bound(self, new_bindings: FxHashMap) -> Self { let new_this = self.0.this.clone(); @@ -166,22 +146,6 @@ impl Default for Context { impl PartialEq for Context { fn eq(&self, other: &Self) -> bool { - Rc::ptr_eq(&self.0, &other.0) - } -} - -#[cfg(feature = "unstable")] -#[derive(Debug, Clone)] -pub struct WeakContext(std::rc::Weak); -#[cfg(feature = "unstable")] -impl WeakContext { - pub fn upgrade(&self) -> Context { - Context(self.0.upgrade().expect("context is removed")) - } -} -#[cfg(feature = "unstable")] -impl PartialEq for WeakContext { - fn eq(&self, other: &Self) -> bool { - self.0.ptr_eq(&other.0) + Gc::ptr_eq(&self.0, &other.0) } } diff --git a/crates/jrsonnet-evaluator/src/dynamic.rs b/crates/jrsonnet-evaluator/src/dynamic.rs index d3722b63..91fa6339 100644 --- a/crates/jrsonnet-evaluator/src/dynamic.rs +++ b/crates/jrsonnet-evaluator/src/dynamic.rs @@ -1,23 +1,23 @@ -use std::{cell::RefCell, rc::Rc}; +use gc::{Finalize, Gc, GcCell, Trace}; -#[derive(Clone)] -pub struct FutureWrapper(pub Rc>>); -impl FutureWrapper { +#[derive(Clone, Trace, Finalize)] +pub struct FutureWrapper(pub Gc>>); +impl FutureWrapper { pub fn new() -> Self { - Self(Rc::new(RefCell::new(None))) + Self(Gc::new(GcCell::new(None))) } pub fn fill(self, value: T) { assert!(self.0.borrow().is_none(), "wrapper is filled already"); self.0.borrow_mut().replace(value); } } -impl FutureWrapper { +impl FutureWrapper { pub fn unwrap(&self) -> T { self.0.borrow().as_ref().cloned().unwrap() } } -impl Default for FutureWrapper { +impl Default for FutureWrapper { fn default() -> Self { Self::new() } diff --git a/crates/jrsonnet-evaluator/src/error.rs b/crates/jrsonnet-evaluator/src/error.rs index 99d808e1..7804a4dc 100644 --- a/crates/jrsonnet-evaluator/src/error.rs +++ b/crates/jrsonnet-evaluator/src/error.rs @@ -2,13 +2,14 @@ use crate::{ builtin::{format::FormatError, sort::SortError}, typed::TypeLocError, }; +use gc::{Finalize, Trace}; use jrsonnet_interner::IStr; use jrsonnet_parser::{BinaryOpType, ExprLocation, UnaryOpType}; use jrsonnet_types::ValType; use std::{path::PathBuf, rc::Rc}; use thiserror::Error; -#[derive(Error, Debug, Clone)] +#[derive(Error, Debug, Clone, Trace, Finalize)] pub enum Error { #[error("intrinsic not found: {0}")] IntrinsicNotFound(IStr), @@ -88,6 +89,7 @@ pub enum Error { ImportSyntaxError { path: Rc, source_code: IStr, + #[unsafe_ignore_trace] error: Box, }, @@ -95,6 +97,8 @@ pub enum Error { RuntimeError(IStr), #[error("stack overflow, try to reduce recursion, or set --max-stack to bigger value")] StackOverflow, + #[error("infinite recursion detected")] + RecursiveLazyValueEvaluation, #[error("tried to index by fractional value")] FractionalIndex, #[error("attempted to divide by zero")] @@ -142,15 +146,15 @@ impl From for LocError { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Trace, Finalize)] pub struct StackTraceElement { pub location: Option, pub desc: String, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Trace, Finalize)] pub struct StackTrace(pub Vec); -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Trace, Finalize)] pub struct LocError(Box<(Error, StackTrace)>); impl LocError { pub fn new(e: Error) -> Self { diff --git a/crates/jrsonnet-evaluator/src/evaluate.rs b/crates/jrsonnet-evaluator/src/evaluate.rs index 755c5c23..511795f5 100644 --- a/crates/jrsonnet-evaluator/src/evaluate.rs +++ b/crates/jrsonnet-evaluator/src/evaluate.rs @@ -1,8 +1,9 @@ use crate::{ - equals, error::Error::*, lazy_val, push, throw, with_state, ArrValue, Context, ContextCreator, - FuncDesc, FuncVal, FutureWrapper, LazyBinding, LazyVal, ObjMember, ObjValue, Result, Val, + equals, error::Error::*, push, throw, with_state, ArrValue, Bindable, Context, ContextCreator, + FuncDesc, FuncVal, FutureWrapper, LazyBinding, LazyVal, LazyValValue, ObjMember, ObjValue, + ObjectAssertion, Result, Val, }; -use closure::closure; +use gc::{custom_trace, Finalize, Gc, Trace}; use jrsonnet_interner::IStr; use jrsonnet_parser::{ ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, ExprLocation, FieldMember, @@ -20,17 +21,62 @@ pub fn evaluate_binding_in_future( 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(), - )) + + struct LazyMethodBinding { + context_creator: FutureWrapper, + name: IStr, + params: ParamsDesc, + value: LocExpr, + } + impl Finalize for LazyMethodBinding {} + unsafe impl Trace for LazyMethodBinding { + custom_trace!(this, { + mark(&this.context_creator); + mark(&this.name); + mark(&this.params); + mark(&this.value); + }); + } + impl LazyValValue for LazyMethodBinding { + fn get(self: Box) -> Result { + Ok(evaluate_method( + self.context_creator.unwrap(), + self.name, + self.params, + self.value, + )) + } + } + + LazyVal::new(Box::new(LazyMethodBinding { + context_creator, + name: b.name.clone(), + params, + value: b.value.clone(), })) } else { - LazyVal::new(Box::new(move || { - evaluate_named(context_creator.unwrap(), &b.value, b.name.clone()) + struct LazyNamedBinding { + context_creator: FutureWrapper, + name: IStr, + value: LocExpr, + } + impl Finalize for LazyNamedBinding {} + unsafe impl Trace for LazyNamedBinding { + custom_trace!(this, { + mark(&this.context_creator); + mark(&this.name); + mark(&this.value); + }); + } + impl LazyValValue for LazyNamedBinding { + fn get(self: Box) -> Result { + evaluate_named(self.context_creator.unwrap(), &self.value, self.name) + } + } + LazyVal::new(Box::new(LazyNamedBinding { + context_creator, + name: b.name.clone(), + value: b.value, })) } } @@ -39,37 +85,129 @@ pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (IStr, let b = b.clone(); if let Some(params) = &b.params { let params = params.clone(); + + struct BindableMethodLazyVal { + this: Option, + super_obj: Option, + + context_creator: ContextCreator, + name: IStr, + params: ParamsDesc, + value: LocExpr, + } + impl Finalize for BindableMethodLazyVal {} + unsafe impl Trace for BindableMethodLazyVal { + custom_trace!(this, { + mark(&this.this); + mark(&this.super_obj); + mark(&this.context_creator); + mark(&this.name); + mark(&this.params); + mark(&this.value); + }); + } + impl LazyValValue for BindableMethodLazyVal { + fn get(self: Box) -> Result { + Ok(evaluate_method( + self.context_creator.create(self.this, self.super_obj)?, + self.name, + self.params, + self.value, + )) + } + } + + #[derive(Trace, Finalize)] + struct BindableMethod { + context_creator: ContextCreator, + name: IStr, + params: ParamsDesc, + value: LocExpr, + } + impl Bindable for BindableMethod { + fn bind(&self, this: Option, super_obj: Option) -> Result { + Ok(LazyVal::new(Box::new(BindableMethodLazyVal { + this: this.clone(), + super_obj: super_obj.clone(), + + context_creator: self.context_creator.clone(), + name: self.name.clone(), + params: self.params.clone(), + value: self.value.clone(), + }))) + } + } + ( b.name.clone(), - LazyBinding::Bindable(Rc::new(move |this, super_obj| { - Ok(lazy_val!( - closure!(clone b, clone params, clone context_creator, || Ok(evaluate_method( - context_creator.create(this.clone(), super_obj.clone())?, - b.name.clone(), - params.clone(), - b.value.clone(), - ))) - )) - })), + LazyBinding::Bindable(Gc::new(Box::new(BindableMethod { + context_creator, + name: b.name.clone(), + params, + value: b.value.clone(), + }))), ) } else { + struct BindableNamedLazyVal { + this: Option, + super_obj: Option, + + context_creator: ContextCreator, + name: IStr, + value: LocExpr, + } + impl Finalize for BindableNamedLazyVal {} + unsafe impl Trace for BindableNamedLazyVal { + custom_trace!(this, { + mark(&this.this); + mark(&this.super_obj); + mark(&this.context_creator); + mark(&this.name); + mark(&this.value); + }); + } + impl LazyValValue for BindableNamedLazyVal { + fn get(self: Box) -> Result { + evaluate_named( + self.context_creator.create(self.this, self.super_obj)?, + &self.value, + self.name, + ) + } + } + + #[derive(Trace, Finalize)] + struct BindableNamed { + context_creator: ContextCreator, + name: IStr, + value: LocExpr, + } + impl Bindable for BindableNamed { + fn bind(&self, this: Option, super_obj: Option) -> Result { + Ok(LazyVal::new(Box::new(BindableNamedLazyVal { + this, + super_obj, + + context_creator: self.context_creator.clone(), + name: self.name.clone(), + value: self.value.clone(), + }))) + } + } + ( b.name.clone(), - LazyBinding::Bindable(Rc::new(move |this, super_obj| { - Ok(lazy_val!(closure!(clone context_creator, clone b, || - evaluate_named( - context_creator.create(this.clone(), super_obj.clone())?, - &b.value, - b.name.clone() - ) - ))) - })), + LazyBinding::Bindable(Gc::new(Box::new(BindableNamed { + context_creator, + name: b.name.clone(), + value: b.value.clone(), + }))), ) } } pub fn evaluate_method(ctx: Context, name: IStr, params: ParamsDesc, body: LocExpr) -> Val { - Val::Func(Rc::new(FuncVal::Normal(FuncDesc { + Val::Func(Gc::new(FuncVal::Normal(FuncDesc { name, ctx, params, @@ -105,6 +243,9 @@ pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Result { pub fn evaluate_add_op(a: &Val, b: &Val) -> Result { Ok(match (a, b) { + (Val::DebugGcTraceValue(v1), Val::DebugGcTraceValue(v2)) => { + evaluate_add_op(&v1.value, &v2.value)? + } (Val::Str(v1), Val::Str(v2)) => Val::Str(((**v1).to_owned() + v2).into()), // Can't use generic json serialization way, because it depends on number to string concatenation (std.jsonnet:890) @@ -257,7 +398,7 @@ pub fn evaluate_member_list_object(context: Context, members: &[Member]) -> Resu } let mut new_members = FxHashMap::default(); - let mut assertions = Vec::new(); + let mut assertions: Vec> = Vec::new(); for member in members.iter() { match member { Member::Field(FieldMember { @@ -272,20 +413,36 @@ pub fn evaluate_member_list_object(context: Context, members: &[Member]) -> Resu continue; } let name = name.unwrap(); + + #[derive(Trace, Finalize)] + struct ObjMemberBinding { + context_creator: ContextCreator, + value: LocExpr, + name: IStr, + } + impl Bindable for ObjMemberBinding { + fn bind( + &self, + this: Option, + super_obj: Option, + ) -> Result { + Ok(LazyVal::new_resolved(evaluate_named( + self.context_creator.create(this, super_obj)?, + &self.value, + self.name.clone(), + )?)) + } + } new_members.insert( name.clone(), ObjMember { add: *plus, visibility: *visibility, - invoke: LazyBinding::Bindable(Rc::new( - closure!(clone name, clone value, clone context_creator, |this, super_obj| { - Ok(LazyVal::new_resolved(evaluate_named( - context_creator.create(this, super_obj)?, - &value, - name.clone(), - )?)) - }), - )), + invoke: LazyBinding::Bindable(Gc::new(Box::new(ObjMemberBinding { + context_creator: context_creator.clone(), + value: value.clone(), + name, + }))), location: value.1.clone(), }, ); @@ -301,33 +458,73 @@ pub fn evaluate_member_list_object(context: Context, members: &[Member]) -> Resu continue; } let name = name.unwrap(); + #[derive(Trace, Finalize)] + struct ObjMemberBinding { + context_creator: ContextCreator, + value: LocExpr, + params: ParamsDesc, + name: IStr, + } + impl Bindable for ObjMemberBinding { + fn bind( + &self, + this: Option, + super_obj: Option, + ) -> Result { + Ok(LazyVal::new_resolved(evaluate_method( + self.context_creator.create(this, super_obj)?, + self.name.clone(), + self.params.clone(), + self.value.clone(), + ))) + } + } new_members.insert( name.clone(), ObjMember { add: false, visibility: Visibility::Hidden, - invoke: LazyBinding::Bindable(Rc::new( - closure!(clone value, clone context_creator, clone params, clone name, |this, super_obj| { - // TODO: Assert - Ok(LazyVal::new_resolved(evaluate_method( - context_creator.create(this, super_obj)?, - name.clone(), - params.clone(), - value.clone(), - ))) - }), - )), + invoke: LazyBinding::Bindable(Gc::new(Box::new(ObjMemberBinding { + context_creator: context_creator.clone(), + value: value.clone(), + params: params.clone(), + name, + }))), location: value.1.clone(), }, ); } Member::BindStmt(_) => {} Member::AssertStmt(stmt) => { - assertions.push(stmt.clone()); + struct ObjectAssert { + context_creator: ContextCreator, + assert: AssertStmt, + } + impl Finalize for ObjectAssert {} + unsafe impl Trace for ObjectAssert { + custom_trace!(this, { + mark(&this.context_creator); + mark(&this.assert); + }); + } + impl ObjectAssertion for ObjectAssert { + fn run( + &self, + this: Option, + super_obj: Option, + ) -> Result<()> { + let ctx = self.context_creator.create(this, super_obj)?; + evaluate_assert(ctx, &self.assert) + } + } + assertions.push(Box::new(ObjectAssert { + context_creator: context_creator.clone(), + assert: stmt.clone(), + })); } } } - let this = ObjValue::new(context, None, Rc::new(new_members), Rc::new(assertions)); + let this = ObjValue::new(None, Gc::new(new_members), Gc::new(assertions)); future_this.fill(this.clone()); Ok(this) } @@ -361,16 +558,37 @@ pub fn evaluate_object(context: Context, object: &ObjBody) -> Result { match key { Val::Null => {} Val::Str(n) => { + #[derive(Trace, Finalize)] + struct ObjCompBinding { + context: Context, + value: LocExpr, + } + impl Bindable for ObjCompBinding { + fn bind( + &self, + this: Option, + _super_obj: Option, + ) -> Result { + Ok(LazyVal::new_resolved(evaluate( + self.context.clone().extend( + FxHashMap::default(), + None, + this, + None, + ), + &self.value, + )?)) + } + } new_members.insert( n, ObjMember { add: false, visibility: Visibility::Normal, - invoke: LazyBinding::Bindable(Rc::new( - closure!(clone ctx, clone obj.value, |this, _super_obj| { - Ok(LazyVal::new_resolved(evaluate(ctx.clone().extend(FxHashMap::default(), None, this, None), &value)?)) - }), - )), + invoke: LazyBinding::Bindable(Gc::new(Box::new(ObjCompBinding { + context: ctx.clone(), + value: obj.value.clone(), + }))), location: obj.value.1.clone(), }, ); @@ -381,7 +599,7 @@ pub fn evaluate_object(context: Context, object: &ObjBody) -> Result { Ok(()) })?; - let this = ObjValue::new(context, None, Rc::new(new_members), Rc::new(Vec::new())); + let this = ObjValue::new(None, Gc::new(new_members), Gc::new(Vec::new())); future_this.fill(this.clone()); this } @@ -486,7 +704,7 @@ pub fn evaluate(context: Context, expr: &LocExpr) -> Result { if let Some(v) = v.get(s.clone())? { Ok(v) } else if v.get("__intrinsic_namespace__".into())?.is_some() { - Ok(Val::Func(Rc::new(FuncVal::Intrinsic(s)))) + Ok(Val::Func(Gc::new(FuncVal::Intrinsic(s)))) } else { throw!(NoSuchField(s)) } @@ -549,11 +767,27 @@ pub fn evaluate(context: Context, expr: &LocExpr) -> Result { Arr(items) => { let mut out = Vec::with_capacity(items.len()); for item in items { - out.push(LazyVal::new(Box::new( - closure!(clone context, clone item, || { - evaluate(context.clone(), &item) - }), - ))); + // TODO: Implement ArrValue::Lazy with same context for every element? + struct ArrayElement { + context: Context, + item: LocExpr, + } + impl Finalize for ArrayElement {} + unsafe impl Trace for ArrayElement { + custom_trace!(this, { + mark(&this.context); + mark(&this.item); + }); + } + impl LazyValValue for ArrayElement { + fn get(self: Box) -> Result { + evaluate(self.context, &self.item) + } + } + out.push(LazyVal::new(Box::new(ArrayElement { + context: context.clone(), + item: item.clone(), + }))); } Val::Arr(out.into()) } @@ -563,7 +797,7 @@ pub fn evaluate(context: Context, expr: &LocExpr) -> Result { out.push(evaluate(ctx, expr)?); Ok(()) })?; - Val::Arr(ArrValue::Eager(Rc::new(out))) + Val::Arr(ArrValue::Eager(Gc::new(out))) } Obj(body) => Val::Obj(evaluate_object(context, body)?), ObjExtend(s, t) => evaluate_add_op( @@ -576,7 +810,7 @@ pub fn evaluate(context: Context, expr: &LocExpr) -> Result { Function(params, body) => { evaluate_method(context, "anonymous".into(), params.clone(), body.clone()) } - Intrinsic(name) => Val::Func(Rc::new(FuncVal::Intrinsic(name.clone()))), + Intrinsic(name) => Val::Func(Gc::new(FuncVal::Intrinsic(name.clone()))), AssertExpr(assert, returned) => { evaluate_assert(context.clone(), assert)?; evaluate(context, returned)? diff --git a/crates/jrsonnet-evaluator/src/function.rs b/crates/jrsonnet-evaluator/src/function.rs index 1c3f317e..065835ec 100644 --- a/crates/jrsonnet-evaluator/src/function.rs +++ b/crates/jrsonnet-evaluator/src/function.rs @@ -1,7 +1,7 @@ -use crate::{error::Error::*, evaluate, lazy_val, resolved_lazy_val, throw, Context, Result, Val}; -use closure::closure; +use crate::{error::Error::*, evaluate, throw, Context, LazyVal, LazyValValue, Result, Val}; +use gc::{custom_trace, Finalize, Trace}; use jrsonnet_interner::IStr; -use jrsonnet_parser::{ArgsDesc, ParamsDesc}; +use jrsonnet_parser::{ArgsDesc, LocExpr, ParamsDesc}; use rustc_hash::FxHashMap; use std::{collections::HashMap, hash::BuildHasherDefault}; @@ -53,9 +53,29 @@ pub fn parse_function_call( throw!(FunctionParameterNotBoundInCall(p.0.clone())); }; let val = if tailstrict { - resolved_lazy_val!(evaluate(ctx, expr)?) + LazyVal::new_resolved(evaluate(ctx, expr)?) } else { - lazy_val!(closure!(clone ctx, clone expr, ||evaluate(ctx.clone(), &expr))) + struct EvaluateLazyVal { + context: Context, + expr: LocExpr, + } + impl Finalize for EvaluateLazyVal {} + unsafe impl Trace for EvaluateLazyVal { + custom_trace!(this, { + mark(&this.context); + mark(&this.expr); + }); + } + impl LazyValValue for EvaluateLazyVal { + fn get(self: Box) -> Result { + evaluate(self.context, &self.expr) + } + } + + LazyVal::new(Box::new(EvaluateLazyVal { + context: ctx.clone(), + expr: expr.clone(), + })) }; out.insert(p.0.clone(), val); } @@ -89,19 +109,30 @@ pub fn parse_function_call_map( // Fill defaults for (id, p) in params.iter().enumerate() { let val = if let Some(arg) = positioned_args[id].take() { - resolved_lazy_val!(arg) + LazyVal::new_resolved(arg) } else if let Some(default) = &p.1 { if tailstrict { - resolved_lazy_val!(evaluate( + LazyVal::new_resolved(evaluate( body_ctx.clone().expect(NO_DEFAULT_CONTEXT), - default + default, )?) } else { let body_ctx = body_ctx.clone(); let default = default.clone(); - lazy_val!(move || { - evaluate(body_ctx.clone().expect(NO_DEFAULT_CONTEXT), &default) - }) + #[derive(Trace, Finalize)] + struct EvaluateLazyVal { + body_ctx: Option, + default: LocExpr, + } + impl LazyValValue for EvaluateLazyVal { + fn get(self: Box) -> Result { + evaluate( + self.body_ctx.clone().expect(NO_DEFAULT_CONTEXT), + &self.default, + ) + } + } + LazyVal::new(Box::new(EvaluateLazyVal { body_ctx, default })) } } else { throw!(FunctionParameterNotBoundInCall(p.0.clone())); @@ -135,7 +166,7 @@ pub fn place_args( } else { throw!(FunctionParameterNotBoundInCall(p.0.clone())); }; - out.insert(p.0.clone(), resolved_lazy_val!(val)); + out.insert(p.0.clone(), LazyVal::new_resolved(val)); } Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None)) diff --git a/crates/jrsonnet-evaluator/src/integrations/serde.rs b/crates/jrsonnet-evaluator/src/integrations/serde.rs index c4d9ca0b..179389cb 100644 --- a/crates/jrsonnet-evaluator/src/integrations/serde.rs +++ b/crates/jrsonnet-evaluator/src/integrations/serde.rs @@ -2,6 +2,7 @@ use crate::{ error::{Error::*, LocError, Result}, throw, Context, LazyBinding, LazyVal, ObjMember, ObjValue, Val, }; +use gc::Gc; use jrsonnet_parser::Visibility; use rustc_hash::FxHasher; use serde_json::{Map, Number, Value}; @@ -9,7 +10,6 @@ use std::{ collections::HashMap, convert::{TryFrom, TryInto}, hash::BuildHasherDefault, - rc::Rc, }; impl TryFrom<&Val> for Value { @@ -42,6 +42,7 @@ impl TryFrom<&Val> for Value { Self::Object(out) } Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())), + Val::DebugGcTraceValue(v) => Value::try_from(&*v.value as &Val)?, }) } } @@ -76,12 +77,7 @@ impl From<&Value> for Val { }, ); } - Self::Obj(ObjValue::new( - Context::new(), - None, - Rc::new(entries), - Rc::new(Vec::new()), - )) + Self::Obj(ObjValue::new(None, Gc::new(entries), Gc::new(Vec::new()))) } } } diff --git a/crates/jrsonnet-evaluator/src/lib.rs b/crates/jrsonnet-evaluator/src/lib.rs index 0a55f319..9f9caa23 100644 --- a/crates/jrsonnet-evaluator/src/lib.rs +++ b/crates/jrsonnet-evaluator/src/lib.rs @@ -25,6 +25,7 @@ pub use dynamic::*; use error::{Error::*, LocError, Result, StackTraceElement}; pub use evaluate::*; pub use function::parse_function_call; +use gc::{Finalize, Gc, Trace}; pub use import::*; use jrsonnet_interner::IStr; use jrsonnet_parser::*; @@ -42,10 +43,12 @@ use std::{ use trace::{offset_to_location, CodeLocation, CompactFormat, TraceFormat}; pub use val::*; -type BindableFn = dyn Fn(Option, Option) -> Result; -#[derive(Clone)] +pub trait Bindable: Trace { + fn bind(&self, this: Option, super_obj: Option) -> Result; +} +#[derive(Trace, Finalize, Clone)] pub enum LazyBinding { - Bindable(Rc), + Bindable(Gc>), Bound(LazyVal), } @@ -57,7 +60,7 @@ impl Debug for LazyBinding { impl LazyBinding { pub fn evaluate(&self, this: Option, super_obj: Option) -> Result { match self { - Self::Bindable(v) => v(this, super_obj), + Self::Bindable(v) => v.bind(this, super_obj), Self::Bound(v) => Ok(v.clone()), } } @@ -71,7 +74,7 @@ pub struct EvaluationSettings { /// Used for s`td.extVar` pub ext_vars: HashMap, /// Used for ext.native - pub ext_natives: HashMap>, + pub ext_natives: HashMap>, /// TLA vars pub tla_vars: HashMap, /// Global variables are inserted in default context @@ -270,7 +273,7 @@ impl EvaluationState { let mut new_bindings: FxHashMap = FxHashMap::with_capacity_and_hasher(globals.len(), BuildHasherDefault::default()); for (name, value) in globals.iter() { - new_bindings.insert(name.clone(), resolved_lazy_val!(value.clone())); + new_bindings.insert(name.clone(), LazyVal::new_resolved(value.clone())); } Context::new().extend_bound(new_bindings) } @@ -449,7 +452,7 @@ impl EvaluationState { self.settings_mut().import_resolver = resolver; } - pub fn add_native(&self, name: IStr, cb: Rc) { + pub fn add_native(&self, name: IStr, cb: Gc) { self.settings_mut().ext_natives.insert(name, cb); } diff --git a/crates/jrsonnet-evaluator/src/map.rs b/crates/jrsonnet-evaluator/src/map.rs index d026ff4e..43b76386 100644 --- a/crates/jrsonnet-evaluator/src/map.rs +++ b/crates/jrsonnet-evaluator/src/map.rs @@ -1,28 +1,29 @@ +use gc::{Finalize, Gc, Trace}; use jrsonnet_interner::IStr; use rustc_hash::FxHashMap; -use std::rc::Rc; -#[derive(Default, Debug)] -struct LayeredHashMapInternals { +pub struct LayeredHashMapInternals { parent: Option>, current: FxHashMap, } -#[derive(Debug)] -pub struct LayeredHashMap(Rc>); +unsafe impl Trace for LayeredHashMapInternals { + gc::custom_trace!(this, { + mark(&this.parent); + mark(&this.current); + }); +} +impl Finalize for LayeredHashMapInternals {} + +#[derive(Trace, Finalize)] +pub struct LayeredHashMap(Gc>); -impl LayeredHashMap { +impl LayeredHashMap { pub fn extend(self, new_layer: FxHashMap) -> Self { - match Rc::try_unwrap(self.0) { - Ok(mut map) => { - map.current.extend(new_layer); - Self(Rc::new(map)) - } - Err(this) => Self(Rc::new(LayeredHashMapInternals { - parent: Some(Self(this)), - current: new_layer, - })), - } + Self(Gc::new(LayeredHashMapInternals { + parent: Some(self), + current: new_layer, + })) } pub fn get(&self, key: &IStr) -> Option<&V> { @@ -33,15 +34,15 @@ impl LayeredHashMap { } } -impl Clone for LayeredHashMap { +impl Clone for LayeredHashMap { fn clone(&self) -> Self { Self(self.0.clone()) } } -impl Default for LayeredHashMap { +impl Default for LayeredHashMap { fn default() -> Self { - Self(Rc::new(LayeredHashMapInternals { + Self(Gc::new(LayeredHashMapInternals { parent: None, current: FxHashMap::default(), })) diff --git a/crates/jrsonnet-evaluator/src/native.rs b/crates/jrsonnet-evaluator/src/native.rs index a6673c48..4a215ec5 100644 --- a/crates/jrsonnet-evaluator/src/native.rs +++ b/crates/jrsonnet-evaluator/src/native.rs @@ -1,27 +1,27 @@ #![allow(clippy::type_complexity)] use crate::{error::Result, Val}; +use gc::{Finalize, Trace}; use jrsonnet_parser::ParamsDesc; use std::fmt::Debug; use std::path::PathBuf; use std::rc::Rc; +pub trait NativeCallbackHandler: Trace { + fn call(&self, from: Option>, args: &[Val]) -> Result; +} + +#[derive(Trace, Finalize)] pub struct NativeCallback { pub params: ParamsDesc, - handler: Box>, &[Val]) -> Result>, + handler: Box, } impl NativeCallback { - pub fn new( - params: ParamsDesc, - handler: impl Fn(Option>, &[Val]) -> Result + 'static, - ) -> Self { - Self { - params, - handler: Box::new(handler), - } + pub fn new(params: ParamsDesc, handler: Box) -> Self { + Self { params, handler } } pub fn call(&self, caller: Option>, args: &[Val]) -> Result { - (self.handler)(caller, args) + self.handler.call(caller, args) } } impl Debug for NativeCallback { diff --git a/crates/jrsonnet-evaluator/src/obj.rs b/crates/jrsonnet-evaluator/src/obj.rs index 0b3a8c14..a4267e47 100644 --- a/crates/jrsonnet-evaluator/src/obj.rs +++ b/crates/jrsonnet-evaluator/src/obj.rs @@ -1,11 +1,12 @@ use crate::{evaluate_add_op, evaluate_assert, Context, LazyBinding, Result, Val}; +use gc::{Finalize, Gc, GcCell, Trace}; use jrsonnet_interner::IStr; use jrsonnet_parser::{AssertStmt, ExprLocation, Visibility}; use rustc_hash::{FxHashMap, FxHashSet}; use std::hash::{Hash, Hasher}; -use std::{cell::RefCell, fmt::Debug, hash::BuildHasherDefault, rc::Rc}; +use std::{fmt::Debug, hash::BuildHasherDefault}; -#[derive(Debug)] +#[derive(Debug, Trace, Finalize)] pub struct ObjMember { pub add: bool, pub visibility: Visibility, @@ -13,21 +14,24 @@ pub struct ObjMember { pub location: Option, } +pub trait ObjectAssertion: Trace { + fn run(&self, this: Option, super_obj: Option) -> Result<()>; +} + // Field => This type CacheKey = (IStr, ObjValue); -#[derive(Debug)] +#[derive(Trace, Finalize)] pub struct ObjValueInternals { - context: Context, super_obj: Option, - assertions: Rc>, - assertions_ran: RefCell>, + assertions: Gc>>, + assertions_ran: GcCell>, this_obj: Option, - this_entries: Rc>, - value_cache: RefCell>>, + this_entries: Gc>, + value_cache: GcCell>>, } -#[derive(Clone)] -pub struct ObjValue(pub(crate) Rc); +#[derive(Clone, Trace, Finalize)] +pub struct ObjValue(pub(crate) Gc); impl Debug for ObjValue { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if let Some(super_obj) = self.0.super_obj.as_ref() { @@ -55,39 +59,30 @@ impl Debug for ObjValue { impl ObjValue { pub fn new( - context: Context, super_obj: Option, - this_entries: Rc>, - assertions: Rc>, + this_entries: Gc>, + assertions: Gc>>, ) -> Self { - Self(Rc::new(ObjValueInternals { - context, + Self(Gc::new(ObjValueInternals { super_obj, assertions, - assertions_ran: RefCell::new(FxHashSet::default()), + assertions_ran: GcCell::new(FxHashSet::default()), this_obj: None, this_entries, - value_cache: RefCell::new(FxHashMap::default()), + value_cache: GcCell::new(FxHashMap::default()), })) } pub fn new_empty() -> Self { - Self::new( - Context::new(), - None, - Rc::new(FxHashMap::default()), - Rc::new(Vec::new()), - ) + Self::new(None, Gc::new(FxHashMap::default()), Gc::new(Vec::new())) } pub fn extend_from(&self, super_obj: Self) -> Self { match &self.0.super_obj { None => Self::new( - self.0.context.clone(), Some(super_obj), self.0.this_entries.clone(), self.0.assertions.clone(), ), Some(v) => Self::new( - self.0.context.clone(), Some(v.extend_from(super_obj)), self.0.this_entries.clone(), self.0.assertions.clone(), @@ -95,14 +90,13 @@ impl ObjValue { } } pub fn with_this(&self, this_obj: Self) -> Self { - Self(Rc::new(ObjValueInternals { - context: self.0.context.clone(), + Self(Gc::new(ObjValueInternals { super_obj: self.0.super_obj.clone(), assertions: self.0.assertions.clone(), - assertions_ran: RefCell::new(FxHashSet::default()), + assertions_ran: GcCell::new(FxHashSet::default()), this_obj: Some(this_obj), this_entries: self.0.this_entries.clone(), - value_cache: RefCell::new(FxHashMap::default()), + value_cache: GcCell::new(FxHashMap::default()), })) } @@ -203,12 +197,7 @@ impl ObjValue { pub fn extend_with_field(self, key: IStr, value: ObjMember) -> Self { let mut new = FxHashMap::with_capacity_and_hasher(1, BuildHasherDefault::default()); new.insert(key, value); - Self::new( - Context::new(), - Some(self), - Rc::new(new), - Rc::new(Vec::new()), - ) + Self::new(Some(self), Gc::new(new), Gc::new(Vec::new())) } fn get_raw(&self, key: IStr, real_this: Option<&Self>) -> Result> { @@ -249,13 +238,7 @@ impl ObjValue { fn run_assertions_raw(&self, real_this: &Self) -> Result<()> { if self.0.assertions_ran.borrow_mut().insert(real_this.clone()) { for assertion in self.0.assertions.iter() { - if let Err(e) = evaluate_assert( - self.0 - .context - .clone() - .with_this_super(real_this.clone(), self.0.super_obj.clone()), - assertion, - ) { + if let Err(e) = assertion.run(Some(real_this.clone()), self.0.super_obj.clone()) { self.0.assertions_ran.borrow_mut().remove(real_this); return Err(e); } @@ -271,19 +254,19 @@ impl ObjValue { } pub fn ptr_eq(a: &Self, b: &Self) -> bool { - Rc::ptr_eq(&a.0, &b.0) + Gc::ptr_eq(&a.0, &b.0) } } impl PartialEq for ObjValue { fn eq(&self, other: &Self) -> bool { - Rc::ptr_eq(&self.0, &other.0) + Gc::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) + fn hash(&self, hasher: &mut H) { + hasher.write_usize(&*self.0 as *const _ as usize) } } diff --git a/crates/jrsonnet-evaluator/src/typed.rs b/crates/jrsonnet-evaluator/src/typed.rs index 4f67b7e2..78a4b0e5 100644 --- a/crates/jrsonnet-evaluator/src/typed.rs +++ b/crates/jrsonnet-evaluator/src/typed.rs @@ -4,6 +4,7 @@ use crate::{ error::{Error, LocError, Result}, push, Val, }; +use gc::{Finalize, Trace}; use jrsonnet_parser::ExprLocation; use jrsonnet_types::{ComplexValType, ValType}; use thiserror::Error; @@ -20,7 +21,7 @@ macro_rules! unwrap_type { }}; } -#[derive(Debug, Error, Clone)] +#[derive(Debug, Error, Clone, Trace, Finalize)] pub enum TypeError { #[error("expected {0}, got {1}")] ExpectedGot(ComplexValType, ValType), @@ -37,7 +38,7 @@ impl From for LocError { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Trace, Finalize)] pub struct TypeLocError(Box, ValuePathStack); impl From for TypeLocError { fn from(e: TypeError) -> Self { @@ -59,7 +60,7 @@ impl Display for TypeLocError { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Trace, Finalize)] pub struct TypeLocErrorList(Vec); impl Display for TypeLocErrorList { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -122,7 +123,7 @@ impl CheckType for ValType { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Trace, Finalize)] enum ValuePathItem { Field(Rc), Index(u64), @@ -137,7 +138,7 @@ impl Display for ValuePathItem { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Trace, Finalize)] struct ValuePathStack(Vec); impl Display for ValuePathStack { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/crates/jrsonnet-evaluator/src/val.rs b/crates/jrsonnet-evaluator/src/val.rs index 44fb3fb8..631ce74c 100644 --- a/crates/jrsonnet-evaluator/src/val.rs +++ b/crates/jrsonnet-evaluator/src/val.rs @@ -3,52 +3,75 @@ use crate::{ call_builtin, manifest::{manifest_json_ex, ManifestJsonOptions, ManifestType}, }, - error::Error::*, + error::{Error::*, LocError}, evaluate, function::{parse_function_call, parse_function_call_map, place_args}, native::NativeCallback, throw, with_state, Context, ObjValue, Result, }; +use gc::{custom_trace, Finalize, Gc, GcCell, Trace}; use jrsonnet_interner::IStr; use jrsonnet_parser::{el, Arg, ArgsDesc, Expr, ExprLocation, LiteralType, LocExpr, ParamsDesc}; use jrsonnet_types::ValType; -use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc}; +use std::{collections::HashMap, fmt::Debug, rc::Rc}; + +pub trait LazyValValue: Trace { + fn get(self: Box) -> Result; +} enum LazyValInternals { Computed(Val), - Waiting(Box Result>), + Errored(LocError), + Waiting(Box), + Pending, } -#[derive(Clone)] -pub struct LazyVal(Rc>); +impl Finalize for LazyValInternals {} +unsafe impl Trace for LazyValInternals { + custom_trace!(this, { + match &this { + LazyValInternals::Computed(v) => mark(v), + LazyValInternals::Errored(e) => mark(e), + LazyValInternals::Waiting(w) => mark(w), + LazyValInternals::Pending => {} + } + }); +} + +#[derive(Clone, Trace, Finalize)] +pub struct LazyVal(Gc>); impl LazyVal { - pub fn new(f: Box Result>) -> Self { - Self(Rc::new(RefCell::new(LazyValInternals::Waiting(f)))) + pub fn new(f: Box) -> Self { + Self(Gc::new(GcCell::new(LazyValInternals::Waiting(f)))) } pub fn new_resolved(val: Val) -> Self { - Self(Rc::new(RefCell::new(LazyValInternals::Computed(val)))) + Self(Gc::new(GcCell::new(LazyValInternals::Computed(val)))) } pub fn evaluate(&self) -> Result { - let new_value = match &*self.0.borrow() { + match &*self.0.borrow() { LazyValInternals::Computed(v) => return Ok(v.clone()), - LazyValInternals::Waiting(f) => f()?, + LazyValInternals::Errored(e) => return Err(e.clone().into()), + LazyValInternals::Pending => return Err(RecursiveLazyValueEvaluation.into()), + _ => (), + }; + let value = if let LazyValInternals::Waiting(value) = + std::mem::replace(&mut *self.0.borrow_mut(), LazyValInternals::Pending) + { + value + } else { + unreachable!() + }; + let new_value = match value.get() { + Ok(v) => v, + Err(e) => { + *self.0.borrow_mut() = LazyValInternals::Errored(e.clone()); + return Err(e); + } }; *self.0.borrow_mut() = LazyValInternals::Computed(new_value.clone()); Ok(new_value) } } -#[macro_export] -macro_rules! lazy_val { - ($f: expr) => { - $crate::LazyVal::new(Box::new($f)) - }; -} -#[macro_export] -macro_rules! resolved_lazy_val { - ($f: expr) => { - $crate::LazyVal::new_resolved($f) - }; -} impl Debug for LazyVal { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Lazy") @@ -56,11 +79,11 @@ impl Debug for LazyVal { } impl PartialEq for LazyVal { fn eq(&self, other: &Self) -> bool { - Rc::ptr_eq(&self.0, &other.0) + Gc::ptr_eq(&self.0, &other.0) } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Trace, Finalize)] pub struct FuncDesc { pub name: IStr, pub ctx: Context, @@ -68,14 +91,14 @@ pub struct FuncDesc { pub body: LocExpr, } -#[derive(Debug)] +#[derive(Debug, Trace, Finalize)] pub enum FuncVal { /// Plain function implemented in jsonnet Normal(FuncDesc), /// Standard library function Intrinsic(IStr), /// Library functions implemented in native - NativeExt(IStr, Rc), + NativeExt(IStr, Gc), } impl PartialEq for FuncVal { @@ -174,13 +197,23 @@ pub enum ManifestFormat { #[derive(Debug, Clone)] pub enum ArrValue { - Lazy(Rc>), - Eager(Rc>), + Lazy(Gc>), + Eager(Gc>), Extended(Box<(Self, Self)>), } +impl Finalize for ArrValue {} +unsafe impl Trace for ArrValue { + custom_trace!(this, { + match &this { + ArrValue::Lazy(l) => mark(l), + ArrValue::Eager(e) => mark(e), + ArrValue::Extended(x) => mark(x), + } + }); +} impl ArrValue { pub fn new_eager() -> Self { - Self::Eager(Rc::new(Vec::new())) + Self::Eager(Gc::new(Vec::new())) } pub fn len(&self) -> usize { @@ -231,14 +264,14 @@ impl ArrValue { } } - pub fn evaluated(&self) -> Result>> { + pub fn evaluated(&self) -> Result>> { Ok(match self { Self::Lazy(vec) => { let mut out = Vec::with_capacity(vec.len()); for item in vec.iter() { out.push(item.evaluate()?); } - Rc::new(out) + Gc::new(out) } Self::Eager(vec) => vec.clone(), Self::Extended(_v) => { @@ -246,7 +279,7 @@ impl ArrValue { for item in self.iter() { out.push(item?); } - Rc::new(out) + Gc::new(out) } }) } @@ -272,12 +305,12 @@ impl ArrValue { Self::Lazy(vec) => { let mut out = (&vec as &Vec<_>).clone(); out.reverse(); - Self::Lazy(Rc::new(out)) + Self::Lazy(Gc::new(out)) } Self::Eager(vec) => { let mut out = (&vec as &Vec<_>).clone(); out.reverse(); - Self::Eager(Rc::new(out)) + Self::Eager(Gc::new(out)) } Self::Extended(b) => Self::Extended(Box::new((b.1.reversed(), b.0.reversed()))), } @@ -290,7 +323,7 @@ impl ArrValue { out.push(mapper(value?)?); } - Ok(Self::Eager(Rc::new(out))) + Ok(Self::Eager(Gc::new(out))) } pub fn filter(self, filter: impl Fn(&Val) -> Result) -> Result { @@ -303,13 +336,13 @@ impl ArrValue { } } - Ok(Self::Eager(Rc::new(out))) + Ok(Self::Eager(Gc::new(out))) } pub fn ptr_eq(a: &Self, b: &Self) -> bool { match (a, b) { - (Self::Lazy(a), Self::Lazy(b)) => Rc::ptr_eq(a, b), - (Self::Eager(a), Self::Eager(b)) => Rc::ptr_eq(a, b), + (Self::Lazy(a), Self::Lazy(b)) => Gc::ptr_eq(a, b), + (Self::Eager(a), Self::Eager(b)) => Gc::ptr_eq(a, b), _ => false, } } @@ -317,13 +350,72 @@ impl ArrValue { impl From> for ArrValue { fn from(v: Vec) -> Self { - Self::Lazy(Rc::new(v)) + Self::Lazy(Gc::new(v)) } } impl From> for ArrValue { fn from(v: Vec) -> Self { - Self::Eager(Rc::new(v)) + Self::Eager(Gc::new(v)) + } +} + +#[derive(Debug)] +pub struct DebugGcTraceValue { + name: IStr, + pub value: Box, +} +impl DebugGcTraceValue { + fn print(&self, action: &str) { + println!("{} {}#{:?}", action, self.name, &*self.value as *const _) + } +} +impl Finalize for DebugGcTraceValue { + fn finalize(&self) { + self.print("Garbage-collecting") + } +} +impl Drop for DebugGcTraceValue { + fn drop(&mut self) { + self.print("Garbage-collected") + } +} +unsafe impl Trace for DebugGcTraceValue { + unsafe fn trace(&self) { + self.print("Traced"); + self.value.trace() + } + unsafe fn root(&self) { + self.print("Rooted"); + self.value.root() + } + unsafe fn unroot(&self) { + self.print("Unrooted"); + self.value.unroot() + } + fn finalize_glue(&self) { + Finalize::finalize(self) + } +} +impl Clone for DebugGcTraceValue { + fn clone(&self) -> Self { + self.print("Cloned"); + let value = DebugGcTraceValue { + name: self.name.clone(), + value: self.value.clone(), + }; + value.print("I'm clone"); + value + } +} +impl DebugGcTraceValue { + pub fn new(name: IStr, value: Val) -> Val { + let value = Self { + name, + value: Box::new(value), + }; + value.print("Constructed"); + Val::DebugGcTraceValue(value) } } @@ -335,7 +427,23 @@ pub enum Val { Num(f64), Arr(ArrValue), Obj(ObjValue), - Func(Rc), + Func(Gc), + DebugGcTraceValue(DebugGcTraceValue), +} +impl Finalize for Val {} +unsafe impl Trace for Val { + custom_trace!(this, { + match &this { + Val::Bool(_) => {} + Val::Null => {} + Val::Str(_) => {} + Val::Num(_) => {} + Val::Arr(a) => mark(a), + Val::Obj(o) => mark(o), + Val::Func(f) => mark(f), + Val::DebugGcTraceValue(v) => mark(v), + } + }); } macro_rules! matches_unwrap { @@ -368,7 +476,7 @@ impl Val { pub fn unwrap_num(self) -> Result { Ok(matches_unwrap!(self, Self::Num(v), v)) } - pub fn unwrap_func(self) -> Result> { + pub fn unwrap_func(self) -> Result> { Ok(matches_unwrap!(self, Self::Func(v), v)) } pub fn try_cast_bool(self, context: &'static str) -> Result { @@ -392,6 +500,7 @@ impl Val { Self::Bool(_) => ValType::Bool, Self::Null => ValType::Null, Self::Func(..) => ValType::Func, + Self::DebugGcTraceValue(v) => v.value.value_type(), } } From 46d0b239f31a8e69d4f72ae6391b5faa822f5673 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sat, 5 Jun 2021 22:00:47 +0300 Subject: [PATCH 05/19] build(bindings): build with gc It will build, but some methods is broken for now Signed-off-by: Yaroslav Bolyukin --- bindings/jsonnet/src/native.rs | 32 +----------------------------- bindings/jsonnet/src/val_make.rs | 2 +- bindings/jsonnet/src/val_modify.rs | 7 ++++--- 3 files changed, 6 insertions(+), 35 deletions(-) diff --git a/bindings/jsonnet/src/native.rs b/bindings/jsonnet/src/native.rs index 99d4d9a0..d67bc3c6 100644 --- a/bindings/jsonnet/src/native.rs +++ b/bindings/jsonnet/src/native.rs @@ -21,35 +21,5 @@ pub unsafe extern "C" fn jsonnet_native_callback( ctx: *const c_void, mut raw_params: *const *const c_char, ) { - let name = CStr::from_ptr(name).to_str().expect("utf8 name").into(); - let mut params = Vec::new(); - loop { - if (*raw_params).is_null() { - break; - } - let param = CStr::from_ptr(*raw_params).to_str().expect("not utf8"); - params.push(Param(param.into(), None)); - raw_params = raw_params.offset(1); - } - let params = ParamsDesc(Rc::new(params)); - - vm.add_native( - name, - Rc::new(NativeCallback::new(params, move |_caller, args| { - let mut n_args = Vec::new(); - for a in args { - n_args.push(Some(Box::new(a.clone()))); - } - n_args.push(None); - let mut success = 1; - let v = cb(ctx, &n_args as *const _ as *const *const Val, &mut success); - let v = *Box::from_raw(v); - if success == 1 { - Ok(v) - } else { - let e = v.try_cast_str("native error").expect("error msg"); - Err(Error::RuntimeError(e).into()) - } - })), - ) + todo!() } diff --git a/bindings/jsonnet/src/val_make.rs b/bindings/jsonnet/src/val_make.rs index 7f5c809d..948f5297 100644 --- a/bindings/jsonnet/src/val_make.rs +++ b/bindings/jsonnet/src/val_make.rs @@ -38,7 +38,7 @@ pub extern "C" fn jsonnet_json_make_null(_vm: &EvaluationState) -> *mut Val { #[no_mangle] pub extern "C" fn jsonnet_json_make_array(_vm: &EvaluationState) -> *mut Val { - Box::into_raw(Box::new(Val::Arr(ArrValue::Eager(Rc::new(Vec::new()))))) + todo!() } #[no_mangle] diff --git a/bindings/jsonnet/src/val_modify.rs b/bindings/jsonnet/src/val_modify.rs index 93d1cb27..29c70609 100644 --- a/bindings/jsonnet/src/val_modify.rs +++ b/bindings/jsonnet/src/val_modify.rs @@ -2,9 +2,9 @@ //! Only tested with variables, which haven't altered by code before appearing here //! In jrsonnet every value is immutable, and this code is probally broken -use jrsonnet_evaluator::{ArrValue, EvaluationState, LazyBinding, LazyVal, ObjMember, Val}; +use jrsonnet_evaluator::{EvaluationState, LazyBinding, LazyVal, ObjMember, Val}; use jrsonnet_parser::Visibility; -use std::{ffi::CStr, os::raw::c_char, rc::Rc}; +use std::{ffi::CStr, os::raw::c_char}; /// # Safety /// @@ -22,7 +22,8 @@ pub unsafe extern "C" fn jsonnet_json_array_append( new.push(item); } new.push(LazyVal::new_resolved(val.clone())); - *arr = Val::Arr(ArrValue::Lazy(Rc::new(new))); + // *arr = Val::Arr(ArrValue::Lazy(Gc::new(new))); + todo!() } _ => panic!("should receive array"), } From 765a4d39e770d2ea30364d0746e26cd1f8111d7a Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sat, 5 Jun 2021 22:13:02 +0300 Subject: [PATCH 06/19] test: fix NativeCallback usage Signed-off-by: Yaroslav Bolyukin --- crates/jrsonnet-evaluator/src/lib.rs | 33 ++++++++++++++++++---------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/crates/jrsonnet-evaluator/src/lib.rs b/crates/jrsonnet-evaluator/src/lib.rs index 9f9caa23..0ce4954f 100644 --- a/crates/jrsonnet-evaluator/src/lib.rs +++ b/crates/jrsonnet-evaluator/src/lib.rs @@ -488,7 +488,10 @@ impl EvaluationState { #[cfg(test)] pub mod tests { use super::Val; - use crate::{error::Error::*, primitive_equals, EvaluationState}; + use crate::{ + error::Error::*, native::NativeCallbackHandler, primitive_equals, EvaluationState, + }; + use gc::Gc; use jrsonnet_interner::IStr; use jrsonnet_parser::*; use std::{path::PathBuf, rc::Rc}; @@ -922,23 +925,29 @@ pub mod tests { let evaluator = EvaluationState::default(); evaluator.with_stdlib(); + + #[derive(gc::Trace, gc::Finalize)] + struct NativeAdd; + impl NativeCallbackHandler for NativeAdd { + fn call(&self, from: Option>, args: &[Val]) -> crate::error::Result { + assert_eq!( + from.unwrap(), + Rc::new(PathBuf::from("native_caller.jsonnet")) + ); + match (&args[0], &args[1]) { + (Val::Num(a), Val::Num(b)) => Ok(Val::Num(a + b)), + (_, _) => unreachable!(), + } + } + } evaluator.settings_mut().ext_natives.insert( "native_add".into(), - Rc::new(NativeCallback::new( + Gc::new(NativeCallback::new( ParamsDesc(Rc::new(vec![ Param("a".into(), None), Param("b".into(), None), ])), - |caller, args| { - assert_eq!( - caller.unwrap(), - Rc::new(PathBuf::from("native_caller.jsonnet")) - ); - match (&args[0], &args[1]) { - (Val::Num(a), Val::Num(b)) => Ok(Val::Num(a + b)), - (_, _) => unreachable!(), - } - }, + Box::new(NativeAdd), )), ); evaluator.evaluate_snippet_raw( From 6dc3278fe59ba28276d693d09b60c6028118e5d7 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Wed, 9 Jun 2021 10:55:51 +0300 Subject: [PATCH 07/19] style: trailing line in Cargo.toml Signed-off-by: Yaroslav Bolyukin --- crates/jrsonnet-interner/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/jrsonnet-interner/Cargo.toml b/crates/jrsonnet-interner/Cargo.toml index fba704da..a1d5fed2 100644 --- a/crates/jrsonnet-interner/Cargo.toml +++ b/crates/jrsonnet-interner/Cargo.toml @@ -9,4 +9,4 @@ edition = "2018" [dependencies] serde = { version = "1.0" } rustc-hash = "1.1.0" -gc = { version = "0.4.1", features = ["derive"] } \ No newline at end of file +gc = { version = "0.4.1", features = ["derive"] } From e3dd72eb5c41367a393641bff7cf5a66e653c249 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Wed, 9 Jun 2021 10:56:08 +0300 Subject: [PATCH 08/19] build: lockfile Signed-off-by: Yaroslav Bolyukin --- Cargo.lock | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4be63235..b9460fa8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -104,10 +104,25 @@ dependencies = [ ] [[package]] -name = "closure" -version = "0.3.0" +name = "gc" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6173fd61b610d15a7566dd7b7620775627441c4ab9dac8906e17cb93a24b782" +checksum = "3edaac0f5832202ebc99520cb77c932248010c4645d20be1dc62d6579f5b3752" +dependencies = [ + "gc_derive", +] + +[[package]] +name = "gc_derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60df8444f094ff7885631d80e78eb7d88c3c2361a98daaabb06256e4500db941" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] [[package]] name = "hashbrown" @@ -179,7 +194,7 @@ dependencies = [ "anyhow", "base64", "bincode", - "closure", + "gc", "jrsonnet-interner", "jrsonnet-parser", "jrsonnet-stdlib", @@ -196,6 +211,7 @@ dependencies = [ name = "jrsonnet-interner" version = "0.3.7" dependencies = [ + "gc", "rustc-hash", "serde", ] @@ -204,6 +220,7 @@ dependencies = [ name = "jrsonnet-parser" version = "0.3.7" dependencies = [ + "gc", "jrsonnet-interner", "jrsonnet-stdlib", "peg", @@ -219,6 +236,7 @@ version = "0.3.7" name = "jrsonnet-types" version = "0.3.7" dependencies = [ + "gc", "peg", ] @@ -409,6 +427,18 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "synstructure" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + [[package]] name = "termcolor" version = "1.1.2" From e71a0d8aa3c759361bf050fe8aa4eeb174a71a37 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sat, 12 Jun 2021 12:39:43 +0300 Subject: [PATCH 09/19] feat: update libjsonnet to use gc Signed-off-by: Yaroslav Bolyukin --- bindings/jsonnet/Cargo.toml | 1 + bindings/jsonnet/src/native.rs | 61 +++++++++++++++++++++++++++++- bindings/jsonnet/src/val_make.rs | 4 +- bindings/jsonnet/src/val_modify.rs | 6 +-- 4 files changed, 65 insertions(+), 7 deletions(-) diff --git a/bindings/jsonnet/Cargo.toml b/bindings/jsonnet/Cargo.toml index d097b848..7468fffe 100644 --- a/bindings/jsonnet/Cargo.toml +++ b/bindings/jsonnet/Cargo.toml @@ -11,6 +11,7 @@ publish = false jrsonnet-interner = { path = "../../crates/jrsonnet-interner", version = "0.3.8" } jrsonnet-evaluator = { path = "../../crates/jrsonnet-evaluator", version = "0.3.8" } jrsonnet-parser = { path = "../../crates/jrsonnet-parser", version = "0.3.8" } +gc = { version = "0.4.1", features = ["derive"] } [lib] crate-type = ["cdylib"] diff --git a/bindings/jsonnet/src/native.rs b/bindings/jsonnet/src/native.rs index d67bc3c6..fb1d9e85 100644 --- a/bindings/jsonnet/src/native.rs +++ b/bindings/jsonnet/src/native.rs @@ -1,8 +1,14 @@ -use jrsonnet_evaluator::{error::Error, native::NativeCallback, EvaluationState, Val}; +use gc::{unsafe_empty_trace, Finalize, Gc, Trace}; +use jrsonnet_evaluator::{ + error::{Error, LocError}, + native::{NativeCallback, NativeCallbackHandler}, + EvaluationState, Val, +}; use jrsonnet_parser::{Param, ParamsDesc}; use std::{ ffi::{c_void, CStr}, os::raw::{c_char, c_int}, + path::PathBuf, rc::Rc, }; @@ -12,6 +18,39 @@ type JsonnetNativeCallback = unsafe extern "C" fn( success: *mut c_int, ) -> *mut Val; +struct JsonnetNativeCallbackHandler { + ctx: *const c_void, + cb: JsonnetNativeCallback, +} +impl Finalize for JsonnetNativeCallbackHandler {} +unsafe impl Trace for JsonnetNativeCallbackHandler { + unsafe_empty_trace!(); +} +impl NativeCallbackHandler for JsonnetNativeCallbackHandler { + fn call(&self, _from: Option>, args: &[Val]) -> Result { + let mut n_args = Vec::new(); + for a in args { + n_args.push(Some(Box::new(a.clone()))); + } + n_args.push(None); + let mut success = 1; + let v = unsafe { + (self.cb)( + self.ctx, + &n_args as *const _ as *const *const Val, + &mut success, + ) + }; + let v = unsafe { *Box::from_raw(v) }; + if success == 1 { + Ok(v) + } else { + let e = v.try_cast_str("native error").expect("error msg"); + Err(Error::RuntimeError(e).into()) + } + } +} + /// # Safety #[no_mangle] pub unsafe extern "C" fn jsonnet_native_callback( @@ -21,5 +60,23 @@ pub unsafe extern "C" fn jsonnet_native_callback( ctx: *const c_void, mut raw_params: *const *const c_char, ) { - todo!() + let name = CStr::from_ptr(name).to_str().expect("utf8 name").into(); + let mut params = Vec::new(); + loop { + if (*raw_params).is_null() { + break; + } + let param = CStr::from_ptr(*raw_params).to_str().expect("not utf8"); + params.push(Param(param.into(), None)); + raw_params = raw_params.offset(1); + } + let params = ParamsDesc(Rc::new(params)); + + vm.add_native( + name, + Gc::new(NativeCallback::new( + params, + Box::new(JsonnetNativeCallbackHandler { ctx, cb }), + )), + ) } diff --git a/bindings/jsonnet/src/val_make.rs b/bindings/jsonnet/src/val_make.rs index 948f5297..63d799cb 100644 --- a/bindings/jsonnet/src/val_make.rs +++ b/bindings/jsonnet/src/val_make.rs @@ -1,10 +1,10 @@ //! Create values in VM +use gc::Gc; use jrsonnet_evaluator::{ArrValue, EvaluationState, ObjValue, Val}; use std::{ ffi::CStr, os::raw::{c_char, c_double, c_int}, - rc::Rc, }; /// # Safety @@ -38,7 +38,7 @@ pub extern "C" fn jsonnet_json_make_null(_vm: &EvaluationState) -> *mut Val { #[no_mangle] pub extern "C" fn jsonnet_json_make_array(_vm: &EvaluationState) -> *mut Val { - todo!() + Box::into_raw(Box::new(Val::Arr(ArrValue::Eager(Gc::new(Vec::new()))))) } #[no_mangle] diff --git a/bindings/jsonnet/src/val_modify.rs b/bindings/jsonnet/src/val_modify.rs index 29c70609..4b173e64 100644 --- a/bindings/jsonnet/src/val_modify.rs +++ b/bindings/jsonnet/src/val_modify.rs @@ -2,7 +2,8 @@ //! Only tested with variables, which haven't altered by code before appearing here //! In jrsonnet every value is immutable, and this code is probally broken -use jrsonnet_evaluator::{EvaluationState, LazyBinding, LazyVal, ObjMember, Val}; +use gc::Gc; +use jrsonnet_evaluator::{ArrValue, EvaluationState, LazyBinding, LazyVal, ObjMember, Val}; use jrsonnet_parser::Visibility; use std::{ffi::CStr, os::raw::c_char}; @@ -22,8 +23,7 @@ pub unsafe extern "C" fn jsonnet_json_array_append( new.push(item); } new.push(LazyVal::new_resolved(val.clone())); - // *arr = Val::Arr(ArrValue::Lazy(Gc::new(new))); - todo!() + *arr = Val::Arr(ArrValue::Lazy(Gc::new(new))); } _ => panic!("should receive array"), } From 8766467a8ebef0125e95754f689d6c43a4cea6cd Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sat, 12 Jun 2021 12:40:14 +0300 Subject: [PATCH 10/19] style: fix clippy warnings Signed-off-by: Yaroslav Bolyukin --- crates/jrsonnet-evaluator/src/builtin/mod.rs | 3 +-- crates/jrsonnet-evaluator/src/evaluate.rs | 6 +++--- crates/jrsonnet-evaluator/src/integrations/serde.rs | 4 ++-- crates/jrsonnet-evaluator/src/obj.rs | 4 ++-- crates/jrsonnet-evaluator/src/val.rs | 6 +++--- 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/crates/jrsonnet-evaluator/src/builtin/mod.rs b/crates/jrsonnet-evaluator/src/builtin/mod.rs index bf809b6a..a80c0fd0 100644 --- a/crates/jrsonnet-evaluator/src/builtin/mod.rs +++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs @@ -466,8 +466,7 @@ fn builtin_gc_trace(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDe 0, name: ty!(string) => Val::Str; 1, rest: ty!(any); ], { - - Ok(DebugGcTraceValue::new(name, rest)) + Ok(DebugGcTraceValue::create(name, rest)) }) } diff --git a/crates/jrsonnet-evaluator/src/evaluate.rs b/crates/jrsonnet-evaluator/src/evaluate.rs index 511795f5..eeb7723f 100644 --- a/crates/jrsonnet-evaluator/src/evaluate.rs +++ b/crates/jrsonnet-evaluator/src/evaluate.rs @@ -127,8 +127,8 @@ pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (IStr, impl Bindable for BindableMethod { fn bind(&self, this: Option, super_obj: Option) -> Result { Ok(LazyVal::new(Box::new(BindableMethodLazyVal { - this: this.clone(), - super_obj: super_obj.clone(), + this, + super_obj, context_creator: self.context_creator.clone(), name: self.name.clone(), @@ -586,7 +586,7 @@ pub fn evaluate_object(context: Context, object: &ObjBody) -> Result { add: false, visibility: Visibility::Normal, invoke: LazyBinding::Bindable(Gc::new(Box::new(ObjCompBinding { - context: ctx.clone(), + context: ctx, value: obj.value.clone(), }))), location: obj.value.1.clone(), diff --git a/crates/jrsonnet-evaluator/src/integrations/serde.rs b/crates/jrsonnet-evaluator/src/integrations/serde.rs index 179389cb..0c31a9db 100644 --- a/crates/jrsonnet-evaluator/src/integrations/serde.rs +++ b/crates/jrsonnet-evaluator/src/integrations/serde.rs @@ -1,6 +1,6 @@ use crate::{ error::{Error::*, LocError, Result}, - throw, Context, LazyBinding, LazyVal, ObjMember, ObjValue, Val, + throw, LazyBinding, LazyVal, ObjMember, ObjValue, Val, }; use gc::Gc; use jrsonnet_parser::Visibility; @@ -42,7 +42,7 @@ impl TryFrom<&Val> for Value { Self::Object(out) } Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())), - Val::DebugGcTraceValue(v) => Value::try_from(&*v.value as &Val)?, + Val::DebugGcTraceValue(v) => Self::try_from(&*v.value as &Val)?, }) } } diff --git a/crates/jrsonnet-evaluator/src/obj.rs b/crates/jrsonnet-evaluator/src/obj.rs index a4267e47..f948eee0 100644 --- a/crates/jrsonnet-evaluator/src/obj.rs +++ b/crates/jrsonnet-evaluator/src/obj.rs @@ -1,7 +1,7 @@ -use crate::{evaluate_add_op, evaluate_assert, Context, LazyBinding, Result, Val}; +use crate::{evaluate_add_op, LazyBinding, Result, Val}; use gc::{Finalize, Gc, GcCell, Trace}; use jrsonnet_interner::IStr; -use jrsonnet_parser::{AssertStmt, ExprLocation, Visibility}; +use jrsonnet_parser::{ExprLocation, Visibility}; use rustc_hash::{FxHashMap, FxHashSet}; use std::hash::{Hash, Hasher}; use std::{fmt::Debug, hash::BuildHasherDefault}; diff --git a/crates/jrsonnet-evaluator/src/val.rs b/crates/jrsonnet-evaluator/src/val.rs index 631ce74c..dda3c87b 100644 --- a/crates/jrsonnet-evaluator/src/val.rs +++ b/crates/jrsonnet-evaluator/src/val.rs @@ -49,7 +49,7 @@ impl LazyVal { pub fn evaluate(&self) -> Result { match &*self.0.borrow() { LazyValInternals::Computed(v) => return Ok(v.clone()), - LazyValInternals::Errored(e) => return Err(e.clone().into()), + LazyValInternals::Errored(e) => return Err(e.clone()), LazyValInternals::Pending => return Err(RecursiveLazyValueEvaluation.into()), _ => (), }; @@ -400,7 +400,7 @@ unsafe impl Trace for DebugGcTraceValue { impl Clone for DebugGcTraceValue { fn clone(&self) -> Self { self.print("Cloned"); - let value = DebugGcTraceValue { + let value = Self { name: self.name.clone(), value: self.value.clone(), }; @@ -409,7 +409,7 @@ impl Clone for DebugGcTraceValue { } } impl DebugGcTraceValue { - pub fn new(name: IStr, value: Val) -> Val { + pub fn create(name: IStr, value: Val) -> Val { let value = Self { name, value: Box::new(value), From 724447cfe7aacddcf1f9c6439a9c27b7f92332f5 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sat, 12 Jun 2021 12:40:46 +0300 Subject: [PATCH 11/19] build: fix mimalloc feature, and use it by default Signed-off-by: Yaroslav Bolyukin --- cmds/jrsonnet/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmds/jrsonnet/Cargo.toml b/cmds/jrsonnet/Cargo.toml index 0dc45817..7dc56025 100644 --- a/cmds/jrsonnet/Cargo.toml +++ b/cmds/jrsonnet/Cargo.toml @@ -8,9 +8,9 @@ edition = "2018" publish = false [features] -default = [] +default = ["mimalloc"] # Use mimalloc as allocator -mimalloc = [] +mimalloc = ["mimallocator"] [dependencies] jrsonnet-evaluator = { path = "../../crates/jrsonnet-evaluator", version = "0.3.8" } From a501dd98e12f92ba300687308d6fde8172a46fa4 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sat, 12 Jun 2021 12:40:58 +0300 Subject: [PATCH 12/19] build: update lockfile Signed-off-by: Yaroslav Bolyukin --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index fc0090e5..d421e3cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -240,6 +240,7 @@ dependencies = [ name = "jsonnet" version = "0.3.8" dependencies = [ + "gc", "jrsonnet-evaluator", "jrsonnet-interner", "jrsonnet-parser", From a2ae6ff1c0dcedb279d60fbcc994ae154fbdf202 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sun, 4 Jul 2021 13:49:04 +0300 Subject: [PATCH 13/19] doc: remove codegenerated-stdlib Signed-off-by: Yaroslav Bolyukin --- crates/jrsonnet-evaluator/README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/crates/jrsonnet-evaluator/README.md b/crates/jrsonnet-evaluator/README.md index 48e66504..5be1680f 100644 --- a/crates/jrsonnet-evaluator/README.md +++ b/crates/jrsonnet-evaluator/README.md @@ -6,9 +6,6 @@ Interpreter for parsed jsonnet tree jsonnet stdlib is embedded into evaluator, but there is different modes for this: -- `codegenerated-stdlib` - - generates source code for reproducing stdlib AST ([Example](https://gist.githubusercontent.com/CertainLach/7b3149df556f3406f5e9368aaa9f32ec/raw/0c80d8ab9aa7b9288c6219a2779cb2ab37287669/a.rs)) - - fastest on interpretation, slowest on compilation (it takes more than 5 minutes to optimize them by llvm) - `serialized-stdlib` - serializes standard library AST using serde - slower than `codegenerated-stdlib` at runtime, but have no compilation speed penality @@ -23,8 +20,6 @@ Because of `codegenerated-stdlib` compilation slowdown, `serialized-stdlib` is u Can also be run via `cargo bench` ```markdown -# codegenerated-stdlib -test tests::bench_codegen ... bench: 401,696 ns/iter (+/- 38,521) # serialized-stdlib test tests::bench_serialize ... bench: 1,763,999 ns/iter (+/- 76,211) # none From c36aa5c4ea368fcd79cd32bcc98055c0cc90c0fa Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sun, 4 Jul 2021 13:50:47 +0300 Subject: [PATCH 14/19] doc: intrinsic handling is a implementation detail Property __intrinsic_namespace__ shouldn't be handled by anyone Signed-off-by: Yaroslav Bolyukin --- crates/jrsonnet-evaluator/README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/jrsonnet-evaluator/README.md b/crates/jrsonnet-evaluator/README.md index 5be1680f..a97bc7fa 100644 --- a/crates/jrsonnet-evaluator/README.md +++ b/crates/jrsonnet-evaluator/README.md @@ -29,7 +29,3 @@ test tests::bench_parse ... bench: 7,206,164 ns/iter (+/- 1,067,418) ## Intrinsics Some functions from stdlib are implemented as intrinsics - -### Intrinsic handling - -If indexed jsonnet object has field '__intrinsic_namespace__' of type 'string', then any not found field/method is resolved as `Val::Intrinsic(__intrinsic_namespace__, name)` From a9d1e03758ef2ae26e956d8ce52b4479aef8c833 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sun, 4 Jul 2021 15:16:54 +0300 Subject: [PATCH 15/19] refactor: switch to jrsonnet-gc Signed-off-by: Yaroslav Bolyukin --- Cargo.lock | 52 +++--- bindings/jsonnet/Cargo.toml | 2 +- bindings/jsonnet/src/native.rs | 2 +- bindings/jsonnet/src/val_make.rs | 2 +- bindings/jsonnet/src/val_modify.rs | 2 +- crates/jrsonnet-evaluator/Cargo.toml | 2 +- .../jrsonnet-evaluator/src/builtin/format.rs | 5 +- crates/jrsonnet-evaluator/src/builtin/mod.rs | 4 +- crates/jrsonnet-evaluator/src/builtin/sort.rs | 2 +- crates/jrsonnet-evaluator/src/ctx.rs | 13 +- crates/jrsonnet-evaluator/src/dynamic.rs | 5 +- crates/jrsonnet-evaluator/src/error.rs | 14 +- crates/jrsonnet-evaluator/src/evaluate.rs | 81 +++------- crates/jrsonnet-evaluator/src/function.rs | 14 +- .../src/integrations/serde.rs | 2 +- crates/jrsonnet-evaluator/src/lib.rs | 2 +- crates/jrsonnet-evaluator/src/map.rs | 33 ++-- crates/jrsonnet-evaluator/src/native.rs | 5 +- crates/jrsonnet-evaluator/src/obj.rs | 11 +- crates/jrsonnet-evaluator/src/typed.rs | 17 +- crates/jrsonnet-evaluator/src/val.rs | 55 ++----- crates/jrsonnet-interner/Cargo.toml | 2 +- crates/jrsonnet-interner/src/lib.rs | 2 +- crates/jrsonnet-parser/Cargo.toml | 2 +- crates/jrsonnet-parser/src/expr.rs | 151 ++++++------------ crates/jrsonnet-types/Cargo.toml | 2 +- crates/jrsonnet-types/src/lib.rs | 16 +- 27 files changed, 193 insertions(+), 307 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 054dbac6..fe81b8c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -99,27 +99,6 @@ dependencies = [ "clap", ] -[[package]] -name = "gc" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3edaac0f5832202ebc99520cb77c932248010c4645d20be1dc62d6579f5b3752" -dependencies = [ - "gc_derive", -] - -[[package]] -name = "gc_derive" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60df8444f094ff7885631d80e78eb7d88c3c2361a98daaabb06256e4500db941" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - [[package]] name = "hashbrown" version = "0.9.1" @@ -190,7 +169,7 @@ dependencies = [ "anyhow", "base64", "bincode", - "gc", + "jrsonnet-gc", "jrsonnet-interner", "jrsonnet-parser", "jrsonnet-stdlib", @@ -203,11 +182,32 @@ dependencies = [ "thiserror", ] +[[package]] +name = "jrsonnet-gc" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68da8bc2f00117b1373bb8877af03b1d391e4c4800e6585d7279e5b99c919dde" +dependencies = [ + "jrsonnet-gc-derive", +] + +[[package]] +name = "jrsonnet-gc-derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcba9c387b64b054f06cc4d724905296e21edeeb7506847f3299117a2d92d12" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "jrsonnet-interner" version = "0.3.8" dependencies = [ - "gc", + "jrsonnet-gc", "rustc-hash", "serde", ] @@ -216,7 +216,7 @@ dependencies = [ name = "jrsonnet-parser" version = "0.3.8" dependencies = [ - "gc", + "jrsonnet-gc", "jrsonnet-interner", "jrsonnet-stdlib", "peg", @@ -232,7 +232,7 @@ version = "0.3.8" name = "jrsonnet-types" version = "0.3.8" dependencies = [ - "gc", + "jrsonnet-gc", "peg", ] @@ -240,8 +240,8 @@ dependencies = [ name = "jsonnet" version = "0.3.8" dependencies = [ - "gc", "jrsonnet-evaluator", + "jrsonnet-gc", "jrsonnet-parser", ] diff --git a/bindings/jsonnet/Cargo.toml b/bindings/jsonnet/Cargo.toml index eaf3a2c7..52e64505 100644 --- a/bindings/jsonnet/Cargo.toml +++ b/bindings/jsonnet/Cargo.toml @@ -10,7 +10,7 @@ publish = false [dependencies] jrsonnet-evaluator = { path = "../../crates/jrsonnet-evaluator", version = "0.3.8" } jrsonnet-parser = { path = "../../crates/jrsonnet-parser", version = "0.3.8" } -gc = { version = "0.4.1", features = ["derive"] } +jrsonnet-gc = { version = "0.4.2", features = ["derive"] } [lib] crate-type = ["cdylib"] diff --git a/bindings/jsonnet/src/native.rs b/bindings/jsonnet/src/native.rs index 251ad3ed..fdb19aa9 100644 --- a/bindings/jsonnet/src/native.rs +++ b/bindings/jsonnet/src/native.rs @@ -1,9 +1,9 @@ -use gc::{unsafe_empty_trace, Finalize, Gc, Trace}; use jrsonnet_evaluator::{ error::{Error, LocError}, native::{NativeCallback, NativeCallbackHandler}, EvaluationState, Val, }; +use jrsonnet_gc::{unsafe_empty_trace, Finalize, Gc, Trace}; use jrsonnet_parser::{Param, ParamsDesc}; use std::{ ffi::{c_void, CStr}, diff --git a/bindings/jsonnet/src/val_make.rs b/bindings/jsonnet/src/val_make.rs index 63d799cb..90ae53a1 100644 --- a/bindings/jsonnet/src/val_make.rs +++ b/bindings/jsonnet/src/val_make.rs @@ -1,7 +1,7 @@ //! Create values in VM -use gc::Gc; use jrsonnet_evaluator::{ArrValue, EvaluationState, ObjValue, Val}; +use jrsonnet_gc::Gc; use std::{ ffi::CStr, os::raw::{c_char, c_double, c_int}, diff --git a/bindings/jsonnet/src/val_modify.rs b/bindings/jsonnet/src/val_modify.rs index 4b173e64..52ec7e14 100644 --- a/bindings/jsonnet/src/val_modify.rs +++ b/bindings/jsonnet/src/val_modify.rs @@ -2,8 +2,8 @@ //! Only tested with variables, which haven't altered by code before appearing here //! In jrsonnet every value is immutable, and this code is probally broken -use gc::Gc; use jrsonnet_evaluator::{ArrValue, EvaluationState, LazyBinding, LazyVal, ObjMember, Val}; +use jrsonnet_gc::Gc; use jrsonnet_parser::Visibility; use std::{ffi::CStr, os::raw::c_char}; diff --git a/crates/jrsonnet-evaluator/Cargo.toml b/crates/jrsonnet-evaluator/Cargo.toml index 3d8a13a3..6fee466e 100644 --- a/crates/jrsonnet-evaluator/Cargo.toml +++ b/crates/jrsonnet-evaluator/Cargo.toml @@ -35,7 +35,7 @@ base64 = "0.13.0" rustc-hash = "1.1.0" thiserror = "1.0" -gc = { version = "0.4.1", features = ["derive"] } +jrsonnet-gc = { version = "0.4.2", features = ["derive"] } [dependencies.anyhow] version = "1.0" diff --git a/crates/jrsonnet-evaluator/src/builtin/format.rs b/crates/jrsonnet-evaluator/src/builtin/format.rs index c326e616..4a69f30b 100644 --- a/crates/jrsonnet-evaluator/src/builtin/format.rs +++ b/crates/jrsonnet-evaluator/src/builtin/format.rs @@ -2,12 +2,13 @@ #![allow(clippy::too_many_arguments)] use crate::{error::Error::*, throw, LocError, ObjValue, Result, Val}; -use gc::{Finalize, Trace}; +use jrsonnet_gc::Trace; use jrsonnet_interner::IStr; use jrsonnet_types::ValType; use thiserror::Error; -#[derive(Debug, Clone, Error, Trace, Finalize)] +#[derive(Debug, Clone, Error, Trace)] +#[trivially_drop] pub enum FormatError { #[error("truncated format code")] TruncatedFormatCode, diff --git a/crates/jrsonnet-evaluator/src/builtin/mod.rs b/crates/jrsonnet-evaluator/src/builtin/mod.rs index 3372a23b..dbc8d6f8 100644 --- a/crates/jrsonnet-evaluator/src/builtin/mod.rs +++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs @@ -5,7 +5,7 @@ use crate::{ EvaluationState, FuncVal, LazyVal, Val, }; use format::{format_arr, format_obj}; -use gc::Gc; +use jrsonnet_gc::Gc; use jrsonnet_interner::IStr; use jrsonnet_parser::{ArgsDesc, BinaryOpType, ExprLocation}; use jrsonnet_types::ty; @@ -454,7 +454,7 @@ fn builtin_gc(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> 0, rest: ty!(any); ], { println!("GC start"); - gc::force_collect(); + jrsonnet_gc::force_collect(); println!("GC done"); Ok(rest) diff --git a/crates/jrsonnet-evaluator/src/builtin/sort.rs b/crates/jrsonnet-evaluator/src/builtin/sort.rs index af68772a..09a4e1bf 100644 --- a/crates/jrsonnet-evaluator/src/builtin/sort.rs +++ b/crates/jrsonnet-evaluator/src/builtin/sort.rs @@ -2,7 +2,7 @@ use crate::{ error::{Error, LocError, Result}, throw, Context, FuncVal, Val, }; -use gc::{Finalize, Gc, Trace}; +use jrsonnet_gc::{Finalize, Gc, Trace}; #[derive(Debug, Clone, thiserror::Error, Trace, Finalize)] pub enum SortError { diff --git a/crates/jrsonnet-evaluator/src/ctx.rs b/crates/jrsonnet-evaluator/src/ctx.rs index 46313cdf..30c98ce1 100644 --- a/crates/jrsonnet-evaluator/src/ctx.rs +++ b/crates/jrsonnet-evaluator/src/ctx.rs @@ -2,13 +2,14 @@ use crate::{ error::Error::*, map::LayeredHashMap, FutureWrapper, LazyBinding, LazyVal, ObjValue, Result, Val, }; -use gc::{Finalize, Gc, Trace}; +use jrsonnet_gc::{Gc, Trace}; use jrsonnet_interner::IStr; use rustc_hash::FxHashMap; use std::fmt::Debug; use std::hash::BuildHasherDefault; -#[derive(Clone, Trace, Finalize)] +#[derive(Clone, Trace)] +#[trivially_drop] pub struct ContextCreator(pub Context, pub FutureWrapper>); impl ContextCreator { pub fn create(&self, this: Option, super_obj: Option) -> Result { @@ -21,12 +22,13 @@ impl ContextCreator { } } -#[derive(Trace, Finalize)] +#[derive(Trace)] +#[trivially_drop] struct ContextInternals { dollar: Option, this: Option, super_obj: Option, - bindings: LayeredHashMap, + bindings: LayeredHashMap, } impl Debug for ContextInternals { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -34,7 +36,8 @@ impl Debug for ContextInternals { } } -#[derive(Debug, Clone, Trace, Finalize)] +#[derive(Debug, Clone, Trace)] +#[trivially_drop] pub struct Context(Gc); impl Context { pub fn new_future() -> FutureWrapper { diff --git a/crates/jrsonnet-evaluator/src/dynamic.rs b/crates/jrsonnet-evaluator/src/dynamic.rs index 91fa6339..d9092c24 100644 --- a/crates/jrsonnet-evaluator/src/dynamic.rs +++ b/crates/jrsonnet-evaluator/src/dynamic.rs @@ -1,6 +1,7 @@ -use gc::{Finalize, Gc, GcCell, Trace}; +use jrsonnet_gc::{Gc, GcCell, Trace}; -#[derive(Clone, Trace, Finalize)] +#[derive(Clone, Trace)] +#[trivially_drop] pub struct FutureWrapper(pub Gc>>); impl FutureWrapper { pub fn new() -> Self { diff --git a/crates/jrsonnet-evaluator/src/error.rs b/crates/jrsonnet-evaluator/src/error.rs index 63cf299d..befb5c8f 100644 --- a/crates/jrsonnet-evaluator/src/error.rs +++ b/crates/jrsonnet-evaluator/src/error.rs @@ -2,7 +2,7 @@ use crate::{ builtin::{format::FormatError, sort::SortError}, typed::TypeLocError, }; -use gc::{Finalize, Trace}; +use jrsonnet_gc::Trace; use jrsonnet_interner::IStr; use jrsonnet_parser::{BinaryOpType, ExprLocation, UnaryOpType}; use jrsonnet_types::ValType; @@ -12,7 +12,8 @@ use std::{ }; use thiserror::Error; -#[derive(Error, Debug, Clone, Trace, Finalize)] +#[derive(Error, Debug, Clone, Trace)] +#[trivially_drop] pub enum Error { #[error("intrinsic not found: {0}")] IntrinsicNotFound(IStr), @@ -149,15 +150,18 @@ impl From for LocError { } } -#[derive(Clone, Debug, Trace, Finalize)] +#[derive(Clone, Debug, Trace)] +#[trivially_drop] pub struct StackTraceElement { pub location: Option, pub desc: String, } -#[derive(Debug, Clone, Trace, Finalize)] +#[derive(Debug, Clone, Trace)] +#[trivially_drop] pub struct StackTrace(pub Vec); -#[derive(Debug, Clone, Trace, Finalize)] +#[derive(Debug, Clone, Trace)] +#[trivially_drop] pub struct LocError(Box<(Error, StackTrace)>); impl LocError { pub fn new(e: Error) -> Self { diff --git a/crates/jrsonnet-evaluator/src/evaluate.rs b/crates/jrsonnet-evaluator/src/evaluate.rs index 5968db5d..795d3865 100644 --- a/crates/jrsonnet-evaluator/src/evaluate.rs +++ b/crates/jrsonnet-evaluator/src/evaluate.rs @@ -3,7 +3,7 @@ use crate::{ FuncDesc, FuncVal, FutureWrapper, LazyBinding, LazyVal, LazyValValue, ObjMember, ObjValue, ObjectAssertion, Result, Val, }; -use gc::{custom_trace, Finalize, Gc, Trace}; +use jrsonnet_gc::{Gc, Trace}; use jrsonnet_interner::IStr; use jrsonnet_parser::{ ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, ExprLocation, FieldMember, @@ -22,21 +22,14 @@ pub fn evaluate_binding_in_future( if let Some(params) = &b.params { let params = params.clone(); + #[derive(Trace)] + #[trivially_drop] struct LazyMethodBinding { context_creator: FutureWrapper, name: IStr, params: ParamsDesc, value: LocExpr, } - impl Finalize for LazyMethodBinding {} - unsafe impl Trace for LazyMethodBinding { - custom_trace!(this, { - mark(&this.context_creator); - mark(&this.name); - mark(&this.params); - mark(&this.value); - }); - } impl LazyValValue for LazyMethodBinding { fn get(self: Box) -> Result { Ok(evaluate_method( @@ -55,19 +48,13 @@ pub fn evaluate_binding_in_future( value: b.value.clone(), })) } else { + #[derive(Trace)] + #[trivially_drop] struct LazyNamedBinding { context_creator: FutureWrapper, name: IStr, value: LocExpr, } - impl Finalize for LazyNamedBinding {} - unsafe impl Trace for LazyNamedBinding { - custom_trace!(this, { - mark(&this.context_creator); - mark(&this.name); - mark(&this.value); - }); - } impl LazyValValue for LazyNamedBinding { fn get(self: Box) -> Result { evaluate_named(self.context_creator.unwrap(), &self.value, self.name) @@ -86,6 +73,8 @@ pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (IStr, if let Some(params) = &b.params { let params = params.clone(); + #[derive(Trace)] + #[trivially_drop] struct BindableMethodLazyVal { this: Option, super_obj: Option, @@ -95,17 +84,6 @@ pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (IStr, params: ParamsDesc, value: LocExpr, } - impl Finalize for BindableMethodLazyVal {} - unsafe impl Trace for BindableMethodLazyVal { - custom_trace!(this, { - mark(&this.this); - mark(&this.super_obj); - mark(&this.context_creator); - mark(&this.name); - mark(&this.params); - mark(&this.value); - }); - } impl LazyValValue for BindableMethodLazyVal { fn get(self: Box) -> Result { Ok(evaluate_method( @@ -117,7 +95,8 @@ pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (IStr, } } - #[derive(Trace, Finalize)] + #[derive(Trace)] + #[trivially_drop] struct BindableMethod { context_creator: ContextCreator, name: IStr, @@ -148,6 +127,8 @@ pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (IStr, }))), ) } else { + #[derive(Trace)] + #[trivially_drop] struct BindableNamedLazyVal { this: Option, super_obj: Option, @@ -156,16 +137,6 @@ pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (IStr, name: IStr, value: LocExpr, } - impl Finalize for BindableNamedLazyVal {} - unsafe impl Trace for BindableNamedLazyVal { - custom_trace!(this, { - mark(&this.this); - mark(&this.super_obj); - mark(&this.context_creator); - mark(&this.name); - mark(&this.value); - }); - } impl LazyValValue for BindableNamedLazyVal { fn get(self: Box) -> Result { evaluate_named( @@ -176,7 +147,8 @@ pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (IStr, } } - #[derive(Trace, Finalize)] + #[derive(Trace)] + #[trivially_drop] struct BindableNamed { context_creator: ContextCreator, name: IStr, @@ -414,7 +386,8 @@ pub fn evaluate_member_list_object(context: Context, members: &[Member]) -> Resu } let name = name.unwrap(); - #[derive(Trace, Finalize)] + #[derive(Trace)] + #[trivially_drop] struct ObjMemberBinding { context_creator: ContextCreator, value: LocExpr, @@ -458,7 +431,8 @@ pub fn evaluate_member_list_object(context: Context, members: &[Member]) -> Resu continue; } let name = name.unwrap(); - #[derive(Trace, Finalize)] + #[derive(Trace)] + #[trivially_drop] struct ObjMemberBinding { context_creator: ContextCreator, value: LocExpr, @@ -496,17 +470,12 @@ pub fn evaluate_member_list_object(context: Context, members: &[Member]) -> Resu } Member::BindStmt(_) => {} Member::AssertStmt(stmt) => { + #[derive(Trace)] + #[trivially_drop] struct ObjectAssert { context_creator: ContextCreator, assert: AssertStmt, } - impl Finalize for ObjectAssert {} - unsafe impl Trace for ObjectAssert { - custom_trace!(this, { - mark(&this.context_creator); - mark(&this.assert); - }); - } impl ObjectAssertion for ObjectAssert { fn run( &self, @@ -558,7 +527,8 @@ pub fn evaluate_object(context: Context, object: &ObjBody) -> Result { match key { Val::Null => {} Val::Str(n) => { - #[derive(Trace, Finalize)] + #[derive(Trace)] + #[trivially_drop] struct ObjCompBinding { context: Context, value: LocExpr, @@ -768,17 +738,12 @@ pub fn evaluate(context: Context, expr: &LocExpr) -> Result { let mut out = Vec::with_capacity(items.len()); for item in items { // TODO: Implement ArrValue::Lazy with same context for every element? + #[derive(Trace)] + #[trivially_drop] struct ArrayElement { context: Context, item: LocExpr, } - impl Finalize for ArrayElement {} - unsafe impl Trace for ArrayElement { - custom_trace!(this, { - mark(&this.context); - mark(&this.item); - }); - } impl LazyValValue for ArrayElement { fn get(self: Box) -> Result { evaluate(self.context, &self.item) diff --git a/crates/jrsonnet-evaluator/src/function.rs b/crates/jrsonnet-evaluator/src/function.rs index 065835ec..c83b5b5a 100644 --- a/crates/jrsonnet-evaluator/src/function.rs +++ b/crates/jrsonnet-evaluator/src/function.rs @@ -1,5 +1,5 @@ use crate::{error::Error::*, evaluate, throw, Context, LazyVal, LazyValValue, Result, Val}; -use gc::{custom_trace, Finalize, Trace}; +use jrsonnet_gc::Trace; use jrsonnet_interner::IStr; use jrsonnet_parser::{ArgsDesc, LocExpr, ParamsDesc}; use rustc_hash::FxHashMap; @@ -55,17 +55,12 @@ pub fn parse_function_call( let val = if tailstrict { LazyVal::new_resolved(evaluate(ctx, expr)?) } else { + #[derive(Trace)] + #[trivially_drop] struct EvaluateLazyVal { context: Context, expr: LocExpr, } - impl Finalize for EvaluateLazyVal {} - unsafe impl Trace for EvaluateLazyVal { - custom_trace!(this, { - mark(&this.context); - mark(&this.expr); - }); - } impl LazyValValue for EvaluateLazyVal { fn get(self: Box) -> Result { evaluate(self.context, &self.expr) @@ -119,7 +114,8 @@ pub fn parse_function_call_map( } else { let body_ctx = body_ctx.clone(); let default = default.clone(); - #[derive(Trace, Finalize)] + #[derive(Trace)] + #[trivially_drop] struct EvaluateLazyVal { body_ctx: Option, default: LocExpr, diff --git a/crates/jrsonnet-evaluator/src/integrations/serde.rs b/crates/jrsonnet-evaluator/src/integrations/serde.rs index 0c31a9db..8721b169 100644 --- a/crates/jrsonnet-evaluator/src/integrations/serde.rs +++ b/crates/jrsonnet-evaluator/src/integrations/serde.rs @@ -2,7 +2,7 @@ use crate::{ error::{Error::*, LocError, Result}, throw, LazyBinding, LazyVal, ObjMember, ObjValue, Val, }; -use gc::Gc; +use jrsonnet_gc::Gc; use jrsonnet_parser::Visibility; use rustc_hash::FxHasher; use serde_json::{Map, Number, Value}; diff --git a/crates/jrsonnet-evaluator/src/lib.rs b/crates/jrsonnet-evaluator/src/lib.rs index 109733b3..6168a881 100644 --- a/crates/jrsonnet-evaluator/src/lib.rs +++ b/crates/jrsonnet-evaluator/src/lib.rs @@ -25,8 +25,8 @@ pub use dynamic::*; use error::{Error::*, LocError, Result, StackTraceElement}; pub use evaluate::*; pub use function::parse_function_call; -use gc::{Finalize, Gc, Trace}; pub use import::*; +use jrsonnet_gc::{Finalize, Gc, Trace}; pub use jrsonnet_interner::IStr; use jrsonnet_parser::*; use native::NativeCallback; diff --git a/crates/jrsonnet-evaluator/src/map.rs b/crates/jrsonnet-evaluator/src/map.rs index 43b76386..0b1a7f75 100644 --- a/crates/jrsonnet-evaluator/src/map.rs +++ b/crates/jrsonnet-evaluator/src/map.rs @@ -1,32 +1,29 @@ -use gc::{Finalize, Gc, Trace}; +use jrsonnet_gc::{Gc, Trace}; use jrsonnet_interner::IStr; use rustc_hash::FxHashMap; -pub struct LayeredHashMapInternals { - parent: Option>, - current: FxHashMap, -} +use crate::LazyVal; -unsafe impl Trace for LayeredHashMapInternals { - gc::custom_trace!(this, { - mark(&this.parent); - mark(&this.current); - }); +#[derive(Trace)] +#[trivially_drop] +pub struct LayeredHashMapInternals { + parent: Option, + current: FxHashMap, } -impl Finalize for LayeredHashMapInternals {} -#[derive(Trace, Finalize)] -pub struct LayeredHashMap(Gc>); +#[derive(Trace)] +#[trivially_drop] +pub struct LayeredHashMap(Gc); -impl LayeredHashMap { - pub fn extend(self, new_layer: FxHashMap) -> Self { +impl LayeredHashMap { + pub fn extend(self, new_layer: FxHashMap) -> Self { Self(Gc::new(LayeredHashMapInternals { parent: Some(self), current: new_layer, })) } - pub fn get(&self, key: &IStr) -> Option<&V> { + pub fn get(&self, key: &IStr) -> Option<&LazyVal> { (self.0) .current .get(key) @@ -34,13 +31,13 @@ impl LayeredHashMap { } } -impl Clone for LayeredHashMap { +impl Clone for LayeredHashMap { fn clone(&self) -> Self { Self(self.0.clone()) } } -impl Default for LayeredHashMap { +impl Default for LayeredHashMap { fn default() -> Self { Self(Gc::new(LayeredHashMapInternals { parent: None, diff --git a/crates/jrsonnet-evaluator/src/native.rs b/crates/jrsonnet-evaluator/src/native.rs index 0abe4221..dbca7204 100644 --- a/crates/jrsonnet-evaluator/src/native.rs +++ b/crates/jrsonnet-evaluator/src/native.rs @@ -1,7 +1,7 @@ #![allow(clippy::type_complexity)] use crate::{error::Result, Val}; -use gc::{Finalize, Trace}; +use jrsonnet_gc::Trace; use jrsonnet_parser::ParamsDesc; use std::fmt::Debug; use std::path::Path; @@ -11,7 +11,8 @@ pub trait NativeCallbackHandler: Trace { fn call(&self, from: Option>, args: &[Val]) -> Result; } -#[derive(Trace, Finalize)] +#[derive(Trace)] +#[trivially_drop] pub struct NativeCallback { pub params: ParamsDesc, handler: Box, diff --git a/crates/jrsonnet-evaluator/src/obj.rs b/crates/jrsonnet-evaluator/src/obj.rs index f948eee0..ec2e86a6 100644 --- a/crates/jrsonnet-evaluator/src/obj.rs +++ b/crates/jrsonnet-evaluator/src/obj.rs @@ -1,12 +1,13 @@ use crate::{evaluate_add_op, LazyBinding, Result, Val}; -use gc::{Finalize, Gc, GcCell, Trace}; +use jrsonnet_gc::{Gc, GcCell, Trace}; use jrsonnet_interner::IStr; use jrsonnet_parser::{ExprLocation, Visibility}; use rustc_hash::{FxHashMap, FxHashSet}; use std::hash::{Hash, Hasher}; use std::{fmt::Debug, hash::BuildHasherDefault}; -#[derive(Debug, Trace, Finalize)] +#[derive(Debug, Trace)] +#[trivially_drop] pub struct ObjMember { pub add: bool, pub visibility: Visibility, @@ -20,7 +21,8 @@ pub trait ObjectAssertion: Trace { // Field => This type CacheKey = (IStr, ObjValue); -#[derive(Trace, Finalize)] +#[derive(Trace)] +#[trivially_drop] pub struct ObjValueInternals { super_obj: Option, assertions: Gc>>, @@ -30,7 +32,8 @@ pub struct ObjValueInternals { value_cache: GcCell>>, } -#[derive(Clone, Trace, Finalize)] +#[derive(Clone, Trace)] +#[trivially_drop] pub struct ObjValue(pub(crate) Gc); impl Debug for ObjValue { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/crates/jrsonnet-evaluator/src/typed.rs b/crates/jrsonnet-evaluator/src/typed.rs index 78a4b0e5..5d139033 100644 --- a/crates/jrsonnet-evaluator/src/typed.rs +++ b/crates/jrsonnet-evaluator/src/typed.rs @@ -4,7 +4,7 @@ use crate::{ error::{Error, LocError, Result}, push, Val, }; -use gc::{Finalize, Trace}; +use jrsonnet_gc::Trace; use jrsonnet_parser::ExprLocation; use jrsonnet_types::{ComplexValType, ValType}; use thiserror::Error; @@ -21,7 +21,8 @@ macro_rules! unwrap_type { }}; } -#[derive(Debug, Error, Clone, Trace, Finalize)] +#[derive(Debug, Error, Clone, Trace)] +#[trivially_drop] pub enum TypeError { #[error("expected {0}, got {1}")] ExpectedGot(ComplexValType, ValType), @@ -38,7 +39,8 @@ impl From for LocError { } } -#[derive(Debug, Clone, Trace, Finalize)] +#[derive(Debug, Clone, Trace)] +#[trivially_drop] pub struct TypeLocError(Box, ValuePathStack); impl From for TypeLocError { fn from(e: TypeError) -> Self { @@ -60,7 +62,8 @@ impl Display for TypeLocError { } } -#[derive(Debug, Clone, Trace, Finalize)] +#[derive(Debug, Clone, Trace)] +#[trivially_drop] pub struct TypeLocErrorList(Vec); impl Display for TypeLocErrorList { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -123,7 +126,8 @@ impl CheckType for ValType { } } -#[derive(Clone, Debug, Trace, Finalize)] +#[derive(Clone, Debug, Trace)] +#[trivially_drop] enum ValuePathItem { Field(Rc), Index(u64), @@ -138,7 +142,8 @@ impl Display for ValuePathItem { } } -#[derive(Clone, Debug, Trace, Finalize)] +#[derive(Clone, Debug, Trace)] +#[trivially_drop] struct ValuePathStack(Vec); impl Display for ValuePathStack { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/crates/jrsonnet-evaluator/src/val.rs b/crates/jrsonnet-evaluator/src/val.rs index dda3c87b..dc9423b5 100644 --- a/crates/jrsonnet-evaluator/src/val.rs +++ b/crates/jrsonnet-evaluator/src/val.rs @@ -9,7 +9,7 @@ use crate::{ native::NativeCallback, throw, with_state, Context, ObjValue, Result, }; -use gc::{custom_trace, Finalize, Gc, GcCell, Trace}; +use jrsonnet_gc::{Finalize, Gc, GcCell, Trace}; use jrsonnet_interner::IStr; use jrsonnet_parser::{el, Arg, ArgsDesc, Expr, ExprLocation, LiteralType, LocExpr, ParamsDesc}; use jrsonnet_types::ValType; @@ -19,25 +19,17 @@ pub trait LazyValValue: Trace { fn get(self: Box) -> Result; } +#[derive(Trace)] +#[trivially_drop] enum LazyValInternals { Computed(Val), Errored(LocError), Waiting(Box), Pending, } -impl Finalize for LazyValInternals {} -unsafe impl Trace for LazyValInternals { - custom_trace!(this, { - match &this { - LazyValInternals::Computed(v) => mark(v), - LazyValInternals::Errored(e) => mark(e), - LazyValInternals::Waiting(w) => mark(w), - LazyValInternals::Pending => {} - } - }); -} -#[derive(Clone, Trace, Finalize)] +#[derive(Clone, Trace)] +#[trivially_drop] pub struct LazyVal(Gc>); impl LazyVal { pub fn new(f: Box) -> Self { @@ -83,7 +75,8 @@ impl PartialEq for LazyVal { } } -#[derive(Debug, PartialEq, Trace, Finalize)] +#[derive(Debug, PartialEq, Trace)] +#[trivially_drop] pub struct FuncDesc { pub name: IStr, pub ctx: Context, @@ -91,7 +84,8 @@ pub struct FuncDesc { pub body: LocExpr, } -#[derive(Debug, Trace, Finalize)] +#[derive(Debug, Trace)] +#[trivially_drop] pub enum FuncVal { /// Plain function implemented in jsonnet Normal(FuncDesc), @@ -195,22 +189,13 @@ pub enum ManifestFormat { String, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Trace)] +#[trivially_drop] pub enum ArrValue { Lazy(Gc>), Eager(Gc>), Extended(Box<(Self, Self)>), } -impl Finalize for ArrValue {} -unsafe impl Trace for ArrValue { - custom_trace!(this, { - match &this { - ArrValue::Lazy(l) => mark(l), - ArrValue::Eager(e) => mark(e), - ArrValue::Extended(x) => mark(x), - } - }); -} impl ArrValue { pub fn new_eager() -> Self { Self::Eager(Gc::new(Vec::new())) @@ -419,7 +404,8 @@ impl DebugGcTraceValue { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Trace)] +#[trivially_drop] pub enum Val { Bool(bool), Null, @@ -430,21 +416,6 @@ pub enum Val { Func(Gc), DebugGcTraceValue(DebugGcTraceValue), } -impl Finalize for Val {} -unsafe impl Trace for Val { - custom_trace!(this, { - match &this { - Val::Bool(_) => {} - Val::Null => {} - Val::Str(_) => {} - Val::Num(_) => {} - Val::Arr(a) => mark(a), - Val::Obj(o) => mark(o), - Val::Func(f) => mark(f), - Val::DebugGcTraceValue(v) => mark(v), - } - }); -} macro_rules! matches_unwrap { ($e: expr, $p: pat, $r: expr) => { diff --git a/crates/jrsonnet-interner/Cargo.toml b/crates/jrsonnet-interner/Cargo.toml index 2745573c..3e056f17 100644 --- a/crates/jrsonnet-interner/Cargo.toml +++ b/crates/jrsonnet-interner/Cargo.toml @@ -9,4 +9,4 @@ edition = "2018" [dependencies] serde = { version = "1.0" } rustc-hash = "1.1.0" -gc = { version = "0.4.1", features = ["derive"] } +jrsonnet-gc = { version = "0.4.2", features = ["derive"] } diff --git a/crates/jrsonnet-interner/src/lib.rs b/crates/jrsonnet-interner/src/lib.rs index 9fc23cda..88dbf185 100644 --- a/crates/jrsonnet-interner/src/lib.rs +++ b/crates/jrsonnet-interner/src/lib.rs @@ -1,4 +1,4 @@ -use gc::{unsafe_empty_trace, Finalize, Trace}; +use jrsonnet_gc::{unsafe_empty_trace, Finalize, Trace}; use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; use std::{ diff --git a/crates/jrsonnet-parser/Cargo.toml b/crates/jrsonnet-parser/Cargo.toml index a52b6cdb..96f4551d 100644 --- a/crates/jrsonnet-parser/Cargo.toml +++ b/crates/jrsonnet-parser/Cargo.toml @@ -18,7 +18,7 @@ peg = "0.7.0" unescape = "0.1.0" serde = { version = "1.0", features = ["derive", "rc"], optional = true } -gc = { version = "0.4.1", features = ["derive"] } +jrsonnet-gc = { version = "0.4.2", features = ["derive"] } [dev-dependencies] jrsonnet-stdlib = { path = "../jrsonnet-stdlib", version = "0.3.8" } diff --git a/crates/jrsonnet-parser/src/expr.rs b/crates/jrsonnet-parser/src/expr.rs index fdcd126c..5f970765 100644 --- a/crates/jrsonnet-parser/src/expr.rs +++ b/crates/jrsonnet-parser/src/expr.rs @@ -1,4 +1,4 @@ -use gc::{unsafe_empty_trace, Finalize, Trace}; +use jrsonnet_gc::{unsafe_empty_trace, Finalize, Trace}; use jrsonnet_interner::IStr; #[cfg(feature = "deserialize")] use serde::Deserialize; @@ -13,21 +13,19 @@ use std::{ #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Trace)] +#[trivially_drop] pub enum FieldName { /// {fixed: 2} Fixed(IStr), /// {["dyn"+"amic"]: 3} Dyn(LocExpr), } -impl Finalize for FieldName {} -unsafe impl Trace for FieldName { - unsafe_empty_trace!(); -} #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Trace)] +#[trivially_drop] pub enum Visibility { /// : Normal, @@ -36,10 +34,6 @@ pub enum Visibility { /// ::: Unhide, } -impl Finalize for Visibility {} -unsafe impl Trace for Visibility { - unsafe_empty_trace!(); -} impl Visibility { pub fn is_visible(&self) -> bool { @@ -49,16 +43,14 @@ impl Visibility { #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Trace)] +#[trivially_drop] pub struct AssertStmt(pub LocExpr, pub Option); -impl Finalize for AssertStmt {} -unsafe impl Trace for AssertStmt { - unsafe_empty_trace!(); -} #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Trace)] +#[trivially_drop] pub struct FieldMember { pub name: FieldName, pub plus: bool, @@ -66,37 +58,27 @@ pub struct FieldMember { pub visibility: Visibility, pub value: LocExpr, } -impl Finalize for FieldMember {} -unsafe impl Trace for FieldMember { - unsafe_empty_trace!(); -} #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Trace)] +#[trivially_drop] pub enum Member { Field(FieldMember), BindStmt(BindSpec), AssertStmt(AssertStmt), } -impl Finalize for Member {} -unsafe impl Trace for Member { - unsafe_empty_trace!(); -} #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Trace)] +#[trivially_drop] pub enum UnaryOpType { Plus, Minus, BitNot, Not, } -impl Finalize for UnaryOpType {} -unsafe impl Trace for UnaryOpType { - unsafe_empty_trace!(); -} impl Display for UnaryOpType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -116,7 +98,8 @@ impl Display for UnaryOpType { #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Trace)] +#[trivially_drop] pub enum BinaryOpType { Mul, Div, @@ -145,10 +128,6 @@ pub enum BinaryOpType { And, Or, } -impl Finalize for BinaryOpType {} -unsafe impl Trace for BinaryOpType { - unsafe_empty_trace!(); -} impl Display for BinaryOpType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -183,22 +162,22 @@ impl Display for BinaryOpType { /// name, default value #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Trace)] +#[trivially_drop] pub struct Param(pub IStr, pub Option); -impl Finalize for Param {} -unsafe impl Trace for Param { - unsafe_empty_trace!(); -} /// Defined function parameters #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] #[derive(Debug, Clone, PartialEq)] pub struct ParamsDesc(pub Rc>); -impl Finalize for ParamsDesc {} + +/// Safety: +/// AST is acyclic, and there should be no gc pointers unsafe impl Trace for ParamsDesc { unsafe_empty_trace!(); } +impl Finalize for ParamsDesc {} impl Deref for ParamsDesc { type Target = Vec; @@ -209,21 +188,15 @@ impl Deref for ParamsDesc { #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Trace)] +#[trivially_drop] pub struct Arg(pub Option, pub LocExpr); -impl Finalize for Arg {} -unsafe impl Trace for Arg { - unsafe_empty_trace!(); -} #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Trace)] +#[trivially_drop] pub struct ArgsDesc(pub Vec); -impl Finalize for ArgsDesc {} -unsafe impl Trace for ArgsDesc { - unsafe_empty_trace!(); -} impl Deref for ArgsDesc { type Target = Vec; @@ -234,50 +207,39 @@ impl Deref for ArgsDesc { #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Trace)] +#[trivially_drop] pub struct BindSpec { pub name: IStr, pub params: Option, pub value: LocExpr, } -impl Finalize for BindSpec {} -unsafe impl Trace for BindSpec { - unsafe_empty_trace!(); -} #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Trace)] +#[trivially_drop] pub struct IfSpecData(pub LocExpr); -impl Finalize for IfSpecData {} -unsafe impl Trace for IfSpecData { - unsafe_empty_trace!(); -} #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Trace)] +#[trivially_drop] pub struct ForSpecData(pub IStr, pub LocExpr); -impl Finalize for ForSpecData {} -unsafe impl Trace for ForSpecData { - unsafe_empty_trace!(); -} #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Trace)] +#[trivially_drop] pub enum CompSpec { IfSpec(IfSpecData), ForSpec(ForSpecData), } -impl Finalize for CompSpec {} -unsafe impl Trace for CompSpec { - unsafe_empty_trace!(); -} #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Trace)] +#[trivially_drop] pub struct ObjComp { pub pre_locals: Vec, pub key: LocExpr, @@ -285,26 +247,20 @@ pub struct ObjComp { pub post_locals: Vec, pub compspecs: Vec, } -impl Finalize for ObjComp {} -unsafe impl Trace for ObjComp { - unsafe_empty_trace!(); -} #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Trace)] +#[trivially_drop] pub enum ObjBody { MemberList(Vec), ObjComp(ObjComp), } -impl Finalize for ObjBody {} -unsafe impl Trace for ObjBody { - unsafe_empty_trace!(); -} #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] -#[derive(Debug, PartialEq, Clone, Copy)] +#[derive(Debug, PartialEq, Clone, Copy, Trace)] +#[trivially_drop] pub enum LiteralType { This, Super, @@ -313,26 +269,20 @@ pub enum LiteralType { True, False, } -impl Finalize for LiteralType {} -unsafe impl Trace for LiteralType { - unsafe_empty_trace!(); -} -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Trace)] +#[trivially_drop] pub struct SliceDesc { pub start: Option, pub end: Option, pub step: Option, } -impl Finalize for SliceDesc {} -unsafe impl Trace for SliceDesc { - unsafe_empty_trace!(); -} /// Syntax base #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Trace)] +#[trivially_drop] pub enum Expr { Literal(LiteralType), @@ -396,20 +346,13 @@ pub enum Expr { cond_else: Option, }, } -impl Finalize for Expr {} -unsafe impl Trace for Expr { - unsafe_empty_trace!(); -} /// file, begin offset, end offset #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, Trace)] +#[trivially_drop] pub struct ExprLocation(pub Rc, pub usize, pub usize); -impl Finalize for ExprLocation {} -unsafe impl Trace for ExprLocation { - unsafe_empty_trace!(); -} impl Debug for ExprLocation { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -422,10 +365,12 @@ impl Debug for ExprLocation { #[cfg_attr(feature = "deserialize", derive(Deserialize))] #[derive(Clone, PartialEq)] pub struct LocExpr(pub Rc, pub Option); -impl Finalize for LocExpr {} +/// Safety: +/// AST is acyclic, and there should be no gc pointers unsafe impl Trace for LocExpr { unsafe_empty_trace!(); } +impl Finalize for LocExpr {} impl Debug for LocExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/crates/jrsonnet-types/Cargo.toml b/crates/jrsonnet-types/Cargo.toml index 4df158ab..660ff8a8 100644 --- a/crates/jrsonnet-types/Cargo.toml +++ b/crates/jrsonnet-types/Cargo.toml @@ -8,4 +8,4 @@ edition = "2018" [dependencies] peg = "0.7.0" -gc = { version = "0.4.1", features = ["derive"] } +jrsonnet-gc = { version = "0.4.2", features = ["derive"] } diff --git a/crates/jrsonnet-types/src/lib.rs b/crates/jrsonnet-types/src/lib.rs index 40349739..cd35a8f8 100644 --- a/crates/jrsonnet-types/src/lib.rs +++ b/crates/jrsonnet-types/src/lib.rs @@ -1,6 +1,6 @@ #![allow(clippy::redundant_closure_call)] -use gc::{unsafe_empty_trace, Finalize, Trace}; +use jrsonnet_gc::Trace; use std::fmt::Display; #[macro_export] @@ -78,7 +78,8 @@ fn test() { ); } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)] +#[trivially_drop] pub enum ValType { Bool, Null, @@ -88,10 +89,6 @@ pub enum ValType { Obj, Func, } -impl Finalize for ValType {} -unsafe impl Trace for ValType { - unsafe_empty_trace!(); -} impl ValType { pub const fn name(&self) -> &'static str { @@ -114,7 +111,8 @@ impl Display for ValType { } } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Trace)] +#[trivially_drop] pub enum ComplexValType { Any, Char, @@ -128,10 +126,6 @@ pub enum ComplexValType { Sum(Vec), SumRef(&'static [ComplexValType]), } -impl Finalize for ComplexValType {} -unsafe impl Trace for ComplexValType { - unsafe_empty_trace!(); -} impl From for ComplexValType { fn from(s: ValType) -> Self { From 0031c6e45bf8f5e210a8fca6f188de228cf91e56 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sun, 4 Jul 2021 15:52:51 +0300 Subject: [PATCH 16/19] feat: gc options Signed-off-by: Yaroslav Bolyukin --- Cargo.lock | 1 + cmds/jrsonnet/src/main.rs | 6 +++- crates/jrsonnet-cli/Cargo.toml | 1 + crates/jrsonnet-cli/src/lib.rs | 52 ++++++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index fe81b8c7..e31f694f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -158,6 +158,7 @@ version = "0.3.8" dependencies = [ "clap", "jrsonnet-evaluator", + "jrsonnet-gc", "jrsonnet-parser", ] diff --git a/cmds/jrsonnet/src/main.rs b/cmds/jrsonnet/src/main.rs index 47047b26..8f8107e9 100644 --- a/cmds/jrsonnet/src/main.rs +++ b/cmds/jrsonnet/src/main.rs @@ -1,5 +1,5 @@ use clap::{AppSettings, Clap, IntoApp}; -use jrsonnet_cli::{ConfigureState, GeneralOpts, InputOpts, ManifestOpts, OutputOpts}; +use jrsonnet_cli::{ConfigureState, GcOpts, GeneralOpts, InputOpts, ManifestOpts, OutputOpts}; use jrsonnet_evaluator::{error::LocError, EvaluationState, ManifestFormat}; use std::{ fs::{create_dir_all, File}, @@ -61,6 +61,8 @@ struct Opts { output: OutputOpts, #[clap(flatten)] debug: DebugOpts, + #[clap(flatten)] + gc: GcOpts, } fn main() { @@ -114,6 +116,7 @@ impl From for Error { } fn main_catch(opts: Opts) -> bool { + let _printer = opts.gc.stats_printer(); let state = EvaluationState::default(); if let Err(e) = main_real(&state, opts) { if let Error::Evaluation(e) = e { @@ -127,6 +130,7 @@ fn main_catch(opts: Opts) -> bool { } fn main_real(state: &EvaluationState, opts: Opts) -> Result<(), Error> { + opts.gc.configure_global(); opts.general.configure(&state)?; opts.manifest.configure(&state)?; diff --git a/crates/jrsonnet-cli/Cargo.toml b/crates/jrsonnet-cli/Cargo.toml index a4531bda..cdc4b841 100644 --- a/crates/jrsonnet-cli/Cargo.toml +++ b/crates/jrsonnet-cli/Cargo.toml @@ -10,6 +10,7 @@ publish = false [dependencies] jrsonnet-evaluator = { path = "../../crates/jrsonnet-evaluator", version = "0.3.6", features = ["explaining-traces"] } jrsonnet-parser = { path = "../../crates/jrsonnet-parser", version = "0.3.6" } +jrsonnet-gc = { version = "0.4.2", features = ["derive", "unstable-config", "unstable-stats"] } [dependencies.clap] git = "https://github.com/clap-rs/clap" diff --git a/crates/jrsonnet-cli/src/lib.rs b/crates/jrsonnet-cli/src/lib.rs index e69c64b8..4bb7945e 100644 --- a/crates/jrsonnet-cli/src/lib.rs +++ b/crates/jrsonnet-cli/src/lib.rs @@ -95,3 +95,55 @@ impl ConfigureState for GeneralOpts { Ok(()) } } + +#[derive(Clap)] +#[clap(help_heading = "GARBAGE COLLECTION")] +pub struct GcOpts { + /// Min bytes allocated to start garbage collection + #[clap(long, default_value = "20000000")] + gc_initial_threshold: usize, + /// How much heap should grow after unsuccessful garbage collection + #[clap(long)] + gc_used_space_ratio: Option, + /// Do not skip gc on exit + #[clap(long)] + gc_collect_on_exit: bool, + /// Print gc stats before exit + #[clap(long)] + gc_print_stats: bool, + /// Force garbage collection before printing stats + /// Useful for checking for memory leaks + /// Does nothing useless --gc-print-stats is specified + #[clap(long)] + gc_collect_before_printing_stats: bool, +} +impl GcOpts { + pub fn stats_printer(&self) -> Option { + self.gc_print_stats + .then(|| GcStatsPrinter(self.gc_collect_before_printing_stats)) + } + pub fn configure_global(&self) { + jrsonnet_gc::configure(|config| { + config.leak_on_drop = !self.gc_collect_on_exit; + config.threshold = self.gc_initial_threshold; + if let Some(used_space_ratio) = self.gc_used_space_ratio { + config.used_space_ratio = used_space_ratio; + } + }); + } +} +pub struct GcStatsPrinter(bool); +impl Drop for GcStatsPrinter { + fn drop(&mut self) { + if self.0 { + jrsonnet_gc::force_collect() + } + eprintln!("=== GC STATS ==="); + jrsonnet_gc::configure(|c| { + eprintln!("Final threshold: {:?}", c.threshold); + }); + let stats = jrsonnet_gc::stats(); + eprintln!("Collections performed: {}", stats.collections_performed); + eprintln!("Bytes still allocated: {}", stats.bytes_allocated); + } +} From cda8521c5b929086bf597c884a2840e0f94bc4ab Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sun, 4 Jul 2021 15:55:06 +0300 Subject: [PATCH 17/19] test: fix gc import Signed-off-by: Yaroslav Bolyukin --- crates/jrsonnet-evaluator/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/jrsonnet-evaluator/src/lib.rs b/crates/jrsonnet-evaluator/src/lib.rs index 6168a881..5ce0cb98 100644 --- a/crates/jrsonnet-evaluator/src/lib.rs +++ b/crates/jrsonnet-evaluator/src/lib.rs @@ -491,7 +491,7 @@ pub mod tests { use crate::{ error::Error::*, native::NativeCallbackHandler, primitive_equals, EvaluationState, }; - use gc::Gc; + use jrsonnet_gc::{Finalize, Gc, Trace}; use jrsonnet_interner::IStr; use jrsonnet_parser::*; use std::{ @@ -924,7 +924,7 @@ pub mod tests { evaluator.with_stdlib(); - #[derive(gc::Trace, gc::Finalize)] + #[derive(Trace, Finalize)] struct NativeAdd; impl NativeCallbackHandler for NativeAdd { fn call(&self, from: Option>, args: &[Val]) -> crate::error::Result { From ce623b5fc4f35ee089bc823c10fbe862cf4fe942 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sun, 4 Jul 2021 16:05:39 +0300 Subject: [PATCH 18/19] doc: remove outdated mimallocator comment Signed-off-by: Yaroslav Bolyukin --- cmds/jrsonnet/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/cmds/jrsonnet/Cargo.toml b/cmds/jrsonnet/Cargo.toml index 7dc56025..329e831a 100644 --- a/cmds/jrsonnet/Cargo.toml +++ b/cmds/jrsonnet/Cargo.toml @@ -16,7 +16,6 @@ mimalloc = ["mimallocator"] jrsonnet-evaluator = { path = "../../crates/jrsonnet-evaluator", version = "0.3.8" } jrsonnet-parser = { path = "../../crates/jrsonnet-parser", version = "0.3.8" } jrsonnet-cli = { path = "../../crates/jrsonnet-cli", version = "0.3.8" } -# TODO: Fix mimalloc compile errors, and use them mimallocator = { version = "0.1.3", optional = true } thiserror = "1.0" From 99a63e2ef04336d70f48accb3663b4d631bbda03 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sun, 4 Jul 2021 16:10:19 +0300 Subject: [PATCH 19/19] build: update flake vendor sha Signed-off-by: Yaroslav Bolyukin --- flake.lock | 12 ++++++------ flake.nix | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/flake.lock b/flake.lock index 3297a68d..0519043e 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "flake-utils": { "locked": { - "lastModified": 1614513358, - "narHash": "sha256-LakhOx3S1dRjnh0b5Dg3mbZyH0ToC9I8Y2wKSkBaTzU=", + "lastModified": 1623875721, + "narHash": "sha256-A8BU7bjS5GirpAUv4QA+QnJ4CceLHkcXdRp4xITDB0s=", "owner": "numtide", "repo": "flake-utils", - "rev": "5466c5bbece17adaab2d82fae80b46e807611bf3", + "rev": "f7e004a55b120c02ecb6219596820fcd32ca8772", "type": "github" }, "original": { @@ -17,11 +17,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1615532953, - "narHash": "sha256-SWpaGjrp/INzorEqMz3HLi6Uuk9I0KAn4YS8B4n3q5g=", + "lastModified": 1625281901, + "narHash": "sha256-DkZDtTIPzhXATqIps2ifNFpnI+PTcfMYdcrx/oFm00Q=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "916ee862e87ac5ee2439f2fb7856386b4dc906ae", + "rev": "09c38c29f2c719cd76ca17a596c2fdac9e186ceb", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 7203887d..aafe525d 100644 --- a/flake.nix +++ b/flake.nix @@ -12,7 +12,7 @@ pname = "jrsonnet"; version = "0.1.0"; src = self; - cargoSha256 = "sha256-6VhaQi3L2LWzR0cq7oRG81MDbrKJbzSNPcvYSoQ5ISo="; + cargoSha256 = "sha256-cez8pJ/uwj+PHAPQwpSB4CKaxcP8Uvv8xguOrVXR2xE="; }; in { defaultPackage = jrsonnet;