difftreelog
perf use prepared call for KeyF
in: master
9 files changed
crates/jrsonnet-evaluator/src/function/builtin.rsdiffbeforeafterboth3use jrsonnet_gcmodule::{cc_dyn, Trace, TraceBox};3use jrsonnet_gcmodule::{cc_dyn, Trace, TraceBox};4use jrsonnet_parser::function::{FunctionSignature, ParamDefault, ParamName, ParamParse};4use jrsonnet_parser::function::{FunctionSignature, ParamDefault, ParamName, ParamParse};556use super::{arglike::ArgsLike, parse::parse_builtin_call, CallLocation};6use super::CallLocation;7use crate::{Context, Result, Val};7use crate::{Result, Thunk, Val};889#[macro_export]9#[macro_export]10macro_rules! params {10macro_rules! params {34 self.0.params()34 self.0.params()35 }35 }363637 fn call(&self, ctx: Context, loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val> {37 fn call(&self, loc: CallLocation<'_>, args: &[Option<Thunk<Val>>]) -> Result<Val> {38 self.0.call(ctx, loc, args)38 self.0.call(loc, args)39 }39 }404041 fn as_any(&self) -> &dyn Any {41 fn as_any(&self) -> &dyn Any {52 /// Parameter names for named calls52 /// Parameter names for named calls53 fn params(&self) -> FunctionSignature;53 fn params(&self) -> FunctionSignature;54 /// Call the builtin54 /// Call the builtin55 fn call(&self, ctx: Context, loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val>;55 fn call(&self, loc: CallLocation<'_>, args: &[Option<Thunk<Val>>]) -> Result<Val>;565657 fn as_any(&self) -> &dyn Any;57 fn as_any(&self) -> &dyn Any;58}58}96 self.params.clone()96 self.params.clone()97 }97 }989899 fn call(&self, ctx: Context, _loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val> {99 fn call(&self, _loc: CallLocation<'_>, args: &[Option<Thunk<Val>>]) -> Result<Val> {100 let args = parse_builtin_call(ctx, self.params.clone(), args, true)?;101 let args = args100 let args = args102 .into_iter()101 .into_iter()103 .map(|a| a.expect("legacy natives have no default params"))102 .map(|a| a.as_ref().expect("legacy natives have no default params"))104 .map(|a| a.evaluate())103 .map(|a| a.evaluate())105 .collect::<Result<Vec<Val>>>()?;104 .collect::<Result<Vec<Val>>>()?;106 self.handler.call(&args)105 self.handler.call(&args)crates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth11 arglike::OptionalContext,11 arglike::OptionalContext,12 builtin::{Builtin, StaticBuiltin},12 builtin::{Builtin, StaticBuiltin},13 native::NativeDesc,13 native::NativeDesc,14 parse::{parse_default_function_call, parse_function_call},14 parse::{parse_builtin_call, parse_default_function_call, parse_function_call},15 prepared::{parse_prepared_builtin_call, parse_prepared_function_call, PreparedCall},15};16};16use crate::{17use crate::{17 bail, error::ErrorKind::*, evaluate, evaluate_trivial, function::builtin::BuiltinFunc, Context,18 bail, error::ErrorKind::*, evaluate, evaluate_trivial, function::builtin::BuiltinFunc, Context,22pub mod builtin;23pub mod builtin;23pub mod native;24pub mod native;24pub mod parse;25pub mod parse;25pub mod prepared;26mod prepared;2728pub use prepared::PreparedFuncVal;262927pub use jrsonnet_parser::function::*;30pub use jrsonnet_parser::function::*;2831173 tailstrict: bool,176 tailstrict: bool,174 ) -> Result<Val> {177 ) -> Result<Val> {175 match self {178 match self {176 Self::Id => ID.call(call_ctx, loc, args),177 Self::Normal(func) => {179 Self::Normal(func) => {178 let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?;180 let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?;179 evaluate(body_ctx, &func.body)181 evaluate(body_ctx, &func.body)184 }186 }185 thunk.evaluate()187 thunk.evaluate()186 }188 }189 Self::Id => {190 let args = parse_builtin_call(call_ctx, ID.params(), args, tailstrict)?;191 ID.call(loc, &args)192 }187 Self::StaticBuiltin(b) => b.call(call_ctx, loc, args),193 Self::StaticBuiltin(b) => {194 let args = parse_builtin_call(call_ctx, b.params(), args, tailstrict)?;195 b.call(loc, &args)196 }188 Self::Builtin(b) => b.call(call_ctx, loc, args),197 Self::Builtin(b) => {198 let args = parse_builtin_call(call_ctx, b.params(), args, tailstrict)?;199 b.call(loc, &args)200 }189 }201 }190 }202 }191 pub fn evaluate_simple<A: ArgsLike + OptionalContext>(203 pub fn evaluate_simple<A: ArgsLike + OptionalContext>(201 )213 )202 }214 }215216 pub(crate) fn evaluate_prepared(217 &self,218 prepared: &PreparedCall,219 loc: CallLocation<'_>,220 unnamed: &[Thunk<Val>],221 named: &[Thunk<Val>],222 _tailstrict: bool,223 ) -> Result<Val> {224 match self {225 FuncVal::Id => {226 let args = parse_prepared_builtin_call(prepared, ID.params(), unnamed, named)?;227 ID.call(loc, &args)228 }229 FuncVal::Normal(func) => {230 let body_ctx = parse_prepared_function_call(231 func.ctx.clone(),232 prepared,233 &func.params,234 unnamed,235 named,236 )?;237 evaluate(body_ctx, &func.body)238 }239 FuncVal::Thunk(t) => t.evaluate(),240 FuncVal::StaticBuiltin(b) => {241 let args = parse_prepared_builtin_call(prepared, b.params(), unnamed, named)?;242 b.call(loc, &args)243 }244 FuncVal::Builtin(b) => {245 let args = parse_prepared_builtin_call(prepared, b.params(), unnamed, named)?;246 b.call(loc, &args)247 }248 }249 }203 /// Convert jsonnet function to plain `Fn` value.250 /// Convert jsonnet function to plain `Fn` value.204 pub fn into_native<D: NativeDesc>(self) -> D::Value {251 pub fn into_native<D: NativeDesc>(self) -> D::Value {205 D::into_native(self)252 D::into_native(self)crates/jrsonnet-evaluator/src/function/prepared.rsdiffbeforeafterboth1use std::rc::Rc;23use jrsonnet_gcmodule::{Acyclic, Trace};1use jrsonnet_parser::function::FunctionSignature;4use jrsonnet_parser::function::FunctionSignature;2use jrsonnet_parser::{ExprParams, IStr};5use jrsonnet_parser::{ExprParams, IStr};3use rustc_hash::{FxHashMap, FxHashSet};6use rustc_hash::{FxHashMap, FxHashSet};7use crate::{bail, error::ErrorKind::*, Result};10use crate::{bail, error::ErrorKind::*, Result};8use crate::{evaluate_named_param, Context, ContextBuilder, Pending, Thunk, Val};11use crate::{evaluate_named_param, Context, ContextBuilder, Pending, Thunk, Val};91213use super::{CallLocation, FuncVal};1415#[derive(Debug, Trace, Clone)]16pub struct PreparedFuncVal {17 fun: FuncVal,18 prepared: Rc<PreparedCall>,19}2021impl PreparedFuncVal {22 pub fn new(fun: FuncVal, unnamed: usize, named: &[IStr]) -> Result<Self> {23 let prepared = prepare_call(fun.params(), unnamed, named)?;24 Ok(Self {25 fun,26 prepared: Rc::new(prepared),27 })28 }29 pub fn call(30 &self,31 loc: CallLocation<'_>,32 unnamed: &[Thunk<Val>],33 named: &[Thunk<Val>],34 ) -> Result<Val> {35 self.fun36 .evaluate_prepared(&self.prepared, loc, unnamed, named, false)37 }38}3940#[derive(Acyclic, Debug)]10pub struct PreparedCall {41pub struct PreparedCall {11 // Param, named input.42 // Param, named input.12 named: Vec<(usize, usize)>,43 named: Vec<(usize, usize)>,crates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth3use proc_macro2::TokenStream;3use proc_macro2::TokenStream;4use quote::{quote, quote_spanned};4use quote::{quote, quote_spanned};5use syn::{5use syn::{6 parenthesized,7 parse::{Parse, ParseStream},8 parse_macro_input,9 punctuated::Punctuated,10 spanned::Spanned,11 token::{self, Comma},12 Attribute, DeriveInput, Error, Expr, ExprClosure, FnArg, GenericArgument, Ident, ItemFn,6 Attribute, DeriveInput, Error, Expr, ExprClosure, FnArg, GenericArgument, Ident, ItemFn, LitStr, Meta, Pat, Path, PathArguments, Result, ReturnType, Token, Type, parenthesized, parse::{Parse, ParseStream}, parse_macro_input, punctuated::Punctuated, spanned::Spanned, token::{self, Comma}13 LitStr, Pat, Path, PathArguments, Result, ReturnType, Token, Type,14};7};1589fn try_parse_attr_noargs<I>(attrs: &[Attribute], ident: I) -> Result<bool>10where11 Ident: PartialEq<I>,12{13 let attrs = attrs14 .iter()15 .filter(|a| a.path().is_ident(&ident))16 .collect::<Vec<_>>();17 if attrs.len() > 1 {18 return Err(Error::new(19 attrs[1].span(),20 "this attribute may be specified only once",21 ));22 } else if attrs.is_empty() {23 return Ok(false);24 }25 let attr = attrs[0];2627 match attr.meta {28 Meta::Path(_) => Ok(true),29 _ => Ok(false),30 }31}16fn parse_attr<A: Parse, I>(attrs: &[Attribute], ident: I) -> Result<Option<A>>32fn parse_attr<A: Parse, I>(attrs: &[Attribute], ident: I) -> Result<Option<A>>17where33where18 Ident: PartialEq<I>,34 Ident: PartialEq<I>,125 Required,141 Required,126 Optional,142 Optional,127 Default(Expr),143 Default(Expr),144 TypeDefault,128}145}129146130#[allow(clippy::large_enum_variant, reason = "this macro is not that hot for it to matter")]147#[allow(170 _ => {}190 _ => {}171 }191 }172192173 let (optionality, ty) = if let Some(default) = parse_attr::<_, _>(&arg.attrs, "default")? {193 let (optionality, ty) = if try_parse_attr_noargs(&mut arg.attrs, "default")? {194 remove_attr(&mut arg.attrs, "default");195 (Optionality::TypeDefault, ty.clone())196 } else if let Some(default) = parse_attr::<_, _>(&arg.attrs, "default")? {174 remove_attr(&mut arg.attrs, "default");197 remove_attr(&mut arg.attrs, "default");175 (Optionality::Default(default), ty.clone())198 (Optionality::Default(default), ty.clone())176 } else if let Some(ty) = extract_type_from_option(ty)? {199 } else if let Some(ty) = extract_type_from_option(ty)? {245 .map_or_else(|| quote! {unnamed}, |n| quote! {named(#n)});268 .map_or_else(|| quote! {unnamed}, |n| quote! {named(#n)});246 let default = match optionality {269 let default = match optionality {247 Optionality::Required => quote!(ParamDefault::None),270 Optionality::Required => quote!(ParamDefault::None),248 Optionality::Optional => quote!(ParamDefault::Exists),271 Optionality::Optional | Optionality::TypeDefault => quote!(ParamDefault::Exists),249 Optionality::Default(e) => quote!(ParamDefault::Literal(stringify!(#e))),272 Optionality::Default(e) => quote!(ParamDefault::Literal(stringify!(#e))),250 };273 };251 Some(quote! {274 Some(quote! {305 let v: #ty = #expr;328 let v: #ty = #expr;306 v329 v307 },},330 },},331 Optionality::TypeDefault => quote! {if let Some(value) = &parsed[#id] {332 #eval333 } else {334 let v: #ty = Default::default();335 v336 },},308 };337 };309 quote! {338 quote! {310 #(#cfg_attrs)*339 #(#cfg_attrs)*371 State, Val,400 State, Val,372 function::{builtin::{Builtin, StaticBuiltin}, FunctionSignature, ParamParse, ParamName, ParamDefault, CallLocation, ArgsLike, parse::parse_builtin_call},401 function::{builtin::{Builtin, StaticBuiltin}, FunctionSignature, ParamParse, ParamName, ParamDefault, CallLocation, ArgsLike, parse::parse_builtin_call},373 Result, Context, typed::Typed,402 Result, Context, typed::Typed,374 parser::Span, params,403 parser::Span, params, Thunk,375 };404 };376 params!(405 params!(377 #(#params_desc)*406 #(#params_desc)*389 PARAMS.with(|p| p.clone())418 PARAMS.with(|p| p.clone())390 }419 }391 #[allow(unused_variables)]420 #[allow(unused_variables)]392 fn call(&self, ctx: Context, location: CallLocation, args: &dyn ArgsLike) -> Result<Val> {421 fn call(&self, location: CallLocation<'_>, parsed: &[Option<Thunk<Val>>]) -> Result<Val> {393 let parsed = parse_builtin_call(ctx.clone(), self.params(), args, false)?;394395 let result: #result = #name(#(#pass)*);422 let result: #result = #name(#(#pass)*);396 <_ as Typed>::into_result(result)423 <_ as Typed>::into_result(result)crates/jrsonnet-stdlib/src/keyf.rsdiffbeforeafterbothno changes
crates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth50mod sort;50mod sort;51mod strings;51mod strings;52mod types;52mod types;53mod keyf;535454#[allow(clippy::too_many_lines)]55#[allow(clippy::too_many_lines)]55pub fn stdlib_uncached(settings: Cc<RefCell<Settings>>) -> ObjValue {56pub fn stdlib_uncached(settings: Cc<RefCell<Settings>>) -> ObjValue {crates/jrsonnet-stdlib/src/sets.rsdiffbeforeafterboth1use std::cmp::Ordering;1use std::cmp::Ordering;223use jrsonnet_evaluator::{3use jrsonnet_evaluator::{4 function::{builtin, FuncVal},4 function::builtin, operator::evaluate_compare_op, val::ArrValue, Result, Thunk, Val,5 operator::evaluate_compare_op,6 val::ArrValue,7 Result, Thunk, Val,8};5};9use jrsonnet_parser::BinaryOpType;6use jrsonnet_parser::BinaryOpType;78use crate::keyf::KeyF;10911#[builtin]10#[builtin]12#[allow(non_snake_case)]11#[allow(non_snake_case)]13pub fn builtin_set_member(x: Thunk<Val>, arr: ArrValue, keyF: Option<FuncVal>) -> Result<bool> {12pub fn builtin_set_member(x: Thunk<Val>, arr: ArrValue, #[default] keyF: KeyF) -> Result<bool> {14 let mut low = 0;13 let mut low = 0;15 let mut high = arr.len();14 let mut high = arr.len();1617 let keyF = keyF18 .unwrap_or(FuncVal::Id)19 .into_native::<((Thunk<Val>,), Val)>();201521 let x = keyF(x)?;16 let x = keyF.eval(x)?;221723 while low < high {18 while low < high {24 let middle = usize::midpoint(high, low);19 let middle = usize::midpoint(high, low);25 let comp = keyF(arr.get_lazy(middle).expect("in bounds"))?;20 let comp = keyF.eval(arr.get_lazy(middle).expect("in bounds"))?;26 match evaluate_compare_op(&comp, &x, BinaryOpType::Lt)? {21 match evaluate_compare_op(&comp, &x, BinaryOpType::Lt)? {27 Ordering::Less => low = middle + 1,22 Ordering::Less => low = middle + 1,28 Ordering::Equal => return Ok(true),23 Ordering::Equal => return Ok(true),342935#[builtin]30#[builtin]36#[allow(non_snake_case, clippy::redundant_closure)]31#[allow(non_snake_case, clippy::redundant_closure)]37pub fn builtin_set_inter(a: ArrValue, b: ArrValue, keyF: Option<FuncVal>) -> Result<ArrValue> {32pub fn builtin_set_inter(a: ArrValue, b: ArrValue, #[default] keyF: KeyF) -> Result<ArrValue> {38 let mut a = a.iter_lazy();33 let mut a = a.iter_lazy();39 let mut b = b.iter_lazy();34 let mut b = b.iter_lazy();403541 let keyF = keyF42 .unwrap_or(FuncVal::identity())43 .into_native::<((Thunk<Val>,), Val)>();44 let keyF = |v| keyF(v);36 let keyF = |v| keyF.eval(v);453746 let mut av = a.next();38 let mut av = a.next();47 let mut bv = b.next();39 let mut bv = b.next();736574#[builtin]66#[builtin]75#[allow(non_snake_case, clippy::redundant_closure)]67#[allow(non_snake_case, clippy::redundant_closure)]76pub fn builtin_set_diff(a: ArrValue, b: ArrValue, keyF: Option<FuncVal>) -> Result<ArrValue> {68pub fn builtin_set_diff(a: ArrValue, b: ArrValue, #[default] keyF: KeyF) -> Result<ArrValue> {77 let mut a = a.iter_lazy();69 let mut a = a.iter_lazy();78 let mut b = b.iter_lazy();70 let mut b = b.iter_lazy();797180 let keyF = keyF81 .unwrap_or(FuncVal::identity())82 .into_native::<((Thunk<Val>,), Val)>();83 let keyF = |v| keyF(v);72 let keyF = |v| keyF.eval(v);847385 let mut av = a.next();74 let mut av = a.next();86 let mut bv = b.next();75 let mut bv = b.next();119108120#[builtin]109#[builtin]121#[allow(non_snake_case, clippy::redundant_closure)]110#[allow(non_snake_case, clippy::redundant_closure)]122pub fn builtin_set_union(a: ArrValue, b: ArrValue, keyF: Option<FuncVal>) -> Result<ArrValue> {111pub fn builtin_set_union(a: ArrValue, b: ArrValue, #[default] keyF: KeyF) -> Result<ArrValue> {123 let mut a = a.iter_lazy();112 let mut a = a.iter_lazy();124 let mut b = b.iter_lazy();113 let mut b = b.iter_lazy();125114126 let keyF = keyF127 .unwrap_or(FuncVal::identity())128 .into_native::<((Thunk<Val>,), Val)>();129 let keyF = |v| keyF(v);115 let keyF = |v| keyF.eval(v);130116131 let mut av = a.next();117 let mut av = a.next();132 let mut bv = b.next();118 let mut bv = b.next();crates/jrsonnet-stdlib/src/sort.rsdiffbeforeafterboth445use jrsonnet_evaluator::{5use jrsonnet_evaluator::{6 bail,6 bail,7 function::{builtin, FuncVal},7 function::builtin,8 operator::evaluate_compare_op,8 operator::evaluate_compare_op,9 val::{equals, ArrValue},9 val::{equals, ArrValue},10 Result, Thunk, Val,10 Result, Thunk, Val,11};11};12use jrsonnet_parser::BinaryOpType;12use jrsonnet_parser::BinaryOpType;131314use crate::eval_on_empty;14use crate::{eval_on_empty, keyf::KeyF};151516#[derive(Copy, Clone)]16#[derive(Copy, Clone)]17enum SortKeyType {17enum SortKeyType {70 Ok(values)70 Ok(values)71}71}727273fn sort_keyf(values: ArrValue, keyf: FuncVal) -> Result<Vec<Thunk<Val>>> {73fn sort_keyf(values: ArrValue, keyf: KeyF) -> Result<Vec<Thunk<Val>>> {74 // Slow path, user provided key getter74 // Slow path, user provided key getter75 let mut vk = Vec::with_capacity(values.len());75 let mut vk = Vec::with_capacity(values.len());76 for value in values.iter_lazy() {76 for value in values.iter_lazy() {77 vk.push((77 vk.push((value.clone(), keyf.eval(value)?));78 value.clone(),79 keyf.evaluate_simple(&(value.clone(),), false)?,80 ));81 }78 }82 let sort_type = get_sort_type(&vk, |v| &v.1)?;79 let sort_type = get_sort_type(&vk, |v| &v.1)?;112}109}113110114/// * `key_getter` - None, if identity sort required111/// * `key_getter` - None, if identity sort required115pub fn sort(values: ArrValue, key_getter: FuncVal) -> Result<ArrValue> {112pub fn sort(values: ArrValue, key_getter: KeyF) -> Result<ArrValue> {116 if values.len() <= 1 {113 if values.len() <= 1 {117 return Ok(values);114 return Ok(values);118 }115 }126}123}127124128#[builtin]125#[builtin]129pub fn builtin_sort(126pub fn builtin_sort(arr: ArrValue, #[default] keyF: KeyF) -> Result<ArrValue> {130 arr: ArrValue,131132 #[default(FuncVal::identity())] keyF: FuncVal,133) -> Result<ArrValue> {134 super::sort::sort(arr, keyF)127 super::sort::sort(arr, keyF)135}128}147 Ok(out)140 Ok(out)148}141}149142150fn uniq_keyf(arr: ArrValue, keyf: FuncVal) -> Result<Vec<Thunk<Val>>> {143fn uniq_keyf(arr: ArrValue, keyf: KeyF) -> Result<Vec<Thunk<Val>>> {151 let mut out = Vec::new();144 let mut out = Vec::new();152 let last_value = arr.get_lazy(0).unwrap();145 let last_value = arr.get_lazy(0).unwrap();153 let mut last_key = keyf.evaluate_simple(&(last_value.clone(),), false)?;146 let mut last_key = keyf.eval(last_value.clone())?;154 out.push(last_value);147 out.push(last_value);155148156 for next in arr.iter_lazy().skip(1) {149 for next in arr.iter_lazy().skip(1) {157 let next_key = keyf.evaluate_simple(&(next.clone(),), false)?;150 let next_key = keyf.eval(next.clone())?;158 if !equals(&last_key, &next_key)? {151 if !equals(&last_key, &next_key)? {159 out.push(next.clone());152 out.push(next.clone());160 }153 }165158166#[builtin]159#[builtin]167#[allow(non_snake_case)]160#[allow(non_snake_case)]168pub fn builtin_uniq(161pub fn builtin_uniq(arr: ArrValue, #[default] keyF: KeyF) -> Result<ArrValue> {169 arr: ArrValue,170171 #[default(FuncVal::identity())] keyF: FuncVal,172) -> Result<ArrValue> {173 if arr.len() <= 1 {162 if arr.len() <= 1 {174 return Ok(arr);163 return Ok(arr);184173185#[builtin]174#[builtin]186#[allow(non_snake_case)]175#[allow(non_snake_case)]187pub fn builtin_set(176pub fn builtin_set(arr: ArrValue, #[default] keyF: KeyF) -> Result<ArrValue> {188 arr: ArrValue,189190 #[default(FuncVal::identity())] keyF: FuncVal,191) -> Result<ArrValue> {192 if arr.len() <= 1 {177 if arr.len() <= 1 {193 return Ok(arr);178 return Ok(arr);204 }189 }205}190}206207fn eval_keyf(val: Val, key_f: Option<&FuncVal>) -> Result<Val> {208 if let Some(key_f) = key_f {209 key_f.evaluate_simple(&(val,), false)210 } else {211 Ok(val)212 }213}214191215fn array_top1(arr: ArrValue, key_f: Option<&FuncVal>, ordering: Ordering) -> Result<Val> {192fn array_top1(arr: ArrValue, keyf: KeyF, ordering: Ordering) -> Result<Val> {216 let mut iter = arr.iter();193 let mut iter = arr.iter();217 let mut min = iter.next().expect("not empty")?;194 let mut min = iter.next().expect("not empty")?;218 let mut min_key = eval_keyf(min.clone(), key_f)?;195 let mut min_key = keyf.eval(Thunk::evaluated(min.clone()))?;219 for item in iter {196 for item in iter {220 let cur = item?;197 let cur = item?;221 let cur_key = eval_keyf(cur.clone(), key_f)?;198 let cur_key = keyf.eval(Thunk::evaluated(cur.clone()))?;222 if evaluate_compare_op(&cur_key, &min_key, BinaryOpType::Lt)? == ordering {199 if evaluate_compare_op(&cur_key, &min_key, BinaryOpType::Lt)? == ordering {223 min = cur;200 min = cur;224 min_key = cur_key;201 min_key = cur_key;230#[builtin]207#[builtin]231pub fn builtin_min_array(208pub fn builtin_min_array(232 arr: ArrValue,209 arr: ArrValue,233 keyF: Option<FuncVal>,210 #[default] keyF: KeyF,234 onEmpty: Option<Thunk<Val>>,211 onEmpty: Option<Thunk<Val>>,235) -> Result<Val> {212) -> Result<Val> {236 if arr.is_empty() {213 if arr.is_empty() {237 return eval_on_empty(onEmpty);214 return eval_on_empty(onEmpty);238 }215 }239 array_top1(arr, keyF.as_ref(), Ordering::Less)216 array_top1(arr, keyF, Ordering::Less)240}217}241#[builtin]218#[builtin]242pub fn builtin_max_array(219pub fn builtin_max_array(243 arr: ArrValue,220 arr: ArrValue,244 keyF: Option<FuncVal>,221 #[default] keyF: KeyF,245 onEmpty: Option<Thunk<Val>>,222 onEmpty: Option<Thunk<Val>>,246) -> Result<Val> {223) -> Result<Val> {247 if arr.is_empty() {224 if arr.is_empty() {248 return eval_on_empty(onEmpty);225 return eval_on_empty(onEmpty);249 }226 }250 array_top1(arr, keyF.as_ref(), Ordering::Greater)227 array_top1(arr, keyF, Ordering::Greater)251}228}252229tests/tests/builtin.rsdiffbeforeafterboth18#[test]18#[test]19fn basic_function() -> Result<()> {19fn basic_function() -> Result<()> {20 let a: a = a {};20 let a: a = a {};21 let v =21 let v = u32::from_untyped(a.call(CallLocation::native(), &[])?)?;22 u32::from_untyped(a.call(ContextBuilder::new().build(), CallLocation::native(), &())?)?;232224 ensure_eq!(v, 1);23 ensure_eq!(v, 1);25 Ok(())24 Ok(())