git.delta.rocks / jrsonnet / refs/commits / 150f28648ad3

difftreelog

refactor always use prepared calls

wulxrvxzYaroslav Bolyukin2026-04-04parent: #0f5b614.patch.diff
in: master

17 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
@@ -19,7 +19,7 @@
 	destructure::evaluate_dest,
 	error::{ErrorKind::*, suggest_object_fields},
 	evaluate::operator::{evaluate_binary_op_special, evaluate_unary_op},
-	function::{CallLocation, FuncDesc, FuncVal},
+	function::{CallLocation, FuncDesc, FuncVal, PreparedFuncVal},
 	gc::WithCapacityExt as _,
 	in_frame,
 	typed::{FromUntyped, IntoUntyped as _, Typed},
@@ -430,11 +430,25 @@
 	let value = evaluate(ctx.clone(), value)?;
 	Ok(match value {
 		Val::Func(f) => {
-			let body = || f.evaluate(ctx, loc, args, tailstrict);
+			let name = f.name();
+			let prepare = PreparedFuncVal::new(f, args.unnamed.len(), &args.names)?;
+			let unnamed = args
+				.unnamed
+				.iter()
+				.cloned()
+				.map(|un| evaluate_thunk(ctx.clone(), un, tailstrict))
+				.collect::<Result<Vec<_>>>()?;
+			let named = args
+				.values
+				.iter()
+				.cloned()
+				.map(|un| evaluate_thunk(ctx.clone(), un, tailstrict))
+				.collect::<Result<Vec<_>>>()?;
+			let body = || prepare.call(loc, &unnamed, &named);
 			if tailstrict {
 				body()?
 			} else {
-				in_frame(loc, || format!("function <{}> call", f.name()), body)?
+				in_frame(loc, || format!("function <{name}> call"), body)?
 			}
 		}
 		v => bail!(OnlyFunctionsCanBeCalledGot(v.value_type())),
@@ -479,6 +493,13 @@
 	})
 }
 
+pub fn evaluate_thunk(ctx: Context, expr: Rc<Expr>, tailstrict: bool) -> Result<Thunk<Val>> {
+	Ok(if tailstrict {
+		Thunk::evaluated(evaluate(ctx, &expr)?)
+	} else {
+		Thunk!(move || { evaluate(ctx, &expr) })
+	})
+}
 #[allow(clippy::too_many_lines)]
 pub fn evaluate(ctx: Context, expr: &Expr) -> Result<Val> {
 	use Expr::*;
modifiedcrates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/function/mod.rs
+++ b/crates/jrsonnet-evaluator/src/function/mod.rs
@@ -3,12 +3,12 @@
 use educe::Educe;
 use jrsonnet_gcmodule::{Cc, Trace};
 use jrsonnet_interner::IStr;
-use jrsonnet_ir::{ArgsDesc, Destruct, Expr, ExprParams, Span};
+use jrsonnet_ir::{Destruct, Expr, ExprParams, Span};
 pub use jrsonnet_macros::builtin;
 
 use self::{
 	builtin::Builtin,
-	parse::{parse_builtin_call, parse_default_function_call, parse_function_call},
+	parse::parse_default_function_call,
 	prepared::{PreparedCall, parse_prepared_builtin_call, parse_prepared_function_call},
 };
 use crate::{
@@ -22,7 +22,7 @@
 
 pub use jrsonnet_ir::function::*;
 pub use native::NativeFn;
-pub use prepared::PreparedFuncVal;
+pub(crate) use prepared::PreparedFuncVal;
 
 /// Function callsite location.
 /// Either from other jsonnet code, specified by expression location, or from native (without location).
@@ -77,16 +77,6 @@
 		parse_default_function_call(self.ctx.clone(), &self.params)
 	}
 
-	/// Create context, with which body code will run
-	pub(crate) fn call_body_context(
-		&self,
-		call_ctx: Context,
-		args: &ArgsDesc,
-		tailstrict: bool,
-	) -> Result<Context> {
-		parse_function_call(call_ctx, self.ctx.clone(), &self.params, args, tailstrict)
-	}
-
 	pub fn evaluate_trivial(&self) -> Option<Val> {
 		evaluate_trivial(&self.body)
 	}
@@ -137,27 +127,6 @@
 		match self {
 			Self::Normal(normal) => normal.name.clone(),
 			Self::Builtin(builtin) => builtin.name().into(),
-		}
-	}
-	/// Call function using arguments evaluated in specified `call_ctx` [`Context`].
-	///
-	/// If `tailstrict` is specified - then arguments will be evaluated before being passed to function body.
-	pub fn evaluate(
-		&self,
-		call_ctx: Context,
-		loc: CallLocation<'_>,
-		args: &ArgsDesc,
-		tailstrict: bool,
-	) -> Result<Val> {
-		match self {
-			Self::Normal(func) => {
-				let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?;
-				evaluate(body_ctx, &func.body)
-			}
-			Self::Builtin(b) => {
-				let args = parse_builtin_call(call_ctx, b.params(), args, tailstrict)?;
-				b.call(loc, &args)
-			}
 		}
 	}
 
