git.delta.rocks / jrsonnet / refs/commits / b2f480a687a1

difftreelog

style rename bindable -> unbound

Yaroslav Bolyukin2022-04-24parent: #c65967c.patch.diff
in: master

4 files changed

modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -3,8 +3,8 @@
 use gcmodule::{Cc, Trace};
 use jrsonnet_interner::IStr;
 use jrsonnet_parser::{
-	ArgsDesc, AssertStmt, BindSpec, CompSpec, Expr, FieldMember, ForSpecData, IfSpecData,
-	LiteralType, LocExpr, Member, ObjBody, ParamsDesc,
+	ArgsDesc, AssertStmt, BindSpec, CompSpec, Expr, FieldMember, FieldName, ForSpecData,
+	IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc,
 };
 use jrsonnet_types::ValType;
 
@@ -16,9 +16,9 @@
 	stdlib::{std_slice, BUILTINS},
 	tb, throw,
 	typed::Typed,
-	val::{ArrValue, CachedBindable, Thunk, ThunkValue},
-	Bindable, Context, GcHashMap, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result,
-	State, Val,
+	val::{ArrValue, CachedUnbound, Thunk, ThunkValue},
+	Context, GcHashMap, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result, State,
+	Unbound, Val,
 };
 pub mod destructure;
 pub mod operator;
@@ -32,14 +32,10 @@
 	})))
 }
 
