difftreelog
refactor do not allocate for BuiltinParam vec
in: master
5 files changed
crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -11,18 +11,7 @@
use self::destructure::destruct;
use crate::{
- arr::ArrValue,
- bail,
- destructure::evaluate_dest,
- error::{suggest_object_fields, ErrorKind::*},
- evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},
- function::{CallLocation, FuncDesc, FuncVal},
- gc::WithCapacityExt as _,
- in_frame,
- typed::Typed,
- val::{CachedUnbound, IndexableVal, NumValue, StrValue, Thunk},
- with_state, Context, Error, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result,
- ResultExt, SupThis, Unbound, Val,
+ 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
};
pub mod destructure;
pub mod operator;
@@ -88,6 +77,15 @@
Val::Func(FuncVal::Normal(Cc::new(FuncDesc {
name,
ctx,
+ params_parse: params
+ .iter()
+ .map(|p| {
+ ParamParse::new(
+ p.0.name().map_or(ParamName::ANONYMOUS, ParamName::new),
+ ParamDefault::exists(p.1.is_some()),
+ )
+ })
+ .collect(),
params,
body,
})))
crates/jrsonnet-evaluator/src/function/builtin.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function/builtin.rs
+++ b/crates/jrsonnet-evaluator/src/function/builtin.rs
@@ -1,22 +1,17 @@
-use std::{any::Any, borrow::Cow};
+use std::any::Any;
-use jrsonnet_gcmodule::{cc_dyn, Trace, TraceBox};
+use jrsonnet_gcmodule::{cc_dyn, Acyclic, Trace, TraceBox};
use jrsonnet_interner::IStr;
use super::{arglike::ArgsLike, parse::parse_builtin_call, CallLocation};
use crate::{Context, Result, Val};
-/// Can't have `str` | `IStr`, because constant `BuiltinParam` causes
-/// `E0492: constant functions cannot refer to interior mutable data`
-#[derive(Clone, Trace)]
-pub struct ParamName(Option<Cow<'static, str>>);
+#[derive(Clone, Acyclic)]
+pub struct ParamName(Option<IStr>);
impl ParamName {
pub const ANONYMOUS: Self = Self(None);
- pub const fn new_static(name: &'static str) -> Self {
- Self(Some(Cow::Borrowed(name)))
- }
- pub fn new_dynamic(name: String) -> Self {
- Self(Some(Cow::Owned(name)))
+ pub fn new(name: IStr) -> Self {
+ Self(Some(name))
}
pub fn as_str(&self) -> Option<&str> {
self.0.as_deref()
@@ -33,7 +28,7 @@
}
}
-#[derive(Clone, Copy, Debug, Trace)]
+#[derive(Clone, Copy, Debug, Acyclic)]
pub enum ParamDefault {
None,
Exists,
@@ -49,13 +44,26 @@
}
}
-#[derive(Clone, Trace)]
-pub struct BuiltinParam {
+#[macro_export]
+macro_rules! params {
+ (@name unnamed) => { ParamName::ANONYMOUS };
+ (@name named $name:literal) => { ParamName::new($crate::IStr::from($name)) };
+ ($($(#[$meta:meta])* [$kind:ident $(($lit:literal))? => $default:expr]),* $(,)?) => {
+ thread_local! {
+ static PARAMS: [ParamParse; { const N: usize = <[u8]>::len(&[$($(#[$meta])* 0u8),*]); N }] = [
+ $($(#[$meta])* ParamParse::new(params!(@name $kind $($lit)?), $default)),*
+ ];
+ }
+ };
+}
+
+#[derive(Clone, Acyclic)]
+pub struct ParamParse {
name: ParamName,
default: ParamDefault,
}
-impl BuiltinParam {
- pub const fn new(name: ParamName, default: ParamDefault) -> Self {
+impl ParamParse {
+ pub fn new(name: ParamName, default: ParamDefault) -> Self {
Self { name, default }
}
/// Parameter name for named call parsing
@@ -81,7 +89,7 @@
self.0.name()
}
- fn params(&self) -> &[BuiltinParam] {
+ fn params(&self) -> &[ParamParse] {
self.0.params()
}
@@ -101,7 +109,7 @@
/// Function name to be used in stack traces
fn name(&self) -> &str;
/// Parameter names for named calls
- fn params(&self) -> &[BuiltinParam];
+ fn params(&self) -> &[ParamParse];
/// Call the builtin
fn call(&self, ctx: Context, loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val>;
@@ -118,7 +126,7 @@
#[derive(Trace)]
pub struct NativeCallback {
- pub(crate) params: Vec<BuiltinParam>,
+ pub(crate) params: Vec<ParamParse>,
handler: TraceBox<dyn NativeCallbackHandler>,
}
impl NativeCallback {
@@ -127,8 +135,8 @@
Self {
params: params
.into_iter()
- .map(|n| BuiltinParam {
- name: ParamName::new_dynamic(n),
+ .map(|n| ParamParse {
+ name: ParamName::new(n.into()),
default: ParamDefault::None,
})
.collect(),
@@ -144,7 +152,7 @@
"<native>"
}
- fn params(&self) -> &[BuiltinParam] {
+ fn params(&self) -> &[ParamParse] {
&self.params
}
crates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function/mod.rs
+++ b/crates/jrsonnet-evaluator/src/function/mod.rs
@@ -1,6 +1,7 @@
use std::{fmt::Debug, rc::Rc};
pub use arglike::{ArgLike, ArgsLike, TlaArg};
+use educe::Educe;
use jrsonnet_gcmodule::{Cc, Trace};
use jrsonnet_interner::IStr;
pub use jrsonnet_macros::builtin;
@@ -8,7 +9,7 @@
use self::{
arglike::OptionalContext,
- builtin::{Builtin, BuiltinParam, ParamDefault, ParamName, StaticBuiltin},
+ builtin::{Builtin, ParamParse, StaticBuiltin},
native::NativeDesc,
parse::{parse_default_function_call, parse_function_call},
};
@@ -40,7 +41,8 @@
}
/// Represents Jsonnet function defined in code.
-#[derive(Debug, Trace, PartialEq)]
+#[derive(Trace, Educe)]
+#[educe(Debug, PartialEq)]
pub struct FuncDesc {
/// # Example
///
@@ -67,6 +69,9 @@
pub params: ParamsDesc,
/// Function body
pub body: Rc<Spanned<Expr>>,
+
+ #[educe(PartialEq = false, Debug = false)]
+ pub(crate) params_parse: Vec<ParamParse>,
}
impl FuncDesc {
/// Create body context, but fill arguments without defaults with lazy error
@@ -134,25 +139,13 @@
Self::StaticBuiltin(static_builtin)
}
- pub fn params(&self) -> Vec<BuiltinParam> {
+ pub fn params(&self) -> &[ParamParse] {
match self {
- Self::Id => ID.params().to_vec(),
- Self::StaticBuiltin(i) => i.params().to_vec(),
- Self::Builtin(i) => i.params().to_vec(),
- Self::Normal(p) => p
- .params
- .iter()
- .map(|p| {
- BuiltinParam::new(
- p.0.name()
- .as_ref()
- .map(IStr::to_string)
- .map_or(ParamName::ANONYMOUS, ParamName::new_dynamic),
- ParamDefault::exists(p.1.is_some()),
- )
- })
- .collect(),
- Self::Thunk(_) => vec![],
+ Self::Id => ID.params(),
+ Self::StaticBuiltin(i) => i.params(),
+ Self::Builtin(i) => i.params(),
+ Self::Normal(p) => &p.params_parse,
+ Self::Thunk(_) => &[],
}
}
/// Amount of non-default required arguments
crates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth1use std::mem::replace;23use jrsonnet_interner::IStr;4use jrsonnet_parser::ParamsDesc;5use rustc_hash::FxHashMap;67use super::{arglike::ArgsLike, builtin::BuiltinParam};8use crate::{9 bail,10 destructure::destruct,11 error::{ErrorKind::*, Result},12 evaluate_named,13 function::builtin::ParamDefault,14 gc::WithCapacityExt as _,15 Context, Pending, Thunk, Val,16};1718/// Creates correct [context](Context) for function body evaluation returning error on invalid call.19///20/// ## Parameters21/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)22/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)23/// * `params`: function parameters' definition24/// * `args`: passed function arguments25/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily26pub fn parse_function_call(27 ctx: Context,28 body_ctx: Context,29 params: &ParamsDesc,30 args: &dyn ArgsLike,31 tailstrict: bool,32) -> Result<Context> {33 let mut passed_args =34 FxHashMap::with_capacity(params.iter().map(|p| p.0.capacity_hint()).sum());35 if args.unnamed_len() > params.len() {36 bail!(TooManyArgsFunctionHas(37 params.len(),38 params39 .iter()40 .map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))41 .collect()42 ))43 }4445 let mut filled_named = 0;46 let mut filled_positionals = 0;4748 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {49 let name = params[id].0.clone();50 destruct(51 &name,52 arg,53 Pending::new_filled(ctx.clone()),54 &mut passed_args,55 )?;56 filled_positionals += 1;57 Ok(())58 })?;5960 args.named_iter(ctx, tailstrict, &mut |name, value| {61 // FIXME: O(n) for arg existence check62 if !params.iter().any(|p| p.0.name().as_ref() == Some(name)) {63 bail!(UnknownFunctionParameter((name as &str).to_owned()));64 }65 if passed_args.insert(name.clone(), value).is_some() {66 bail!(BindingParameterASecondTime(name.clone()));67 }68 filled_named += 1;69 Ok(())70 })?;7172 if filled_named + filled_positionals < params.len() {73 // Some args are unset, but maybe we have defaults for them74 // Default values should be created in newly created context75 let fctx = Context::new_future();76 let mut defaults = FxHashMap::with_capacity(77 params.iter().map(|p| p.0.capacity_hint()).sum::<usize>()78 - filled_named79 - filled_positionals,80 );8182 for (idx, param) in params.iter().enumerate().filter(|p| p.1 .1.is_some()) {83 if let Some(name) = param.0.name() {84 if passed_args.contains_key(&name) {85 continue;86 }87 } else if idx < filled_positionals {88 continue;89 }9091 destruct(92 ¶m.0,93 {94 let ctx = fctx.clone();95 let name = param.0.name().unwrap_or_else(|| "<destruct>".into());96 let value = param.1.clone().expect("default exists");97 Thunk!(move || evaluate_named(ctx.unwrap(), &value, name))98 },99 fctx.clone(),100 &mut defaults,101 )?;102 if param.0.name().is_some() {103 filled_named += 1;104 } else {105 filled_positionals += 1;106 }107 }108109 // Some args still weren't filled110 if filled_named + filled_positionals != params.len() {111 for param in params.iter().skip(args.unnamed_len()) {112 let mut found = false;113 args.named_names(&mut |name| {114 if Some(name) == param.0.name().as_ref() {115 found = true;116 }117 });118 if !found {119 bail!(FunctionParameterNotBoundInCall(120 param.0.clone().name(),121 params122 .iter()123 .map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))124 .collect()125 ));126 }127 }128 unreachable!();129 }130131 Ok(body_ctx132 .extend_bindings(passed_args)133 .extend_bindings(defaults)134 .into_future(fctx))135 } else {136 let body_ctx = body_ctx.extend_bindings(passed_args);137 Ok(body_ctx)138 }139}140141/// You shouldn't probally use this function, use `jrsonnet_macros::builtin` instead142///143/// ## Parameters144/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)145/// * `params`: function parameters' definition146/// * `args`: passed function arguments147/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily148pub fn parse_builtin_call(149 ctx: Context,150 params: &[BuiltinParam],151 args: &dyn ArgsLike,152 tailstrict: bool,153) -> Result<Vec<Option<Thunk<Val>>>> {154 let mut passed_args: Vec<Option<Thunk<Val>>> = vec![None; params.len()];155 if args.unnamed_len() > params.len() {156 bail!(TooManyArgsFunctionHas(157 params.len(),158 params159 .iter()160 .map(|p| (p.name().as_str().map(IStr::from), p.default()))161 .collect()162 ))163 }164165 let mut filled_args = 0;166167 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {168 passed_args[id] = Some(arg);169 filled_args += 1;170 Ok(())171 })?;172173 args.named_iter(ctx, tailstrict, &mut |name, arg| {174 // FIXME: O(n) for arg existence check175 let id = params176 .iter()177 .position(|p| p.name() == name)178 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;179 if replace(&mut passed_args[id], Some(arg)).is_some() {180 bail!(BindingParameterASecondTime(name.clone()));181 }182 filled_args += 1;183 Ok(())184 })?;185186 if filled_args < params.len() {187 for (id, _) in params.iter().enumerate().filter(|(_, p)| p.has_default()) {188 if passed_args[id].is_some() {189 continue;190 }191 filled_args += 1;192 }193194 // Some args still wasn't filled195 if filled_args != params.len() {196 for param in params.iter().skip(args.unnamed_len()) {197 let mut found = false;198 args.named_names(&mut |name| {199 if param.name() == name {200 found = true;201 }202 });203 if !found {204 bail!(FunctionParameterNotBoundInCall(205 param.name().as_str().map(IStr::from),206 params207 .iter()208 .map(|p| (p.name().as_str().map(IStr::from), p.default()))209 .collect()210 ));211 }212 }213 unreachable!();214 }215 }216 Ok(passed_args)217}218219/// Creates Context, which has all argument default values applied220/// and with unbound values causing error to be returned221pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {222 let fctx = Context::new_future();223224 let mut bindings = FxHashMap::with_capacity(params.iter().map(|p| p.0.capacity_hint()).sum());225226 for param in params.iter() {227 if let Some(v) = ¶m.1 {228 destruct(229 ¶m.0.clone(),230 {231 let ctx = fctx.clone();232 let name = param.0.name().unwrap_or_else(|| "<destruct>".into());233 let value = v.clone();234 Thunk!(move || evaluate_named(ctx.unwrap(), &value, name))235 },236 fctx.clone(),237 &mut bindings,238 )?;239 } else {240 destruct(241 ¶m.0,242 {243 let param_name = param.0.name().unwrap_or_else(|| "<destruct>".into());244 let params = params.clone();245 Thunk!(move || Err(FunctionParameterNotBoundInCall(246 Some(param_name),247 params248 .iter()249 .map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))250 .collect(),251 )252 .into()))253 },254 fctx.clone(),255 &mut bindings,256 )?;257 }258 }259260 Ok(body_ctx.extend_bindings(bindings).into_future(fctx))261}1use std::mem::replace;23use jrsonnet_interner::IStr;4use jrsonnet_parser::ParamsDesc;5use rustc_hash::FxHashMap;67use super::{arglike::ArgsLike, builtin::ParamParse};8use crate::{9 bail,10 destructure::destruct,11 error::{ErrorKind::*, Result},12 evaluate_named,13 function::builtin::ParamDefault,14 gc::WithCapacityExt as _,15 Context, Pending, Thunk, Val,16};1718/// Creates correct [context](Context) for function body evaluation returning error on invalid call.19///20/// ## Parameters21/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)22/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)23/// * `params`: function parameters' definition24/// * `args`: passed function arguments25/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily26pub fn parse_function_call(27 ctx: Context,28 body_ctx: Context,29 params: &ParamsDesc,30 args: &dyn ArgsLike,31 tailstrict: bool,32) -> Result<Context> {33 let mut passed_args =34 FxHashMap::with_capacity(params.iter().map(|p| p.0.capacity_hint()).sum());35 if args.unnamed_len() > params.len() {36 bail!(TooManyArgsFunctionHas(37 params.len(),38 params39 .iter()40 .map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))41 .collect()42 ))43 }4445 let mut filled_named = 0;46 let mut filled_positionals = 0;4748 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {49 let name = params[id].0.clone();50 destruct(51 &name,52 arg,53 Pending::new_filled(ctx.clone()),54 &mut passed_args,55 )?;56 filled_positionals += 1;57 Ok(())58 })?;5960 args.named_iter(ctx, tailstrict, &mut |name, value| {61 // FIXME: O(n) for arg existence check62 if !params.iter().any(|p| p.0.name().as_ref() == Some(name)) {63 bail!(UnknownFunctionParameter((name as &str).to_owned()));64 }65 if passed_args.insert(name.clone(), value).is_some() {66 bail!(BindingParameterASecondTime(name.clone()));67 }68 filled_named += 1;69 Ok(())70 })?;7172 if filled_named + filled_positionals < params.len() {73 // Some args are unset, but maybe we have defaults for them74 // Default values should be created in newly created context75 let fctx = Context::new_future();76 let mut defaults = FxHashMap::with_capacity(77 params.iter().map(|p| p.0.capacity_hint()).sum::<usize>()78 - filled_named79 - filled_positionals,80 );8182 for (idx, param) in params.iter().enumerate().filter(|p| p.1 .1.is_some()) {83 if let Some(name) = param.0.name() {84 if passed_args.contains_key(&name) {85 continue;86 }87 } else if idx < filled_positionals {88 continue;89 }9091 destruct(92 ¶m.0,93 {94 let ctx = fctx.clone();95 let name = param.0.name().unwrap_or_else(|| "<destruct>".into());96 let value = param.1.clone().expect("default exists");97 Thunk!(move || evaluate_named(ctx.unwrap(), &value, name))98 },99 fctx.clone(),100 &mut defaults,101 )?;102 if param.0.name().is_some() {103 filled_named += 1;104 } else {105 filled_positionals += 1;106 }107 }108109 // Some args still weren't filled110 if filled_named + filled_positionals != params.len() {111 for param in params.iter().skip(args.unnamed_len()) {112 let mut found = false;113 args.named_names(&mut |name| {114 if Some(name) == param.0.name().as_ref() {115 found = true;116 }117 });118 if !found {119 bail!(FunctionParameterNotBoundInCall(120 param.0.clone().name(),121 params122 .iter()123 .map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))124 .collect()125 ));126 }127 }128 unreachable!();129 }130131 Ok(body_ctx132 .extend_bindings(passed_args)133 .extend_bindings(defaults)134 .into_future(fctx))135 } else {136 let body_ctx = body_ctx.extend_bindings(passed_args);137 Ok(body_ctx)138 }139}140141/// You shouldn't probally use this function, use `jrsonnet_macros::builtin` instead142///143/// ## Parameters144/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)145/// * `params`: function parameters' definition146/// * `args`: passed function arguments147/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily148pub fn parse_builtin_call(149 ctx: Context,150 params: &[ParamParse],151 args: &dyn ArgsLike,152 tailstrict: bool,153) -> Result<Vec<Option<Thunk<Val>>>> {154 let mut passed_args: Vec<Option<Thunk<Val>>> = vec![None; params.len()];155 if args.unnamed_len() > params.len() {156 bail!(TooManyArgsFunctionHas(157 params.len(),158 params159 .iter()160 .map(|p| (p.name().as_str().map(IStr::from), p.default()))161 .collect()162 ))163 }164165 let mut filled_args = 0;166167 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {168 passed_args[id] = Some(arg);169 filled_args += 1;170 Ok(())171 })?;172173 args.named_iter(ctx, tailstrict, &mut |name, arg| {174 // FIXME: O(n) for arg existence check175 let id = params176 .iter()177 .position(|p| p.name() == name)178 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;179 if replace(&mut passed_args[id], Some(arg)).is_some() {180 bail!(BindingParameterASecondTime(name.clone()));181 }182 filled_args += 1;183 Ok(())184 })?;185186 if filled_args < params.len() {187 for (id, _) in params.iter().enumerate().filter(|(_, p)| p.has_default()) {188 if passed_args[id].is_some() {189 continue;190 }191 filled_args += 1;192 }193194 // Some args still wasn't filled195 if filled_args != params.len() {196 for param in params.iter().skip(args.unnamed_len()) {197 let mut found = false;198 args.named_names(&mut |name| {199 if param.name() == name {200 found = true;201 }202 });203 if !found {204 bail!(FunctionParameterNotBoundInCall(205 param.name().as_str().map(IStr::from),206 params207 .iter()208 .map(|p| (p.name().as_str().map(IStr::from), p.default()))209 .collect()210 ));211 }212 }213 unreachable!();214 }215 }216 Ok(passed_args)217}218219/// Creates Context, which has all argument default values applied220/// and with unbound values causing error to be returned221pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {222 let fctx = Context::new_future();223224 let mut bindings = FxHashMap::with_capacity(params.iter().map(|p| p.0.capacity_hint()).sum());225226 for param in params.iter() {227 if let Some(v) = ¶m.1 {228 destruct(229 ¶m.0.clone(),230 {231 let ctx = fctx.clone();232 let name = param.0.name().unwrap_or_else(|| "<destruct>".into());233 let value = v.clone();234 Thunk!(move || evaluate_named(ctx.unwrap(), &value, name))235 },236 fctx.clone(),237 &mut bindings,238 )?;239 } else {240 destruct(241 ¶m.0,242 {243 let param_name = param.0.name().unwrap_or_else(|| "<destruct>".into());244 let params = params.clone();245 Thunk!(move || Err(FunctionParameterNotBoundInCall(246 Some(param_name),247 params248 .iter()249 .map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))250 .collect(),251 )252 .into()))253 },254 fctx.clone(),255 &mut bindings,256 )?;257 }258 }259260 Ok(body_ctx.extend_bindings(bindings).into_future(fctx))261}crates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-macros/src/lib.rs
+++ b/crates/jrsonnet-macros/src/lib.rs
@@ -239,9 +239,7 @@
cfg_attrs,
..
} => {
- let name = name
- .as_ref()
- .map_or_else(|| quote! {None}, |n| quote! {ParamName::new_static(#n)});
+ let name = name.as_ref().map_or_else(|| quote! {unnamed}, |n| quote! {named(#n)});
let default = match optionality {
Optionality::Required => quote!(ParamDefault::None),
Optionality::Optional => quote!(ParamDefault::Exists),
@@ -249,15 +247,13 @@
};
Some(quote! {
#(#cfg_attrs)*
- BuiltinParam::new(#name, #default),
+ [#name => #default],
})
}
ArgInfo::Lazy { is_option, name } => {
- let name = name
- .as_ref()
- .map_or_else(|| quote! {None}, |n| quote! {ParamName::new_static(#n)});
+ let name = name.as_ref().map_or_else(|| quote! {unnamed}, |n| quote! {named(#n)});
Some(quote! {
- BuiltinParam::new(#name, ParamDefault::exists(#is_option)),
+ [#name => ParamDefault::exists(#is_option)],
})
}
ArgInfo::Context | ArgInfo::Location | ArgInfo::This => None,
@@ -368,13 +364,13 @@
const _: () = {
use ::jrsonnet_evaluator::{
State, Val,
- function::{builtin::{Builtin, StaticBuiltin, BuiltinParam, ParamName, ParamDefault}, CallLocation, ArgsLike, parse::parse_builtin_call},
+ function::{builtin::{Builtin, StaticBuiltin, ParamParse, ParamName, ParamDefault}, CallLocation, ArgsLike, parse::parse_builtin_call},
Result, Context, typed::Typed,
- parser::Span,
+ parser::Span, params,
};
- const PARAMS: &'static [BuiltinParam] = &[
+ params!(
#(#params_desc)*
- ];
+ );
#static_ext
impl Builtin for #name
@@ -384,12 +380,15 @@
fn name(&self) -> &str {
stringify!(#name)
}
- fn params(&self) -> &[BuiltinParam] {
- PARAMS
+ fn params(&self) -> &[ParamParse] {
+ /// Safety: ParamParse contains IStr, which is thread-local, thus neither Send or Sync
+ /// The result of this transmute can not outlive the thread, thus 'static here is equivalent to the
+ /// nightly-only 'thread
+ PARAMS.with(|p| unsafe { std::mem::transmute::<&[ParamParse], &'static [ParamParse]>(p.as_slice()) })
}
#[allow(unused_variables)]
fn call(&self, ctx: Context, location: CallLocation, args: &dyn ArgsLike) -> Result<Val> {
- let parsed = parse_builtin_call(ctx.clone(), &PARAMS, args, false)?;
+ let parsed = parse_builtin_call(ctx.clone(), self.params(), args, false)?;
let result: #result = #name(#(#pass)*);
<_ as Typed>::into_result(result)