modifiedcrates/jrsonnet-evaluator/src/function/native.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/function/native.rs
+++ b/crates/jrsonnet-evaluator/src/function/native.rs
@@ -39,12 +39,19 @@
 		impl<$($gen,)* O> FromUntyped for NativeFn<($($gen,)* O,)> {
 			fn from_untyped(untyped: Val) -> Result<Self> {
 				let func = FuncVal::from_untyped(untyped)?;
+				Self::try_from(func)
+			}
+		}
+		impl<$($gen,)* O> TryFrom<FuncVal> for NativeFn<($($gen,)* O,)> {
+			type Error = crate::Error;
+			fn try_from(v: FuncVal) -> Result<Self> {
 				Ok(Self(
-					PreparedFuncVal::new(func, $i, &[])?,
+					PreparedFuncVal::new(v, $i, &[])?,
 					PhantomData,
 				))
 			}
 		}
+
 	};
 	($i:expr; $($cur:ident)* @ $c:ident $($rest:ident)*) => {
 		impl_native_desc!($i; $($cur)*);
modifiedcrates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/function/parse.rs
1use std::rc::Rc;23use jrsonnet_ir::{4	ArgsDesc, Expr, ExprParams,5	function::{FunctionSignature, ParamName},6};7use rustc_hash::FxHashMap;89use crate::{10	Context, Pending, Thunk, Val, bail,11	destructure::destruct,12	error::{ErrorKind::*, Result},13	evaluate, evaluate_named_param,14	gc::WithCapacityExt as _,15};1617fn eval_arg(ctx: Context, arg: &Rc<Expr>, tailstrict: bool) -> Result<Thunk<Val>> {18	if tailstrict {19		Ok(Thunk::evaluated(evaluate(ctx, arg)?))20	} else {21		let arg = arg.clone();22		Ok(Thunk!(move || evaluate(ctx, &arg)))23	}24}2526/// Creates correct [context](Context) for function body evaluation returning error on invalid call.27///28/// ## Parameters29/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)30/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)31/// * `params`: function parameters' definition32/// * `args`: passed function arguments33/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily34pub(crate) fn parse_function_call(35	ctx: Context,36	body_ctx: Context,37	params: &ExprParams,38	args: &ArgsDesc,39	tailstrict: bool,40) -> Result<Context> {41	let mut passed_args = FxHashMap::with_capacity(params.binds_len());42	if args.unnamed.len() > params.signature.len() {43		bail!(TooManyArgsFunctionHas(44			params.signature.len(),45			params.signature.clone(),46		))47	}4849	let mut filled_named = 0;50	let mut filled_positionals = 0;5152	for (id, arg) in args.unnamed.iter().enumerate() {53		destruct(54			&params.exprs[id].destruct,55			eval_arg(ctx.clone(), arg, tailstrict)?,56			Pending::new_filled(ctx.clone()),57			&mut passed_args,58		)?;59		filled_positionals += 1;60	}6162	for (name, value) in &args.named {63		// FIXME: O(n) for arg existence check64		if !params.exprs.iter().any(|p| &p.destruct.name() == name) {65			bail!(UnknownFunctionParameter(name.clone()));66		}67		if passed_args68			.insert(name.clone(), eval_arg(ctx.clone(), value, tailstrict)?)69			.is_some()70		{71			bail!(BindingParameterASecondTime(name.clone()));72		}73		filled_named += 1;74	}7576	if filled_named + filled_positionals < params.len() {77		// Some args are unset, but maybe we have defaults for them78		// Default values should be created in newly created context79		let fctx = Context::new_future();80		let mut defaults =81			FxHashMap::with_capacity(params.binds_len() - filled_named - filled_positionals);8283		for (idx, into, default) in params84			.exprs85			.iter()86			.enumerate()87			.filter_map(|(i, p)| Some((i, &p.destruct, p.default.as_ref()?)))88		{89			if let ParamName::Named(name) = into.name() {90				if passed_args.contains_key(&name) {91					continue;92				}93			} else if idx < filled_positionals {94				continue;95			}9697			destruct(98				into,99				{100					let ctx = fctx.clone();101					let name = into.name();102					let value = default.clone();103					Thunk!(move || evaluate_named_param(ctx.unwrap(), &value, name))104				},105				fctx.clone(),106				&mut defaults,107			)?;108			if into.name().is_named() {109				filled_named += 1;110			} else {111				filled_positionals += 1;112			}113		}114115		// Some args still weren't filled116		if filled_named + filled_positionals != params.len() {117			for param in params.exprs.iter().skip(args.unnamed.len()) {118				let mut found = false;119				for (name, _) in &args.named {120					if &param.destruct.name() == name {121						found = true;122					}123				}124				if !found {125					bail!(FunctionParameterNotBoundInCall(126						param.destruct.name(),127						params.signature.clone()128					));129				}130			}131			unreachable!();132		}133134		Ok(body_ctx135			.extend_bindings(passed_args)136			.extend_bindings(defaults)137			.into_future(fctx))138	} else {139		let body_ctx = body_ctx.extend_bindings(passed_args);140		Ok(body_ctx)141	}142}143144/// You shouldn't probally use this function, use `jrsonnet_macros::builtin` instead145///146/// ## Parameters147/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)148/// * `params`: function parameters' definition149/// * `args`: passed function arguments150/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily151pub fn parse_builtin_call(152	ctx: Context,153	params: FunctionSignature,154	args: &ArgsDesc,155	tailstrict: bool,156) -> Result<Vec<Option<Thunk<Val>>>> {157	let mut passed_args: Vec<Option<Thunk<Val>>> = vec![None; params.len()];158	if args.unnamed.len() > params.len() {159		bail!(TooManyArgsFunctionHas(params.len(), params,))160	}161162	let mut filled_args = 0;163164	for (id, arg) in args.unnamed.iter().enumerate() {165		passed_args[id] = Some(eval_arg(ctx.clone(), arg, tailstrict)?);166		filled_args += 1;167	}168169	for (name, arg) in &args.named {170		// FIXME: O(n) for arg existence check171		let id = params172			.iter()173			.position(|p| p.name() == name)174			.ok_or_else(|| UnknownFunctionParameter(name.clone()))?;175		if passed_args[id]176			.replace(eval_arg(ctx.clone(), arg, tailstrict)?)177			.is_some()178		{179			bail!(BindingParameterASecondTime(name.clone()));180		}181		filled_args += 1;182	}183184	if filled_args < params.len() {185		for (id, _) in params.iter().enumerate().filter(|(_, p)| p.has_default()) {186			if passed_args[id].is_some() {187				continue;188			}189			filled_args += 1;190		}191192		// Some args still wasn't filled193		if filled_args != params.len() {194			for param in params.iter().skip(args.unnamed.len()) {195				let mut found = false;196				for (name, _) in &args.named {197					if param.name() == name {198						found = true;199					}200				}201				if !found {202					bail!(FunctionParameterNotBoundInCall(203						param.name().clone(),204						params,205					));206				}207			}208			unreachable!();209		}210	}211	Ok(passed_args)212}213214/// Creates Context, which has all argument default values applied215/// and with unbound values causing error to be returned216pub fn parse_default_function_call(body_ctx: Context, params: &ExprParams) -> Result<Context> {217	let fctx = Context::new_future();218219	let mut bindings = FxHashMap::with_capacity(params.binds_len());220221	for param in params.exprs.iter() {222		if let Some(v) = &param.default {223			destruct(224				&param.destruct.clone(),225				{226					let ctx = fctx.clone();227					let name = param.destruct.name();228					let value = v.clone();229					Thunk!(move || evaluate_named_param(ctx.unwrap(), &value, name))230				},231				fctx.clone(),232				&mut bindings,233			)?;234		} else {235			destruct(236				&param.destruct,237				{238					let param_name = param.destruct.name();239					let params = params.clone();240					Thunk!(move || Err(FunctionParameterNotBoundInCall(241						param_name,242						params.signature243					)244					.into()))245				},246				fctx.clone(),247				&mut bindings,248			)?;249		}250	}251252	Ok(body_ctx.extend_bindings(bindings).into_future(fctx))253}
after · crates/jrsonnet-evaluator/src/function/parse.rs
1use jrsonnet_ir::ExprParams;2use rustc_hash::FxHashMap;34use crate::{5	Context, Thunk,6	destructure::destruct,7	error::{ErrorKind::*, Result},8	evaluate_named_param,9	gc::WithCapacityExt as _,10};1112/// Creates Context, which has all argument default values applied13/// and with unbound values causing error to be returned14pub fn parse_default_function_call(body_ctx: Context, params: &ExprParams) -> Result<Context> {15	let fctx = Context::new_future();1617	let mut bindings = FxHashMap::with_capacity(params.binds_len());1819	for param in params.exprs.iter() {20		if let Some(v) = &param.default {21			destruct(22				&param.destruct.clone(),23				{24					let ctx = fctx.clone();25					let name = param.destruct.name();26					let value = v.clone();27					Thunk!(move || evaluate_named_param(ctx.unwrap(), &value, name))28				},29				fctx.clone(),30				&mut bindings,31			)?;32		} else {33			destruct(34				&param.destruct,35				{36					let param_name = param.destruct.name();37					let params = params.clone();38					Thunk!(move || Err(FunctionParameterNotBoundInCall(39						param_name,40						params.signature41					)42					.into()))43				},44				fctx.clone(),45				&mut bindings,46			)?;47		}48	}4950	Ok(body_ctx.extend_bindings(bindings).into_future(fctx))51}
modifiedcrates/jrsonnet-ir-parser/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-ir-parser/src/lib.rs
+++ b/crates/jrsonnet-ir-parser/src/lib.rs
@@ -416,10 +416,11 @@
 
 fn args(p: &mut Parser<'_>) -> Result<ArgsDesc> {
 	if p.at(T![')']) {
-		return Ok(ArgsDesc::new(Vec::new(), Vec::new()));
+		return Ok(ArgsDesc::new(Vec::new(), Vec::new(), Vec::new()));
 	}
 	let mut unnamed = Vec::new();
-	let mut named = Vec::new();
+	let mut names = Vec::new();
+	let mut values = Vec::new();
 	let mut named_started = false;
 	loop {
 		let is_named = p.at(SyntaxKind::IDENT) && {
@@ -430,7 +431,9 @@
 			let name: IStr = ident(p)?;
 			p.eat(T![=])?;
 			let value = Rc::new(expr(p)?);
-			named.push((name, value));
+
+			names.push(name);
+			values.push(value);
 			named_started = true;
 		} else {
 			if named_started {
@@ -445,7 +448,7 @@
 			break;
 		}
 	}
-	Ok(ArgsDesc::new(unnamed, named))
+	Ok(ArgsDesc::new(unnamed, names, values))
 }
 
 fn bind(p: &mut Parser<'_>) -> Result<BindSpec> {
modifiedcrates/jrsonnet-ir-parser/src/snapshots/jrsonnet_ir_parser__tests__function_and_call.snapdiffbeforeafterboth
--- a/crates/jrsonnet-ir-parser/src/snapshots/jrsonnet_ir_parser__tests__function_and_call.snap
+++ b/crates/jrsonnet-ir-parser/src/snapshots/jrsonnet_ir_parser__tests__function_and_call.snap
@@ -66,12 +66,12 @@
                     2.0,
                 ),
             ],
-            named: [
-                (
-                    "y",
-                    Num(
-                        3.0,
-                    ),
+            names: [
+                "y",
+            ],
+            values: [
+                Num(
+                    3.0,
                 ),
             ],
         } from virtual:<test>:26-34,
modifiedcrates/jrsonnet-ir-parser/src/snapshots/jrsonnet_ir_parser__tests__index_and_suffix.snapdiffbeforeafterboth
--- a/crates/jrsonnet-ir-parser/src/snapshots/jrsonnet_ir_parser__tests__index_and_suffix.snap
+++ b/crates/jrsonnet-ir-parser/src/snapshots/jrsonnet_ir_parser__tests__index_and_suffix.snap
@@ -23,7 +23,8 @@
                     2.0,
                 ),
             ],
