Skip to content

Commit b28a33f

Browse files
committed
feat(jrsonnet-evaluator): implement gc
Some manual Trace/Finalize implementations can be replaced with derives with Manishearth/rust-gc#116 getting merged Signed-off-by: Yaroslav Bolyukin <[email protected]>
1 parent 1dda2e4 commit b28a33f

File tree

17 files changed

+656
-304
lines changed

17 files changed

+656
-304
lines changed

crates/jrsonnet-evaluator/Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,12 @@ jrsonnet-stdlib = { path = "../jrsonnet-stdlib", version = "0.3.7" }
3030
jrsonnet-types = { path = "../jrsonnet-types", version = "0.3.7" }
3131
pathdiff = "0.2.0"
3232

33-
closure = "0.3.0"
34-
3533
md5 = "0.7.0"
3634
base64 = "0.13.0"
3735
rustc-hash = "1.1.0"
3836

3937
thiserror = "1.0"
38+
gc = { version = "0.4.1", features = ["derive"] }
4039

4140
[dependencies.anyhow]
4241
version = "1.0"

crates/jrsonnet-evaluator/src/builtin/format.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
#![allow(clippy::too_many_arguments)]
33

44
use crate::{error::Error::*, throw, LocError, ObjValue, Result, Val};
5+
use gc::{Finalize, Trace};
56
use jrsonnet_interner::IStr;
67
use jrsonnet_types::ValType;
78
use thiserror::Error;
89

9-
#[derive(Debug, Clone, Error)]
10+
#[derive(Debug, Clone, Error, Trace, Finalize)]
1011
pub enum FormatError {
1112
#[error("truncated format code")]
1213
TruncatedFormatCode,

crates/jrsonnet-evaluator/src/builtin/manifest.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ fn manifest_json_ex_buf(
126126
buf.push('}');
127127
}
128128
Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),
129+
Val::DebugGcTraceValue(v) => manifest_json_ex_buf(&v.value, buf, cur_padding, options)?,
129130
};
130131
Ok(())
131132
}

crates/jrsonnet-evaluator/src/builtin/mod.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use crate::{
22
equals,
33
error::{Error::*, Result},
4-
parse_args, primitive_equals, push, throw, with_state, ArrValue, Context, EvaluationState,
5-
FuncVal, LazyVal, Val,
4+
parse_args, primitive_equals, push, throw, with_state, ArrValue, Context, DebugGcTraceValue,
5+
EvaluationState, FuncVal, LazyVal, Val,
66
};
77
use format::{format_arr, format_obj};
8+
use gc::Gc;
89
use jrsonnet_interner::IStr;
910
use jrsonnet_parser::{ArgsDesc, BinaryOpType, ExprLocation};
1011
use jrsonnet_types::ty;
@@ -68,6 +69,8 @@ thread_local! {
6869
("md5".into(), builtin_md5),
6970
("base64".into(), builtin_base64),
7071
("trace".into(), builtin_trace),
72+
("gc".into(), builtin_gc),
73+
("gcTrace".into(), builtin_gc_trace),
7174
("join".into(), builtin_join),
7275
("escapeStringJson".into(), builtin_escape_string_json),
7376
("manifestJsonEx".into(), builtin_manifest_json_ex),
@@ -301,7 +304,7 @@ fn builtin_native(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc
301304
parse_args!(context, "native", args, 1, [
302305
0, x: ty!(string) => Val::Str;
303306
], {
304-
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))?)
307+
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))?)
305308
})
306309
}
307310

@@ -446,6 +449,28 @@ fn builtin_trace(context: Context, loc: Option<&ExprLocation>, args: &ArgsDesc)
446449
})
447450
}
448451

