Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement tracing Gc with rust-gc #45

Merged
merged 22 commits into from
Jul 4, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 1 addition & 31 deletions bindings/jsonnet/src/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!()
}
2 changes: 1 addition & 1 deletion bindings/jsonnet/src/val_make.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
7 changes: 4 additions & 3 deletions bindings/jsonnet/src/val_modify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
///
Expand All @@ -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"),
}
Expand Down
3 changes: 1 addition & 2 deletions crates/jrsonnet-evaluator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
3 changes: 2 additions & 1 deletion crates/jrsonnet-evaluator/src/builtin/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions crates/jrsonnet-evaluator/src/builtin/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
Expand Down
31 changes: 28 additions & 3 deletions crates/jrsonnet-evaluator/src/builtin/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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))?)
})
}

Expand Down Expand Up @@ -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<Val> {
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<Val> {
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<Val> {
parse_args!(context, "base64", args, 1, [
0, input: ty!((string | (Array<number>)));
Expand Down
14 changes: 7 additions & 7 deletions crates/jrsonnet-evaluator/src/builtin/sort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -59,13 +59,13 @@ fn get_sort_type<T>(
Ok(sort_type)
}

pub fn sort(ctx: Context, mut values: Rc<Vec<Val>>, key_getter: &FuncVal) -> Result<Rc<Vec<Val>>> {
pub fn sort(ctx: Context, values: Gc<Vec<Val>>, key_getter: &FuncVal) -> Result<Gc<Vec<Val>>> {
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),
Expand All @@ -77,7 +77,7 @@ pub fn sort(ctx: Context, mut values: Rc<Vec<Val>>, 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() {
Expand All @@ -98,6 +98,6 @@ pub fn sort(ctx: Context, mut values: Rc<Vec<Val>>, 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()))
}
}
90 changes: 27 additions & 63 deletions crates/jrsonnet-evaluator/src/ctx.rs
Original file line number Diff line number Diff line change
@@ -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<FxHashMap<IStr, LazyBinding>>);
impl ContextCreator {
pub fn create(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<Context> {
Expand All @@ -20,6 +21,7 @@ impl ContextCreator {
}
}

#[derive(Trace, Finalize)]
struct ContextInternals {
dollar: Option<ObjValue>,
this: Option<ObjValue>,
Expand All @@ -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<ContextInternals>);
#[derive(Debug, Clone, Trace, Finalize)]
pub struct Context(Gc<ContextInternals>);
impl Context {
pub fn new_future() -> FutureWrapper<Self> {
FutureWrapper::new()
Expand All @@ -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,
Expand All @@ -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)
}

Expand All @@ -96,40 +95,21 @@ impl Context {
new_this: Option<ObjValue>,
new_super_obj: Option<ObjValue>,
) -> 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<IStr, LazyVal>) -> Self {
let new_this = self.0.this.clone();
Expand Down Expand Up @@ -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<ContextInternals>);
#[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)
}
}
14 changes: 7 additions & 7 deletions crates/jrsonnet-evaluator/src/dynamic.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
use std::{cell::RefCell, rc::Rc};
use gc::{Finalize, Gc, GcCell, Trace};

#[derive(Clone)]
pub struct FutureWrapper<V>(pub Rc<RefCell<Option<V>>>);
impl<T> FutureWrapper<T> {
#[derive(Clone, Trace, Finalize)]
pub struct FutureWrapper<V: Trace + 'static>(pub Gc<GcCell<Option<V>>>);
impl<T: Trace + 'static> FutureWrapper<T> {
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<T: Clone> FutureWrapper<T> {
impl<T: Clone + Trace + 'static> FutureWrapper<T> {
pub fn unwrap(&self) -> T {
self.0.borrow().as_ref().cloned().unwrap()
}
}

impl<T> Default for FutureWrapper<T> {
impl<T: Trace + 'static> Default for FutureWrapper<T> {
fn default() -> Self {
Self::new()
}
Expand Down
Loading