difftreelog
perf unify function calls
in: master
8 files changed
crates/jrsonnet-evaluator/build.rsdiffbeforeafterboth39 if **name == *"join" || **name == *"manifestJsonEx" ||39 if **name == *"join" || **name == *"manifestJsonEx" ||40 **name == *"escapeStringJson" || **name == *"equals" ||40 **name == *"escapeStringJson" || **name == *"equals" ||41 **name == *"base64" || **name == *"foldl" || **name == *"foldr" ||41 **name == *"base64" || **name == *"foldl" || **name == *"foldr" ||42 **name == *"sortImpl" || **name == *"format" || **name == *"range"42 **name == *"sortImpl" || **name == *"format" || **name == *"range" || **name == *"reverse"43 )43 )44 })44 })45 .collect(),45 .collect(),crates/jrsonnet-evaluator/src/builtin/manifest.rsdiffbeforeafterboth117 }117 }118 buf.push('}');118 buf.push('}');119 }119 }120 Val::Func(_) | Val::Intristic(_, _) | Val::NativeExt(_, _) => {120 Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),121 throw!(RuntimeError("tried to manifest function".into()))122 }123 Val::Lazy(_) => unreachable!(),121 Val::Lazy(_) => unreachable!(),124 };122 };125 Ok(())123 Ok(())crates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth1use crate::{2 equals,3 error::{Error::*, Result},4 evaluate, parse_args, primitive_equals, push, throw, with_state, Context, FuncVal, Val,5 ValType,6};7use format::{format_arr, format_obj};8use jrsonnet_parser::{ArgsDesc, ExprLocation};9use manifest::{escape_string_json, manifest_json_ex, ManifestJsonOptions, ManifestType};10use std::{path::PathBuf, rc::Rc};111pub mod stdlib;12pub mod stdlib;2pub use stdlib::*;13pub use stdlib::*;3144pub mod format;15pub mod format;5pub mod manifest;16pub mod manifest;17pub mod sort;1819pub fn call_builtin(20 context: Context,21 loc: &Option<ExprLocation>,22 ns: &str,23 name: &str,24 args: &ArgsDesc,25) -> Result<Val> {26 Ok(match (ns, &name as &str) {27 // arr/string/function28 ("std", "length") => parse_args!(context, "std.length", args, 1, [29 0, x: [Val::Str|Val::Arr|Val::Obj], vec![ValType::Str, ValType::Arr, ValType::Obj];30 ], {31 Ok(match x {32 Val::Str(n) => Val::Num(n.chars().count() as f64),33 Val::Arr(i) => Val::Num(i.len() as f64),34 Val::Obj(o) => Val::Num(35 o.fields_visibility()36 .into_iter()37 .filter(|(_k, v)| *v)38 .count() as f64,39 ),40 _ => unreachable!(),41 })42 })?,43 // any44 ("std", "type") => parse_args!(context, "std.type", args, 1, [45 0, x, vec![];46 ], {47 Ok(Val::Str(x.value_type()?.name().into()))48 })?,49 // length, idx=>any50 ("std", "makeArray") => parse_args!(context, "std.makeArray", args, 2, [51 0, sz: [Val::Num]!!Val::Num, vec![ValType::Num];52 1, func: [Val::Func]!!Val::Func, vec![ValType::Func];53 ], {54 if sz < 0.0 {55 throw!(RuntimeError(format!("makeArray requires size >= 0, got {}", sz).into()));56 }57 let mut out = Vec::with_capacity(sz as usize);58 for i in 0..sz as usize {59 out.push(func.evaluate_values(60 Context::new(),61 &[Val::Num(i as f64)]62 )?)63 }64 Ok(Val::Arr(Rc::new(out)))65 })?,66 // string67 ("std", "codepoint") => parse_args!(context, "std.codepoint", args, 1, [68 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];69 ], {70 assert!(71 str.chars().count() == 1,72 "std.codepoint should receive single char string"73 );74 Ok(Val::Num(str.chars().take(1).next().unwrap() as u32 as f64))75 })?,76 // object, includeHidden77 ("std", "objectFieldsEx") => parse_args!(context, "std.objectFieldsEx",args, 2, [78 0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];79 1, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];80 ], {81 let mut out = obj.fields_visibility()82 .into_iter()83 .filter(|(_k, v)| *v || inc_hidden)84 .map(|(k, _v)|k)85 .collect::<Vec<_>>();86 out.sort();87 Ok(Val::Arr(Rc::new(out.into_iter().map(Val::Str).collect())))88 })?,89 // object, field, includeHidden90 ("std", "objectHasEx") => parse_args!(context, "std.objectHasEx", args, 3, [91 0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];92 1, f: [Val::Str]!!Val::Str, vec![ValType::Str];93 2, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];94 ], {95 Ok(Val::Bool(96 obj.fields_visibility()97 .into_iter()98 .filter(|(_k, v)| *v || inc_hidden)99 .any(|(k, _v)| *k == *f),100 ))101 })?,102 ("std", "primitiveEquals") => parse_args!(context, "std.primitiveEquals", args, 2, [103 0, a, vec![];104 1, b, vec![];105 ], {106 Ok(Val::Bool(primitive_equals(&a, &b)?))107 })?,108 // faster109 ("std", "equals") => parse_args!(context, "std.equals", args, 2, [110 0, a, vec![];111 1, b, vec![];112 ], {113 Ok(Val::Bool(equals(&a, &b)?))114 })?,115 ("std", "modulo") => parse_args!(context, "std.modulo", args, 2, [116 0, a: [Val::Num]!!Val::Num, vec![ValType::Num];117 1, b: [Val::Num]!!Val::Num, vec![ValType::Num];118 ], {119 Ok(Val::Num(a % b))120 })?,121 ("std", "floor") => parse_args!(context, "std.floor", args, 1, [122 0, x: [Val::Num]!!Val::Num, vec![ValType::Num];123 ], {124 Ok(Val::Num(x.floor()))125 })?,126 ("std", "log") => parse_args!(context, "std.log", args, 2, [127 0, n: [Val::Num]!!Val::Num, vec![ValType::Num];128 ], {129 Ok(Val::Num(n.ln()))130 })?,131 ("std", "trace") => parse_args!(context, "std.trace", args, 2, [132 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];133 1, rest, vec![];134 ], {135 eprint!("TRACE:");136 if let Some(loc) = loc {137 with_state(|s|{138 let locs = s.map_source_locations(&loc.0, &[loc.1]);139 eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);140 });141 }142 eprintln!(" {}", str);143 Ok(rest)144 })?,145 ("std", "pow") => parse_args!(context, "std.modulo", args, 2, [146 0, x: [Val::Num]!!Val::Num, vec![ValType::Num];147 1, n: [Val::Num]!!Val::Num, vec![ValType::Num];148 ], {149 Ok(Val::Num(x.powf(n)))150 })?,151 ("std", "extVar") => parse_args!(context, "std.extVar", args, 1, [152 0, x: [Val::Str]!!Val::Str, vec![ValType::Str];153 ], {154 Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or_else(155 || UndefinedExternalVariable(x),156 )?)157 })?,158 ("std", "native") => parse_args!(context, "std.native", args, 1, [159 0, x: [Val::Str]!!Val::Str, vec![ValType::Str];160 ], {161 Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::Func(FuncVal::NativeExt(x.clone(), v))).ok_or_else(162 || UndefinedExternalFunction(x),163 )?)164 })?,165 ("std", "filter") => parse_args!(context, "std.filter", args, 2, [166 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];167 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];168 ], {169 Ok(Val::Arr(Rc::new(170 arr.iter()171 .cloned()172 .filter(|e| {173 func174 .evaluate_values(context.clone(), &[e.clone()])175 .unwrap()176 .try_cast_bool("filter predicate")177 .unwrap()178 })179 .collect(),180 )))181 })?,182 // faster183 ("std", "foldl") => parse_args!(context, "std.foldl", args, 3, [184 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];185 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];186 2, init, vec![];187 ], {188 let mut acc = init;189 for i in arr.iter().cloned() {190 acc = func.evaluate_values(context.clone(), &[acc, i])?;191 }192 Ok(acc)193 })?,194 // faster195 ("std", "foldr") => parse_args!(context, "std.foldr", args, 3, [196 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];197 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];198 2, init, vec![];199 ], {200 let mut acc = init;201 for i in arr.iter().rev().cloned() {202 acc = func.evaluate_values(context.clone(), &[acc, i])?;203 }204 Ok(acc)205 })?,206 // faster207 #[allow(non_snake_case)]208 ("std", "sortImpl") => parse_args!(context, "std.sort", args, 2, [209 0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];210 1, keyF: [Val::Func]!!Val::Func, vec![ValType::Func];211 ], {212 if arr.len() <= 1 {213 return Ok(Val::Arr(arr))214 }215 Ok(Val::Arr(sort::sort(context, arr, keyF)?))216 })?,217 // faster218 ("std", "format") => parse_args!(context, "std.format", args, 2, [219 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];220 1, vals, vec![]221 ], {222 push(&Some(ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0)), ||format!("std.format of {}", str), ||{223 Ok(match vals {224 Val::Arr(vals) => Val::Str(format_arr(&str, &vals)?.into()),225 Val::Obj(obj) => Val::Str(format_obj(&str, &obj)?.into()),226 o => Val::Str(format_arr(&str, &[o])?.into()),227 })228 })229 })?,230 // faster231 ("std", "range") => parse_args!(context, "std.range", args, 2, [232 0, from: [Val::Num]!!Val::Num, vec![ValType::Num];233 1, to: [Val::Num]!!Val::Num, vec![ValType::Num];234 ], {235 let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));236 for i in from as usize..=to as usize {237 out.push(Val::Num(i as f64));238 }239 Ok(Val::Arr(Rc::new(out)))240 })?,241 ("std", "char") => parse_args!(context, "std.char", args, 1, [242 0, n: [Val::Num]!!Val::Num, vec![ValType::Num];243 ], {244 let mut out = String::new();245 out.push(std::char::from_u32(n as u32).ok_or_else(||246 InvalidUnicodeCodepointGot(n as u32)247 )?);248 Ok(Val::Str(out.into()))249 })?,250 ("std", "encodeUTF8") => parse_args!(context, "std.encodeUtf8", args, 1, [251 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];252 ], {253 Ok(Val::Arr(Rc::new(str.bytes().map(|b| Val::Num(b as f64)).collect())))254 })?,255 ("std", "md5") => parse_args!(context, "std.md5", args, 1, [256 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];257 ], {258 Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))259 })?,260 // faster261 ("std", "base64") => parse_args!(context, "std.base64", args, 1, [262 0, input: [Val::Str | Val::Arr], vec![ValType::Arr, ValType::Str];263 ], {264 Ok(Val::Str(match input {265 Val::Str(s) => {266 base64::encode(s.bytes().collect::<Vec<_>>()).into()267 },268 Val::Arr(a) => {269 base64::encode(a.iter().map(|v| {270 Ok(v.clone().try_cast_num("base64 array")? as u8)271 }).collect::<Result<Vec<_>>>()?).into()272 },273 _ => unreachable!()274 }))275 })?,276 // faster277 ("std", "join") => parse_args!(context, "std.join", args, 2, [278 0, sep: [Val::Str|Val::Arr], vec![ValType::Str, ValType::Arr];279 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];280 ], {281 Ok(match sep {282 Val::Arr(joiner_items) => {283 let mut out = Vec::new();284285 let mut first = true;286 for item in arr.iter().cloned() {287 if let Val::Arr(items) = item.unwrap_if_lazy()? {288 if !first {289 out.reserve(joiner_items.len());290 out.extend(joiner_items.iter().cloned());291 }292 first = false;293 out.reserve(items.len());294 out.extend(items.iter().cloned());295 } else {296 throw!(RuntimeError("in std.join all items should be arrays".into()));297 }298 }299300 Val::Arr(Rc::new(out))301 },302 Val::Str(sep) => {303 let mut out = String::new();304305 let mut first = true;306 for item in arr.iter().cloned() {307 if let Val::Str(item) = item.unwrap_if_lazy()? {308 if !first {309 out += &sep;310 }311 first = false;312 out += &item;313 } else {314 throw!(RuntimeError("in std.join all items should be strings".into()));315 }316 }317318 Val::Str(out.into())319 },320 _ => unreachable!()321 })322 })?,323 // Faster324 ("std", "escapeStringJson") => parse_args!(context, "std.escapeStringJson", args, 1, [325 0, str_: [Val::Str]!!Val::Str, vec![ValType::Str];326 ], {327 Ok(Val::Str(escape_string_json(&str_).into()))328 })?,329 // Faster330 ("std", "manifestJsonEx") => parse_args!(context, "std.manifestJsonEx", args, 2, [331 0, value, vec![];332 1, indent: [Val::Str]!!Val::Str, vec![ValType::Str];333 ], {334 Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {335 padding: &indent,336 mtype: ManifestType::Std,337 })?.into()))338 })?,339 // Faster340 ("std", "reverse") => parse_args!(context, "std.reverse", args, 1, [341 0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];342 ], {343 let mut marr = arr;344 Rc::make_mut(&mut marr).reverse();345 Ok(Val::Arr(marr))346 })?,347 ("std", "id") => parse_args!(context, "std.id", args, 1, [348 0, v, vec![];349 ], {350 Ok(v)351 })?,352 (ns, name) => throw!(IntristicNotFound(ns.into(), name.into())),353 })354}6355crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth1use crate::{builtin::format::FormatError, ValType};1use crate::{2 builtin::{format::FormatError, sort::SortError},3 ValType,4};2use jrsonnet_parser::{BinaryOpType, ExprLocation, UnaryOpType};5use jrsonnet_parser::{BinaryOpType, ExprLocation, UnaryOpType};3use std::{path::PathBuf, rc::Rc};6use std::{path::PathBuf, rc::Rc};69 InvalidUnicodeCodepointGot(u32),72 InvalidUnicodeCodepointGot(u32),707371 Format(FormatError),74 Format(FormatError),75 Sort(SortError),72}76}73impl From<Error> for LocError {77impl From<Error> for LocError {74 fn from(e: Error) -> Self {78 fn from(e: Error) -> Self {crates/jrsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth1use crate::{1use crate::{2 builtin::{3 format::{format_arr, format_obj},4 manifest::{escape_string_json, manifest_json_ex, ManifestJsonOptions, ManifestType},5 },6 context_creator, equals,2 context_creator, error::Error::*, future_wrapper, lazy_val, push, throw, with_state, Context,7 error::Error::*,8 future_wrapper, lazy_val, parse_args, parse_function_call, primitive_equals, push, throw,9 with_state, Context, ContextCreator, FuncDesc, LazyBinding, LazyVal, LocError, ObjMember,3 ContextCreator, FuncDesc, FuncVal, LazyBinding, LazyVal, ObjMember, ObjValue, Result, Val,10 ObjValue, Result, Val, ValType,4 ValType,11};5};12use closure::closure;6use closure::closure;15 ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType,9 ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType,16 Visibility,10 Visibility,17};11};18use std::{cmp::Ordering, collections::HashMap, path::PathBuf, rc::Rc};12use std::{collections::HashMap, rc::Rc};191320pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (Rc<str>, LazyBinding) {14pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (Rc<str>, LazyBinding) {21 let b = b.clone();15 let b = b.clone();51}45}524653pub fn evaluate_method(ctx: Context, name: Rc<str>, params: ParamsDesc, body: LocExpr) -> Val {47pub fn evaluate_method(ctx: Context, name: Rc<str>, params: ParamsDesc, body: LocExpr) -> Val {54 Val::Func(Rc::new(FuncDesc {48 Val::Func(FuncVal::Normal(Rc::new(FuncDesc {55 name,49 name,56 ctx,50 ctx,57 params,51 params,58 body,52 body,59 }))53 })))60}54}615562pub fn evaluate_field_name(56pub fn evaluate_field_name(389 })383 })390}384}391392/// Extracts code block and disables inlining for them393/// Fixes WASM to java bytecode compilation failing because of very large method394#[cfg(feature = "unstable")]395macro_rules! noinline {396 ($e:expr) => {397 (#![inline(never)] move || $e)()398 };399}400#[cfg(not(feature = "unstable"))]401macro_rules! noinline {402 ($e:expr) => {403 (move || $e)()404 };405}406385407pub fn evaluate_apply(386pub fn evaluate_apply(408 context: Context,387 context: Context,414 let lazy = evaluate(context.clone(), value)?;393 let lazy = evaluate(context.clone(), value)?;415 let value = lazy.unwrap_if_lazy()?;394 let value = lazy.unwrap_if_lazy()?;416 Ok(match value {395 Ok(match value {417 Val::Intristic(ns, name) => push(418 loc,419 || format!("intristic <{}.{}> call", ns, name),420 || {421 Ok(match (&ns as &str, &name as &str) {422 // arr/string/function423 ("std", "length") => parse_args!(context, "std.length", args, 1, [424 0, x: [Val::Str|Val::Arr|Val::Obj], vec![ValType::Str, ValType::Arr, ValType::Obj];425 ], {426 Ok(match x {427 Val::Str(n) => Val::Num(n.chars().count() as f64),428 Val::Arr(i) => Val::Num(i.len() as f64),429 Val::Obj(o) => Val::Num(430 o.fields_visibility()431 .into_iter()432 .filter(|(_k, v)| *v)433 .count() as f64,434 ),435 _ => unreachable!(),436 })437 })?,438 // any439 ("std", "type") => parse_args!(context, "std.type", args, 1, [440 0, x, vec![];441 ], {442 Ok(Val::Str(x.value_type()?.name().into()))443 })?,444 // length, idx=>any445 ("std", "makeArray") => {446 noinline!(parse_args!(context, "std.makeArray", args, 2, [447 0, sz: [Val::Num]!!Val::Num, vec![ValType::Num];448 1, func: [Val::Func]!!Val::Func, vec![ValType::Func];449 ], {450 if sz < 0.0 {451 throw!(RuntimeError(format!("makeArray requires size >= 0, got {}", sz).into()));452 }453 let mut out = Vec::with_capacity(sz as usize);454 for i in 0..sz as usize {455 out.push(func.evaluate_values(456 Context::new(),457 &[Val::Num(i as f64)]458 )?)459 }460 Ok(Val::Arr(Rc::new(out)))461 }))?462 }463 // string464 ("std", "codepoint") => parse_args!(context, "std.codepoint", args, 1, [465 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];466 ], {467 assert!(468 str.chars().count() == 1,469 "std.codepoint should receive single char string"470 );471 Ok(Val::Num(str.chars().take(1).next().unwrap() as u32 as f64))472 })?,473 // object, includeHidden474 ("std", "objectFieldsEx") => {475 noinline!(parse_args!(context, "std.objectFieldsEx",args, 2, [476 0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];477 1, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];478 ], {479 let mut out = obj.fields_visibility()480 .into_iter()481 .filter(|(_k, v)| *v || inc_hidden)482 .map(|(k, _v)|k)483 .collect::<Vec<_>>();484 out.sort();485 Ok(Val::Arr(Rc::new(out.into_iter().map(Val::Str).collect())))486 }))?487 }488 // object, field, includeHidden489 ("std", "objectHasEx") => parse_args!(context, "std.objectHasEx", args, 3, [490 0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];491 1, f: [Val::Str]!!Val::Str, vec![ValType::Str];492 2, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];493 ], {494 Ok(Val::Bool(495 obj.fields_visibility()496 .into_iter()497 .filter(|(_k, v)| *v || inc_hidden)498 .any(|(k, _v)| *k == *f),499 ))500 })?,501 ("std", "primitiveEquals") => {502 parse_args!(context, "std.primitiveEquals", args, 2, [503 0, a, vec![];504 1, b, vec![];505 ], {506 Ok(Val::Bool(primitive_equals(&a, &b)?))507 })?508 }509 // faster510 ("std", "equals") => parse_args!(context, "std.equals", args, 2, [511 0, a, vec![];512 1, b, vec![];513 ], {514 Ok(Val::Bool(equals(&a, &b)?))515 })?,516 ("std", "modulo") => parse_args!(context, "std.modulo", args, 2, [517 0, a: [Val::Num]!!Val::Num, vec![ValType::Num];518 1, b: [Val::Num]!!Val::Num, vec![ValType::Num];519 ], {520 Ok(Val::Num(a % b))521 })?,522 ("std", "floor") => parse_args!(context, "std.floor", args, 1, [523 0, x: [Val::Num]!!Val::Num, vec![ValType::Num];524 ], {525 Ok(Val::Num(x.floor()))526 })?,527 ("std", "log") => parse_args!(context, "std.log", args, 2, [528 0, n: [Val::Num]!!Val::Num, vec![ValType::Num];529 ], {530 Ok(Val::Num(n.ln()))531 })?,532 ("std", "trace") => parse_args!(context, "std.trace", args, 2, [533 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];534 1, rest, vec![];535 ], {536 eprint!("TRACE:");537 if let Some(loc) = loc {538 with_state(|s|{539 let locs = s.map_source_locations(&loc.0, &[loc.1]);540 eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);541 });542 }543 eprintln!(" {}", str);544 Ok(rest)545 })?,546 ("std", "pow") => parse_args!(context, "std.modulo", args, 2, [547 0, x: [Val::Num]!!Val::Num, vec![ValType::Num];548 1, n: [Val::Num]!!Val::Num, vec![ValType::Num];549 ], {550 Ok(Val::Num(x.powf(n)))551 })?,552 ("std", "extVar") => parse_args!(context, "std.extVar", args, 1, [553 0, x: [Val::Str]!!Val::Str, vec![ValType::Str];554 ], {555 Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or_else(556 || UndefinedExternalVariable(x),557 )?)558 })?,559 ("std", "native") => parse_args!(context, "std.native", args, 1, [560 0, x: [Val::Str]!!Val::Str, vec![ValType::Str];561 ], {562 Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::NativeExt(x.clone(), v)).ok_or_else(563 || UndefinedExternalFunction(x),564 )?)565 })?,566 ("std", "filter") => noinline!(parse_args!(context, "std.filter", args, 2, [567 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];568 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];569 ], {570 Ok(Val::Arr(Rc::new(571 arr.iter()572 .cloned()573 .filter(|e| {574 func575 .evaluate_values(context.clone(), &[e.clone()])576 .unwrap()577 .try_cast_bool("filter predicate")578 .unwrap()579 })580 .collect(),581 )))582 }))?,583 // faster584 ("std", "foldl") => noinline!(parse_args!(context, "std.foldl", args, 3, [585 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];586 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];587 2, init, vec![];588 ], {589 let mut acc = init;590 for i in arr.iter().cloned() {591 acc = func.evaluate_values(context.clone(), &[acc, i])?;592 }593 Ok(acc)594 }))?,595 // faster596 ("std", "foldr") => noinline!(parse_args!(context, "std.foldr", args, 3, [597 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];598 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];599 2, init, vec![];600 ], {601 let mut acc = init;602 for i in arr.iter().rev().cloned() {603 acc = func.evaluate_values(context.clone(), &[acc, i])?;604 }605 Ok(acc)606 }))?,607 // faster608 #[allow(non_snake_case)]609 ("std", "sortImpl") => noinline!(parse_args!(context, "std.sort", args, 2, [610 0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];611 1, keyF: [Val::Func]!!Val::Func, vec![ValType::Func];612 ], {613 if arr.len() <= 1 {614 return Ok(Val::Arr(arr))615 }616 let mut new_arr = arr.iter().cloned().collect::<Vec<_>>();617 match keyF.evaluate_values(context.clone(), &[new_arr[0].clone()])? {618 Val::Str(_) => {619 let mut err = None;620 new_arr.sort_by_cached_key(|k| {621 match keyF.evaluate_values(context.clone(), &[k.clone()]) {622 Ok(Val::Str(v)) => v,623 Ok(_) => {624 err = Some(LocError::new(RuntimeError("types of all array elements should equal".into())));625 "".into()626 }627 Err(e) => {628 err = Some(e);629 "".into()630 }631 }632 });633 if let Some(e) = err {634 return Err(e);635 }636 },637 Val::Num(_) => {638 let mut err = None;639 new_arr.sort_unstable_by(|a, b| {640 match (keyF.evaluate_values(context.clone(), &[a.clone()]), keyF.evaluate_values(context.clone(), &[b.clone()])) {641 (Ok(Val::Num(a)), Ok(Val::Num(b))) => a.partial_cmp(&b).unwrap(),642 (Ok(_a), Ok(_b)) => {643 err = Some(RuntimeError("types of all array elements should equal".into()).into());644 Ordering::Equal645 }646 (Err(e), _) | (_, Err(e)) => {647 err = Some(e);648 Ordering::Equal649 }650 }651 });652 if let Some(e) = err {653 return Err(e);654 }655 },656 _ => throw!(RuntimeError("keys should be number or string".into()))657 }658 Ok(Val::Arr(Rc::new(new_arr)))659 }))?,660 // faster661 ("std", "format") => parse_args!(context, "std.format", args, 2, [662 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];663 1, vals, vec![]664 ], {665 push(&Some(ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0)), ||format!("std.format of {}", str), ||{666 Ok(match vals {667 Val::Arr(vals) => Val::Str(format_arr(&str, &vals)?.into()),668 Val::Obj(obj) => Val::Str(format_obj(&str, &obj)?.into()),669 o => Val::Str(format_arr(&str, &[o])?.into()),670 })671 })672 })?,673 // faster674 ("std", "range") => parse_args!(context, "std.range", args, 2, [675 0, from: [Val::Num]!!Val::Num, vec![ValType::Num];676 1, to: [Val::Num]!!Val::Num, vec![ValType::Num];677 ], {678 let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));679 for i in from as usize..=to as usize {680 out.push(Val::Num(i as f64));681 }682 Ok(Val::Arr(Rc::new(out)))683 })?,684 ("std", "char") => parse_args!(context, "std.char", args, 1, [685 0, n: [Val::Num]!!Val::Num, vec![ValType::Num];686 ], {687 let mut out = String::new();688 out.push(std::char::from_u32(n as u32).ok_or_else(||689 InvalidUnicodeCodepointGot(n as u32)690 )?);691 Ok(Val::Str(out.into()))692 })?,693 ("std", "encodeUTF8") => parse_args!(context, "std.encodeUtf8", args, 1, [694 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];695 ], {696 Ok(Val::Arr(Rc::new(str.bytes().map(|b| Val::Num(b as f64)).collect())))697 })?,698 ("std", "md5") => noinline!(parse_args!(context, "std.md5", args, 1, [699 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];700 ], {701 Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))702 }))?,703 // faster704 ("std", "base64") => parse_args!(context, "std.base64", args, 1, [705 0, input: [Val::Str | Val::Arr], vec![ValType::Arr, ValType::Str];706 ], {707 Ok(Val::Str(match input {708 Val::Str(s) => {709 base64::encode(s.bytes().collect::<Vec<_>>()).into()710 },711 Val::Arr(a) => {712 base64::encode(a.iter().map(|v| {713 Ok(v.clone().try_cast_num("base64 array")? as u8)714 }).collect::<Result<Vec<_>>>()?).into()715 },716 _ => unreachable!()717 }))718 })?,719 // faster720 ("std", "join") => noinline!(parse_args!(context, "std.join", args, 2, [721 0, sep: [Val::Str|Val::Arr], vec![ValType::Str, ValType::Arr];722 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];723 ], {724 Ok(match sep {725 Val::Arr(joiner_items) => {726 let mut out = Vec::new();727728 let mut first = true;729 for item in arr.iter().cloned() {730 if let Val::Arr(items) = item.unwrap_if_lazy()? {731 if !first {732 out.reserve(joiner_items.len());733 out.extend(joiner_items.iter().cloned());734 }735 first = false;736 out.reserve(items.len());737 out.extend(items.iter().cloned());738 } else {739 throw!(RuntimeError("in std.join all items should be arrays".into()));740 }741 }742743 Val::Arr(Rc::new(out))744 },745 Val::Str(sep) => {746 let mut out = String::new();747748 let mut first = true;749 for item in arr.iter().cloned() {750 if let Val::Str(item) = item.unwrap_if_lazy()? {751 if !first {752 out += &sep;753 }754 first = false;755 out += &item;756 } else {757 throw!(RuntimeError("in std.join all items should be strings".into()));758 }759 }760761 Val::Str(out.into())762 },763 _ => unreachable!()764 })765 }))?,766 // Faster767 ("std", "escapeStringJson") => {768 parse_args!(context, "std.escapeStringJson", args, 1, [769 0, str_: [Val::Str]!!Val::Str, vec![ValType::Str];770 ], {771 Ok(Val::Str(escape_string_json(&str_).into()))772 })?773 }774 // Faster775 ("std", "manifestJsonEx") => {776 parse_args!(context, "std.manifestJsonEx", args, 2, [777 0, value, vec![];778 1, indent: [Val::Str]!!Val::Str, vec![ValType::Str];779 ], {780 Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {781 padding: &indent,782 mtype: ManifestType::Std,783 })?.into()))784 })?785 }786 (ns, name) => throw!(IntristicNotFound(ns.into(), name.into())),787 })788 },789 )?,790 Val::NativeExt(n, f) => push(791 loc,792 || format!("native <{}> call", n),793 || {794 let args = parse_function_call(context, None, &f.params, args, true)?;795 let mut out_args = Vec::with_capacity(f.params.len());796 for p in f.params.0.iter() {797 out_args.push(args.binding(p.0.clone())?.evaluate()?);798 }799 Ok(f.call(&out_args)?)800 },801 )?,802 Val::Func(f) => {396 Val::Func(f) => {803 let body = || f.evaluate(context, args, tailstrict);397 let body = || f.evaluate(context, loc, args, tailstrict);804 if tailstrict {398 if tailstrict {805 body()?399 body()?806 } else {400 } else {807 push(loc, || format!("function <{}> call", f.name), body)?401 push(loc, || format!("function <{}> call", f.name()), body)?808 }402 }809 }403 }810 v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type()?)),404 v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type()?)),874 } else if let Some(Val::Str(n)) =468 } else if let Some(Val::Str(n)) =875 v.get("__intristic_namespace__".into())?469 v.get("__intristic_namespace__".into())?876 {470 {877 Ok(Val::Intristic(n, s))471 Ok(Val::Func(FuncVal::Intristic(n, s)))878 } else {472 } else {879 throw!(NoSuchField(s))473 throw!(NoSuchField(s))880 }474 }crates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth40 }40 }41 Value::Object(out)41 Value::Object(out)42 }42 }43 Val::Func(_) | Val::Intristic(_, _) | Val::NativeExt(_, _) => {43 Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),44 throw!(RuntimeError("tried to manifest function".into()))45 }46 })44 })47 }45 }48}46}crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth1use crate::{1use crate::{2 builtin::manifest::{manifest_json_ex, ManifestJsonOptions, ManifestType},2 builtin::{3 call_builtin,4 manifest::{manifest_json_ex, ManifestJsonOptions, ManifestType},5 },3 error::Error::*,6 error::Error::*,4 evaluate,7 evaluate,5 function::{parse_function_call, parse_function_call_map, place_args},8 function::{parse_function_call, parse_function_call_map, place_args},6 native::NativeCallback,9 native::NativeCallback,7 throw, with_state, Context, ObjValue, Result,10 throw, with_state, Context, ObjValue, Result,8};11};9use jrsonnet_parser::{el, Arg, ArgsDesc, Expr, LocExpr, ParamsDesc};12use jrsonnet_parser::{el, Arg, ArgsDesc, Expr, ExprLocation, LocExpr, ParamsDesc};10use std::{13use std::{11 cell::RefCell,14 cell::RefCell,12 collections::HashMap,15 collections::HashMap,68 pub body: LocExpr,71 pub body: LocExpr,69}72}7374#[derive(Debug, Clone)]75pub enum FuncVal {76 /// Plain function implemented in jsonnet77 Normal(Rc<FuncDesc>),78 /// Standard library function79 Intristic(Rc<str>, Rc<str>),80 /// Library functions implemented in native81 NativeExt(Rc<str>, Rc<NativeCallback>),82}83impl PartialEq for FuncVal {84 fn eq(&self, other: &Self) -> bool {85 match (self, other) {86 (FuncVal::Normal(a), FuncVal::Normal(b)) => a == b,87 (FuncVal::Intristic(ans, an), FuncVal::Intristic(bns, bn)) => ans == bns && an == bn,88 (FuncVal::NativeExt(an, _), FuncVal::NativeExt(bn, _)) => an == bn,89 (..) => false,90 }91 }92}70impl FuncDesc {93impl FuncVal {71 /// This function is always inlined to make tailstrict work94 pub fn is_ident(&self) -> bool {95 matches!(&self, FuncVal::Intristic(ns, n) if ns as &str == "std" && n as &str == "id")96 }97 pub fn name(&self) -> Rc<str> {98 match self {99 FuncVal::Normal(normal) => normal.name.clone(),100 FuncVal::Intristic(ns, name) => format!("intristic.{}.{}", ns, name).into(),101 FuncVal::NativeExt(n, _) => format!("native.{}", n).into(),102 }103 }72 pub fn evaluate(&self, call_ctx: Context, args: &ArgsDesc, tailstrict: bool) -> Result<Val> {104 pub fn evaluate(105 &self,106 call_ctx: Context,107 loc: &Option<ExprLocation>,108 args: &ArgsDesc,109 tailstrict: bool,110 ) -> Result<Val> {111 match self {112 FuncVal::Normal(func) => {73 let ctx = parse_function_call(113 let ctx = parse_function_call(74 call_ctx,114 call_ctx,75 Some(self.ctx.clone()),115 Some(func.ctx.clone()),76 &self.params,116 &func.params,77 args,117 args,78 tailstrict,118 tailstrict,79 )?;119 )?;120 evaluate(ctx, &func.body)121 }122 FuncVal::Intristic(ns, name) => call_builtin(call_ctx, loc, &ns, &name, args),123 FuncVal::NativeExt(_name, handler) => {80 evaluate(ctx, &self.body)124 let args = parse_function_call(call_ctx, None, &handler.params, args, true)?;125 let mut out_args = Vec::with_capacity(handler.params.len());126 for p in handler.params.0.iter() {127 out_args.push(args.binding(p.0.clone())?.evaluate()?);128 }129 Ok(handler.call(&out_args)?)130 }131 }81 }132 }8213383 pub fn evaluate_map(134 pub fn evaluate_map(86 args: &HashMap<Rc<str>, Val>,137 args: &HashMap<Rc<str>, Val>,87 tailstrict: bool,138 tailstrict: bool,88 ) -> Result<Val> {139 ) -> Result<Val> {140 match self {141 FuncVal::Normal(func) => {89 let ctx = parse_function_call_map(142 let ctx = parse_function_call_map(90 call_ctx,143 call_ctx,91 Some(self.ctx.clone()),144 Some(func.ctx.clone()),92 &self.params,145 &func.params,93 args,146 args,94 tailstrict,147 tailstrict,95 )?;148 )?;96 evaluate(ctx, &self.body)149 evaluate(ctx, &func.body)150 }151 FuncVal::Intristic(_, _) => todo!(),152 FuncVal::NativeExt(_, _) => todo!(),153 }97 }154 }9815599 pub fn evaluate_values(&self, call_ctx: Context, args: &[Val]) -> Result<Val> {156 pub fn evaluate_values(&self, call_ctx: Context, args: &[Val]) -> Result<Val> {157 match self {158 FuncVal::Normal(func) => {100 let ctx = place_args(call_ctx, Some(self.ctx.clone()), &self.params, args)?;159 let ctx = place_args(call_ctx, Some(func.ctx.clone()), &func.params, args)?;101 evaluate(ctx, &self.body)160 evaluate(ctx, &func.body)161 }162 FuncVal::Intristic(_, _) => todo!(),163 FuncVal::NativeExt(_, _) => todo!(),164 }102 }165 }103}166}104167149 Lazy(LazyVal),212 Lazy(LazyVal),150 Arr(Rc<Vec<Val>>),213 Arr(Rc<Vec<Val>>),151 Obj(ObjValue),214 Obj(ObjValue),152 Func(Rc<FuncDesc>),215 Func(FuncVal),153154 // Library functions implemented in native155 Intristic(Rc<str>, Rc<str>),156 NativeExt(Rc<str>, Rc<NativeCallback>),157}216}158macro_rules! matches_unwrap {217macro_rules! matches_unwrap {159 ($e: expr, $p: pat, $r: expr) => {218 ($e: expr, $p: pat, $r: expr) => {193 self.assert_type(context, ValType::Num)?;252 self.assert_type(context, ValType::Num)?;194 Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Num(v), v))253 Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Num(v), v))195 }254 }255 pub fn inplace_unwrap(&mut self) -> Result<()> {256 while let Val::Lazy(lazy) = self {257 *self = lazy.evaluate()?;258 }259 Ok(())260 }196 pub fn unwrap_if_lazy(&self) -> Result<Self> {261 pub fn unwrap_if_lazy(&self) -> Result<Self> {197 Ok(if let Val::Lazy(v) = self {262 Ok(if let Val::Lazy(v) = self {198 v.evaluate()?.unwrap_if_lazy()?263 v.evaluate()?.unwrap_if_lazy()?208 Val::Obj(..) => ValType::Obj,273 Val::Obj(..) => ValType::Obj,209 Val::Bool(_) => ValType::Bool,274 Val::Bool(_) => ValType::Bool,210 Val::Null => ValType::Null,275 Val::Null => ValType::Null,211 Val::Func(..) | Val::Intristic(_, _) | Val::NativeExt(_, _) => ValType::Func,276 Val::Func(..) => ValType::Func,212 Val::Lazy(_) => self.clone().unwrap_if_lazy()?.value_type()?,277 Val::Lazy(_) => self.clone().unwrap_if_lazy()?.value_type()?,213 })278 })214 }279 }374}439}375440376fn is_function_like(val: &Val) -> bool {441fn is_function_like(val: &Val) -> bool {377 matches!(val, Val::Func(_) | Val::Intristic(_, _) | Val::NativeExt(_, _))442 matches!(val, Val::Func(_))378}443}379444380/// Implements std.primitiveEquals builtin445/// Implements std.primitiveEquals builtincrates/jrsonnet-stdlib/src/std.jsonnetdiffbeforeafterboth2 __intristic_namespace__:: 'std',2 __intristic_namespace__:: 'std',334 local std = self,4 local std = self,5 local id = function(x) x,5 local id = std.id,667 isString(v):: std.type(v) == 'string',7 isString(v):: std.type(v) == 'string',8 isNumber(v):: std.type(v) == 'number',8 isNumber(v):: std.type(v) == 'number',