-            named: [],
+            names: [],
+            values: [],
         } from virtual:<test>:8-11,
         false,
     ),
modifiedcrates/jrsonnet-ir-parser/src/snapshots/jrsonnet_ir_parser__tests__peg_snapshots@array_comp.jsonnet.snapdiffbeforeafterboth
--- a/crates/jrsonnet-ir-parser/src/snapshots/jrsonnet_ir_parser__tests__peg_snapshots@array_comp.jsonnet.snap
+++ b/crates/jrsonnet-ir-parser/src/snapshots/jrsonnet_ir_parser__tests__peg_snapshots@array_comp.jsonnet.snap
@@ -26,7 +26,8 @@
                             "x" from virtual:<test>:16-17,
                         ),
                     ],
-                    named: [],
+                    names: [],
+                    values: [],
                 } from virtual:<test>:15-18,
                 false,
             ),
modifiedcrates/jrsonnet-ir-parser/src/snapshots/jrsonnet_ir_parser__tests__peg_snapshots@reserved.jsonnet.snapdiffbeforeafterboth
--- a/crates/jrsonnet-ir-parser/src/snapshots/jrsonnet_ir_parser__tests__peg_snapshots@reserved.jsonnet.snap
+++ b/crates/jrsonnet-ir-parser/src/snapshots/jrsonnet_ir_parser__tests__peg_snapshots@reserved.jsonnet.snap
@@ -24,7 +24,8 @@
                         "null_fields" from virtual:<test>:20-31,
                     ),
                 ],
