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

difftreelog

refactor do not allocate for BuiltinParam vec

mqrttluxYaroslav Bolyukin2026-03-21parent: #6a2bca3.patch.diff
in: master

5 files changed

modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
1111
12use self::destructure::destruct;12use self::destructure::destruct;
13use crate::{13use crate::{
14 arr::ArrValue,14 Context, Error, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result, ResultExt, SupThis, Unbound, Val, arr::ArrValue, bail, destructure::evaluate_dest, error::{ErrorKind::*, suggest_object_fields}, evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op}, function::{CallLocation, FuncDesc, FuncVal, builtin::{ParamDefault, ParamName, ParamParse}}, gc::WithCapacityExt as _, in_frame, typed::Typed, val::{CachedUnbound, IndexableVal, NumValue, StrValue, Thunk}, with_state
15 bail,
16 destructure::evaluate_dest,
17 error::{suggest_object_fields, ErrorKind::*},
18 evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},
19 function::{CallLocation, FuncDesc, FuncVal},
20 gc::WithCapacityExt as _,
21 in_frame,
22 typed::Typed,
23 val::{CachedUnbound, IndexableVal, NumValue, StrValue, Thunk},
24 with_state, Context, Error, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result,
25 ResultExt, SupThis, Unbound, Val,
26};15};
27pub mod destructure;16pub mod destructure;
28pub mod operator;17pub mod operator;
88 Val::Func(FuncVal::Normal(Cc::new(FuncDesc {77 Val::Func(FuncVal::Normal(Cc::new(FuncDesc {
89 name,78 name,
90 ctx,79 ctx,
80 params_parse: params
81 .iter()
82 .map(|p| {
83 ParamParse::new(
84 p.0.name().map_or(ParamName::ANONYMOUS, ParamName::new),
85 ParamDefault::exists(p.1.is_some()),
86 )
87 })
88 .collect(),
91 params,89 params,
92 body,90 body,
93 })))91 })))
modifiedcrates/jrsonnet-evaluator/src/function/builtin.rsdiffbeforeafterboth
1use std::{any::Any, borrow::Cow};1use std::any::Any;
22
3use jrsonnet_gcmodule::{cc_dyn, Trace, TraceBox};3use jrsonnet_gcmodule::{cc_dyn, Acyclic, Trace, TraceBox};
4use jrsonnet_interner::IStr;4use jrsonnet_interner::IStr;
55
6use super::{arglike::ArgsLike, parse::parse_builtin_call, CallLocation};6use super::{arglike::ArgsLike, parse::parse_builtin_call, CallLocation};
7use crate::{Context, Result, Val};7use crate::{Context, Result, Val};
88
9/// Can't have `str` | `IStr`, because constant `BuiltinParam` causes
10/// `E0492: constant functions cannot refer to interior mutable data`
11#[derive(Clone, Trace)]9#[derive(Clone, Acyclic)]
12pub struct ParamName(Option<Cow<'static, str>>);10pub struct ParamName(Option<IStr>);
13impl ParamName {11impl ParamName {
14 pub const ANONYMOUS: Self = Self(None);12 pub const ANONYMOUS: Self = Self(None);
15 pub const fn new_static(name: &'static str) -> Self {13 pub fn new(name: IStr) -> Self {
16 Self(Some(Cow::Borrowed(name)))14 Self(Some(name))
17 }15 }
18 pub fn new_dynamic(name: String) -> Self {
19 Self(Some(Cow::Owned(name)))
20 }
21 pub fn as_str(&self) -> Option<&str> {16 pub fn as_str(&self) -> Option<&str> {
22 self.0.as_deref()17 self.0.as_deref()
23 }18 }
33 }28 }
34}29}
3530
36#[derive(Clone, Copy, Debug, Trace)]31#[derive(Clone, Copy, Debug, Acyclic)]
37pub enum ParamDefault {32pub enum ParamDefault {
38 None,33 None,
39 Exists,34 Exists,
49 }44 }
50}45}
46
47#[macro_export]
48macro_rules! params {
49 (@name unnamed) => { ParamName::ANONYMOUS };
50 (@name named $name:literal) => { ParamName::new($crate::IStr::from($name)) };
51 ($($(#[$meta:meta])* [$kind:ident $(($lit:literal))? => $default:expr]),* $(,)?) => {
52 thread_local! {
53 static PARAMS: [ParamParse; { const N: usize = <[u8]>::len(&[$($(#[$meta])* 0u8),*]); N }] = [
54 $($(#[$meta])* ParamParse::new(params!(@name $kind $($lit)?), $default)),*
55 ];
56 }
57 };
58}
5159
52#[derive(Clone, Trace)]60#[derive(Clone, Acyclic)]
53pub struct BuiltinParam {61pub struct ParamParse {
54 name: ParamName,62 name: ParamName,
55 default: ParamDefault,63 default: ParamDefault,
56}64}
57impl BuiltinParam {65impl ParamParse {
58 pub const fn new(name: ParamName, default: ParamDefault) -> Self {66 pub fn new(name: ParamName, default: ParamDefault) -> Self {
59 Self { name, default }67 Self { name, default }
60 }68 }
61 /// Parameter name for named call parsing69 /// Parameter name for named call parsing
81 self.0.name()89 self.0.name()
82 }90 }
8391
84 fn params(&self) -> &[BuiltinParam] {92 fn params(&self) -> &[ParamParse] {
85 self.0.params()93 self.0.params()
86 }94 }
8795
101 /// Function name to be used in stack traces109 /// Function name to be used in stack traces
102 fn name(&self) -> &str;110 fn name(&self) -> &str;
103 /// Parameter names for named calls111 /// Parameter names for named calls
104 fn params(&self) -> &[BuiltinParam];112 fn params(&self) -> &[ParamParse];
105 /// Call the builtin113 /// Call the builtin
106 fn call(&self, ctx: Context, loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val>;114 fn call(&self, ctx: Context, loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val>;
107115
118126
119#[derive(Trace)]127#[derive(Trace)]
120pub struct NativeCallback {128pub struct NativeCallback {
121 pub(crate) params: Vec<BuiltinParam>,129 pub(crate) params: Vec<ParamParse>,
122 handler: TraceBox<dyn NativeCallbackHandler>,130 handler: TraceBox<dyn NativeCallbackHandler>,
123}131}
124impl NativeCallback {132impl NativeCallback {
127 Self {135 Self {
128 params: params136 params: params
129 .into_iter()137 .into_iter()
130 .map(|n| BuiltinParam {138 .map(|n| ParamParse {
131 name: ParamName::new_dynamic(n),139 name: ParamName::new(n.into()),
132 default: ParamDefault::None,140 default: ParamDefault::None,
133 })141 })
134 .collect(),142 .collect(),
144 "<native>"152 "<native>"
145 }153 }
146154
147 fn params(&self) -> &[BuiltinParam] {155 fn params(&self) -> &[ParamParse] {
148 &self.params156 &self.params
149 }157 }
150158
modifiedcrates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth
1use std::{fmt::Debug, rc::Rc};1use std::{fmt::Debug, rc::Rc};
22
3pub use arglike::{ArgLike, ArgsLike, TlaArg};3pub use arglike::{ArgLike, ArgsLike, TlaArg};
4use educe::Educe;
4use jrsonnet_gcmodule::{Cc, Trace};5use jrsonnet_gcmodule::{Cc, Trace};
5use jrsonnet_interner::IStr;6use jrsonnet_interner::IStr;
6pub use jrsonnet_macros::builtin;7pub use jrsonnet_macros::builtin;
7use jrsonnet_parser::{Destruct, Expr, ParamsDesc, Span, Spanned};8use jrsonnet_parser::{Destruct, Expr, ParamsDesc, Span, Spanned};
89
9use self::{10use self::{
10 arglike::OptionalContext,11 arglike::OptionalContext,
11 builtin::{Builtin, BuiltinParam, ParamDefault, ParamName, StaticBuiltin},12 builtin::{Builtin, ParamParse, StaticBuiltin},
12 native::NativeDesc,13 native::NativeDesc,
13 parse::{parse_default_function_call, parse_function_call},14 parse::{parse_default_function_call, parse_function_call},
14};15};
40}41}
4142
42/// Represents Jsonnet function defined in code.43/// Represents Jsonnet function defined in code.
43#[derive(Debug, Trace, PartialEq)]44#[derive(Trace, Educe)]
45#[educe(Debug, PartialEq)]
44pub struct FuncDesc {46pub struct FuncDesc {
45 /// # Example47 /// # Example
46 ///48 ///
68 /// Function body70 /// Function body
69 pub body: Rc<Spanned<Expr>>,71 pub body: Rc<Spanned<Expr>>,
72
73 #[educe(PartialEq = false, Debug = false)]
74 pub(crate) params_parse: Vec<ParamParse>,
70}75}
71impl FuncDesc {76impl FuncDesc {
72 /// Create body context, but fill arguments without defaults with lazy error77 /// Create body context, but fill arguments without defaults with lazy error
134 Self::StaticBuiltin(static_builtin)139 Self::StaticBuiltin(static_builtin)
135 }140 }
136141
137 pub fn params(&self) -> Vec<BuiltinParam> {142 pub fn params(&self) -> &[ParamParse] {
138 match self {143 match self {
139 Self::Id => ID.params().to_vec(),144 Self::Id => ID.params(),
140 Self::StaticBuiltin(i) => i.params().to_vec(),145 Self::StaticBuiltin(i) => i.params(),
141 Self::Builtin(i) => i.params().to_vec(),146 Self::Builtin(i) => i.params(),
142 Self::Normal(p) => p147 Self::Normal(p) => &p.params_parse,
143 .params
144 .iter()
145 .map(|p| {
146 BuiltinParam::new(
147 p.0.name()
148 .as_ref()
149 .map(IStr::to_string)
150 .map_or(ParamName::ANONYMOUS, ParamName::new_dynamic),
151 ParamDefault::exists(p.1.is_some()),
152 )
153 })
154 .collect(),
155 Self::Thunk(_) => vec![],148 Self::Thunk(_) => &[],
156 }149 }
157 }150 }
158 /// Amount of non-default required arguments151 /// Amount of non-default required arguments
modifiedcrates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth
4use jrsonnet_parser::ParamsDesc;4use jrsonnet_parser::ParamsDesc;
5use rustc_hash::FxHashMap;5use rustc_hash::FxHashMap;
66
7use super::{arglike::ArgsLike, builtin::BuiltinParam};7use super::{arglike::ArgsLike, builtin::ParamParse};
8use crate::{8use crate::{
9 bail,9 bail,
10 destructure::destruct,10 destructure::destruct,
147/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily147/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily
148pub fn parse_builtin_call(148pub fn parse_builtin_call(
149 ctx: Context,149 ctx: Context,
150 params: &[BuiltinParam],150 params: &[ParamParse],
151 args: &dyn ArgsLike,151 args: &dyn ArgsLike,
152 tailstrict: bool,152 tailstrict: bool,
153) -> Result<Vec<Option<Thunk<Val>>>> {153) -> Result<Vec<Option<Thunk<Val>>>> {
modifiedcrates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth
239 cfg_attrs,239 cfg_attrs,
240 ..240 ..
241 } => {241 } => {
242 let name = name242 let name = name.as_ref().map_or_else(|| quote! {unnamed}, |n| quote! {named(#n)});
243 .as_ref()
244 .map_or_else(|| quote! {None}, |n| quote! {ParamName::new_static(#n)});
245 let default = match optionality {243 let default = match optionality {
246 Optionality::Required => quote!(ParamDefault::None),244 Optionality::Required => quote!(ParamDefault::None),
247 Optionality::Optional => quote!(ParamDefault::Exists),245 Optionality::Optional => quote!(ParamDefault::Exists),
248 Optionality::Default(e) => quote!(ParamDefault::Literal(stringify!(#e))),246 Optionality::Default(e) => quote!(ParamDefault::Literal(stringify!(#e))),
249 };247 };
250 Some(quote! {248 Some(quote! {
251 #(#cfg_attrs)*249 #(#cfg_attrs)*
252 BuiltinParam::new(#name, #default),250 [#name => #default],
253 })251 })
254 }252 }
255 ArgInfo::Lazy { is_option, name } => {253 ArgInfo::Lazy { is_option, name } => {
256 let name = name254 let name = name.as_ref().map_or_else(|| quote! {unnamed}, |n| quote! {named(#n)});
257 .as_ref()
258 .map_or_else(|| quote! {None}, |n| quote! {ParamName::new_static(#n)});
259 Some(quote! {255 Some(quote! {
260 BuiltinParam::new(#name, ParamDefault::exists(#is_option)),256 [#name => ParamDefault::exists(#is_option)],
261 })257 })
262 }258 }
263 ArgInfo::Context | ArgInfo::Location | ArgInfo::This => None,259 ArgInfo::Context | ArgInfo::Location | ArgInfo::This => None,
368 const _: () = {364 const _: () = {
369 use ::jrsonnet_evaluator::{365 use ::jrsonnet_evaluator::{
370 State, Val,366 State, Val,
371 function::{builtin::{Builtin, StaticBuiltin, BuiltinParam, ParamName, ParamDefault}, CallLocation, ArgsLike, parse::parse_builtin_call},367 function::{builtin::{Builtin, StaticBuiltin, ParamParse, ParamName, ParamDefault}, CallLocation, ArgsLike, parse::parse_builtin_call},
372 Result, Context, typed::Typed,368 Result, Context, typed::Typed,
373 parser::Span,369 parser::Span, params,
374 };370 };
375 const PARAMS: &'static [BuiltinParam] = &[371 params!(
376 #(#params_desc)*372 #(#params_desc)*
377 ];373 );
378374
379 #static_ext375 #static_ext
380 impl Builtin for #name376 impl Builtin for #name
384 fn name(&self) -> &str {380 fn name(&self) -> &str {
385 stringify!(#name)381 stringify!(#name)
386 }382 }
387 fn params(&self) -> &[BuiltinParam] {383 fn params(&self) -> &[ParamParse] {
384 /// Safety: ParamParse contains IStr, which is thread-local, thus neither Send or Sync
385 /// The result of this transmute can not outlive the thread, thus 'static here is equivalent to the
386 /// nightly-only 'thread
388 PARAMS387 PARAMS.with(|p| unsafe { std::mem::transmute::<&[ParamParse], &'static [ParamParse]>(p.as_slice()) })
389 }388 }
390 #[allow(unused_variables)]389 #[allow(unused_variables)]
391 fn call(&self, ctx: Context, location: CallLocation, args: &dyn ArgsLike) -> Result<Val> {390 fn call(&self, ctx: Context, location: CallLocation, args: &dyn ArgsLike) -> Result<Val> {
392 let parsed = parse_builtin_call(ctx.clone(), &PARAMS, args, false)?;391 let parsed = parse_builtin_call(ctx.clone(), self.params(), args, false)?;
393392
394 let result: #result = #name(#(#pass)*);393 let result: #result = #name(#(#pass)*);
395 <_ as Typed>::into_result(result)394 <_ as Typed>::into_result(result)