git.delta.rocks / jrsonnet / refs/commits / 8d08861d28f9

difftreelog

refactor remove debug gc trace

Yaroslav Bolyukin2021-07-04parent: #b2b77e5.patch.diff
in: master

5 files changed

modifiedcrates/jrsonnet-evaluator/src/builtin/manifest.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/builtin/manifest.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/manifest.rs
@@ -126,7 +126,6 @@
 			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(())
 }
modifiedcrates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/builtin/mod.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs
@@ -1,8 +1,8 @@
 use crate::{
 	equals,
 	error::{Error::*, Result},
-	parse_args, primitive_equals, push, throw, with_state, ArrValue, Context, DebugGcTraceValue,
-	EvaluationState, FuncVal, LazyVal, Val,
+	parse_args, primitive_equals, push, throw, with_state, ArrValue, Context, EvaluationState,
+	FuncVal, LazyVal, Val,
 };
 use format::{format_arr, format_obj};
 use jrsonnet_gc::Gc;
@@ -69,8 +69,6 @@
 			("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),
@@ -446,27 +444,6 @@
 		}
 		eprintln!(" {}", str);
 		Ok(rest)
-	})
-}
-
-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");
-		jrsonnet_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::create(name, rest))
 	})
 }
 
modifiedcrates/jrsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/evaluate.rs
1use crate::{2	equals, error::Error::*, push, throw, with_state, ArrValue, Bindable, Context, ContextCreator,3	FuncDesc, FuncVal, FutureWrapper, LazyBinding, LazyVal, LazyValValue, ObjMember, ObjValue,4	ObjectAssertion, Result, Val,5};6use jrsonnet_gc::{Gc, Trace};7use jrsonnet_interner::IStr;8use jrsonnet_parser::{9	ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, ExprLocation, FieldMember,10	ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType,11	Visibility,12};13use jrsonnet_types::ValType;14use rustc_hash::{FxHashMap, FxHasher};15use std::{collections::HashMap, hash::BuildHasherDefault};1617pub fn evaluate_binding_in_future(18	b: &BindSpec,19	context_creator: FutureWrapper<Context>,20) -> LazyVal {21	let b = b.clone();22	if let Some(params) = &b.params {23		let params = params.clone();2425		#[derive(Trace)]26		#[trivially_drop]27		struct LazyMethodBinding {28			context_creator: FutureWrapper<Context>,29			name: IStr,30			params: ParamsDesc,31			value: LocExpr,32		}33		impl LazyValValue for LazyMethodBinding {34			fn get(self: Box<Self>) -> Result<Val> {35				Ok(evaluate_method(36					self.context_creator.unwrap(),37					self.name,38					self.params,39					self.value,40				))41			}42		}4344		LazyVal::new(Box::new(LazyMethodBinding {45			context_creator,46			name: b.name.clone(),47			params,48			value: b.value.clone(),49		}))50	} else {51		#[derive(Trace)]52		#[trivially_drop]53		struct LazyNamedBinding {54			context_creator: FutureWrapper<Context>,55			name: IStr,56			value: LocExpr,57		}58		impl LazyValValue for LazyNamedBinding {59			fn get(self: Box<Self>) -> Result<Val> {60				evaluate_named(self.context_creator.unwrap(), &self.value, self.name)61			}62		}63		LazyVal::new(Box::new(LazyNamedBinding {64			context_creator,65			name: b.name.clone(),66			value: b.value,67		}))68	}69}7071pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (IStr, LazyBinding) {72	let b = b.clone();73	if let Some(params) = &b.params {74		let params = params.clone();7576		#[derive(Trace)]77		#[trivially_drop]78		struct BindableMethodLazyVal {79			this: Option<ObjValue>,80			super_obj: Option<ObjValue>,8182			context_creator: ContextCreator,83			name: IStr,84			params: ParamsDesc,85			value: LocExpr,86		}87		impl LazyValValue for BindableMethodLazyVal {88			fn get(self: Box<Self>) -> Result<Val> {89				Ok(evaluate_method(90					self.context_creator.create(self.this, self.super_obj)?,91					self.name,92					self.params,93					self.value,94				))95			}96		}9798		#[derive(Trace)]99		#[trivially_drop]100		struct BindableMethod {101			context_creator: ContextCreator,102			name: IStr,103			params: ParamsDesc,104			value: LocExpr,105		}106		impl Bindable for BindableMethod {107			fn bind(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal> {108				Ok(LazyVal::new(Box::new(BindableMethodLazyVal {109					this,110					super_obj,111112					context_creator: self.context_creator.clone(),113					name: self.name.clone(),114					params: self.params.clone(),115					value: self.value.clone(),116				})))117			}118		}119120		(121			b.name.clone(),122			LazyBinding::Bindable(Gc::new(Box::new(BindableMethod {123				context_creator,124				name: b.name.clone(),125				params,126				value: b.value.clone(),127			}))),128		)129	} else {130		#[derive(Trace)]131		#[trivially_drop]132		struct BindableNamedLazyVal {133			this: Option<ObjValue>,134			super_obj: Option<ObjValue>,135136			context_creator: ContextCreator,137			name: IStr,138			value: LocExpr,139		}140		impl LazyValValue for BindableNamedLazyVal {141			fn get(self: Box<Self>) -> Result<Val> {142				evaluate_named(143					self.context_creator.create(self.this, self.super_obj)?,144					&self.value,145					self.name,146				)147			}148		}149150		#[derive(Trace)]151		#[trivially_drop]152		struct BindableNamed {153			context_creator: ContextCreator,154			name: IStr,155			value: LocExpr,156		}157		impl Bindable for BindableNamed {158			fn bind(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal> {159				Ok(LazyVal::new(Box::new(BindableNamedLazyVal {160					this,161					super_obj,162163					context_creator: self.context_creator.clone(),164					name: self.name.clone(),165					value: self.value.clone(),166				})))167			}168		}169170		(171			b.name.clone(),172			LazyBinding::Bindable(Gc::new(Box::new(BindableNamed {173				context_creator,174				name: b.name.clone(),175				value: b.value.clone(),176			}))),177		)178	}179}180181pub fn evaluate_method(ctx: Context, name: IStr, params: ParamsDesc, body: LocExpr) -> Val {182	Val::Func(Gc::new(FuncVal::Normal(FuncDesc {183		name,184		ctx,185		params,186		body,187	})))188}189190pub fn evaluate_field_name(191	context: Context,192	field_name: &jrsonnet_parser::FieldName,193) -> Result<Option<IStr>> {194	Ok(match field_name {195		jrsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),196		jrsonnet_parser::FieldName::Dyn(expr) => {197			let value = evaluate(context, expr)?;198			if matches!(value, Val::Null) {199				None200			} else {201				Some(value.try_cast_str("dynamic field name")?)202			}203		}204	})205}206207pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Result<Val> {208	Ok(match (op, b) {209		(UnaryOpType::Not, Val::Bool(v)) => Val::Bool(!v),210		(UnaryOpType::Minus, Val::Num(n)) => Val::Num(-*n),211		(UnaryOpType::BitNot, Val::Num(n)) => Val::Num(!(*n as i32) as f64),212		(op, o) => throw!(UnaryOperatorDoesNotOperateOnType(op, o.value_type())),213	})214}215216pub fn evaluate_add_op(a: &Val, b: &Val) -> Result<Val> {217	Ok(match (a, b) {218		(Val::DebugGcTraceValue(v1), Val::DebugGcTraceValue(v2)) => {219			evaluate_add_op(&v1.value, &v2.value)?220		}221		(Val::Str(v1), Val::Str(v2)) => Val::Str(((**v1).to_owned() + v2).into()),222223		// Can't use generic json serialization way, because it depends on number to string concatenation (std.jsonnet:890)224		(Val::Num(n), Val::Str(o)) => Val::Str(format!("{}{}", n, o).into()),225		(Val::Str(o), Val::Num(n)) => Val::Str(format!("{}{}", o, n).into()),226227		(Val::Str(s), o) => Val::Str(format!("{}{}", s, o.clone().to_string()?).into()),228		(o, Val::Str(s)) => Val::Str(format!("{}{}", o.clone().to_string()?, s).into()),229230		(Val::Obj(v1), Val::Obj(v2)) => Val::Obj(v2.extend_from(v1.clone())),231		(Val::Arr(a), Val::Arr(b)) => {232			let mut out = Vec::with_capacity(a.len() + b.len());233			out.extend(a.iter_lazy());234			out.extend(b.iter_lazy());235			Val::Arr(out.into())236		}237		(Val::Num(v1), Val::Num(v2)) => Val::new_checked_num(v1 + v2)?,238		_ => throw!(BinaryOperatorDoesNotOperateOnValues(239			BinaryOpType::Add,240			a.value_type(),241			b.value_type(),242		)),243	})244}245246pub fn evaluate_binary_op_special(247	context: Context,248	a: &LocExpr,249	op: BinaryOpType,250	b: &LocExpr,251) -> Result<Val> {252	Ok(match (evaluate(context.clone(), a)?, op, b) {253		(Val::Bool(true), BinaryOpType::Or, _o) => Val::Bool(true),254		(Val::Bool(false), BinaryOpType::And, _o) => Val::Bool(false),255		(a, op, eb) => evaluate_binary_op_normal(&a, op, &evaluate(context, eb)?)?,256	})257}258259pub fn evaluate_binary_op_normal(a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {260	Ok(match (a, op, b) {261		(a, BinaryOpType::Add, b) => evaluate_add_op(a, b)?,262263		(a, BinaryOpType::Eq, b) => Val::Bool(equals(a, b)?),264		(a, BinaryOpType::Neq, b) => Val::Bool(!equals(a, b)?),265266		(Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize).into()),267268		// Bool X Bool269		(Val::Bool(a), BinaryOpType::And, Val::Bool(b)) => Val::Bool(*a && *b),270		(Val::Bool(a), BinaryOpType::Or, Val::Bool(b)) => Val::Bool(*a || *b),271272		// Str X Str273		(Val::Str(v1), BinaryOpType::Lt, Val::Str(v2)) => Val::Bool(v1 < v2),274		(Val::Str(v1), BinaryOpType::Gt, Val::Str(v2)) => Val::Bool(v1 > v2),275		(Val::Str(v1), BinaryOpType::Lte, Val::Str(v2)) => Val::Bool(v1 <= v2),276		(Val::Str(v1), BinaryOpType::Gte, Val::Str(v2)) => Val::Bool(v1 >= v2),277278		// Num X Num279		(Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::new_checked_num(v1 * v2)?,280		(Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => {281			if *v2 <= f64::EPSILON {282				throw!(DivisionByZero)283			}284			Val::new_checked_num(v1 / v2)?285		}286287		(Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::new_checked_num(v1 - v2)?,288289		(Val::Num(v1), BinaryOpType::Lt, Val::Num(v2)) => Val::Bool(v1 < v2),290		(Val::Num(v1), BinaryOpType::Gt, Val::Num(v2)) => Val::Bool(v1 > v2),291		(Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => Val::Bool(v1 <= v2),292		(Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => Val::Bool(v1 >= v2),293294		(Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => {295			Val::Num(((*v1 as i32) & (*v2 as i32)) as f64)296		}297		(Val::Num(v1), BinaryOpType::BitOr, Val::Num(v2)) => {298			Val::Num(((*v1 as i32) | (*v2 as i32)) as f64)299		}300		(Val::Num(v1), BinaryOpType::BitXor, Val::Num(v2)) => {301			Val::Num(((*v1 as i32) ^ (*v2 as i32)) as f64)302		}303		(Val::Num(v1), BinaryOpType::Lhs, Val::Num(v2)) => {304			if *v2 < 0.0 {305				throw!(RuntimeError("shift by negative exponent".into()))306			}307			Val::Num(((*v1 as i32) << (*v2 as i32)) as f64)308		}309		(Val::Num(v1), BinaryOpType::Rhs, Val::Num(v2)) => {310			if *v2 < 0.0 {311				throw!(RuntimeError("shift by negative exponent".into()))312			}313			Val::Num(((*v1 as i32) >> (*v2 as i32)) as f64)314		}315316		_ => throw!(BinaryOperatorDoesNotOperateOnValues(317			op,318			a.value_type(),319			b.value_type(),320		)),321	})322}323324pub fn evaluate_comp(325	context: Context,326	specs: &[CompSpec],327	callback: &mut impl FnMut(Context) -> Result<()>,328) -> Result<()> {329	match specs.get(0) {330		None => callback(context)?,331		Some(CompSpec::IfSpec(IfSpecData(cond))) => {332			if evaluate(context.clone(), cond)?.try_cast_bool("if spec")? {333				evaluate_comp(context, &specs[1..], callback)?334			}335		}336		Some(CompSpec::ForSpec(ForSpecData(var, expr))) => match evaluate(context.clone(), expr)? {337			Val::Arr(list) => {338				for item in list.iter() {339					evaluate_comp(340						context.clone().with_var(var.clone(), item?.clone()),341						&specs[1..],342						callback,343					)?344				}345			}346			_ => throw!(InComprehensionCanOnlyIterateOverArray),347		},348	}349	Ok(())350}351352pub fn evaluate_member_list_object(context: Context, members: &[Member]) -> Result<ObjValue> {353	let new_bindings = FutureWrapper::new();354	let future_this = FutureWrapper::new();355	let context_creator = ContextCreator(context.clone(), new_bindings.clone());356	{357		let mut bindings: FxHashMap<IStr, LazyBinding> =358			FxHashMap::with_capacity_and_hasher(members.len(), BuildHasherDefault::default());359		for (n, b) in members360			.iter()361			.filter_map(|m| match m {362				Member::BindStmt(b) => Some(b.clone()),363				_ => None,364			})365			.map(|b| evaluate_binding(&b, context_creator.clone()))366		{367			bindings.insert(n, b);368		}369		new_bindings.fill(bindings);370	}371372	let mut new_members = FxHashMap::default();373	let mut assertions: Vec<Box<dyn ObjectAssertion>> = Vec::new();374	for member in members.iter() {375		match member {376			Member::Field(FieldMember {377				name,378				plus,379				params: None,380				visibility,381				value,382			}) => {383				let name = evaluate_field_name(context.clone(), name)?;384				if name.is_none() {385					continue;386				}387				let name = name.unwrap();388389				#[derive(Trace)]390				#[trivially_drop]391				struct ObjMemberBinding {392					context_creator: ContextCreator,393					value: LocExpr,394					name: IStr,395				}396				impl Bindable for ObjMemberBinding {397					fn bind(398						&self,399						this: Option<ObjValue>,400						super_obj: Option<ObjValue>,401					) -> Result<LazyVal> {402						Ok(LazyVal::new_resolved(evaluate_named(403							self.context_creator.create(this, super_obj)?,404							&self.value,405							self.name.clone(),406						)?))407					}408				}409				new_members.insert(410					name.clone(),411					ObjMember {412						add: *plus,413						visibility: *visibility,414						invoke: LazyBinding::Bindable(Gc::new(Box::new(ObjMemberBinding {415							context_creator: context_creator.clone(),416							value: value.clone(),417							name,418						}))),419						location: value.1.clone(),420					},421				);422			}423			Member::Field(FieldMember {424				name,425				params: Some(params),426				value,427				..428			}) => {429				let name = evaluate_field_name(context.clone(), name)?;430				if name.is_none() {431					continue;432				}433				let name = name.unwrap();434				#[derive(Trace)]435				#[trivially_drop]436				struct ObjMemberBinding {437					context_creator: ContextCreator,438					value: LocExpr,439					params: ParamsDesc,440					name: IStr,441				}442				impl Bindable for ObjMemberBinding {443					fn bind(444						&self,445						this: Option<ObjValue>,446						super_obj: Option<ObjValue>,447					) -> Result<LazyVal> {448						Ok(LazyVal::new_resolved(evaluate_method(449							self.context_creator.create(this, super_obj)?,450							self.name.clone(),451							self.params.clone(),452							self.value.clone(),453						)))454					}455				}456				new_members.insert(457					name.clone(),458					ObjMember {459						add: false,460						visibility: Visibility::Hidden,461						invoke: LazyBinding::Bindable(Gc::new(Box::new(ObjMemberBinding {462							context_creator: context_creator.clone(),463							value: value.clone(),464							params: params.clone(),465							name,466						}))),467						location: value.1.clone(),468					},469				);470			}471			Member::BindStmt(_) => {}472			Member::AssertStmt(stmt) => {473				#[derive(Trace)]474				#[trivially_drop]475				struct ObjectAssert {476					context_creator: ContextCreator,477					assert: AssertStmt,478				}479				impl ObjectAssertion for ObjectAssert {480					fn run(481						&self,482						this: Option<ObjValue>,483						super_obj: Option<ObjValue>,484					) -> Result<()> {485						let ctx = self.context_creator.create(this, super_obj)?;486						evaluate_assert(ctx, &self.assert)487					}488				}489				assertions.push(Box::new(ObjectAssert {490					context_creator: context_creator.clone(),491					assert: stmt.clone(),492				}));493			}494		}495	}496	let this = ObjValue::new(None, Gc::new(new_members), Gc::new(assertions));497	future_this.fill(this.clone());498	Ok(this)499}500501pub fn evaluate_object(context: Context, object: &ObjBody) -> Result<ObjValue> {502	Ok(match object {503		ObjBody::MemberList(members) => evaluate_member_list_object(context, members)?,504		ObjBody::ObjComp(obj) => {505			let future_this = FutureWrapper::new();506			let mut new_members = FxHashMap::default();507			evaluate_comp(context.clone(), &obj.compspecs, &mut |ctx| {508				let new_bindings = FutureWrapper::new();509				let context_creator = ContextCreator(context.clone(), new_bindings.clone());510				let mut bindings: FxHashMap<IStr, LazyBinding> =511					FxHashMap::with_capacity_and_hasher(512						obj.pre_locals.len() + obj.post_locals.len(),513						BuildHasherDefault::default(),514					);515				for (n, b) in obj516					.pre_locals517					.iter()518					.chain(obj.post_locals.iter())519					.map(|b| evaluate_binding(b, context_creator.clone()))520				{521					bindings.insert(n, b);522				}523				new_bindings.fill(bindings.clone());524				let ctx = ctx.extend_unbound(bindings, None, None, None)?;525				let key = evaluate(ctx.clone(), &obj.key)?;526527				match key {528					Val::Null => {}529					Val::Str(n) => {530						#[derive(Trace)]531						#[trivially_drop]532						struct ObjCompBinding {533							context: Context,534							value: LocExpr,535						}536						impl Bindable for ObjCompBinding {537							fn bind(538								&self,539								this: Option<ObjValue>,540								_super_obj: Option<ObjValue>,541							) -> Result<LazyVal> {542								Ok(LazyVal::new_resolved(evaluate(543									self.context.clone().extend(544										FxHashMap::default(),545										None,546										this,547										None,548									),549									&self.value,550								)?))551							}552						}553						new_members.insert(554							n,555							ObjMember {556								add: false,557								visibility: Visibility::Normal,558								invoke: LazyBinding::Bindable(Gc::new(Box::new(ObjCompBinding {559									context: ctx,560									value: obj.value.clone(),561								}))),562								location: obj.value.1.clone(),563							},564						);565					}566					v => throw!(FieldMustBeStringGot(v.value_type())),567				}568569				Ok(())570			})?;571572			let this = ObjValue::new(None, Gc::new(new_members), Gc::new(Vec::new()));573			future_this.fill(this.clone());574			this575		}576	})577}578579pub fn evaluate_apply(580	context: Context,581	value: &LocExpr,582	args: &ArgsDesc,583	loc: Option<&ExprLocation>,584	tailstrict: bool,585) -> Result<Val> {586	let value = evaluate(context.clone(), value)?;587	Ok(match value {588		Val::Func(f) => {589			let body = || f.evaluate(context, loc, args, tailstrict);590			if tailstrict {591				body()?592			} else {593				push(loc, || format!("function <{}> call", f.name()), body)?594			}595		}596		v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type())),597	})598}599600pub fn evaluate_assert(context: Context, assertion: &AssertStmt) -> Result<()> {601	let value = &assertion.0;602	let msg = &assertion.1;603	let assertion_result = push(604		value.1.as_ref(),605		|| "assertion condition".to_owned(),606		|| {607			evaluate(context.clone(), value)?608				.try_cast_bool("assertion condition should be of type `boolean`")609		},610	)?;611	if !assertion_result {612		push(613			value.1.as_ref(),614			|| "assertion failure".to_owned(),615			|| {616				if let Some(msg) = msg {617					throw!(AssertionFailed(evaluate(context, msg)?.to_string()?));618				} else {619					throw!(AssertionFailed(Val::Null.to_string()?));620				}621			},622		)?623	}624	Ok(())625}626627pub fn evaluate_named(context: Context, lexpr: &LocExpr, name: IStr) -> Result<Val> {628	use Expr::*;629	let LocExpr(expr, _loc) = lexpr;630	Ok(match &**expr {631		Function(params, body) => evaluate_method(context, name, params.clone(), body.clone()),632		_ => evaluate(context, lexpr)?,633	})634}635636pub fn evaluate(context: Context, expr: &LocExpr) -> Result<Val> {637	use Expr::*;638	let LocExpr(expr, loc) = expr;639	Ok(match &**expr {640		Literal(LiteralType::This) => {641			Val::Obj(context.this().clone().ok_or(CantUseSelfOutsideOfObject)?)642		}643		Literal(LiteralType::Super) => Val::Obj(644			context645				.super_obj()646				.clone()647				.ok_or(NoSuperFound)?648				.with_this(context.this().clone().unwrap()),649		),650		Literal(LiteralType::Dollar) => {651			Val::Obj(context.dollar().clone().ok_or(NoTopLevelObjectFound)?)652		}653		Literal(LiteralType::True) => Val::Bool(true),654		Literal(LiteralType::False) => Val::Bool(false),655		Literal(LiteralType::Null) => Val::Null,656		Parened(e) => evaluate(context, e)?,657		Str(v) => Val::Str(v.clone()),658		Num(v) => Val::new_checked_num(*v)?,659		BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, v1, *o, v2)?,660		UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,661		Var(name) => push(662			loc.as_ref(),663			|| format!("variable <{}>", name),664			|| context.binding(name.clone())?.evaluate(),665		)?,666		Index(value, index) => {667			match (evaluate(context.clone(), value)?, evaluate(context, index)?) {668				(Val::Obj(v), Val::Str(s)) => {669					let sn = s.clone();670					push(671						loc.as_ref(),672						|| format!("field <{}> access", sn),673						|| {674							if let Some(v) = v.get(s.clone())? {675								Ok(v)676							} else if v.get("__intrinsic_namespace__".into())?.is_some() {677								Ok(Val::Func(Gc::new(FuncVal::Intrinsic(s))))678							} else {679								throw!(NoSuchField(s))680							}681						},682					)?683				}684				(Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot(685					ValType::Obj,686					ValType::Str,687					n.value_type(),688				)),689690				(Val::Arr(v), Val::Num(n)) => {691					if n.fract() > f64::EPSILON {692						throw!(FractionalIndex)693					}694					v.get(n as usize)?695						.ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?696				}697				(Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)),698				(Val::Arr(_), n) => throw!(ValueIndexMustBeTypeGot(699					ValType::Arr,700					ValType::Num,701					n.value_type(),702				)),703704				(Val::Str(s), Val::Num(n)) => Val::Str(705					s.chars()706						.skip(n as usize)707						.take(1)708						.collect::<String>()709						.into(),710				),711				(Val::Str(_), n) => throw!(ValueIndexMustBeTypeGot(712					ValType::Str,713					ValType::Num,714					n.value_type(),715				)),716717				(v, _) => throw!(CantIndexInto(v.value_type())),718			}719		}720		LocalExpr(bindings, returned) => {721			let mut new_bindings: FxHashMap<IStr, LazyVal> = HashMap::with_capacity_and_hasher(722				bindings.len(),723				BuildHasherDefault::<FxHasher>::default(),724			);725			let future_context = Context::new_future();726			for b in bindings {727				new_bindings.insert(728					b.name.clone(),729					evaluate_binding_in_future(b, future_context.clone()),730				);731			}732			let context = context733				.extend_bound(new_bindings)734				.into_future(future_context);735			evaluate(context, &returned.clone())?736		}737		Arr(items) => {738			let mut out = Vec::with_capacity(items.len());739			for item in items {740				// TODO: Implement ArrValue::Lazy with same context for every element?741				#[derive(Trace)]742				#[trivially_drop]743				struct ArrayElement {744					context: Context,745					item: LocExpr,746				}747				impl LazyValValue for ArrayElement {748					fn get(self: Box<Self>) -> Result<Val> {749						evaluate(self.context, &self.item)750					}751				}752				out.push(LazyVal::new(Box::new(ArrayElement {753					context: context.clone(),754					item: item.clone(),755				})));756			}757			Val::Arr(out.into())758		}759		ArrComp(expr, comp_specs) => {760			let mut out = Vec::new();761			evaluate_comp(context, comp_specs, &mut |ctx| {762				out.push(evaluate(ctx, expr)?);763				Ok(())764			})?;765			Val::Arr(ArrValue::Eager(Gc::new(out)))766		}767		Obj(body) => Val::Obj(evaluate_object(context, body)?),768		ObjExtend(s, t) => evaluate_add_op(769			&evaluate(context.clone(), s)?,770			&Val::Obj(evaluate_object(context, t)?),771		)?,772		Apply(value, args, tailstrict) => {773			evaluate_apply(context, value, args, loc.as_ref(), *tailstrict)?774		}775		Function(params, body) => {776			evaluate_method(context, "anonymous".into(), params.clone(), body.clone())777		}778		Intrinsic(name) => Val::Func(Gc::new(FuncVal::Intrinsic(name.clone()))),779		AssertExpr(assert, returned) => {780			evaluate_assert(context.clone(), assert)?;781			evaluate(context, returned)?782		}783		ErrorStmt(e) => push(784			loc.as_ref(),785			|| "error statement".to_owned(),786			|| {787				throw!(RuntimeError(788					evaluate(context, e)?.try_cast_str("error text should be of type `string`")?,789				))790			},791		)?,792		IfElse {793			cond,794			cond_then,795			cond_else,796		} => {797			if push(798				loc.as_ref(),799				|| "if condition".to_owned(),800				|| evaluate(context.clone(), &cond.0)?.try_cast_bool("in if condition"),801			)? {802				evaluate(context, cond_then)?803			} else {804				match cond_else {805					Some(v) => evaluate(context, v)?,806					None => Val::Null,807				}808			}809		}810		Import(path) => {811			let tmp = loc812				.clone()813				.expect("imports cannot be used without loc_data")814				.0;815			let mut import_location = tmp.to_path_buf();816			import_location.pop();817			push(818				loc.as_ref(),819				|| format!("import {:?}", path),820				|| with_state(|s| s.import_file(&import_location, path)),821			)?822		}823		ImportStr(path) => {824			let tmp = loc825				.clone()826				.expect("imports cannot be used without loc_data")827				.0;828			let mut import_location = tmp.to_path_buf();829			import_location.pop();830			Val::Str(with_state(|s| s.import_file_str(&import_location, path))?)831		}832	})833}
after · crates/jrsonnet-evaluator/src/evaluate.rs
1use crate::{2	equals, error::Error::*, push, throw, with_state, ArrValue, Bindable, Context, ContextCreator,3	FuncDesc, FuncVal, FutureWrapper, LazyBinding, LazyVal, LazyValValue, ObjMember, ObjValue,4	ObjectAssertion, Result, Val,5};6use jrsonnet_gc::{Gc, Trace};7use jrsonnet_interner::IStr;8use jrsonnet_parser::{9	ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, ExprLocation, FieldMember,10	ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType,11	Visibility,12};13use jrsonnet_types::ValType;14use rustc_hash::{FxHashMap, FxHasher};15use std::{collections::HashMap, hash::BuildHasherDefault};1617pub fn evaluate_binding_in_future(18	b: &BindSpec,19	context_creator: FutureWrapper<Context>,20) -> LazyVal {21	let b = b.clone();22	if let Some(params) = &b.params {23		let params = params.clone();2425		#[derive(Trace)]26		#[trivially_drop]27		struct LazyMethodBinding {28			context_creator: FutureWrapper<Context>,29			name: IStr,30			params: ParamsDesc,31			value: LocExpr,32		}33		impl LazyValValue for LazyMethodBinding {34			fn get(self: Box<Self>) -> Result<Val> {35				Ok(evaluate_method(36					self.context_creator.unwrap(),37					self.name,38					self.params,39					self.value,40				))41			}42		}4344		LazyVal::new(Box::new(LazyMethodBinding {45			context_creator,46			name: b.name.clone(),47			params,48			value: b.value.clone(),49		}))50	} else {51		#[derive(Trace)]52		#[trivially_drop]53		struct LazyNamedBinding {54			context_creator: FutureWrapper<Context>,55			name: IStr,56			value: LocExpr,57		}58		impl LazyValValue for LazyNamedBinding {59			fn get(self: Box<Self>) -> Result<Val> {60				evaluate_named(self.context_creator.unwrap(), &self.value, self.name)61			}62		}63		LazyVal::new(Box::new(LazyNamedBinding {64			context_creator,65			name: b.name.clone(),66			value: b.value,67		}))68	}69}7071pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (IStr, LazyBinding) {72	let b = b.clone();73	if let Some(params) = &b.params {74		let params = params.clone();7576		#[derive(Trace)]77		#[trivially_drop]78		struct BindableMethodLazyVal {79			this: Option<ObjValue>,80			super_obj: Option<ObjValue>,8182			context_creator: ContextCreator,83			name: IStr,84			params: ParamsDesc,85			value: LocExpr,86		}87		impl LazyValValue for BindableMethodLazyVal {88			fn get(self: Box<Self>) -> Result<Val> {89				Ok(evaluate_method(90					self.context_creator.create(self.this, self.super_obj)?,91					self.name,92					self.params,93					self.value,94				))95			}96		}9798		#[derive(Trace)]99		#[trivially_drop]100		struct BindableMethod {101			context_creator: ContextCreator,102			name: IStr,103			params: ParamsDesc,104			value: LocExpr,105		}106		impl Bindable for BindableMethod {107			fn bind(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal> {108				Ok(LazyVal::new(Box::new(BindableMethodLazyVal {109					this,110					super_obj,111112					context_creator: self.context_creator.clone(),113					name: self.name.clone(),114					params: self.params.clone(),115					value: self.value.clone(),116				})))117			}118		}119120		(121			b.name.clone(),122			LazyBinding::Bindable(Gc::new(Box::new(BindableMethod {123				context_creator,124				name: b.name.clone(),125				params,126				value: b.value.clone(),127			}))),128		)129	} else {130		#[derive(Trace)]131		#[trivially_drop]132		struct BindableNamedLazyVal {133			this: Option<ObjValue>,134			super_obj: Option<ObjValue>,135136			context_creator: ContextCreator,137			name: IStr,138			value: LocExpr,139		}140		impl LazyValValue for BindableNamedLazyVal {141			fn get(self: Box<Self>) -> Result<Val> {142				evaluate_named(143					self.context_creator.create(self.this, self.super_obj)?,144					&self.value,145					self.name,146				)147			}148		}149150		#[derive(Trace)]151		#[trivially_drop]152		struct BindableNamed {153			context_creator: ContextCreator,154			name: IStr,155			value: LocExpr,156		}157		impl Bindable for BindableNamed {158			fn bind(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal> {159				Ok(LazyVal::new(Box::new(BindableNamedLazyVal {160					this,161					super_obj,162163					context_creator: self.context_creator.clone(),164					name: self.name.clone(),165					value: self.value.clone(),166				})))167			}168		}169170		(171			b.name.clone(),172			LazyBinding::Bindable(Gc::new(Box::new(BindableNamed {173				context_creator,174				name: b.name.clone(),175				value: b.value.clone(),176			}))),177		)178	}179}180181pub fn evaluate_method(ctx: Context, name: IStr, params: ParamsDesc, body: LocExpr) -> Val {182	Val::Func(Gc::new(FuncVal::Normal(FuncDesc {183		name,184		ctx,185		params,186		body,187	})))188}189190pub fn evaluate_field_name(191	context: Context,192	field_name: &jrsonnet_parser::FieldName,193) -> Result<Option<IStr>> {194	Ok(match field_name {195		jrsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),196		jrsonnet_parser::FieldName::Dyn(expr) => {197			let value = evaluate(context, expr)?;198			if matches!(value, Val::Null) {199				None200			} else {201				Some(value.try_cast_str("dynamic field name")?)202			}203		}204	})205}206207pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Result<Val> {208	Ok(match (op, b) {209		(UnaryOpType::Not, Val::Bool(v)) => Val::Bool(!v),210		(UnaryOpType::Minus, Val::Num(n)) => Val::Num(-*n),211		(UnaryOpType::BitNot, Val::Num(n)) => Val::Num(!(*n as i32) as f64),212		(op, o) => throw!(UnaryOperatorDoesNotOperateOnType(op, o.value_type())),213	})214}215216pub fn evaluate_add_op(a: &Val, b: &Val) -> Result<Val> {217	Ok(match (a, b) {218		(Val::Str(v1), Val::Str(v2)) => Val::Str(((**v1).to_owned() + v2).into()),219220		// Can't use generic json serialization way, because it depends on number to string concatenation (std.jsonnet:890)221		(Val::Num(n), Val::Str(o)) => Val::Str(format!("{}{}", n, o).into()),222		(Val::Str(o), Val::Num(n)) => Val::Str(format!("{}{}", o, n).into()),223224		(Val::Str(s), o) => Val::Str(format!("{}{}", s, o.clone().to_string()?).into()),225		(o, Val::Str(s)) => Val::Str(format!("{}{}", o.clone().to_string()?, s).into()),226227		(Val::Obj(v1), Val::Obj(v2)) => Val::Obj(v2.extend_from(v1.clone())),228		(Val::Arr(a), Val::Arr(b)) => {229			let mut out = Vec::with_capacity(a.len() + b.len());230			out.extend(a.iter_lazy());231			out.extend(b.iter_lazy());232			Val::Arr(out.into())233		}234		(Val::Num(v1), Val::Num(v2)) => Val::new_checked_num(v1 + v2)?,235		_ => throw!(BinaryOperatorDoesNotOperateOnValues(236			BinaryOpType::Add,237			a.value_type(),238			b.value_type(),239		)),240	})241}242243pub fn evaluate_binary_op_special(244	context: Context,245	a: &LocExpr,246	op: BinaryOpType,247	b: &LocExpr,248) -> Result<Val> {249	Ok(match (evaluate(context.clone(), a)?, op, b) {250		(Val::Bool(true), BinaryOpType::Or, _o) => Val::Bool(true),251		(Val::Bool(false), BinaryOpType::And, _o) => Val::Bool(false),252		(a, op, eb) => evaluate_binary_op_normal(&a, op, &evaluate(context, eb)?)?,253	})254}255256pub fn evaluate_binary_op_normal(a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {257	Ok(match (a, op, b) {258		(a, BinaryOpType::Add, b) => evaluate_add_op(a, b)?,259260		(a, BinaryOpType::Eq, b) => Val::Bool(equals(a, b)?),261		(a, BinaryOpType::Neq, b) => Val::Bool(!equals(a, b)?),262263		(Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize).into()),264265		// Bool X Bool266		(Val::Bool(a), BinaryOpType::And, Val::Bool(b)) => Val::Bool(*a && *b),267		(Val::Bool(a), BinaryOpType::Or, Val::Bool(b)) => Val::Bool(*a || *b),268269		// Str X Str270		(Val::Str(v1), BinaryOpType::Lt, Val::Str(v2)) => Val::Bool(v1 < v2),271		(Val::Str(v1), BinaryOpType::Gt, Val::Str(v2)) => Val::Bool(v1 > v2),272		(Val::Str(v1), BinaryOpType::Lte, Val::Str(v2)) => Val::Bool(v1 <= v2),273		(Val::Str(v1), BinaryOpType::Gte, Val::Str(v2)) => Val::Bool(v1 >= v2),274275		// Num X Num276		(Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::new_checked_num(v1 * v2)?,277		(Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => {278			if *v2 <= f64::EPSILON {279				throw!(DivisionByZero)280			}281			Val::new_checked_num(v1 / v2)?282		}283284		(Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::new_checked_num(v1 - v2)?,285286		(Val::Num(v1), BinaryOpType::Lt, Val::Num(v2)) => Val::Bool(v1 < v2),287		(Val::Num(v1), BinaryOpType::Gt, Val::Num(v2)) => Val::Bool(v1 > v2),288		(Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => Val::Bool(v1 <= v2),289		(Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => Val::Bool(v1 >= v2),290291		(Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => {292			Val::Num(((*v1 as i32) & (*v2 as i32)) as f64)293		}294		(Val::Num(v1), BinaryOpType::BitOr, Val::Num(v2)) => {295			Val::Num(((*v1 as i32) | (*v2 as i32)) as f64)296		}297		(Val::Num(v1), BinaryOpType::BitXor, Val::Num(v2)) => {298			Val::Num(((*v1 as i32) ^ (*v2 as i32)) as f64)299		}300		(Val::Num(v1), BinaryOpType::Lhs, Val::Num(v2)) => {301			if *v2 < 0.0 {302				throw!(RuntimeError("shift by negative exponent".into()))303			}304			Val::Num(((*v1 as i32) << (*v2 as i32)) as f64)305		}306		(Val::Num(v1), BinaryOpType::Rhs, Val::Num(v2)) => {307			if *v2 < 0.0 {308				throw!(RuntimeError("shift by negative exponent".into()))309			}310			Val::Num(((*v1 as i32) >> (*v2 as i32)) as f64)311		}312313		_ => throw!(BinaryOperatorDoesNotOperateOnValues(314			op,315			a.value_type(),316			b.value_type(),317		)),318	})319}320321pub fn evaluate_comp(322	context: Context,323	specs: &[CompSpec],324	callback: &mut impl FnMut(Context) -> Result<()>,325) -> Result<()> {326	match specs.get(0) {327		None => callback(context)?,328		Some(CompSpec::IfSpec(IfSpecData(cond))) => {329			if evaluate(context.clone(), cond)?.try_cast_bool("if spec")? {330				evaluate_comp(context, &specs[1..], callback)?331			}332		}333		Some(CompSpec::ForSpec(ForSpecData(var, expr))) => match evaluate(context.clone(), expr)? {334			Val::Arr(list) => {335				for item in list.iter() {336					evaluate_comp(337						context.clone().with_var(var.clone(), item?.clone()),338						&specs[1..],339						callback,340					)?341				}342			}343			_ => throw!(InComprehensionCanOnlyIterateOverArray),344		},345	}346	Ok(())347}348349pub fn evaluate_member_list_object(context: Context, members: &[Member]) -> Result<ObjValue> {350	let new_bindings = FutureWrapper::new();351	let future_this = FutureWrapper::new();352	let context_creator = ContextCreator(context.clone(), new_bindings.clone());353	{354		let mut bindings: FxHashMap<IStr, LazyBinding> =355			FxHashMap::with_capacity_and_hasher(members.len(), BuildHasherDefault::default());356		for (n, b) in members357			.iter()358			.filter_map(|m| match m {359				Member::BindStmt(b) => Some(b.clone()),360				_ => None,361			})362			.map(|b| evaluate_binding(&b, context_creator.clone()))363		{364			bindings.insert(n, b);365		}366		new_bindings.fill(bindings);367	}368369	let mut new_members = FxHashMap::default();370	let mut assertions: Vec<Box<dyn ObjectAssertion>> = Vec::new();371	for member in members.iter() {372		match member {373			Member::Field(FieldMember {374				name,375				plus,376				params: None,377				visibility,378				value,379			}) => {380				let name = evaluate_field_name(context.clone(), name)?;381				if name.is_none() {382					continue;383				}384				let name = name.unwrap();385386				#[derive(Trace)]387				#[trivially_drop]388				struct ObjMemberBinding {389					context_creator: ContextCreator,390					value: LocExpr,391					name: IStr,392				}393				impl Bindable for ObjMemberBinding {394					fn bind(395						&self,396						this: Option<ObjValue>,397						super_obj: Option<ObjValue>,398					) -> Result<LazyVal> {399						Ok(LazyVal::new_resolved(evaluate_named(400							self.context_creator.create(this, super_obj)?,401							&self.value,402							self.name.clone(),403						)?))404					}405				}406				new_members.insert(407					name.clone(),408					ObjMember {409						add: *plus,410						visibility: *visibility,411						invoke: LazyBinding::Bindable(Gc::new(Box::new(ObjMemberBinding {412							context_creator: context_creator.clone(),413							value: value.clone(),414							name,415						}))),416						location: value.1.clone(),417					},418				);419			}420			Member::Field(FieldMember {421				name,422				params: Some(params),423				value,424				..425			}) => {426				let name = evaluate_field_name(context.clone(), name)?;427				if name.is_none() {428					continue;429				}430				let name = name.unwrap();431				#[derive(Trace)]432				#[trivially_drop]433				struct ObjMemberBinding {434					context_creator: ContextCreator,435					value: LocExpr,436					params: ParamsDesc,437					name: IStr,438				}439				impl Bindable for ObjMemberBinding {440					fn bind(441						&self,442						this: Option<ObjValue>,443						super_obj: Option<ObjValue>,444					) -> Result<LazyVal> {445						Ok(LazyVal::new_resolved(evaluate_method(446							self.context_creator.create(this, super_obj)?,447							self.name.clone(),448							self.params.clone(),449							self.value.clone(),450						)))451					}452				}453				new_members.insert(454					name.clone(),455					ObjMember {456						add: false,457						visibility: Visibility::Hidden,458						invoke: LazyBinding::Bindable(Gc::new(Box::new(ObjMemberBinding {459							context_creator: context_creator.clone(),460							value: value.clone(),461							params: params.clone(),462							name,463						}))),464						location: value.1.clone(),465					},466				);467			}468			Member::BindStmt(_) => {}469			Member::AssertStmt(stmt) => {470				#[derive(Trace)]471				#[trivially_drop]472				struct ObjectAssert {473					context_creator: ContextCreator,474					assert: AssertStmt,475				}476				impl ObjectAssertion for ObjectAssert {477					fn run(478						&self,479						this: Option<ObjValue>,480						super_obj: Option<ObjValue>,481					) -> Result<()> {482						let ctx = self.context_creator.create(this, super_obj)?;483						evaluate_assert(ctx, &self.assert)484					}485				}486				assertions.push(Box::new(ObjectAssert {487					context_creator: context_creator.clone(),488					assert: stmt.clone(),489				}));490			}491		}492	}493	let this = ObjValue::new(None, Gc::new(new_members), Gc::new(assertions));494	future_this.fill(this.clone());495	Ok(this)496}497498pub fn evaluate_object(context: Context, object: &ObjBody) -> Result<ObjValue> {499	Ok(match object {500		ObjBody::MemberList(members) => evaluate_member_list_object(context, members)?,501		ObjBody::ObjComp(obj) => {502			let future_this = FutureWrapper::new();503			let mut new_members = FxHashMap::default();504			evaluate_comp(context.clone(), &obj.compspecs, &mut |ctx| {505				let new_bindings = FutureWrapper::new();506				let context_creator = ContextCreator(context.clone(), new_bindings.clone());507				let mut bindings: FxHashMap<IStr, LazyBinding> =508					FxHashMap::with_capacity_and_hasher(509						obj.pre_locals.len() + obj.post_locals.len(),510						BuildHasherDefault::default(),511					);512				for (n, b) in obj513					.pre_locals514					.iter()515					.chain(obj.post_locals.iter())516					.map(|b| evaluate_binding(b, context_creator.clone()))517				{518					bindings.insert(n, b);519				}520				new_bindings.fill(bindings.clone());521				let ctx = ctx.extend_unbound(bindings, None, None, None)?;522				let key = evaluate(ctx.clone(), &obj.key)?;523524				match key {525					Val::Null => {}526					Val::Str(n) => {527						#[derive(Trace)]528						#[trivially_drop]529						struct ObjCompBinding {530							context: Context,531							value: LocExpr,532						}533						impl Bindable for ObjCompBinding {534							fn bind(535								&self,536								this: Option<ObjValue>,537								_super_obj: Option<ObjValue>,538							) -> Result<LazyVal> {539								Ok(LazyVal::new_resolved(evaluate(540									self.context.clone().extend(541										FxHashMap::default(),542										None,543										this,544										None,545									),546									&self.value,547								)?))548							}549						}550						new_members.insert(551							n,552							ObjMember {553								add: false,554								visibility: Visibility::Normal,555								invoke: LazyBinding::Bindable(Gc::new(Box::new(ObjCompBinding {556									context: ctx,557									value: obj.value.clone(),558								}))),559								location: obj.value.1.clone(),560							},561						);562					}563					v => throw!(FieldMustBeStringGot(v.value_type())),564				}565566				Ok(())567			})?;568569			let this = ObjValue::new(None, Gc::new(new_members), Gc::new(Vec::new()));570			future_this.fill(this.clone());571			this572		}573	})574}575576pub fn evaluate_apply(577	context: Context,578	value: &LocExpr,579	args: &ArgsDesc,580	loc: Option<&ExprLocation>,581	tailstrict: bool,582) -> Result<Val> {583	let value = evaluate(context.clone(), value)?;584	Ok(match value {585		Val::Func(f) => {586			let body = || f.evaluate(context, loc, args, tailstrict);587			if tailstrict {588				body()?589			} else {590				push(loc, || format!("function <{}> call", f.name()), body)?591			}592		}593		v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type())),594	})595}596597pub fn evaluate_assert(context: Context, assertion: &AssertStmt) -> Result<()> {598	let value = &assertion.0;599	let msg = &assertion.1;600	let assertion_result = push(601		value.1.as_ref(),602		|| "assertion condition".to_owned(),603		|| {604			evaluate(context.clone(), value)?605				.try_cast_bool("assertion condition should be of type `boolean`")606		},607	)?;608	if !assertion_result {609		push(610			value.1.as_ref(),611			|| "assertion failure".to_owned(),612			|| {613				if let Some(msg) = msg {614					throw!(AssertionFailed(evaluate(context, msg)?.to_string()?));615				} else {616					throw!(AssertionFailed(Val::Null.to_string()?));617				}618			},619		)?620	}621	Ok(())622}623624pub fn evaluate_named(context: Context, lexpr: &LocExpr, name: IStr) -> Result<Val> {625	use Expr::*;626	let LocExpr(expr, _loc) = lexpr;627	Ok(match &**expr {628		Function(params, body) => evaluate_method(context, name, params.clone(), body.clone()),629		_ => evaluate(context, lexpr)?,630	})631}632633pub fn evaluate(context: Context, expr: &LocExpr) -> Result<Val> {634	use Expr::*;635	let LocExpr(expr, loc) = expr;636	Ok(match &**expr {637		Literal(LiteralType::This) => {638			Val::Obj(context.this().clone().ok_or(CantUseSelfOutsideOfObject)?)639		}640		Literal(LiteralType::Super) => Val::Obj(641			context642				.super_obj()643				.clone()644				.ok_or(NoSuperFound)?645				.with_this(context.this().clone().unwrap()),646		),647		Literal(LiteralType::Dollar) => {648			Val::Obj(context.dollar().clone().ok_or(NoTopLevelObjectFound)?)649		}650		Literal(LiteralType::True) => Val::Bool(true),651		Literal(LiteralType::False) => Val::Bool(false),652		Literal(LiteralType::Null) => Val::Null,653		Parened(e) => evaluate(context, e)?,654		Str(v) => Val::Str(v.clone()),655		Num(v) => Val::new_checked_num(*v)?,656		BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, v1, *o, v2)?,657		UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,658		Var(name) => push(659			loc.as_ref(),660			|| format!("variable <{}>", name),661			|| context.binding(name.clone())?.evaluate(),662		)?,663		Index(value, index) => {664			match (evaluate(context.clone(), value)?, evaluate(context, index)?) {665				(Val::Obj(v), Val::Str(s)) => {666					let sn = s.clone();667					push(668						loc.as_ref(),669						|| format!("field <{}> access", sn),670						|| {671							if let Some(v) = v.get(s.clone())? {672								Ok(v)673							} else if v.get("__intrinsic_namespace__".into())?.is_some() {674								Ok(Val::Func(Gc::new(FuncVal::Intrinsic(s))))675							} else {676								throw!(NoSuchField(s))677							}678						},679					)?680				}681				(Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot(682					ValType::Obj,683					ValType::Str,684					n.value_type(),685				)),686687				(Val::Arr(v), Val::Num(n)) => {688					if n.fract() > f64::EPSILON {689						throw!(FractionalIndex)690					}691					v.get(n as usize)?692						.ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?693				}694				(Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)),695				(Val::Arr(_), n) => throw!(ValueIndexMustBeTypeGot(696					ValType::Arr,697					ValType::Num,698					n.value_type(),699				)),700701				(Val::Str(s), Val::Num(n)) => Val::Str(702					s.chars()703						.skip(n as usize)704						.take(1)705						.collect::<String>()706						.into(),707				),708				(Val::Str(_), n) => throw!(ValueIndexMustBeTypeGot(709					ValType::Str,710					ValType::Num,711					n.value_type(),712				)),713714				(v, _) => throw!(CantIndexInto(v.value_type())),715			}716		}717		LocalExpr(bindings, returned) => {718			let mut new_bindings: FxHashMap<IStr, LazyVal> = HashMap::with_capacity_and_hasher(719				bindings.len(),720				BuildHasherDefault::<FxHasher>::default(),721			);722			let future_context = Context::new_future();723			for b in bindings {724				new_bindings.insert(725					b.name.clone(),726					evaluate_binding_in_future(b, future_context.clone()),727				);728			}729			let context = context730				.extend_bound(new_bindings)731				.into_future(future_context);732			evaluate(context, &returned.clone())?733		}734		Arr(items) => {735			let mut out = Vec::with_capacity(items.len());736			for item in items {737				// TODO: Implement ArrValue::Lazy with same context for every element?738				#[derive(Trace)]739				#[trivially_drop]740				struct ArrayElement {741					context: Context,742					item: LocExpr,743				}744				impl LazyValValue for ArrayElement {745					fn get(self: Box<Self>) -> Result<Val> {746						evaluate(self.context, &self.item)747					}748				}749				out.push(LazyVal::new(Box::new(ArrayElement {750					context: context.clone(),751					item: item.clone(),752				})));753			}754			Val::Arr(out.into())755		}756		ArrComp(expr, comp_specs) => {757			let mut out = Vec::new();758			evaluate_comp(context, comp_specs, &mut |ctx| {759				out.push(evaluate(ctx, expr)?);760				Ok(())761			})?;762			Val::Arr(ArrValue::Eager(Gc::new(out)))763		}764		Obj(body) => Val::Obj(evaluate_object(context, body)?),765		ObjExtend(s, t) => evaluate_add_op(766			&evaluate(context.clone(), s)?,767			&Val::Obj(evaluate_object(context, t)?),768		)?,769		Apply(value, args, tailstrict) => {770			evaluate_apply(context, value, args, loc.as_ref(), *tailstrict)?771		}772		Function(params, body) => {773			evaluate_method(context, "anonymous".into(), params.clone(), body.clone())774		}775		Intrinsic(name) => Val::Func(Gc::new(FuncVal::Intrinsic(name.clone()))),776		AssertExpr(assert, returned) => {777			evaluate_assert(context.clone(), assert)?;778			evaluate(context, returned)?779		}780		ErrorStmt(e) => push(781			loc.as_ref(),782			|| "error statement".to_owned(),783			|| {784				throw!(RuntimeError(785					evaluate(context, e)?.try_cast_str("error text should be of type `string`")?,786				))787			},788		)?,789		IfElse {790			cond,791			cond_then,792			cond_else,793		} => {794			if push(795				loc.as_ref(),796				|| "if condition".to_owned(),797				|| evaluate(context.clone(), &cond.0)?.try_cast_bool("in if condition"),798			)? {799				evaluate(context, cond_then)?800			} else {801				match cond_else {802					Some(v) => evaluate(context, v)?,803					None => Val::Null,804				}805			}806		}807		Import(path) => {808			let tmp = loc809				.clone()810				.expect("imports cannot be used without loc_data")811				.0;812			let mut import_location = tmp.to_path_buf();813			import_location.pop();814			push(815				loc.as_ref(),816				|| format!("import {:?}", path),817				|| with_state(|s| s.import_file(&import_location, path)),818			)?819		}820		ImportStr(path) => {821			let tmp = loc822				.clone()823				.expect("imports cannot be used without loc_data")824				.0;825			let mut import_location = tmp.to_path_buf();826			import_location.pop();827			Val::Str(with_state(|s| s.import_file_str(&import_location, path))?)828		}829	})830}
modifiedcrates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/integrations/serde.rs
+++ b/crates/jrsonnet-evaluator/src/integrations/serde.rs
@@ -42,7 +42,6 @@
 				Self::Object(out)
 			}
 			Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),
-			Val::DebugGcTraceValue(v) => Self::try_from(&*v.value as &Val)?,
 		})
 	}
 }
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -9,7 +9,7 @@
 	native::NativeCallback,
 	throw, with_state, Context, ObjValue, Result,
 };