-                named: [],
+                names: [],
+                values: [],
             } from virtual:<test>:16-32,
             false,
         ),
modifiedcrates/jrsonnet-ir-parser/src/snapshots/jrsonnet_ir_parser__tests__peg_snapshots@suffix.jsonnet.snapdiffbeforeafterboth
--- a/crates/jrsonnet-ir-parser/src/snapshots/jrsonnet_ir_parser__tests__peg_snapshots@suffix.jsonnet.snap
+++ b/crates/jrsonnet-ir-parser/src/snapshots/jrsonnet_ir_parser__tests__peg_snapshots@suffix.jsonnet.snap
@@ -28,7 +28,8 @@
                         2.0,
                     ),
                 ],
-                named: [],
+                names: [],
+                values: [],
             } from virtual:<test>:15-18,
             false,
         ),
@@ -52,7 +53,8 @@
                         2.0,
                     ),
                 ],
-                named: [],
+                names: [],
+                values: [],
             } from virtual:<test>:28-31,
             false,
         ),
modifiedcrates/jrsonnet-ir/src/expr.rsdiffbeforeafterboth
--- a/crates/jrsonnet-ir/src/expr.rs
+++ b/crates/jrsonnet-ir/src/expr.rs
@@ -195,11 +195,16 @@
 #[derive(Debug, PartialEq, Acyclic)]
 pub struct ArgsDesc {
 	pub unnamed: Vec<Rc<Expr>>,
-	pub named: Vec<(IStr, Rc<Expr>)>,
+	pub names: Vec<IStr>,
+	pub values: Vec<Rc<Expr>>,
 }
 impl ArgsDesc {
-	pub fn new(unnamed: Vec<Rc<Expr>>, named: Vec<(IStr, Rc<Expr>)>) -> Self {
-		Self { unnamed, named }
+	pub fn new(unnamed: Vec<Rc<Expr>>, names: Vec<IStr>, values: Vec<Rc<Expr>>) -> Self {
+		Self {
+			unnamed,
+			names,
+			values,
+		}
 	}
 }
 
modifiedcrates/jrsonnet-ir/src/visit.rsdiffbeforeafterboth
--- a/crates/jrsonnet-ir/src/visit.rs
+++ b/crates/jrsonnet-ir/src/visit.rs
@@ -216,11 +216,15 @@
 		}
 		Expr::Apply(expr, spanned, _) => {
 			v.visit_expr(expr);
-			let ArgsDesc { unnamed, named } = &**spanned;
+			let ArgsDesc {
+				unnamed,
+				names: _,
+				values,
+			} = &**spanned;
 			for unnamed in unnamed {
 				v.visit_expr(unnamed);
 			}
-			for (_name, named) in named {
+			for named in values {
 				v.visit_expr(named);
 			}
 		}
modifiedcrates/jrsonnet-peg-parser/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-peg-parser/src/lib.rs
+++ b/crates/jrsonnet-peg-parser/src/lib.rs
@@ -73,12 +73,14 @@
 			= args:arg(s)**comma() comma()? {?
 				let unnamed_count = args.iter().take_while(|(n, _)| n.is_none()).count();
 				let mut unnamed = Vec::with_capacity(unnamed_count);
-				let mut named = Vec::with_capacity(args.len() - unnamed_count);
+				let mut names = Vec::with_capacity(args.len() - unnamed_count);
+				let mut values = Vec::with_capacity(args.len() - unnamed_count);
 				let mut named_started = false;
 				for (name, value) in args {
 					if let Some(name) = name {
 						named_started = true;
-						named.push((name, value));
+						names.push(name);
+						values.push(value);
 					} else {
 						if named_started {
 							return Err("<named argument>")
@@ -86,7 +88,7 @@
 						unnamed.push(value);
 					}
 				}
-				Ok(ArgsDesc::new(unnamed, named))
+				Ok(ArgsDesc{unnamed, names, values})
 			}
 
 		pub rule destruct_rest() -> DestructRest
modifiedcrates/jrsonnet-peg-parser/src/snapshots/jrsonnet_peg_parser__tests__snapshots@array_comp.jsonnet.snapdiffbeforeafterboth
--- a/crates/jrsonnet-peg-parser/src/snapshots/jrsonnet_peg_parser__tests__snapshots@array_comp.jsonnet.snap
+++ b/crates/jrsonnet-peg-parser/src/snapshots/jrsonnet_peg_parser__tests__snapshots@array_comp.jsonnet.snap
@@ -26,7 +26,8 @@
                             "x" from virtual:<test>:16-17,
                         ),
                     ],
