git.delta.rocks / jrsonnet / refs/commits / 321e7ee3e21c

difftreelog

fix string index bounds check

Yaroslav Bolyukin2022-04-22parent: #cb29d29.patch.diff
in: master

2 files changed

modifiedcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -36,6 +36,8 @@
 
 	#[error("array out of bounds: {0} is not within [0,{1})")]
 	ArrayBoundsError(usize, usize),
+	#[error("string out of bounds: {0} is not within [0,{1})")]
+	StringBoundsError(usize, usize),
 
 	#[error("assert failed: {0}")]
 	AssertionFailed(IStr),
modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/evaluate/mod.rs
1use gcmodule::{Cc, Trace};2use jrsonnet_interner::IStr;3use jrsonnet_parser::{4	ArgsDesc, AssertStmt, BindSpec, CompSpec, Expr, FieldMember, ForSpecData, IfSpecData,5	LiteralType, LocExpr, Member, ObjBody, ParamsDesc,6};7use jrsonnet_types::ValType;89use crate::{10	builtin::{std_slice, BUILTINS},11	error::Error::*,12	evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},13	function::CallLocation,14	gc::TraceBox,15	throw,16	typed::Typed,17	val::{ArrValue, FuncDesc, FuncVal, LazyValValue},18	Bindable, Context, ContextCreator, FutureWrapper, GcHashMap, LazyBinding, LazyVal, ObjValue,19	ObjValueBuilder, ObjectAssertion, Result, State, Val,20};21pub mod operator;2223pub fn evaluate_binding_in_future(b: &BindSpec, fctx: FutureWrapper<Context>) -> LazyVal {24	let b = b.clone();25	if let Some(params) = &b.params {26		let params = params.clone();2728		#[derive(Trace)]29		struct LazyMethodBinding {30			fctx: FutureWrapper<Context>,31			name: IStr,32			params: ParamsDesc,33			value: LocExpr,34		}35		impl LazyValValue for LazyMethodBinding {36			fn get(self: Box<Self>, _: State) -> Result<Val> {37				Ok(evaluate_method(38					self.fctx.unwrap(),39					self.name,40					self.params,41					self.value,42				))43			}44		}4546		LazyVal::new(TraceBox(Box::new(LazyMethodBinding {47			fctx,48			name: b.name.clone(),49			params,50			value: b.value.clone(),51		})))52	} else {53		#[derive(Trace)]54		struct LazyNamedBinding {55			fctx: FutureWrapper<Context>,56			name: IStr,57			value: LocExpr,58		}59		impl LazyValValue for LazyNamedBinding {60			fn get(self: Box<Self>, s: State) -> Result<Val> {61				evaluate_named(s, self.fctx.unwrap(), &self.value, self.name)62			}63		}64		LazyVal::new(TraceBox(Box::new(LazyNamedBinding {65			fctx,66			name: b.name.clone(),67			value: b.value,68		})))69	}70}7172pub fn evaluate_binding(b: &BindSpec, cctx: ContextCreator) -> (IStr, LazyBinding) {73	let b = b.clone();74	if let Some(params) = &b.params {75		let params = params.clone();7677		#[derive(Trace)]78		struct BindableMethodLazyVal {79			this: Option<ObjValue>,80			super_obj: Option<ObjValue>,8182			cctx: ContextCreator,83			name: IStr,84			params: ParamsDesc,85			value: LocExpr,86		}87		impl LazyValValue for BindableMethodLazyVal {88			fn get(self: Box<Self>, s: State) -> Result<Val> {89				Ok(evaluate_method(90					self.cctx.create(s, self.this, self.super_obj)?,91					self.name,92					self.params,93					self.value,94				))95			}96		}9798		#[derive(Trace)]99		struct BindableMethod {100			cctx: ContextCreator,101			name: IStr,102			params: ParamsDesc,103			value: LocExpr,104		}105		impl Bindable for BindableMethod {106			fn bind(107				&self,108				_: State,109				this: Option<ObjValue>,110				super_obj: Option<ObjValue>,111			) -> Result<LazyVal> {112				Ok(LazyVal::new(TraceBox(Box::new(BindableMethodLazyVal {113					this,114					super_obj,115116					cctx: self.cctx.clone(),117					name: self.name.clone(),118					params: self.params.clone(),119					value: self.value.clone(),120				}))))121			}122		}123124		(125			b.name.clone(),126			LazyBinding::Bindable(Cc::new(TraceBox(Box::new(BindableMethod {127				cctx,128				name: b.name.clone(),129				params,130				value: b.value.clone(),131			})))),132		)133	} else {134		#[derive(Trace)]135		struct BindableNamedLazyVal {136			this: Option<ObjValue>,137			super_obj: Option<ObjValue>,138139			cctx: ContextCreator,140			name: IStr,141			value: LocExpr,142		}143		impl LazyValValue for BindableNamedLazyVal {144			fn get(self: Box<Self>, s: State) -> Result<Val> {145				evaluate_named(146					s.clone(),147					self.cctx.create(s, self.this, self.super_obj)?,148					&self.value,149					self.name,150				)151			}152		}153154		#[derive(Trace)]155		struct BindableNamed {156			cctx: ContextCreator,157			name: IStr,158			value: LocExpr,159		}160		impl Bindable for BindableNamed {161			fn bind(162				&self,163				_: State,164				this: Option<ObjValue>,165				super_obj: Option<ObjValue>,166			) -> Result<LazyVal> {167				Ok(LazyVal::new(TraceBox(Box::new(BindableNamedLazyVal {168					this,169					super_obj,170171					cctx: self.cctx.clone(),172					name: self.name.clone(),173					value: self.value.clone(),174				}))))175			}176		}177178		(179			b.name.clone(),180			LazyBinding::Bindable(Cc::new(TraceBox(Box::new(BindableNamed {181				cctx,182				name: b.name.clone(),183				value: b.value.clone(),184			})))),185		)186	}187}188189pub fn evaluate_method(ctx: Context, name: IStr, params: ParamsDesc, body: LocExpr) -> Val {190	Val::Func(FuncVal::Normal(Cc::new(FuncDesc {191		name,192		ctx,193		params,194		body,195	})))196}197198pub fn evaluate_field_name(199	s: State,200	ctx: Context,201	field_name: &jrsonnet_parser::FieldName,202) -> Result<Option<IStr>> {203	Ok(match field_name {204		jrsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),205		jrsonnet_parser::FieldName::Dyn(expr) => s.push(206			CallLocation::new(&expr.1),207			|| "evaluating field name".to_string(),208			|| {209				let value = evaluate(s.clone(), ctx, expr)?;210				if matches!(value, Val::Null) {211					Ok(None)212				} else {213					Ok(Some(IStr::from_untyped(value, s.clone())?))214				}215			},216		)?,217	})218}219220pub fn evaluate_comp(221	s: State,222	ctx: Context,223	specs: &[CompSpec],224	callback: &mut impl FnMut(Context) -> Result<()>,225) -> Result<()> {226	match specs.get(0) {227		None => callback(ctx)?,228		Some(CompSpec::IfSpec(IfSpecData(cond))) => {229			if bool::from_untyped(evaluate(s.clone(), ctx.clone(), cond)?, s.clone())? {230				evaluate_comp(s, ctx, &specs[1..], callback)?231			}232		}233		Some(CompSpec::ForSpec(ForSpecData(var, expr))) => {234			match evaluate(s.clone(), ctx.clone(), expr)? {235				Val::Arr(list) => {236					for item in list.iter(s.clone()) {237						evaluate_comp(238							s.clone(),239							ctx.clone().with_var(var.clone(), item?.clone()),240							&specs[1..],241							callback,242						)?243					}244				}245				_ => throw!(InComprehensionCanOnlyIterateOverArray),246			}247		}248	}249	Ok(())250}251252pub fn evaluate_member_list_object(s: State, ctx: Context, members: &[Member]) -> Result<ObjValue> {253	let new_bindings = FutureWrapper::new();254	let future_this = FutureWrapper::new();255	let cctx = ContextCreator(ctx.clone(), new_bindings.clone());256	{257		let mut bindings: GcHashMap<IStr, LazyBinding> = GcHashMap::with_capacity(members.len());258		for (n, b) in members259			.iter()260			.filter_map(|m| match m {261				Member::BindStmt(b) => Some(b.clone()),262				_ => None,263			})264			.map(|b| evaluate_binding(&b, cctx.clone()))265		{266			bindings.insert(n, b);267		}268		new_bindings.fill(bindings);269	}270271	let mut builder = ObjValueBuilder::new();272	for member in members.iter() {273		match member {274			Member::Field(FieldMember {275				name,276				plus,277				params: None,278				visibility,279				value,280			}) => {281				let name = evaluate_field_name(s.clone(), ctx.clone(), name)?;282				if name.is_none() {283					continue;284				}285				let name = name.unwrap();286287				#[derive(Trace)]288				struct ObjMemberBinding {289					cctx: ContextCreator,290					value: LocExpr,291					name: IStr,292				}293				impl Bindable for ObjMemberBinding {294					fn bind(295						&self,296						s: State,297						this: Option<ObjValue>,298						super_obj: Option<ObjValue>,299					) -> Result<LazyVal> {300						Ok(LazyVal::new_resolved(evaluate_named(301							s.clone(),302							self.cctx.create(s, this, super_obj)?,303							&self.value,304							self.name.clone(),305						)?))306					}307				}308				builder309					.member(name.clone())310					.with_add(*plus)311					.with_visibility(*visibility)312					.with_location(value.1.clone())313					.bindable(314						s.clone(),315						TraceBox(Box::new(ObjMemberBinding {316							cctx: cctx.clone(),317							value: value.clone(),318							name,319						})),320					)?;321			}322			Member::Field(FieldMember {323				name,324				params: Some(params),325				value,326				..327			}) => {328				let name = evaluate_field_name(s.clone(), ctx.clone(), name)?;329				if name.is_none() {330					continue;331				}332				let name = name.unwrap();333				#[derive(Trace)]334				struct ObjMemberBinding {335					cctx: ContextCreator,336					value: LocExpr,337					params: ParamsDesc,338					name: IStr,339				}340				impl Bindable for ObjMemberBinding {341					fn bind(342						&self,343						s: State,344						this: Option<ObjValue>,345						super_obj: Option<ObjValue>,346					) -> Result<LazyVal> {347						Ok(LazyVal::new_resolved(evaluate_method(348							self.cctx.create(s, this, super_obj)?,349							self.name.clone(),350							self.params.clone(),351							self.value.clone(),352						)))353					}354				}355				builder356					.member(name.clone())357					.hide()358					.with_location(value.1.clone())359					.bindable(360						s.clone(),361						TraceBox(Box::new(ObjMemberBinding {362							cctx: cctx.clone(),363							value: value.clone(),364							params: params.clone(),365							name,366						})),367					)?;368			}369			Member::BindStmt(_) => {}370			Member::AssertStmt(stmt) => {371				#[derive(Trace)]372				struct ObjectAssert {373					cctx: ContextCreator,374					assert: AssertStmt,375				}376				impl ObjectAssertion for ObjectAssert {377					fn run(378						&self,379						s: State,380						this: Option<ObjValue>,381						super_obj: Option<ObjValue>,382					) -> Result<()> {383						let ctx = self.cctx.create(s.clone(), this, super_obj)?;384						evaluate_assert(s, ctx, &self.assert)385					}386				}387				builder.assert(TraceBox(Box::new(ObjectAssert {388					cctx: cctx.clone(),389					assert: stmt.clone(),390				})));391			}392		}393	}394	let this = builder.build();395	future_this.fill(this.clone());396	Ok(this)397}398399pub fn evaluate_object(s: State, ctx: Context, object: &ObjBody) -> Result<ObjValue> {400	Ok(match object {401		ObjBody::MemberList(members) => evaluate_member_list_object(s, ctx, members)?,402		ObjBody::ObjComp(obj) => {403			let future_this = FutureWrapper::new();404			let mut builder = ObjValueBuilder::new();405			evaluate_comp(s.clone(), ctx, &obj.compspecs, &mut |ctx| {406				let new_bindings = FutureWrapper::new();407				let cctx = ContextCreator(ctx.clone(), new_bindings.clone());408				let mut bindings: GcHashMap<IStr, LazyBinding> =409					GcHashMap::with_capacity(obj.pre_locals.len() + obj.post_locals.len());410				for (n, b) in obj411					.pre_locals412					.iter()413					.chain(obj.post_locals.iter())414					.map(|b| evaluate_binding(b, cctx.clone()))415				{416					bindings.insert(n, b);417				}418				new_bindings.fill(bindings.clone());419				let ctx = ctx.extend_unbound(s.clone(), bindings, None, None, None)?;420				let key = evaluate(s.clone(), ctx.clone(), &obj.key)?;421422				match key {423					Val::Null => {}424					Val::Str(n) => {425						#[derive(Trace)]426						struct ObjCompBinding {427							ctx: Context,428							value: LocExpr,429						}430						impl Bindable for ObjCompBinding {431							fn bind(432								&self,433								s: State,434								this: Option<ObjValue>,435								_super_obj: Option<ObjValue>,436							) -> Result<LazyVal> {437								Ok(LazyVal::new_resolved(evaluate(438									s,439									self.ctx.clone().extend(GcHashMap::new(), None, this, None),440									&self.value,441								)?))442							}443						}444						builder445							.member(n)446							.with_location(obj.value.1.clone())447							.with_add(obj.plus)448							.bindable(449								s.clone(),450								TraceBox(Box::new(ObjCompBinding {451									ctx,452									value: obj.value.clone(),453								})),454							)?;455					}456					v => throw!(FieldMustBeStringGot(v.value_type())),457				}458459				Ok(())460			})?;461462			let this = builder.build();463			future_this.fill(this.clone());464			this465		}466	})467}468469pub fn evaluate_apply(470	s: State,471	ctx: Context,472	value: &LocExpr,473	args: &ArgsDesc,474	loc: CallLocation,475	tailstrict: bool,476) -> Result<Val> {477	let value = evaluate(s.clone(), ctx.clone(), value)?;478	Ok(match value {479		Val::Func(f) => {480			let body = || f.evaluate(s.clone(), ctx, loc, args, tailstrict);481			if tailstrict {482				body()?483			} else {484				s.push(loc, || format!("function <{}> call", f.name()), body)?485			}486		}487		v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type())),488	})489}490491pub fn evaluate_assert(s: State, ctx: Context, assertion: &AssertStmt) -> Result<()> {492	let value = &assertion.0;493	let msg = &assertion.1;494	let assertion_result = s.push(495		CallLocation::new(&value.1),496		|| "assertion condition".to_owned(),497		|| bool::from_untyped(evaluate(s.clone(), ctx.clone(), value)?, s.clone()),498	)?;499	if !assertion_result {500		s.push(501			CallLocation::new(&value.1),502			|| "assertion failure".to_owned(),503			|| {504				if let Some(msg) = msg {505					throw!(AssertionFailed(506						evaluate(s.clone(), ctx, msg)?.to_string(s.clone())?507					));508				} else {509					throw!(AssertionFailed(Val::Null.to_string(s.clone())?));510				}511			},512		)?513	}514	Ok(())515}516517pub fn evaluate_named(s: State, ctx: Context, lexpr: &LocExpr, name: IStr) -> Result<Val> {518	use Expr::*;519	let LocExpr(expr, _loc) = lexpr;520	Ok(match &**expr {521		Function(params, body) => evaluate_method(ctx, name, params.clone(), body.clone()),522		_ => evaluate(s, ctx, lexpr)?,523	})524}525526pub fn evaluate(s: State, ctx: Context, expr: &LocExpr) -> Result<Val> {527	use Expr::*;528	let LocExpr(expr, loc) = expr;529	// let bp = with_state(|s| s.0.stop_at.borrow().clone());530	Ok(match &**expr {531		Literal(LiteralType::This) => {532			Val::Obj(ctx.this().clone().ok_or(CantUseSelfOutsideOfObject)?)533		}534		Literal(LiteralType::Super) => Val::Obj(535			ctx.super_obj()536				.clone()537				.ok_or(NoSuperFound)?538				.with_this(ctx.this().clone().unwrap()),539		),540		Literal(LiteralType::Dollar) => {541			Val::Obj(ctx.dollar().clone().ok_or(NoTopLevelObjectFound)?)542		}543		Literal(LiteralType::True) => Val::Bool(true),544		Literal(LiteralType::False) => Val::Bool(false),545		Literal(LiteralType::Null) => Val::Null,546		Parened(e) => evaluate(s, ctx, e)?,547		Str(v) => Val::Str(v.clone()),548		Num(v) => Val::new_checked_num(*v)?,549		BinaryOp(v1, o, v2) => evaluate_binary_op_special(s, ctx, v1, *o, v2)?,550		UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(s, ctx, v)?)?,551		Var(name) => s.push(552			CallLocation::new(loc),553			|| format!("variable <{}> access", name),554			|| ctx.binding(name.clone())?.evaluate(s.clone()),555		)?,556		Index(value, index) => {557			match (558				evaluate(s.clone(), ctx.clone(), value)?,559				evaluate(s.clone(), ctx, index)?,560			) {561				(Val::Obj(v), Val::Str(key)) => s.push(562					CallLocation::new(loc),563					|| format!("field <{}> access", key),564					|| {565						if let Some(v) = v.get(s.clone(), key.clone())? {566							Ok(v)567						} else {568							throw!(NoSuchField(key.clone()))569						}570					},571				)?,572				(Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot(573					ValType::Obj,574					ValType::Str,575					n.value_type(),576				)),577578				(Val::Arr(v), Val::Num(n)) => {579					if n.fract() > f64::EPSILON {580						throw!(FractionalIndex)581					}582					v.get(s, n as usize)?583						.ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?584				}585				(Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)),586				(Val::Arr(_), n) => throw!(ValueIndexMustBeTypeGot(587					ValType::Arr,588					ValType::Num,589					n.value_type(),590				)),591592				(Val::Str(s), Val::Num(n)) => Val::Str(593					s.chars()594						.skip(n as usize)595						.take(1)596						.collect::<String>()597						.into(),598				),599				(Val::Str(_), n) => throw!(ValueIndexMustBeTypeGot(600					ValType::Str,601					ValType::Num,602					n.value_type(),603				)),604605				(v, _) => throw!(CantIndexInto(v.value_type())),606			}607		}608		LocalExpr(bindings, returned) => {609			let mut new_bindings: GcHashMap<IStr, LazyVal> =610				GcHashMap::with_capacity(bindings.len());611			let fctx = Context::new_future();612			for b in bindings {613				new_bindings.insert(b.name.clone(), evaluate_binding_in_future(b, fctx.clone()));614			}615			let ctx = ctx.extend_bound(new_bindings).into_future(fctx);616			evaluate(s, ctx, &returned.clone())?617		}618		Arr(items) => {619			let mut out = Vec::with_capacity(items.len());620			for item in items {621				// TODO: Implement ArrValue::Lazy with same context for every element?622				#[derive(Trace)]623				struct ArrayElement {624					ctx: Context,625					item: LocExpr,626				}627				impl LazyValValue for ArrayElement {628					fn get(self: Box<Self>, s: State) -> Result<Val> {629						evaluate(s, self.ctx, &self.item)630					}631				}632				out.push(LazyVal::new(TraceBox(Box::new(ArrayElement {633					ctx: ctx.clone(),634					item: item.clone(),635				}))));636			}637			Val::Arr(out.into())638		}639		ArrComp(expr, comp_specs) => {640			let mut out = Vec::new();641			evaluate_comp(s.clone(), ctx, comp_specs, &mut |ctx| {642				out.push(evaluate(s.clone(), ctx, expr)?);643				Ok(())644			})?;645			Val::Arr(ArrValue::Eager(Cc::new(out)))646		}647		Obj(body) => Val::Obj(evaluate_object(s, ctx, body)?),648		ObjExtend(a, b) => evaluate_add_op(649			s.clone(),650			&evaluate(s.clone(), ctx.clone(), a)?,651			&Val::Obj(evaluate_object(s, ctx, b)?),652		)?,653		Apply(value, args, tailstrict) => {654			evaluate_apply(s, ctx, value, args, CallLocation::new(loc), *tailstrict)?655		}656		Function(params, body) => {657			evaluate_method(ctx, "anonymous".into(), params.clone(), body.clone())658		}659		Intrinsic(name) => Val::Func(FuncVal::StaticBuiltin(660			BUILTINS661				.with(|b| b.get(name).copied())662				.ok_or_else(|| IntrinsicNotFound(name.clone()))?,663		)),664		AssertExpr(assert, returned) => {665			evaluate_assert(s.clone(), ctx.clone(), assert)?;666			evaluate(s, ctx, returned)?667		}668		ErrorStmt(e) => s.push(669			CallLocation::new(loc),670			|| "error statement".to_owned(),671			|| {672				throw!(RuntimeError(673					evaluate(s.clone(), ctx, e)?.to_string(s.clone())?,674				))675			},676		)?,677		IfElse {678			cond,679			cond_then,680			cond_else,681		} => {682			if s.push(683				CallLocation::new(loc),684				|| "if condition".to_owned(),685				|| bool::from_untyped(evaluate(s.clone(), ctx.clone(), &cond.0)?, s.clone()),686			)? {687				evaluate(s, ctx, cond_then)?688			} else {689				match cond_else {690					Some(v) => evaluate(s, ctx, v)?,691					None => Val::Null,692				}693			}694		}695		Slice(value, desc) => {696			let indexable = evaluate(s.clone(), ctx.clone(), value)?;697			let loc = CallLocation::new(loc);698699			fn parse_idx<T: Typed>(700				loc: CallLocation,701				s: State,702				ctx: &Context,703				expr: &Option<LocExpr>,704				desc: &'static str,705			) -> Result<Option<T>> {706				if let Some(value) = expr {707					Ok(Some(s.push(708						loc,709						|| format!("slice {}", desc),710						|| T::from_untyped(evaluate(s.clone(), ctx.clone(), value)?, s.clone()),711					)?))712				} else {713					Ok(None)714				}715			}716717			let start = parse_idx(loc, s.clone(), &ctx, &desc.start, "start")?;718			let end = parse_idx(loc, s.clone(), &ctx, &desc.end, "end")?;719			let step = parse_idx(loc, s, &ctx, &desc.step, "step")?;720721			std_slice(indexable.into_indexable()?, start, end, step)?722		}723		Import(path) => {724			let tmp = loc.clone().0;725			let mut import_location = tmp.to_path_buf();726			import_location.pop();727			s.push(728				CallLocation::new(loc),729				|| format!("import {:?}", path),730				|| s.import_file(&import_location, path),731			)?732		}733		ImportStr(path) => {734			let tmp = loc.clone().0;735			let mut import_location = tmp.to_path_buf();736			import_location.pop();737			Val::Str(s.import_file_str(&import_location, path)?)738		}739		ImportBin(path) => {740			let tmp = loc.clone().0;741			let mut import_location = tmp.to_path_buf();742			import_location.pop();743			let bytes = s.import_file_bin(&import_location, path)?;744			Val::Arr(ArrValue::Bytes(bytes))745		}746	})747}
after · crates/jrsonnet-evaluator/src/evaluate/mod.rs
1use gcmodule::{Cc, Trace};2use jrsonnet_interner::IStr;3use jrsonnet_parser::{4	ArgsDesc, AssertStmt, BindSpec, CompSpec, Expr, FieldMember, ForSpecData, IfSpecData,5	LiteralType, LocExpr, Member, ObjBody, ParamsDesc,6};7use jrsonnet_types::ValType;89use crate::{10	builtin::{std_slice, BUILTINS},11	error::Error::*,12	evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},13	function::CallLocation,14	gc::TraceBox,15	throw,16	typed::Typed,17	val::{ArrValue, FuncDesc, FuncVal, LazyValValue},18	Bindable, Context, ContextCreator, FutureWrapper, GcHashMap, LazyBinding, LazyVal, ObjValue,19	ObjValueBuilder, ObjectAssertion, Result, State, Val,20};21pub mod operator;2223pub fn evaluate_binding_in_future(b: &BindSpec, fctx: FutureWrapper<Context>) -> LazyVal {24	let b = b.clone();25	if let Some(params) = &b.params {26		let params = params.clone();2728		#[derive(Trace)]29		struct LazyMethodBinding {30			fctx: FutureWrapper<Context>,31			name: IStr,32			params: ParamsDesc,33			value: LocExpr,34		}35		impl LazyValValue for LazyMethodBinding {36			fn get(self: Box<Self>, _: State) -> Result<Val> {37				Ok(evaluate_method(38					self.fctx.unwrap(),39					self.name,40					self.params,41					self.value,42				))43			}44		}4546		LazyVal::new(TraceBox(Box::new(LazyMethodBinding {47			fctx,48			name: b.name.clone(),49			params,50			value: b.value.clone(),51		})))52	} else {53		#[derive(Trace)]54		struct LazyNamedBinding {55			fctx: FutureWrapper<Context>,56			name: IStr,57			value: LocExpr,58		}59		impl LazyValValue for LazyNamedBinding {60			fn get(self: Box<Self>, s: State) -> Result<Val> {61				evaluate_named(s, self.fctx.unwrap(), &self.value, self.name)62			}63		}64		LazyVal::new(TraceBox(Box::new(LazyNamedBinding {65			fctx,66			name: b.name.clone(),67			value: b.value,68		})))69	}70}7172pub fn evaluate_binding(b: &BindSpec, cctx: ContextCreator) -> (IStr, LazyBinding) {73	let b = b.clone();74	if let Some(params) = &b.params {75		let params = params.clone();7677		#[derive(Trace)]78		struct BindableMethodLazyVal {79			this: Option<ObjValue>,80			super_obj: Option<ObjValue>,8182			cctx: ContextCreator,83			name: IStr,84			params: ParamsDesc,85			value: LocExpr,86		}87		impl LazyValValue for BindableMethodLazyVal {88			fn get(self: Box<Self>, s: State) -> Result<Val> {89				Ok(evaluate_method(90					self.cctx.create(s, self.this, self.super_obj)?,91					self.name,92					self.params,93					self.value,94				))95			}96		}9798		#[derive(Trace)]99		struct BindableMethod {100			cctx: ContextCreator,101			name: IStr,102			params: ParamsDesc,103			value: LocExpr,104		}105		impl Bindable for BindableMethod {106			fn bind(107				&self,108				_: State,109				this: Option<ObjValue>,110				super_obj: Option<ObjValue>,111			) -> Result<LazyVal> {112				Ok(LazyVal::new(TraceBox(Box::new(BindableMethodLazyVal {113					this,114					super_obj,115116					cctx: self.cctx.clone(),117					name: self.name.clone(),118					params: self.params.clone(),119					value: self.value.clone(),120				}))))121			}122		}123124		(125			b.name.clone(),126			LazyBinding::Bindable(Cc::new(TraceBox(Box::new(BindableMethod {127				cctx,128				name: b.name.clone(),129				params,130				value: b.value.clone(),131			})))),132		)133	} else {134		#[derive(Trace)]135		struct BindableNamedLazyVal {136			this: Option<ObjValue>,137			super_obj: Option<ObjValue>,138139			cctx: ContextCreator,140			name: IStr,141			value: LocExpr,142		}143		impl LazyValValue for BindableNamedLazyVal {144			fn get(self: Box<Self>, s: State) -> Result<Val> {145				evaluate_named(146					s.clone(),147					self.cctx.create(s, self.this, self.super_obj)?,148					&self.value,149					self.name,150				)151			}152		}153154		#[derive(Trace)]155		struct BindableNamed {156			cctx: ContextCreator,157			name: IStr,158			value: LocExpr,159		}160		impl Bindable for BindableNamed {161			fn bind(162				&self,163				_: State,164				this: Option<ObjValue>,165				super_obj: Option<ObjValue>,166			) -> Result<LazyVal> {167				Ok(LazyVal::new(TraceBox(Box::new(BindableNamedLazyVal {168					this,169					super_obj,170171					cctx: self.cctx.clone(),172					name: self.name.clone(),173					value: self.value.clone(),174				}))))175			}176		}177178		(179			b.name.clone(),180			LazyBinding::Bindable(Cc::new(TraceBox(Box::new(BindableNamed {181				cctx,182				name: b.name.clone(),183				value: b.value.clone(),184			})))),185		)186	}187}188189pub fn evaluate_method(ctx: Context, name: IStr, params: ParamsDesc, body: LocExpr) -> Val {190	Val::Func(FuncVal::Normal(Cc::new(FuncDesc {191		name,192		ctx,193		params,194		body,195	})))196}197198pub fn evaluate_field_name(199	s: State,200	ctx: Context,201	field_name: &jrsonnet_parser::FieldName,202) -> Result<Option<IStr>> {203	Ok(match field_name {204		jrsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),205		jrsonnet_parser::FieldName::Dyn(expr) => s.push(206			CallLocation::new(&expr.1),207			|| "evaluating field name".to_string(),208			|| {209				let value = evaluate(s.clone(), ctx, expr)?;210				if matches!(value, Val::Null) {211					Ok(None)212				} else {213					Ok(Some(IStr::from_untyped(value, s.clone())?))214				}215			},216		)?,217	})218}219220pub fn evaluate_comp(221	s: State,222	ctx: Context,223	specs: &[CompSpec],224	callback: &mut impl FnMut(Context) -> Result<()>,225) -> Result<()> {226	match specs.get(0) {227		None => callback(ctx)?,228		Some(CompSpec::IfSpec(IfSpecData(cond))) => {229			if bool::from_untyped(evaluate(s.clone(), ctx.clone(), cond)?, s.clone())? {230				evaluate_comp(s, ctx, &specs[1..], callback)?231			}232		}233		Some(CompSpec::ForSpec(ForSpecData(var, expr))) => {234			match evaluate(s.clone(), ctx.clone(), expr)? {235				Val::Arr(list) => {236					for item in list.iter(s.clone()) {237						evaluate_comp(238							s.clone(),239							ctx.clone().with_var(var.clone(), item?.clone()),240							&specs[1..],241							callback,242						)?243					}244				}245				_ => throw!(InComprehensionCanOnlyIterateOverArray),246			}247		}248	}249	Ok(())250}251252pub fn evaluate_member_list_object(s: State, ctx: Context, members: &[Member]) -> Result<ObjValue> {253	let new_bindings = FutureWrapper::new();254	let future_this = FutureWrapper::new();255	let cctx = ContextCreator(ctx.clone(), new_bindings.clone());256	{257		let mut bindings: GcHashMap<IStr, LazyBinding> = GcHashMap::with_capacity(members.len());258		for (n, b) in members259			.iter()260			.filter_map(|m| match m {261				Member::BindStmt(b) => Some(b.clone()),262				_ => None,263			})264			.map(|b| evaluate_binding(&b, cctx.clone()))265		{266			bindings.insert(n, b);267		}268		new_bindings.fill(bindings);269	}270271	let mut builder = ObjValueBuilder::new();272	for member in members.iter() {273		match member {274			Member::Field(FieldMember {275				name,276				plus,277				params: None,278				visibility,279				value,280			}) => {281				let name = evaluate_field_name(s.clone(), ctx.clone(), name)?;282				if name.is_none() {283					continue;284				}285				let name = name.unwrap();286287				#[derive(Trace)]288				struct ObjMemberBinding {289					cctx: ContextCreator,290					value: LocExpr,291					name: IStr,292				}293				impl Bindable for ObjMemberBinding {294					fn bind(295						&self,296						s: State,297						this: Option<ObjValue>,298						super_obj: Option<ObjValue>,299					) -> Result<LazyVal> {300						Ok(LazyVal::new_resolved(evaluate_named(301							s.clone(),302							self.cctx.create(s, this, super_obj)?,303							&self.value,304							self.name.clone(),305						)?))306					}307				}308				builder309					.member(name.clone())310					.with_add(*plus)311					.with_visibility(*visibility)312					.with_location(value.1.clone())313					.bindable(314						s.clone(),315						TraceBox(Box::new(ObjMemberBinding {316							cctx: cctx.clone(),317							value: value.clone(),318							name,319						})),320					)?;321			}322			Member::Field(FieldMember {323				name,324				params: Some(params),325				value,326				..327			}) => {328				let name = evaluate_field_name(s.clone(), ctx.clone(), name)?;329				if name.is_none() {330					continue;331				}332				let name = name.unwrap();333				#[derive(Trace)]334				struct ObjMemberBinding {335					cctx: ContextCreator,336					value: LocExpr,337					params: ParamsDesc,338					name: IStr,339				}340				impl Bindable for ObjMemberBinding {341					fn bind(342						&self,343						s: State,344						this: Option<ObjValue>,345						super_obj: Option<ObjValue>,346					) -> Result<LazyVal> {347						Ok(LazyVal::new_resolved(evaluate_method(348							self.cctx.create(s, this, super_obj)?,349							self.name.clone(),350							self.params.clone(),351							self.value.clone(),352						)))353					}354				}355				builder356					.member(name.clone())357					.hide()358					.with_location(value.1.clone())359					.bindable(360						s.clone(),361						TraceBox(Box::new(ObjMemberBinding {362							cctx: cctx.clone(),363							value: value.clone(),364							params: params.clone(),365							name,366						})),367					)?;368			}369			Member::BindStmt(_) => {}370			Member::AssertStmt(stmt) => {371				#[derive(Trace)]372				struct ObjectAssert {373					cctx: ContextCreator,374					assert: AssertStmt,375				}376				impl ObjectAssertion for ObjectAssert {377					fn run(378						&self,379						s: State,380						this: Option<ObjValue>,381						super_obj: Option<ObjValue>,382					) -> Result<()> {383						let ctx = self.cctx.create(s.clone(), this, super_obj)?;384						evaluate_assert(s, ctx, &self.assert)385					}386				}387				builder.assert(TraceBox(Box::new(ObjectAssert {388					cctx: cctx.clone(),389					assert: stmt.clone(),390				})));391			}392		}393	}394	let this = builder.build();395	future_this.fill(this.clone());396	Ok(this)397}398399pub fn evaluate_object(s: State, ctx: Context, object: &ObjBody) -> Result<ObjValue> {400	Ok(match object {401		ObjBody::MemberList(members) => evaluate_member_list_object(s, ctx, members)?,402		ObjBody::ObjComp(obj) => {403			let future_this = FutureWrapper::new();404			let mut builder = ObjValueBuilder::new();405			evaluate_comp(s.clone(), ctx, &obj.compspecs, &mut |ctx| {406				let new_bindings = FutureWrapper::new();407				let cctx = ContextCreator(ctx.clone(), new_bindings.clone());408				let mut bindings: GcHashMap<IStr, LazyBinding> =409					GcHashMap::with_capacity(obj.pre_locals.len() + obj.post_locals.len());410				for (n, b) in obj411					.pre_locals412					.iter()413					.chain(obj.post_locals.iter())414					.map(|b| evaluate_binding(b, cctx.clone()))415				{416					bindings.insert(n, b);417				}418				new_bindings.fill(bindings.clone());419				let ctx = ctx.extend_unbound(s.clone(), bindings, None, None, None)?;420				let key = evaluate(s.clone(), ctx.clone(), &obj.key)?;421422				match key {423					Val::Null => {}424					Val::Str(n) => {425						#[derive(Trace)]426						struct ObjCompBinding {427							ctx: Context,428							value: LocExpr,429						}430						impl Bindable for ObjCompBinding {431							fn bind(432								&self,433								s: State,434								this: Option<ObjValue>,435								_super_obj: Option<ObjValue>,436							) -> Result<LazyVal> {437								Ok(LazyVal::new_resolved(evaluate(438									s,439									self.ctx.clone().extend(GcHashMap::new(), None, this, None),440									&self.value,441								)?))442							}443						}444						builder445							.member(n)446							.with_location(obj.value.1.clone())447							.with_add(obj.plus)448							.bindable(449								s.clone(),450								TraceBox(Box::new(ObjCompBinding {451									ctx,452									value: obj.value.clone(),453								})),454							)?;455					}456					v => throw!(FieldMustBeStringGot(v.value_type())),457				}458459				Ok(())460			})?;461462			let this = builder.build();463			future_this.fill(this.clone());464			this465		}466	})467}468469pub fn evaluate_apply(470	s: State,471	ctx: Context,472	value: &LocExpr,473	args: &ArgsDesc,474	loc: CallLocation,475	tailstrict: bool,476) -> Result<Val> {477	let value = evaluate(s.clone(), ctx.clone(), value)?;478	Ok(match value {479		Val::Func(f) => {480			let body = || f.evaluate(s.clone(), ctx, loc, args, tailstrict);481			if tailstrict {482				body()?483			} else {484				s.push(loc, || format!("function <{}> call", f.name()), body)?485			}486		}487		v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type())),488	})489}490491pub fn evaluate_assert(s: State, ctx: Context, assertion: &AssertStmt) -> Result<()> {492	let value = &assertion.0;493	let msg = &assertion.1;494	let assertion_result = s.push(495		CallLocation::new(&value.1),496		|| "assertion condition".to_owned(),497		|| bool::from_untyped(evaluate(s.clone(), ctx.clone(), value)?, s.clone()),498	)?;499	if !assertion_result {500		s.push(501			CallLocation::new(&value.1),502			|| "assertion failure".to_owned(),503			|| {504				if let Some(msg) = msg {505					throw!(AssertionFailed(506						evaluate(s.clone(), ctx, msg)?.to_string(s.clone())?507					));508				} else {509					throw!(AssertionFailed(Val::Null.to_string(s.clone())?));510				}511			},512		)?513	}514	Ok(())515}516517pub fn evaluate_named(s: State, ctx: Context, lexpr: &LocExpr, name: IStr) -> Result<Val> {518	use Expr::*;519	let LocExpr(expr, _loc) = lexpr;520	Ok(match &**expr {521		Function(params, body) => evaluate_method(ctx, name, params.clone(), body.clone()),522		_ => evaluate(s, ctx, lexpr)?,523	})524}525526pub fn evaluate(s: State, ctx: Context, expr: &LocExpr) -> Result<Val> {527	use Expr::*;528	let LocExpr(expr, loc) = expr;529	// let bp = with_state(|s| s.0.stop_at.borrow().clone());530	Ok(match &**expr {531		Literal(LiteralType::This) => {532			Val::Obj(ctx.this().clone().ok_or(CantUseSelfOutsideOfObject)?)533		}534		Literal(LiteralType::Super) => Val::Obj(535			ctx.super_obj()536				.clone()537				.ok_or(NoSuperFound)?538				.with_this(ctx.this().clone().unwrap()),539		),540		Literal(LiteralType::Dollar) => {541			Val::Obj(ctx.dollar().clone().ok_or(NoTopLevelObjectFound)?)542		}543		Literal(LiteralType::True) => Val::Bool(true),544		Literal(LiteralType::False) => Val::Bool(false),545		Literal(LiteralType::Null) => Val::Null,546		Parened(e) => evaluate(s, ctx, e)?,547		Str(v) => Val::Str(v.clone()),548		Num(v) => Val::new_checked_num(*v)?,549		BinaryOp(v1, o, v2) => evaluate_binary_op_special(s, ctx, v1, *o, v2)?,550		UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(s, ctx, v)?)?,551		Var(name) => s.push(552			CallLocation::new(loc),553			|| format!("variable <{}> access", name),554			|| ctx.binding(name.clone())?.evaluate(s.clone()),555		)?,556		Index(value, index) => {557			match (558				evaluate(s.clone(), ctx.clone(), value)?,559				evaluate(s.clone(), ctx, index)?,560			) {561				(Val::Obj(v), Val::Str(key)) => s.push(562					CallLocation::new(loc),563					|| format!("field <{}> access", key),564					|| {565						if let Some(v) = v.get(s.clone(), key.clone())? {566							Ok(v)567						} else {568							throw!(NoSuchField(key.clone()))569						}570					},571				)?,572				(Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot(573					ValType::Obj,574					ValType::Str,575					n.value_type(),576				)),577578				(Val::Arr(v), Val::Num(n)) => {579					if n.fract() > f64::EPSILON {580						throw!(FractionalIndex)581					}582					v.get(s, n as usize)?583						.ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?584				}585				(Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)),586				(Val::Arr(_), n) => throw!(ValueIndexMustBeTypeGot(587					ValType::Arr,588					ValType::Num,589					n.value_type(),590				)),591592				(Val::Str(s), Val::Num(n)) => Val::Str({593					let v: IStr = s594						.chars()595						.skip(n as usize)596						.take(1)597						.collect::<String>()598						.into();599					if v.is_empty() {600						let size = s.chars().count();601						throw!(StringBoundsError(n as usize, size))602					}603					v604				}),605				(Val::Str(_), n) => throw!(ValueIndexMustBeTypeGot(606					ValType::Str,607					ValType::Num,608					n.value_type(),609				)),610611				(v, _) => throw!(CantIndexInto(v.value_type())),612			}613		}614		LocalExpr(bindings, returned) => {615			let mut new_bindings: GcHashMap<IStr, LazyVal> =616				GcHashMap::with_capacity(bindings.len());617			let fctx = Context::new_future();618			for b in bindings {619				new_bindings.insert(b.name.clone(), evaluate_binding_in_future(b, fctx.clone()));620			}621			let ctx = ctx.extend_bound(new_bindings).into_future(fctx);622			evaluate(s, ctx, &returned.clone())?623		}624		Arr(items) => {625			let mut out = Vec::with_capacity(items.len());626			for item in items {627				// TODO: Implement ArrValue::Lazy with same context for every element?628				#[derive(Trace)]629				struct ArrayElement {630					ctx: Context,631					item: LocExpr,632				}633				impl LazyValValue for ArrayElement {634					fn get(self: Box<Self>, s: State) -> Result<Val> {635						evaluate(s, self.ctx, &self.item)636					}637				}638				out.push(LazyVal::new(TraceBox(Box::new(ArrayElement {639					ctx: ctx.clone(),640					item: item.clone(),641				}))));642			}643			Val::Arr(out.into())644		}645		ArrComp(expr, comp_specs) => {646			let mut out = Vec::new();647			evaluate_comp(s.clone(), ctx, comp_specs, &mut |ctx| {648				out.push(evaluate(s.clone(), ctx, expr)?);649				Ok(())650			})?;651			Val::Arr(ArrValue::Eager(Cc::new(out)))652		}653		Obj(body) => Val::Obj(evaluate_object(s, ctx, body)?),654		ObjExtend(a, b) => evaluate_add_op(655			s.clone(),656			&evaluate(s.clone(), ctx.clone(), a)?,657			&Val::Obj(evaluate_object(s, ctx, b)?),658		)?,659		Apply(value, args, tailstrict) => {660			evaluate_apply(s, ctx, value, args, CallLocation::new(loc), *tailstrict)?661		}662		Function(params, body) => {663			evaluate_method(ctx, "anonymous".into(), params.clone(), body.clone())664		}665		Intrinsic(name) => Val::Func(FuncVal::StaticBuiltin(666			BUILTINS667				.with(|b| b.get(name).copied())668				.ok_or_else(|| IntrinsicNotFound(name.clone()))?,669		)),670		AssertExpr(assert, returned) => {671			evaluate_assert(s.clone(), ctx.clone(), assert)?;672			evaluate(s, ctx, returned)?673		}674		ErrorStmt(e) => s.push(675			CallLocation::new(loc),676			|| "error statement".to_owned(),677			|| {678				throw!(RuntimeError(679					evaluate(s.clone(), ctx, e)?.to_string(s.clone())?,680				))681			},682		)?,683		IfElse {684			cond,685			cond_then,686			cond_else,687		} => {688			if s.push(689				CallLocation::new(loc),690				|| "if condition".to_owned(),691				|| bool::from_untyped(evaluate(s.clone(), ctx.clone(), &cond.0)?, s.clone()),692			)? {693				evaluate(s, ctx, cond_then)?694			} else {695				match cond_else {696					Some(v) => evaluate(s, ctx, v)?,697					None => Val::Null,698				}699			}700		}701		Slice(value, desc) => {702			let indexable = evaluate(s.clone(), ctx.clone(), value)?;703			let loc = CallLocation::new(loc);704705			fn parse_idx<T: Typed>(706				loc: CallLocation,707				s: State,708				ctx: &Context,709				expr: &Option<LocExpr>,710				desc: &'static str,711			) -> Result<Option<T>> {712				if let Some(value) = expr {713					Ok(Some(s.push(714						loc,715						|| format!("slice {}", desc),716						|| T::from_untyped(evaluate(s.clone(), ctx.clone(), value)?, s.clone()),717					)?))718				} else {719					Ok(None)720				}721			}722723			let start = parse_idx(loc, s.clone(), &ctx, &desc.start, "start")?;724			let end = parse_idx(loc, s.clone(), &ctx, &desc.end, "end")?;725			let step = parse_idx(loc, s, &ctx, &desc.step, "step")?;726727			std_slice(indexable.into_indexable()?, start, end, step)?728		}729		Import(path) => {730			let tmp = loc.clone().0;731			let mut import_location = tmp.to_path_buf();732			import_location.pop();733			s.push(734				CallLocation::new(loc),735				|| format!("import {:?}", path),736				|| s.import_file(&import_location, path),737			)?738		}739		ImportStr(path) => {740			let tmp = loc.clone().0;741			let mut import_location = tmp.to_path_buf();742			import_location.pop();743			Val::Str(s.import_file_str(&import_location, path)?)744		}745		ImportBin(path) => {746			let tmp = loc.clone().0;747			let mut import_location = tmp.to_path_buf();748			import_location.pop();749			let bytes = s.import_file_bin(&import_location, path)?;750			Val::Arr(ArrValue::Bytes(bytes))751		}752	})753}