-use jrsonnet_gc::{Finalize, Gc, GcCell, Trace};
+use jrsonnet_gc::{Gc, GcCell, Trace};
 use jrsonnet_interner::IStr;
 use jrsonnet_parser::{el, Arg, ArgsDesc, Expr, ExprLocation, LiteralType, LocExpr, ParamsDesc};
 use jrsonnet_types::ValType;
@@ -345,65 +345,6 @@
 	}
 }
 
-#[derive(Debug)]
-pub struct DebugGcTraceValue {
-	name: IStr,
-	pub value: Box<Val>,
-}
-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 = Self {
-			name: self.name.clone(),
-			value: self.value.clone(),
-		};
-		value.print("I'm clone");
-		value
-	}
-}
-impl DebugGcTraceValue {
-	pub fn create(name: IStr, value: Val) -> Val {
-		let value = Self {
-			name,
-			value: Box::new(value),
-		};
-		value.print("Constructed");
-		Val::DebugGcTraceValue(value)
-	}
-}
-
 #[derive(Debug, Clone, Trace)]
 #[trivially_drop]
 pub enum Val {
@@ -414,7 +355,6 @@
 	Arr(ArrValue),
 	Obj(ObjValue),
 	Func(Gc<FuncVal>),
-	DebugGcTraceValue(DebugGcTraceValue),
 }
 
 macro_rules! matches_unwrap {
@@ -471,7 +411,6 @@
 			Self::Bool(_) => ValType::Bool,
 			Self::Null => ValType::Null,
 			Self::Func(..) => ValType::Func,
-			Self::DebugGcTraceValue(v) => v.value.value_type(),
 		}
 	}