-                    named: [],
+                    names: [],
+                    values: [],
                 } from virtual:<test>:15-18,
                 false,
             ),
modifiedcrates/jrsonnet-peg-parser/src/snapshots/jrsonnet_peg_parser__tests__snapshots@reserved.jsonnet.snapdiffbeforeafterboth
--- a/crates/jrsonnet-peg-parser/src/snapshots/jrsonnet_peg_parser__tests__snapshots@reserved.jsonnet.snap
+++ b/crates/jrsonnet-peg-parser/src/snapshots/jrsonnet_peg_parser__tests__snapshots@reserved.jsonnet.snap
@@ -24,7 +24,8 @@
                         "null_fields" from virtual:<test>:20-31,
                     ),
                 ],
-                named: [],
+                names: [],
+                values: [],
             } from virtual:<test>:16-32,
             false,
         ),
modifiedcrates/jrsonnet-peg-parser/src/snapshots/jrsonnet_peg_parser__tests__snapshots@suffix.jsonnet.snapdiffbeforeafterboth
--- a/crates/jrsonnet-peg-parser/src/snapshots/jrsonnet_peg_parser__tests__snapshots@suffix.jsonnet.snap
+++ b/crates/jrsonnet-peg-parser/src/snapshots/jrsonnet_peg_parser__tests__snapshots@suffix.jsonnet.snap
@@ -28,7 +28,8 @@
                         2.0,
                     ),
                 ],