-pub fn evaluate_field_name(
-	s: State,
-	ctx: Context,
-	field_name: &jrsonnet_parser::FieldName,
-) -> Result<Option<IStr>> {
+pub fn evaluate_field_name(s: State, ctx: Context, field_name: &FieldName) -> Result<Option<IStr>> {
 	Ok(match field_name {
-		jrsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),
-		jrsonnet_parser::FieldName::Dyn(expr) => s.push(
+		FieldName::Fixed(n) => Some(n.clone()),
+		FieldName::Dyn(expr) => s.push(
 			CallLocation::new(&expr.1),
 			|| "evaluating field name".to_string(),
 			|| {
@@ -86,19 +82,19 @@
 	Ok(())
 }
 
-trait CloneableBindable<T>: Bindable<Bound = T> + Clone {}
+trait CloneableUnbound<T>: Unbound<Bound = T> + Clone {}
 
 fn evaluate_object_locals(
 	fctx: Pending<Context>,
 	locals: Rc<Vec<BindSpec>>,
-) -> impl CloneableBindable<Context> {
+) -> impl CloneableUnbound<Context> {
 	#[derive(Trace, Clone)]
-	struct WithObjectLocals {
+	struct UnboundLocals {
 		fctx: Pending<Context>,
 		locals: Rc<Vec<BindSpec>>,
 	}
-	impl CloneableBindable<Context> for WithObjectLocals {}
-	impl Bindable for WithObjectLocals {
+	impl CloneableUnbound<Context> for UnboundLocals {}
+	impl Unbound for UnboundLocals {
 		type Bound = Context;
 
 		fn bind(
@@ -124,7 +120,7 @@
 		}
 	}
 
-	WithObjectLocals { fctx, locals }
+	UnboundLocals { fctx, locals }
 }
 
 #[allow(clippy::too_many_lines)]
@@ -143,7 +139,7 @@
 	let fctx = Context::new_future();
 
 	// We have single context for all fields, so we can cache binds
-	let uctx = CachedBindable::new(evaluate_object_locals(fctx.clone(), locals));
+	let uctx = CachedUnbound::new(evaluate_object_locals(fctx.clone(), locals));
 
 	for member in members.iter() {
 		match member {
@@ -155,12 +151,12 @@
 				value,
 			}) => {
 				#[derive(Trace)]
-				struct ObjMemberBinding<B> {
+				struct UnboundValue<B> {
 					uctx: B,
 					value: LocExpr,
 					name: IStr,
 				}
-				impl<B: Bindable<Bound = Context>> Bindable for ObjMemberBinding<B> {
+				impl<B: Unbound<Bound = Context>> Unbound for UnboundValue<B> {
 					type Bound = Thunk<Val>;
 					fn bind(
 						&self,
@@ -191,7 +187,7 @@
 					.with_location(value.1.clone())
 					.bindable(
 						s.clone(),
-						tb!(ObjMemberBinding {
+						tb!(UnboundValue {
 							uctx: uctx.clone(),
 							value: value.clone(),
 							name: name.clone()
@@ -205,13 +201,13 @@
 				..
 			}) => {
 				#[derive(Trace)]
-				struct ObjMemberBinding<B> {
+				struct UnboundMethod<B> {
 					uctx: B,
 					value: LocExpr,
 					params: ParamsDesc,
 					name: IStr,
 				}
-				impl<B: Bindable<Bound = Context>> Bindable for ObjMemberBinding<B> {
+				impl<B: Unbound<Bound = Context>> Unbound for UnboundMethod<B> {
 					type Bound = Thunk<Val>;
 					fn bind(
 						&self,
@@ -240,7 +236,7 @@
 					.with_location(value.1.clone())
 					.bindable(
 						s.clone(),
-						tb!(ObjMemberBinding {
+						tb!(UnboundMethod {
 							uctx: uctx.clone(),
 							value: value.clone(),
 							params: params.clone(),
@@ -255,7 +251,7 @@
 					uctx: B,
 					assert: AssertStmt,
 				}
-				impl<B: Bindable<Bound = Context>> ObjectAssertion for ObjectAssert<B> {
+				impl<B: Unbound<Bound = Context>> ObjectAssertion for ObjectAssert<B> {
 					fn run(
 						&self,
 						s: State,
@@ -303,11 +299,11 @@
 					Val::Null => {}
 					Val::Str(n) => {
 						#[derive(Trace)]
-						struct ObjCompBinding<B> {
+						struct UnboundValue<B> {
 							uctx: B,
 							value: LocExpr,
 						}
-						impl<B: Bindable<Bound = Context>> Bindable for ObjCompBinding<B> {
+						impl<B: Unbound<Bound = Context>> Unbound for UnboundValue<B> {
 							type Bound = Thunk<Val>;
 							fn bind(
 								&self,
@@ -333,7 +329,7 @@
 							.with_add(obj.plus)
 							.bindable(
 								s.clone(),
-								tb!(ObjCompBinding {
+								tb!(UnboundValue {
 									uctx,
 									value: obj.value.clone(),
 								}),
modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/lib.rs
1#![warn(clippy::all, clippy::nursery, clippy::pedantic)]2#![allow(3	macro_expanded_macro_exports_accessed_by_absolute_paths,4	clippy::ptr_arg,5	// Too verbose6	clippy::must_use_candidate,7	// A lot of functions pass around errors thrown by code8	clippy::missing_errors_doc,9	// A lot of pointers have interior Rc10	clippy::needless_pass_by_value,11	// Its fine12	clippy::wildcard_imports,13	clippy::enum_glob_use,14	clippy::module_name_repetitions,15	// TODO: fix individual issues, however this works as intended almost everywhere16	clippy::cast_precision_loss,17	clippy::cast_possible_wrap,18	clippy::cast_possible_truncation,19	clippy::cast_sign_loss,20)]2122// For jrsonnet-macros23extern crate self as jrsonnet_evaluator;2425mod ctx;26mod dynamic;27pub mod error;28mod evaluate;29pub mod function;30pub mod gc;31mod import;32mod integrations;33mod map;34mod obj;35mod stdlib;36pub mod trace;37pub mod typed;38pub mod val;3940use std::{41	cell::{Ref, RefCell, RefMut},42	collections::HashMap,43	fmt::{self, Debug},44	path::{Path, PathBuf},45	rc::Rc,46};4748pub use ctx::*;49pub use dynamic::*;50use error::{Error::*, LocError, Result, StackTraceElement};51pub use evaluate::*;52use function::{builtin::Builtin, CallLocation, TlaArg};53use gc::{GcHashMap, TraceBox};54use gcmodule::{Cc, Trace, Weak};55pub use import::*;56pub use jrsonnet_interner::IStr;57pub use jrsonnet_parser as parser;58use jrsonnet_parser::*;59pub use obj::*;60use trace::{location_to_offset, offset_to_location, CodeLocation, CompactFormat, TraceFormat};61pub use val::{ManifestFormat, Thunk, Val};6263pub trait Bindable: Trace + 'static {64	type Bound;65	fn bind(&self, s: State, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<Self::Bound>;66}6768#[derive(Clone, Trace)]69pub enum LazyBinding {70	Bindable(Cc<TraceBox<dyn Bindable<Bound = Thunk<Val>>>>),71	Bound(Thunk<Val>),72}7374impl Debug for LazyBinding {75	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {76		write!(f, "LazyBinding")77	}78}79impl LazyBinding {80	pub fn evaluate(81		&self,82		s: State,83		sup: Option<ObjValue>,84		this: Option<ObjValue>,85	) -> Result<Thunk<Val>> {86		match self {87			Self::Bindable(v) => v.bind(s, sup, this),88			Self::Bound(v) => Ok(v.clone()),89		}90	}91}9293pub struct EvaluationSettings {94	/// Limits recursion by limiting the number of stack frames95	pub max_stack: usize,96	/// Limits amount of stack trace items preserved97	pub max_trace: usize,98	/// Used for s`td.extVar`99	pub ext_vars: HashMap<IStr, Val>,100	/// Used for ext.native101	pub ext_natives: HashMap<IStr, Cc<TraceBox<dyn Builtin>>>,102	/// TLA vars103	pub tla_vars: HashMap<IStr, TlaArg>,104	/// Global variables are inserted in default context105	pub globals: HashMap<IStr, Val>,106	/// Used to resolve file locations/contents107	pub import_resolver: Box<dyn ImportResolver>,108	/// Used in manifestification functions109	pub manifest_format: ManifestFormat,110	/// Used for bindings111	pub trace_format: Box<dyn TraceFormat>,112}113impl Default for EvaluationSettings {114	fn default() -> Self {115		Self {116			max_stack: 200,117			max_trace: 20,118			globals: HashMap::default(),119			ext_vars: HashMap::default(),120			ext_natives: HashMap::default(),121			tla_vars: HashMap::default(),122			import_resolver: Box::new(DummyImportResolver),123			manifest_format: ManifestFormat::Json {124				padding: 4,125				#[cfg(feature = "exp-preserve-order")]126				preserve_order: false,127			},128			trace_format: Box::new(CompactFormat {129				padding: 4,130				resolver: trace::PathResolver::Absolute,131			}),132		}133	}134}135136#[derive(Default)]137struct EvaluationData {138	/// Used for stack overflow detection, stacktrace is populated on unwind139	stack_depth: usize,140	/// Updated every time stack entry is popt141	stack_generation: usize,142143	breakpoints: Breakpoints,144	/// Contains file source codes and evaluation results for imports and pretty-printed stacktraces145	files: GcHashMap<Rc<Path>, FileData>,146	str_files: GcHashMap<Rc<Path>, IStr>,147	bin_files: GcHashMap<Rc<Path>, Rc<[u8]>>,148}149150pub struct FileData {151	source_code: IStr,152	parsed: LocExpr,153	evaluated: Option<Val>,154}155156#[allow(clippy::type_complexity)]157pub struct Breakpoint {158	loc: ExprLocation,159	collected: RefCell<HashMap<usize, (usize, Vec<Result<Val>>)>>,160}161#[derive(Default)]162struct Breakpoints(Vec<Rc<Breakpoint>>);163impl Breakpoints {164	fn insert(165		&self,166		stack_depth: usize,167		stack_generation: usize,168		loc: &ExprLocation,169		result: Result<Val>,170	) -> Result<Val> {171		if self.0.is_empty() {172			return result;173		}174		for item in &self.0 {175			if item.loc.belongs_to(loc) {176				let mut collected = item.collected.borrow_mut();177				let (depth, vals) = collected.entry(stack_generation).or_default();178				if stack_depth > *depth {179					vals.clear();180				}181				vals.push(result.clone());182			}183		}184		result185	}186}187188#[derive(Default)]189pub struct EvaluationStateInternals {190	/// Internal state191	data: RefCell<EvaluationData>,192	/// Settings, safe to change at runtime193	settings: RefCell<EvaluationSettings>,194}195196/// Maintains stack trace and import resolution197#[derive(Default, Clone)]198pub struct State(Rc<EvaluationStateInternals>);199200impl State {201	/// Parses and adds file as loaded202	pub fn add_file(&self, path: Rc<Path>, source_code: IStr) -> Result<LocExpr> {203		let parsed = parse(204			&source_code,205			&ParserSettings {206				file_name: path.clone(),207			},208		)209		.map_err(|error| ImportSyntaxError {210			error: Box::new(error),211			path: path.clone(),212			source_code: source_code.clone(),213		})?;214		self.add_parsed_file(path, source_code, parsed.clone())?;215216		Ok(parsed)217	}218219	pub fn reset_evaluation_state(&self, name: &Path) {220		self.data_mut()221			.files222			.get_mut(name)223			.expect("file not found")224			.evaluated225			.take();226	}227228	/// Adds file by source code and parsed expr229	pub fn add_parsed_file(230		&self,231		name: Rc<Path>,232		source_code: IStr,233		parsed: LocExpr,234	) -> Result<()> {235		self.data_mut().files.insert(236			name,237			FileData {238				source_code,239				parsed,240				evaluated: None,241			},242		);243244		Ok(())245	}246	pub fn get_source(&self, name: &Path) -> Option<IStr> {247		let ro_map = &self.data().files;248		ro_map.get(name).map(|value| value.source_code.clone())249	}250	pub fn map_source_locations(&self, file: &Path, locs: &[usize]) -> Vec<CodeLocation> {251		offset_to_location(&self.get_source(file).unwrap_or_else(|| "".into()), locs)252	}253	pub fn map_from_source_location(254		&self,255		file: &Path,256		line: usize,257		column: usize,258	) -> Option<usize> {259		location_to_offset(260			&self.get_source(file).expect("file not found"),261			line,262			column,263		)264	}265	pub fn import_file(&self, from: &Path, path: &Path) -> Result<Val> {266		let file_path = self.resolve_file(from, path)?;267		{268			let data = self.data();269			let files = &data.files;270			if files.contains_key(&file_path as &Path) {271				drop(data);272				return self.evaluate_loaded_file_raw(&file_path);273			}274		}275		let contents = self.load_file_str(&file_path)?;276		self.add_file(file_path.clone(), contents)?;277		self.evaluate_loaded_file_raw(&file_path)278	}279	pub(crate) fn import_file_str(&self, from: &Path, path: &Path) -> Result<IStr> {280		let path = self.resolve_file(from, path)?;281		if !self.data().str_files.contains_key(&path) {282			let file_str = self.load_file_str(&path)?;283			self.data_mut().str_files.insert(path.clone(), file_str);284		}285		Ok(self.data().str_files.get(&path).cloned().unwrap())286	}287	pub(crate) fn import_file_bin(&self, from: &Path, path: &Path) -> Result<Rc<[u8]>> {288		let path = self.resolve_file(from, path)?;289		if !self.data().bin_files.contains_key(&path) {290			let file_bin = self.load_file_bin(&path)?;291			self.data_mut().bin_files.insert(path.clone(), file_bin);292		}293		Ok(self.data().bin_files.get(&path).cloned().unwrap())294	}295296	fn evaluate_loaded_file_raw(&self, name: &Path) -> Result<Val> {297		let expr: LocExpr = {298			let ro_map = &self.data().files;299			let value = ro_map300				.get(name)301				.unwrap_or_else(|| panic!("file not added: {:?}", name));302			if let Some(ref evaluated) = value.evaluated {303				return Ok(evaluated.clone());304			}305			value.parsed.clone()306		};307		let value = evaluate(self.clone(), self.create_default_context(), &expr)?;308		{309			self.data_mut()310				.files311				.get_mut(name)312				.unwrap()313				.evaluated314				.replace(value.clone());315		}316		Ok(value)317	}318319	/// Adds standard library global variable (std) to this evaluator320	pub fn with_stdlib(&self) -> &Self {321		use jrsonnet_stdlib::STDLIB_STR;322		let std_path: Rc<Path> = PathBuf::from("std.jsonnet").into();323324		self.add_parsed_file(325			std_path.clone(),326			STDLIB_STR.to_owned().into(),327			stdlib::get_parsed_stdlib(),328		)329		.expect("stdlib is correct");330		let val = self331			.evaluate_loaded_file_raw(&std_path)332			.expect("stdlib is correct");333		self.settings_mut().globals.insert("std".into(), val);334		self335	}336337	/// Creates context with all passed global variables338	pub fn create_default_context(&self) -> Context {339		let globals = &self.settings().globals;340		let mut new_bindings = GcHashMap::with_capacity(globals.len());341		for (name, value) in globals.iter() {342			new_bindings.insert(name.clone(), Thunk::evaluated(value.clone()));343		}344		Context::new().extend(new_bindings, None, None, None)345	}346347	/// Executes code creating a new stack frame348	pub fn push<T>(349		&self,350		e: CallLocation,351		frame_desc: impl FnOnce() -> String,352		f: impl FnOnce() -> Result<T>,353	) -> Result<T> {354		{355			let mut data = self.data_mut();356			let stack_depth = &mut data.stack_depth;357			if *stack_depth > self.max_stack() {358				// Error creation uses data, so i drop guard here359				drop(data);360				throw!(StackOverflow);361			}362			*stack_depth += 1;363		}364		let result = f();365		{366			let mut data = self.data_mut();367			data.stack_depth -= 1;368			data.stack_generation += 1;369		}370		if let Err(mut err) = result {371			err.trace_mut().0.push(StackTraceElement {372				location: e.0.cloned(),373				desc: frame_desc(),374			});375			return Err(err);376		}377		result378	}379380	/// Executes code creating a new stack frame381	pub fn push_val(382		&self,383		e: &ExprLocation,384		frame_desc: impl FnOnce() -> String,385		f: impl FnOnce() -> Result<Val>,386	) -> Result<Val> {387		{388			let mut data = self.data_mut();389			let stack_depth = &mut data.stack_depth;390			if *stack_depth > self.max_stack() {391				// Error creation uses data, so i drop guard here392				drop(data);393				throw!(StackOverflow);394			}395			*stack_depth += 1;396		}397		let mut result = f();398		{399			let mut data = self.data_mut();400			data.stack_depth -= 1;401			data.stack_generation += 1;402			result = data403				.breakpoints404				.insert(data.stack_depth, data.stack_generation, e, result);405		}406		if let Err(mut err) = result {407			err.trace_mut().0.push(StackTraceElement {408				location: Some(e.clone()),409				desc: frame_desc(),410			});411			return Err(err);412		}413		result414	}415	/// Executes code creating a new stack frame416	pub fn push_description<T>(417		&self,418		frame_desc: impl FnOnce() -> String,419		f: impl FnOnce() -> Result<T>,420	) -> Result<T> {421		{422			let mut data = self.data_mut();423			let stack_depth = &mut data.stack_depth;424			if *stack_depth > self.max_stack() {425				// Error creation uses data, so i drop guard here426				drop(data);427				throw!(StackOverflow);428			}429			*stack_depth += 1;430		}431		let result = f();432		{433			let mut data = self.data_mut();434			data.stack_depth -= 1;435			data.stack_generation += 1;436		}437		if let Err(mut err) = result {438			err.trace_mut().0.push(StackTraceElement {439				location: None,440				desc: frame_desc(),441			});442			return Err(err);443		}444		result445	}446447	/// # Panics448	/// In case of formatting failure449	pub fn stringify_err(&self, e: &LocError) -> String {450		let mut out = String::new();451		self.settings()452			.trace_format453			.write_trace(&mut out, self, e)454			.unwrap();455		out456	}457458	pub fn manifest(&self, val: Val) -> Result<IStr> {459		self.push_description(460			|| "manifestification".to_string(),461			|| val.manifest(self.clone(), &self.manifest_format()),462		)463	}464	pub fn manifest_multi(&self, val: Val) -> Result<Vec<(IStr, IStr)>> {465		val.manifest_multi(self.clone(), &self.manifest_format())466	}467	pub fn manifest_stream(&self, val: Val) -> Result<Vec<IStr>> {468		val.manifest_stream(self.clone(), &self.manifest_format())469	}470471	/// If passed value is function then call with set TLA472	pub fn with_tla(&self, val: Val) -> Result<Val> {473		Ok(match val {474			Val::Func(func) => self.push_description(475				|| "during TLA call".to_owned(),476				|| {477					func.evaluate(478						self.clone(),479						self.create_default_context(),480						CallLocation::native(),481						&self.settings().tla_vars,482						true,483					)484				},485			)?,486			v => v,487		})488	}489}490491/// Internals492impl State {493	fn data(&self) -> Ref<EvaluationData> {494		self.0.data.borrow()495	}496	fn data_mut(&self) -> RefMut<EvaluationData> {497		self.0.data.borrow_mut()498	}499	pub fn settings(&self) -> Ref<EvaluationSettings> {500		self.0.settings.borrow()501	}502	pub fn settings_mut(&self) -> RefMut<EvaluationSettings> {503		self.0.settings.borrow_mut()504	}505}506507/// Raw methods evaluate passed values but don't perform TLA execution508impl State {509	pub fn evaluate_file_raw(&self, name: &Path) -> Result<Val> {510		self.import_file(&std::env::current_dir().expect("cwd"), name)511	}512	pub fn evaluate_file_raw_nocwd(&self, name: &Path) -> Result<Val> {513		self.import_file(&PathBuf::from("."), name)514	}515	/// Parses and evaluates the given snippet516	pub fn evaluate_snippet_raw(&self, source: Rc<Path>, code: IStr) -> Result<Val> {517		let parsed = parse(518			&code,519			&ParserSettings {520				file_name: source.clone(),521			},522		)523		.map_err(|e| ImportSyntaxError {524			path: source.clone(),525			source_code: code.clone(),526			error: Box::new(e),527		})?;528		self.add_parsed_file(source, code, parsed.clone())?;529		self.evaluate_expr_raw(parsed)530	}531	/// Evaluates the parsed expression532	pub fn evaluate_expr_raw(&self, code: LocExpr) -> Result<Val> {533		evaluate(self.clone(), self.create_default_context(), &code)534	}535}536537/// Settings utilities538impl State {539	pub fn add_ext_var(&self, name: IStr, value: Val) {540		self.settings_mut().ext_vars.insert(name, value);541	}542	pub fn add_ext_str(&self, name: IStr, value: IStr) {543		self.add_ext_var(name, Val::Str(value));544	}545	pub fn add_ext_code(&self, name: IStr, code: IStr) -> Result<()> {546		let value =547			self.evaluate_snippet_raw(PathBuf::from(format!("ext_code {}", name)).into(), code)?;548		self.add_ext_var(name, value);549		Ok(())550	}551552	pub fn add_tla(&self, name: IStr, value: Val) {553		self.settings_mut()554			.tla_vars555			.insert(name, TlaArg::Val(value));556	}557	pub fn add_tla_str(&self, name: IStr, value: IStr) {558		self.settings_mut()559			.tla_vars560			.insert(name, TlaArg::String(value));561	}562	pub fn add_tla_code(&self, name: IStr, code: IStr) -> Result<()> {563		let parsed = self.add_file(PathBuf::from(format!("tla_code {}", name)).into(), code)?;564		self.settings_mut()565			.tla_vars566			.insert(name, TlaArg::Code(parsed));567		Ok(())568	}569570	pub fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>> {571		self.settings().import_resolver.resolve_file(from, path)572	}573	pub fn load_file_str(&self, path: &Path) -> Result<IStr> {574		self.settings().import_resolver.load_file_str(path)575	}576	pub fn load_file_bin(&self, path: &Path) -> Result<Rc<[u8]>> {577		self.settings().import_resolver.load_file_bin(path)578	}579580	pub fn import_resolver(&self) -> Ref<dyn ImportResolver> {581		Ref::map(self.settings(), |s| &*s.import_resolver)582	}583	pub fn set_import_resolver(&self, resolver: Box<dyn ImportResolver>) {584		self.settings_mut().import_resolver = resolver;585	}586587	pub fn add_native(&self, name: IStr, cb: Cc<TraceBox<dyn Builtin>>) {588		self.settings_mut().ext_natives.insert(name, cb);589	}590591	pub fn manifest_format(&self) -> ManifestFormat {592		self.settings().manifest_format.clone()593	}594	pub fn set_manifest_format(&self, format: ManifestFormat) {595		self.settings_mut().manifest_format = format;596	}597598	pub fn trace_format(&self) -> Ref<dyn TraceFormat> {599		Ref::map(self.settings(), |s| &*s.trace_format)600	}601	pub fn set_trace_format(&self, format: Box<dyn TraceFormat>) {602		self.settings_mut().trace_format = format;603	}604605	pub fn max_trace(&self) -> usize {606		self.settings().max_trace607	}608	pub fn set_max_trace(&self, trace: usize) {609		self.settings_mut().max_trace = trace;610	}611612	pub fn max_stack(&self) -> usize {613		self.settings().max_stack614	}615	pub fn set_max_stack(&self, trace: usize) {616		self.settings_mut().max_stack = trace;617	}618}619620pub fn cc_ptr_eq<T>(a: &Cc<T>, b: &Cc<T>) -> bool {621	let a = a as &T;622	let b = b as &T;623	std::ptr::eq(a, b)624}625626fn weak_raw<T>(a: Weak<T>) -> *const () {627	unsafe { std::mem::transmute(a) }628}629fn weak_ptr_eq<T>(a: Weak<T>, b: Weak<T>) -> bool {630	std::ptr::eq(weak_raw(a), weak_raw(b))631}632633#[test]634fn weak_unsafe() {635	let a = Cc::new(1);636	let b = Cc::new(2);637638	let aw1 = a.clone().downgrade();639	let aw2 = a.clone().downgrade();640	let aw3 = a.clone().downgrade();641642	let bw = b.clone().downgrade();643644	assert!(weak_ptr_eq(aw1, aw2));645	assert!(!weak_ptr_eq(aw3, bw));646}
after · crates/jrsonnet-evaluator/src/lib.rs
1#![warn(clippy::all, clippy::nursery, clippy::pedantic)]2#![allow(3	macro_expanded_macro_exports_accessed_by_absolute_paths,4	clippy::ptr_arg,5	// Too verbose6	clippy::must_use_candidate,7	// A lot of functions pass around errors thrown by code8	clippy::missing_errors_doc,9	// A lot of pointers have interior Rc10	clippy::needless_pass_by_value,11	// Its fine12	clippy::wildcard_imports,13	clippy::enum_glob_use,14	clippy::module_name_repetitions,15	// TODO: fix individual issues, however this works as intended almost everywhere16	clippy::cast_precision_loss,17	clippy::cast_possible_wrap,18	clippy::cast_possible_truncation,19	clippy::cast_sign_loss,20)]2122// For jrsonnet-macros23extern crate self as jrsonnet_evaluator;2425mod ctx;26mod dynamic;27pub mod error;28mod evaluate;29pub mod function;30pub mod gc;31mod import;32mod integrations;33mod map;34mod obj;35mod stdlib;36pub mod trace;37pub mod typed;38pub mod val;3940use std::{41	cell::{Ref, RefCell, RefMut},42	collections::HashMap,43	fmt::{self, Debug},44	path::{Path, PathBuf},45	rc::Rc,46};4748pub use ctx::*;49pub use dynamic::*;50use error::{Error::*, LocError, Result, StackTraceElement};51pub use evaluate::*;52use function::{builtin::Builtin, CallLocation, TlaArg};53use gc::{GcHashMap, TraceBox};54use gcmodule::{Cc, Trace, Weak};55pub use import::*;56pub use jrsonnet_interner::IStr;57pub use jrsonnet_parser as parser;58use jrsonnet_parser::*;59pub use obj::*;60use trace::{location_to_offset, offset_to_location, CodeLocation, CompactFormat, TraceFormat};61pub use val::{ManifestFormat, Thunk, Val};6263pub trait Unbound: Trace {64	type Bound;65	fn bind(&self, s: State, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<Self::Bound>;66}6768#[derive(Clone, Trace)]69pub enum LazyBinding {70	Bindable(Cc<TraceBox<dyn Unbound<Bound = Thunk<Val>>>>),71	Bound(Thunk<Val>),72}7374impl Debug for LazyBinding {75	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {76		write!(f, "LazyBinding")77	}78}79impl LazyBinding {80	pub fn evaluate(81		&self,82		s: State,83		sup: Option<ObjValue>,84		this: Option<ObjValue>,85	) -> Result<Thunk<Val>> {86		match self {87			Self::Bindable(v) => v.bind(s, sup, this),88			Self::Bound(v) => Ok(v.clone()),89		}90	}91}9293pub struct EvaluationSettings {94	/// Limits recursion by limiting the number of stack frames95	pub max_stack: usize,96	/// Limits amount of stack trace items preserved97	pub max_trace: usize,98	/// Used for s`td.extVar`99	pub ext_vars: HashMap<IStr, Val>,100	/// Used for ext.native101	pub ext_natives: HashMap<IStr, Cc<TraceBox<dyn Builtin>>>,102	/// TLA vars103	pub tla_vars: HashMap<IStr, TlaArg>,104	/// Global variables are inserted in default context105	pub globals: HashMap<IStr, Val>,106	/// Used to resolve file locations/contents107	pub import_resolver: Box<dyn ImportResolver>,108	/// Used in manifestification functions109	pub manifest_format: ManifestFormat,110	/// Used for bindings111	pub trace_format: Box<dyn TraceFormat>,112}113impl Default for EvaluationSettings {114	fn default() -> Self {115		Self {116			max_stack: 200,117			max_trace: 20,118			globals: HashMap::default(),119			ext_vars: HashMap::default(),120			ext_natives: HashMap::default(),121			tla_vars: HashMap::default(),122			import_resolver: Box::new(DummyImportResolver),123			manifest_format: ManifestFormat::Json {124				padding: 4,125				#[cfg(feature = "exp-preserve-order")]126				preserve_order: false,127			},128			trace_format: Box::new(CompactFormat {129				padding: 4,130				resolver: trace::PathResolver::Absolute,131			}),132		}133	}134}135136#[derive(Default)]137struct EvaluationData {138	/// Used for stack overflow detection, stacktrace is populated on unwind139	stack_depth: usize,140	/// Updated every time stack entry is popt141	stack_generation: usize,142143	breakpoints: Breakpoints,144	/// Contains file source codes and evaluation results for imports and pretty-printed stacktraces145	files: GcHashMap<Rc<Path>, FileData>,146	str_files: GcHashMap<Rc<Path>, IStr>,147	bin_files: GcHashMap<Rc<Path>, Rc<[u8]>>,148}149150pub struct FileData {151	source_code: IStr,152	parsed: LocExpr,153	evaluated: Option<Val>,154}155156#[allow(clippy::type_complexity)]157pub struct Breakpoint {158	loc: ExprLocation,159	collected: RefCell<HashMap<usize, (usize, Vec<Result<Val>>)>>,160}161#[derive(Default)]162struct Breakpoints(Vec<Rc<Breakpoint>>);163impl Breakpoints {164	fn insert(165		&self,166		stack_depth: usize,167		stack_generation: usize,168		loc: &ExprLocation,169		result: Result<Val>,170	) -> Result<Val> {171		if self.0.is_empty() {172			return result;173		}174		for item in &self.0 {175			if item.loc.belongs_to(loc) {176				let mut collected = item.collected.borrow_mut();177				let (depth, vals) = collected.entry(stack_generation).or_default();178				if stack_depth > *depth {179					vals.clear();180				}181				vals.push(result.clone());182			}183		}184		result185	}186}187188#[derive(Default)]189pub struct EvaluationStateInternals {190	/// Internal state191	data: RefCell<EvaluationData>,192	/// Settings, safe to change at runtime193	settings: RefCell<EvaluationSettings>,194}195196/// Maintains stack trace and import resolution197#[derive(Default, Clone)]198pub struct State(Rc<EvaluationStateInternals>);199200impl State {201	/// Parses and adds file as loaded202	pub fn add_file(&self, path: Rc<Path>, source_code: IStr) -> Result<LocExpr> {203		let parsed = parse(204			&source_code,205			&ParserSettings {206				file_name: path.clone(),207			},208		)209		.map_err(|error| ImportSyntaxError {210			error: Box::new(error),211			path: path.clone(),212			source_code: source_code.clone(),213		})?;214		self.add_parsed_file(path, source_code, parsed.clone())?;215216		Ok(parsed)217	}218219	pub fn reset_evaluation_state(&self, name: &Path) {220		self.data_mut()221			.files222			.get_mut(name)223			.expect("file not found")224			.evaluated225			.take();226	}227228	/// Adds file by source code and parsed expr229	pub fn add_parsed_file(230		&self,231		name: Rc<Path>,232		source_code: IStr,233		parsed: LocExpr,234	) -> Result<()> {235		self.data_mut().files.insert(236			name,237			FileData {238				source_code,239				parsed,240				evaluated: None,241			},242		);243244		Ok(())245	}246	pub fn get_source(&self, name: &Path) -> Option<IStr> {247		let ro_map = &self.data().files;248		ro_map.get(name).map(|value| value.source_code.clone())249	}250	pub fn map_source_locations(&self, file: &Path, locs: &[usize]) -> Vec<CodeLocation> {251		offset_to_location(&self.get_source(file).unwrap_or_else(|| "".into()), locs)252	}253	pub fn map_from_source_location(254		&self,255		file: &Path,256		line: usize,257		column: usize,258	) -> Option<usize> {259		location_to_offset(260			&self.get_source(file).expect("file not found"),261			line,262			column,263		)264	}265	pub fn import_file(&self, from: &Path, path: &Path) -> Result<Val> {266		let file_path = self.resolve_file(from, path)?;267		{268			let data = self.data();269			let files = &data.files;270			if files.contains_key(&file_path as &Path) {271				drop(data);272				return self.evaluate_loaded_file_raw(&file_path);273			}274		}275		let contents = self.load_file_str(&file_path)?;276		self.add_file(file_path.clone(), contents)?;277		self.evaluate_loaded_file_raw(&file_path)278	}279	pub(crate) fn import_file_str(&self, from: &Path, path: &Path) -> Result<IStr> {280		let path = self.resolve_file(from, path)?;281		if !self.data().str_files.contains_key(&path) {282			let file_str = self.load_file_str(&path)?;283			self.data_mut().str_files.insert(path.clone(), file_str);284		}285		Ok(self.data().str_files.get(&path).cloned().unwrap())286	}287	pub(crate) fn import_file_bin(&self, from: &Path, path: &Path) -> Result<Rc<[u8]>> {288		let path = self.resolve_file(from, path)?;289		if !self.data().bin_files.contains_key(&path) {290			let file_bin = self.load_file_bin(&path)?;291			self.data_mut().bin_files.insert(path.clone(), file_bin);292		}293		Ok(self.data().bin_files.get(&path).cloned().unwrap())294	}295296	fn evaluate_loaded_file_raw(&self, name: &Path) -> Result<Val> {297		let expr: LocExpr = {298			let ro_map = &self.data().files;299			let value = ro_map300				.get(name)301				.unwrap_or_else(|| panic!("file not added: {:?}", name));302			if let Some(ref evaluated) = value.evaluated {303				return Ok(evaluated.clone());304			}305			value.parsed.clone()306		};307		let value = evaluate(self.clone(), self.create_default_context(), &expr)?;308		{309			self.data_mut()310				.files311				.get_mut(name)312				.unwrap()313				.evaluated314				.replace(value.clone());315		}316		Ok(value)317	}318319	/// Adds standard library global variable (std) to this evaluator320	pub fn with_stdlib(&self) -> &Self {321		use jrsonnet_stdlib::STDLIB_STR;322		let std_path: Rc<Path> = PathBuf::from("std.jsonnet").into();323324		self.add_parsed_file(325			std_path.clone(),326			STDLIB_STR.to_owned().into(),327			stdlib::get_parsed_stdlib(),328		)329		.expect("stdlib is correct");330		let val = self331			.evaluate_loaded_file_raw(&std_path)332			.expect("stdlib is correct");333		self.settings_mut().globals.insert("std".into(), val);334		self335	}336337	/// Creates context with all passed global variables338	pub fn create_default_context(&self) -> Context {339		let globals = &self.settings().globals;340		let mut new_bindings = GcHashMap::with_capacity(globals.len());341		for (name, value) in globals.iter() {342			new_bindings.insert(name.clone(), Thunk::evaluated(value.clone()));343		}344		Context::new().extend(new_bindings, None, None, None)345	}346347	/// Executes code creating a new stack frame348	pub fn push<T>(349		&self,350		e: CallLocation,351		frame_desc: impl FnOnce() -> String,352		f: impl FnOnce() -> Result<T>,353	) -> Result<T> {354		{355			let mut data = self.data_mut();356			let stack_depth = &mut data.stack_depth;357			if *stack_depth > self.max_stack() {358				// Error creation uses data, so i drop guard here359				drop(data);360				throw!(StackOverflow);361			}362			*stack_depth += 1;363		}364		let result = f();365		{366			let mut data = self.data_mut();367			data.stack_depth -= 1;368			data.stack_generation += 1;369		}370		if let Err(mut err) = result {371			err.trace_mut().0.push(StackTraceElement {372				location: e.0.cloned(),373				desc: frame_desc(),374			});375			return Err(err);376		}377		result378	}379380	/// Executes code creating a new stack frame381	pub fn push_val(382		&self,383		e: &ExprLocation,384		frame_desc: impl FnOnce() -> String,385		f: impl FnOnce() -> Result<Val>,386	) -> Result<Val> {387		{388			let mut data = self.data_mut();389			let stack_depth = &mut data.stack_depth;390			if *stack_depth > self.max_stack() {391				// Error creation uses data, so i drop guard here392				drop(data);393				throw!(StackOverflow);394			}395			*stack_depth += 1;396		}397		let mut result = f();398		{399			let mut data = self.data_mut();400			data.stack_depth -= 1;401			data.stack_generation += 1;402			result = data403				.breakpoints404				.insert(data.stack_depth, data.stack_generation, e, result);405		}406		if let Err(mut err) = result {407			err.trace_mut().0.push(StackTraceElement {408				location: Some(e.clone()),409				desc: frame_desc(),410			});411			return Err(err);412		}413		result414	}415	/// Executes code creating a new stack frame416	pub fn push_description<T>(417		&self,418		frame_desc: impl FnOnce() -> String,419		f: impl FnOnce() -> Result<T>,420	) -> Result<T> {421		{422			let mut data = self.data_mut();423			let stack_depth = &mut data.stack_depth;424			if *stack_depth > self.max_stack() {425				// Error creation uses data, so i drop guard here426				drop(data);427				throw!(StackOverflow);428			}429			*stack_depth += 1;430		}431		let result = f();432		{433			let mut data = self.data_mut();434			data.stack_depth -= 1;435			data.stack_generation += 1;436		}437		if let Err(mut err) = result {438			err.trace_mut().0.push(StackTraceElement {439				location: None,440				desc: frame_desc(),441			});442			return Err(err);443		}444		result445	}446447	/// # Panics448	/// In case of formatting failure449	pub fn stringify_err(&self, e: &LocError) -> String {450		let mut out = String::new();451		self.settings()452			.trace_format453			.write_trace(&mut out, self, e)454			.unwrap();455		out456	}457458	pub fn manifest(&self, val: Val) -> Result<IStr> {459		self.push_description(460			|| "manifestification".to_string(),461			|| val.manifest(self.clone(), &self.manifest_format()),462		)463	}464	pub fn manifest_multi(&self, val: Val) -> Result<Vec<(IStr, IStr)>> {465		val.manifest_multi(self.clone(), &self.manifest_format())466	}467	pub fn manifest_stream(&self, val: Val) -> Result<Vec<IStr>> {468		val.manifest_stream(self.clone(), &self.manifest_format())469	}470471	/// If passed value is function then call with set TLA472	pub fn with_tla(&self, val: Val) -> Result<Val> {473		Ok(match val {474			Val::Func(func) => self.push_description(475				|| "during TLA call".to_owned(),476				|| {477					func.evaluate(478						self.clone(),479						self.create_default_context(),480						CallLocation::native(),481						&self.settings().tla_vars,482						true,483					)484				},485			)?,486			v => v,487		})488	}489}490491/// Internals492impl State {493	fn data(&self) -> Ref<EvaluationData> {494		self.0.data.borrow()495	}496	fn data_mut(&self) -> RefMut<EvaluationData> {497		self.0.data.borrow_mut()498	}499	pub fn settings(&self) -> Ref<EvaluationSettings> {500		self.0.settings.borrow()501	}502	pub fn settings_mut(&self) -> RefMut<EvaluationSettings> {503		self.0.settings.borrow_mut()504	}505}506507/// Raw methods evaluate passed values but don't perform TLA execution508impl State {509	pub fn evaluate_file_raw(&self, name: &Path) -> Result<Val> {510		self.import_file(&std::env::current_dir().expect("cwd"), name)511	}512	pub fn evaluate_file_raw_nocwd(&self, name: &Path) -> Result<Val> {513		self.import_file(&PathBuf::from("."), name)514	}515	/// Parses and evaluates the given snippet516	pub fn evaluate_snippet_raw(&self, source: Rc<Path>, code: IStr) -> Result<Val> {517		let parsed = parse(518			&code,519			&ParserSettings {520				file_name: source.clone(),521			},522		)523		.map_err(|e| ImportSyntaxError {524			path: source.clone(),525			source_code: code.clone(),526			error: Box::new(e),527		})?;528		self.add_parsed_file(source, code, parsed.clone())?;529		self.evaluate_expr_raw(parsed)530	}531	/// Evaluates the parsed expression532	pub fn evaluate_expr_raw(&self, code: LocExpr) -> Result<Val> {533		evaluate(self.clone(), self.create_default_context(), &code)534	}535}536537/// Settings utilities538impl State {539	pub fn add_ext_var(&self, name: IStr, value: Val) {540		self.settings_mut().ext_vars.insert(name, value);541	}542	pub fn add_ext_str(&self, name: IStr, value: IStr) {543		self.add_ext_var(name, Val::Str(value));544	}545	pub fn add_ext_code(&self, name: IStr, code: IStr) -> Result<()> {546		let value =547			self.evaluate_snippet_raw(PathBuf::from(format!("ext_code {}", name)).into(), code)?;548		self.add_ext_var(name, value);549		Ok(())550	}551552	pub fn add_tla(&self, name: IStr, value: Val) {553		self.settings_mut()554			.tla_vars555			.insert(name, TlaArg::Val(value));556	}557	pub fn add_tla_str(&self, name: IStr, value: IStr) {558		self.settings_mut()559			.tla_vars560			.insert(name, TlaArg::String(value));561	}562	pub fn add_tla_code(&self, name: IStr, code: IStr) -> Result<()> {563		let parsed = self.add_file(PathBuf::from(format!("tla_code {}", name)).into(), code)?;564		self.settings_mut()565			.tla_vars566			.insert(name, TlaArg::Code(parsed));567		Ok(())568	}569570	pub fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>> {571		self.settings().import_resolver.resolve_file(from, path)572	}573	pub fn load_file_str(&self, path: &Path) -> Result<IStr> {574		self.settings().import_resolver.load_file_str(path)575	}576	pub fn load_file_bin(&self, path: &Path) -> Result<Rc<[u8]>> {577		self.settings().import_resolver.load_file_bin(path)578	}579580	pub fn import_resolver(&self) -> Ref<dyn ImportResolver> {581		Ref::map(self.settings(), |s| &*s.import_resolver)582	}583	pub fn set_import_resolver(&self, resolver: Box<dyn ImportResolver>) {584		self.settings_mut().import_resolver = resolver;585	}586587	pub fn add_native(&self, name: IStr, cb: Cc<TraceBox<dyn Builtin>>) {588		self.settings_mut().ext_natives.insert(name, cb);589	}590591	pub fn manifest_format(&self) -> ManifestFormat {592		self.settings().manifest_format.clone()593	}594	pub fn set_manifest_format(&self, format: ManifestFormat) {595		self.settings_mut().manifest_format = format;596	}597598	pub fn trace_format(&self) -> Ref<dyn TraceFormat> {599		Ref::map(self.settings(), |s| &*s.trace_format)600	}601	pub fn set_trace_format(&self, format: Box<dyn TraceFormat>) {602		self.settings_mut().trace_format = format;603	}604605	pub fn max_trace(&self) -> usize {606		self.settings().max_trace607	}608	pub fn set_max_trace(&self, trace: usize) {609		self.settings_mut().max_trace = trace;610	}611612	pub fn max_stack(&self) -> usize {613		self.settings().max_stack614	}615	pub fn set_max_stack(&self, trace: usize) {616		self.settings_mut().max_stack = trace;617	}618}619620pub fn cc_ptr_eq<T>(a: &Cc<T>, b: &Cc<T>) -> bool {621	let a = a as &T;622	let b = b as &T;623	std::ptr::eq(a, b)624}625626fn weak_raw<T>(a: Weak<T>) -> *const () {627	unsafe { std::mem::transmute(a) }628}629fn weak_ptr_eq<T>(a: Weak<T>, b: Weak<T>) -> bool {630	std::ptr::eq(weak_raw(a), weak_raw(b))631}632633#[test]634fn weak_unsafe() {635	let a = Cc::new(1);636	let b = Cc::new(2);637638	let aw1 = a.clone().downgrade();639	let aw2 = a.clone().downgrade();640	let aw3 = a.clone().downgrade();641642	let bw = b.clone().downgrade();643644	assert!(weak_ptr_eq(aw1, aw2));645	assert!(!weak_ptr_eq(aw3, bw));646}
modifiedcrates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/obj.rs
+++ b/crates/jrsonnet-evaluator/src/obj.rs
@@ -16,7 +16,7 @@
 	function::CallLocation,
 	gc::{GcHashMap, GcHashSet, TraceBox},
 	operator::evaluate_add_op,
-	throw, weak_ptr_eq, weak_raw, Bindable, LazyBinding, Result, State, Thunk, Val,
+	throw, weak_ptr_eq, weak_raw, LazyBinding, Result, State, Thunk, Unbound, Val,
 };
 
 #[cfg(not(feature = "exp-preserve-order"))]
@@ -589,7 +589,7 @@
 	pub fn bindable(
 		self,
 		s: State,
-		bindable: TraceBox<dyn Bindable<Bound = Thunk<Val>>>,
+		bindable: TraceBox<dyn Unbound<Bound = Thunk<Val>>>,
 	) -> Result<()> {
 		self.binding(s, LazyBinding::Bindable(Cc::new(bindable)))
 	}
@@ -613,7 +613,7 @@
 	pub fn value(self, value: Val) {
 		self.binding(LazyBinding::Bound(Thunk::evaluated(value)));
 	}
-	pub fn bindable(self, bindable: TraceBox<dyn Bindable<Bound = Thunk<Val>>>) {
+	pub fn bindable(self, bindable: TraceBox<dyn Unbound<Bound = Thunk<Val>>>) {
 		self.binding(LazyBinding::Bindable(Cc::new(bindable)));
 	}
 	pub fn binding(self, binding: LazyBinding) {
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -12,7 +12,7 @@
 	stdlib::manifest::{
 		manifest_json_ex, manifest_yaml_ex, ManifestJsonOptions, ManifestType, ManifestYamlOptions,
 	},
-	throw, Bindable, ObjValue, Result, State, WeakObjValue,
+	throw, ObjValue, Result, State, Unbound, WeakObjValue,
 };
 
 pub trait ThunkValue: Trace {
@@ -74,14 +74,14 @@
 type CacheKey = (Option<WeakObjValue>, Option<WeakObjValue>);
 
 #[derive(Trace, Clone)]
-pub struct CachedBindable<I, T>
+pub struct CachedUnbound<I, T>
 where
-	I: Bindable<Bound = T>,
+	I: Unbound<Bound = T>,
 {
 	cache: Cc<RefCell<GcHashMap<CacheKey, T>>>,
 	value: I,
 }
-impl<I: Bindable<Bound = T>, T: Trace> CachedBindable<I, T> {
+impl<I: Unbound<Bound = T>, T: Trace> CachedUnbound<I, T> {
 	pub fn new(value: I) -> Self {
 		Self {
 			cache: Cc::new(RefCell::new(GcHashMap::new())),
@@ -89,7 +89,7 @@
 		}
 	}
 }
-impl<I: Bindable<Bound = T>, T: Clone + Trace> Bindable for CachedBindable<I, T> {
+impl<I: Unbound<Bound = T>, T: Clone + Trace> Unbound for CachedUnbound<I, T> {
 	type Bound = T;
 	fn bind(&self, s: State, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<T> {
 		let cache_key = (