452+
fn builtin_gc(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
453+
parse_args!(context, "gc", args, 1, [
454+
0, rest: ty!(any);
455+
], {
456+
println!("GC start");
457+
gc::force_collect();
458+
println!("GC done");
459+
460+
Ok(rest)
461+
})
462+
}
463+
464+
fn builtin_gc_trace(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
465+
parse_args!(context, "gcTrace", args, 2, [
466+
0, name: ty!(string) => Val::Str;
467+
1, rest: ty!(any);
468+
], {
469+
470+
Ok(DebugGcTraceValue::new(name, rest))
471+
})
472+
}
473+
449474
fn builtin_base64(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
450475
parse_args!(context, "base64", args, 1, [
451476
0, input: ty!((string | (Array<number>)));

crates/jrsonnet-evaluator/src/builtin/sort.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ use crate::{
22
error::{Error, LocError, Result},
33
throw, Context, FuncVal, Val,
44
};
5-
use std::rc::Rc;
5+
use gc::{Finalize, Gc, Trace};
66

7-
#[derive(Debug, Clone, thiserror::Error)]
7+
#[derive(Debug, Clone, thiserror::Error, Trace, Finalize)]
88
pub enum SortError {
99
#[error("sort key should be string or number")]
1010
SortKeyShouldBeStringOrNumber,
@@ -59,13 +59,13 @@ fn get_sort_type<T>(
5959
Ok(sort_type)
6060
}
6161

62-
pub fn sort(ctx: Context, mut values: Rc<Vec<Val>>, key_getter: &FuncVal) -> Result<Rc<Vec<Val>>> {
62+
pub fn sort(ctx: Context, values: Gc<Vec<Val>>, key_getter: &FuncVal) -> Result<Gc<Vec<Val>>> {
6363
if values.len() <= 1 {
6464
return Ok(values);
6565
}
6666
if key_getter.is_ident() {
67-
let mvalues = Rc::make_mut(&mut values);
68-
let sort_type = get_sort_type(mvalues, |k| k)?;
67+
let mut mvalues = (*values).clone();
68+
let sort_type = get_sort_type(&mut mvalues, |k| k)?;
6969
match sort_type {
7070
SortKeyType::Number => mvalues.sort_by_key(|v| match v {
7171
Val::Num(n) => NonNaNf64(*n),
@@ -77,7 +77,7 @@ pub fn sort(ctx: Context, mut values: Rc<Vec<Val>>, key_getter: &FuncVal) -> Res
7777
}),
7878
SortKeyType::Unknown => unreachable!(),
7979
};
80-
Ok(values)
80+
Ok(Gc::new(mvalues))
8181
} else {
8282
let mut vk = Vec::with_capacity(values.len());
8383
for value in values.iter() {
@@ -98,6 +98,6 @@ pub fn sort(ctx: Context, mut values: Rc<Vec<Val>>, key_getter: &FuncVal) -> Res
9898
}),
9999
SortKeyType::Unknown => unreachable!(),
100100
};
101-
Ok(Rc::new(vk.into_iter().map(|v| v.0).collect()))
101+
Ok(Gc::new(vk.into_iter().map(|v| v.0).collect()))
102102
}
103103
}

crates/jrsonnet-evaluator/src/ctx.rs

Lines changed: 27 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
use crate::{
2-
error::Error::*, map::LayeredHashMap, resolved_lazy_val, FutureWrapper, LazyBinding, LazyVal,
3-
ObjValue, Result, Val,
2+
error::Error::*, map::LayeredHashMap, FutureWrapper, LazyBinding, LazyVal, ObjValue, Result,
3+
Val,
44
};
5+
use gc::{Finalize, Gc, Trace};
56
use jrsonnet_interner::IStr;
67
use rustc_hash::FxHashMap;
8+
use std::fmt::Debug;
79
use std::hash::BuildHasherDefault;
8-
use std::{fmt::Debug, rc::Rc};
910

10-
#[derive(Clone)]
11+
#[derive(Clone, Trace, Finalize)]
1112
pub struct ContextCreator(pub Context, pub FutureWrapper<FxHashMap<IStr, LazyBinding>>);
1213
impl ContextCreator {
1314
pub fn create(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<Context> {
@@ -20,6 +21,7 @@ impl ContextCreator {
2021
}
2122
}
2223

24+
#[derive(Trace, Finalize)]
2325
struct ContextInternals {
2426
dollar: Option<ObjValue>,
2527
this: Option<ObjValue>,
@@ -28,15 +30,12 @@ struct ContextInternals {
2830
}
2931
impl Debug for ContextInternals {
3032
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31-
f.debug_struct("Context")
32-
.field("this", &self.this.as_ref().map(|e| Rc::as_ptr(&e.0)))
33-
.field("bindings", &self.bindings)
34-
.finish()
33+
f.debug_struct("Context").finish()
3534
}
3635
}
3736

38-
#[derive(Debug, Clone)]
39-
pub struct Context(Rc<ContextInternals>);
37+
#[derive(Debug, Clone, Trace, Finalize)]
38+
pub struct Context(Gc<ContextInternals>);
4039
impl Context {
4140
pub fn new_future() -> FutureWrapper<Self> {
4241
FutureWrapper::new()
@@ -55,7 +54,7 @@ impl Context {
5554
}
5655

5756
pub fn new() -> Self {
58-
Self(Rc::new(ContextInternals {
57+
Self(Gc::new(ContextInternals {
5958
dollar: None,
6059
this: None,
6160
super_obj: None,
@@ -81,7 +80,7 @@ impl Context {
8180
pub fn with_var(self, name: IStr, value: Val) -> Self {
8281
let mut new_bindings =
8382
FxHashMap::with_capacity_and_hasher(1, BuildHasherDefault::default());
84-
new_bindings.insert(name, resolved_lazy_val!(value));
83+
new_bindings.insert(name, LazyVal::new_resolved(value));
8584
self.extend(new_bindings, None, None, None)
8685
}
8786

@@ -96,40 +95,21 @@ impl Context {
9695
new_this: Option<ObjValue>,
9796
new_super_obj: Option<ObjValue>,
9897
) -> Self {
99-
match Rc::try_unwrap(self.0) {
100-
Ok(mut ctx) => {
101-
// Extended context aren't used by anything else, we can freely mutate it without cloning
102-
if let Some(dollar) = new_dollar {
103-
ctx.dollar = Some(dollar);
104-
}
105-
if let Some(this) = new_this {
106-
ctx.this = Some(this);
107-
}
108-
if let Some(super_obj) = new_super_obj {
109-
ctx.super_obj = Some(super_obj);
110-
}
111-
if !new_bindings.is_empty() {
112-
ctx.bindings = ctx.bindings.extend(new_bindings);
113-
}
114-
Self(Rc::new(ctx))
115-
}
116-
Err(ctx) => {
117-
let dollar = new_dollar.or_else(|| ctx.dollar.clone());
118-
let this = new_this.or_else(|| ctx.this.clone());
119-
let super_obj = new_super_obj.or_else(|| ctx.super_obj.clone());
120-
let bindings = if new_bindings.is_empty() {
121-
ctx.bindings.clone()
122-
} else {
123-
ctx.bindings.clone().extend(new_bindings)
124-
};
125-
Self(Rc::new(ContextInternals {
126-
dollar,
127-
this,
128-
super_obj,
129-
bindings,
130-
}))
131-
}
132-
}
98+
let ctx = &self.0;
99+
let dollar = new_dollar.or_else(|| ctx.dollar.clone());
100+
let this = new_this.or_else(|| ctx.this.clone());
101+
let super_obj = new_super_obj.or_else(|| ctx.super_obj.clone());
102+
let bindings = if new_bindings.is_empty() {
103+
ctx.bindings.clone()
104+
} else {
105+
ctx.bindings.clone().extend(new_bindings)
106+
};
107+
Self(Gc::new(ContextInternals {
108+
dollar,
109+
this,
110+
super_obj,
111+
bindings,
112+
}))
133113
}
134114
pub fn extend_bound(self, new_bindings: FxHashMap<IStr, LazyVal>) -> Self {
135115
let new_this = self.0.this.clone();
@@ -166,22 +146,6 @@ impl Default for Context {
166146

167147
impl PartialEq for Context {
168148
fn eq(&self, other: &Self) -> bool {
169-
Rc::ptr_eq(&self.0, &other.0)
170-
}
171-
}
172-
173-
#[cfg(feature = "unstable")]
174-
#[derive(Debug, Clone)]
175-
pub struct WeakContext(std::rc::Weak<ContextInternals>);
176-
#[cfg(feature = "unstable")]
177-
impl WeakContext {
178-
pub fn upgrade(&self) -> Context {
179-
Context(self.0.upgrade().expect("context is removed"))
180-
}
181-
}
182-
#[cfg(feature = "unstable")]
183-
impl PartialEq for WeakContext {
184-
fn eq(&self, other: &Self) -> bool {
185-
self.0.ptr_eq(&other.0)
149+
Gc::ptr_eq(&self.0, &other.0)
186150
}
187151
}

crates/jrsonnet-evaluator/src/dynamic.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
1-
use std::{cell::RefCell, rc::Rc};
1+
use gc::{Finalize, Gc, GcCell, Trace};
22

3-
#[derive(Clone)]
4-
pub struct FutureWrapper<V>(pub Rc<RefCell<Option<V>>>);
5-
impl<T> FutureWrapper<T> {
3+
#[derive(Clone, Trace, Finalize)]
4+
pub struct FutureWrapper<V: Trace + 'static>(pub Gc<GcCell<Option<V>>>);
5+
impl<T: Trace + 'static> FutureWrapper<T> {
66
pub fn new() -> Self {
7-
Self(Rc::new(RefCell::new(None)))
7+
Self(Gc::new(GcCell::new(None)))
88
}
99
pub fn fill(self, value: T) {
1010
assert!(self.0.borrow().is_none(), "wrapper is filled already");
1111
self.0.borrow_mut().replace(value);
1212
}
1313
}
14-
impl<T: Clone> FutureWrapper<T> {
14+
impl<T: Clone + Trace + 'static> FutureWrapper<T> {
1515
pub fn unwrap(&self) -> T {
1616
self.0.borrow().as_ref().cloned().unwrap()
1717
}
1818
}
1919

20-
impl<T> Default for FutureWrapper<T> {
20+
impl<T: Trace + 'static> Default for FutureWrapper<T> {
2121
fn default() -> Self {
2222
Self::new()
2323
}

crates/jrsonnet-evaluator/src/error.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ use crate::{
22
builtin::{format::FormatError, sort::SortError},
33
typed::TypeLocError,
44
};
5+
use gc::{Finalize, Trace};
56
use jrsonnet_interner::IStr;
67
use jrsonnet_parser::{BinaryOpType, ExprLocation, UnaryOpType};
78
use jrsonnet_types::ValType;
89
use std::{path::PathBuf, rc::Rc};
910
use thiserror::Error;
1011

11-
#[derive(Error, Debug, Clone)]
12+
#[derive(Error, Debug, Clone, Trace, Finalize)]
1213
pub enum Error {
1314
#[error("intrinsic not found: {0}")]
1415
IntrinsicNotFound(IStr),
@@ -88,13 +89,16 @@ pub enum Error {
8889
ImportSyntaxError {
8990
path: Rc<PathBuf>,
9091
source_code: IStr,
92+
#[unsafe_ignore_trace]
9193
error: Box<jrsonnet_parser::ParseError>,
9294
},
9395

9496
#[error("runtime error: {0}")]
9597
RuntimeError(IStr),
9698
#[error("stack overflow, try to reduce recursion, or set --max-stack to bigger value")]
9799
StackOverflow,
100+
#[error("infinite recursion detected")]
101+
RecursiveLazyValueEvaluation,
98102
#[error("tried to index by fractional value")]
99103
FractionalIndex,
100104
#[error("attempted to divide by zero")]
@@ -142,15 +146,15 @@ impl From<Error> for LocError {
142146
}
143147
}
144148

145-
#[derive(Clone, Debug)]
149+
#[derive(Clone, Debug, Trace, Finalize)]
146150
pub struct StackTraceElement {
147151
pub location: Option<ExprLocation>,
148152
pub desc: String,
149153
}
150-
#[derive(Debug, Clone)]
154+
#[derive(Debug, Clone, Trace, Finalize)]
151155
pub struct StackTrace(pub Vec<StackTraceElement>);
152156

153-
#[derive(Debug, Clone)]
157+
#[derive(Debug, Clone, Trace, Finalize)]
154158
pub struct LocError(Box<(Error, StackTrace)>);
155159
impl LocError {
156160
pub fn new(e: Error) -> Self {

0 commit comments

Comments
 (0)