-                named: [],
+                names: [],
+                values: [],
             } from virtual:<test>:15-18,
             false,
         ),
@@ -52,7 +53,8 @@
                         2.0,
                     ),
                 ],
-                named: [],
+                names: [],
+                values: [],
             } from virtual:<test>:28-31,
             false,
         ),
modifiedcrates/jrsonnet-stdlib/src/keyf.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/keyf.rs
+++ b/crates/jrsonnet-stdlib/src/keyf.rs
@@ -1,14 +1,16 @@
 use jrsonnet_evaluator::{
 	Error, Result, Thunk, Val,
-	function::{CallLocation, FuncVal, PreparedFuncVal},
+	function::{FuncVal, NativeFn},
 	typed::{ComplexValType, FromUntyped, Typed, ValType},
 };
 
+type PreparedKeyF = NativeFn!((Thunk<Val>) -> Val);
+
 #[derive(Default, Clone)]
 pub enum KeyF {
 	#[default]
 	Identity,
-	Prepared(PreparedFuncVal),
+	Prepared(PreparedKeyF),
 	PrepareFailure(Error),
 }
 impl KeyF {
@@ -19,13 +21,13 @@
 		if val.is_identity() {
 			Self::Identity
 		} else {
-			PreparedFuncVal::new(val, 1, &[]).map_or_else(Self::PrepareFailure, Self::Prepared)
+			PreparedKeyF::try_from(val).map_or_else(Self::PrepareFailure, Self::Prepared)
 		}
 	}
 	pub fn eval(&self, val: impl Into<Thunk<Val>>) -> Result<Val> {
 		match self {
 			KeyF::Identity => val.into().evaluate(),
-			KeyF::Prepared(p) => p.call(CallLocation::native(), &[val.into()], &[]),
+			KeyF::Prepared(p) => p.call(val.into()),
 			KeyF::PrepareFailure(e) => Err(e.clone()),
 		}
 	}