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.rsdiffbeforeafterboth1use std::{fmt::Debug, rc::Rc};23pub use arglike::{ArgLike, ArgsLike, TlaArg};4use jrsonnet_gcmodule::{Cc, Trace};5use jrsonnet_interner::IStr;6pub use jrsonnet_macros::builtin;7use jrsonnet_parser::{Destruct, Expr, ParamsDesc, Span, Spanned};89use self::{10 arglike::OptionalContext,11 builtin::{Builtin, BuiltinParam, ParamDefault, ParamName, StaticBuiltin},12 native::NativeDesc,13 parse::{parse_default_function_call, parse_function_call},14};15use crate::{16 bail, error::ErrorKind::*, evaluate, evaluate_trivial, function::builtin::BuiltinFunc, Context,17 ContextBuilder, Result, Thunk, Val,18};1920pub mod arglike;21pub mod builtin;22pub mod native;23pub mod parse;2425/// Function callsite location.26/// Either from other jsonnet code, specified by expression location, or from native (without location).27#[derive(Clone, Copy)]28pub struct CallLocation<'l>(pub Option<&'l Span>);29impl<'l> CallLocation<'l> {30 /// Construct new location for calls coming from specified jsonnet expression location.31 pub const fn new(loc: &'l Span) -> Self {32 Self(Some(loc))33 }34}35impl CallLocation<'static> {36 /// Construct new location for calls coming from native code.37 pub const fn native() -> Self {38 Self(None)39 }40}4142/// Represents Jsonnet function defined in code.43#[derive(Debug, Trace, PartialEq)]44pub struct FuncDesc {45 /// # Example46 ///47 /// In expressions like this, deducted to `a`, unspecified otherwise.48 /// ```jsonnet49 /// local a = function() ...50 /// local a() ...51 /// { a: function() ... }52 /// { a() = ... }53 /// ```54 pub name: IStr,55 /// Context, in which this function was evaluated.56 ///57 /// # Example58 /// In59 /// ```jsonnet60 /// local a = 2;61 /// function() ...62 /// ```63 /// context will contain `a`.64 pub ctx: Context,6566 /// Function parameter definition67 pub params: ParamsDesc,68 /// Function body69 pub body: Rc<Spanned<Expr>>,70}71impl FuncDesc {72 /// Create body context, but fill arguments without defaults with lazy error73 pub fn default_body_context(&self) -> Result<Context> {74 parse_default_function_call(self.ctx.clone(), &self.params)75 }7677 /// Create context, with which body code will run78 pub fn call_body_context(79 &self,80 call_ctx: Context,81 args: &dyn ArgsLike,82 tailstrict: bool,83 ) -> Result<Context> {84 parse_function_call(call_ctx, self.ctx.clone(), &self.params, args, tailstrict)85 }8687 pub fn evaluate_trivial(&self) -> Option<Val> {88 evaluate_trivial(&self.body)89 }90}9192/// Represents a Jsonnet function value, including plain functions and user-provided builtins.93#[allow(clippy::module_name_repetitions)]94#[derive(Trace, Clone)]95pub enum FuncVal {96 /// Identity function, kept this way for comparsions.97 Id,98 /// Plain function implemented in jsonnet.99 Normal(Cc<FuncDesc>),100 /// Function without arguments works just as a fancy thunk value.101 Thunk(Thunk<Val>),102 /// Standard library function.103 StaticBuiltin(#[trace(skip)] &'static dyn StaticBuiltin),104 /// User-provided function.105 Builtin(BuiltinFunc),106}107108impl Debug for FuncVal {109 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {110 match self {111 Self::Id => f.debug_tuple("Id").finish(),112 Self::Thunk(arg0) => f.debug_tuple("Thunk").field(arg0).finish(),113 Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),114 Self::StaticBuiltin(arg0) => {115 f.debug_tuple("StaticBuiltin").field(&arg0.name()).finish()116 }117 Self::Builtin(arg0) => f.debug_tuple("Builtin").field(&arg0.name()).finish(),118 }119 }120}121122#[allow(clippy::unnecessary_wraps)]123#[builtin]124const fn builtin_id(x: Val) -> Val {125 x126}127static ID: &builtin_id = &builtin_id {};128129impl FuncVal {130 pub fn builtin(builtin: impl Builtin) -> Self {131 Self::Builtin(BuiltinFunc::new(builtin))132 }133 pub fn static_builtin(static_builtin: &'static dyn StaticBuiltin) -> Self {134 Self::StaticBuiltin(static_builtin)135 }136137 pub fn params(&self) -> Vec<BuiltinParam> {138 match self {139 Self::Id => ID.params().to_vec(),140 Self::StaticBuiltin(i) => i.params().to_vec(),141 Self::Builtin(i) => i.params().to_vec(),142 Self::Normal(p) => p143 .params144 .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![],156 }157 }158 /// Amount of non-default required arguments159 pub fn params_len(&self) -> usize {160 match self {161 Self::Id => 1,162 Self::Normal(n) => n.params.iter().filter(|p| p.1.is_none()).count(),163 Self::StaticBuiltin(i) => i.params().iter().filter(|p| !p.has_default()).count(),164 Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default()).count(),165 Self::Thunk(_) => 0,166 }167 }168 /// Function name, as defined in code.169 pub fn name(&self) -> IStr {170 match self {171 Self::Id => "id".into(),172 Self::Normal(normal) => normal.name.clone(),173 Self::StaticBuiltin(builtin) => builtin.name().into(),174 Self::Builtin(builtin) => builtin.name().into(),175 Self::Thunk(_) => "thunk".into(),176 }177 }178 /// Call function using arguments evaluated in specified `call_ctx` [`Context`].179 ///180 /// If `tailstrict` is specified - then arguments will be evaluated before being passed to function body.181 pub fn evaluate(182 &self,183 call_ctx: Context,184 loc: CallLocation<'_>,185 args: &dyn ArgsLike,186 tailstrict: bool,187 ) -> Result<Val> {188 match self {189 Self::Id => ID.call(call_ctx, loc, args),190 Self::Normal(func) => {191 let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?;192 evaluate(body_ctx, &func.body)193 }194 Self::Thunk(thunk) => {195 if args.is_empty() {196 bail!(TooManyArgsFunctionHas(0, vec![],))197 }198 thunk.evaluate()199 }200 Self::StaticBuiltin(b) => b.call(call_ctx, loc, args),201 Self::Builtin(b) => b.call(call_ctx, loc, args),202 }203 }204 pub fn evaluate_simple<A: ArgsLike + OptionalContext>(205 &self,206 args: &A,207 tailstrict: bool,208 ) -> Result<Val> {209 self.evaluate(210 ContextBuilder::new().build(),211 CallLocation::native(),212 args,213 tailstrict,214 )215 }216 /// Convert jsonnet function to plain `Fn` value.217 pub fn into_native<D: NativeDesc>(self) -> D::Value {218 D::into_native(self)219 }220221 /// Is this function an indentity function.222 ///223 /// Currently only works for builtin `std.id`, aka `Self::Id` value, and `function(x) x`.224 ///225 /// This function should only be used for optimization, not for the conditional logic, i.e code should work with syntetic identity function too226 pub fn is_identity(&self) -> bool {227 match self {228 Self::Id => true,229 Self::Normal(desc) => {230 if desc.params.len() != 1 {231 return false;232 }233 let param = &desc.params[0];234 if param.1.is_some() {235 return false;236 }237 #[allow(clippy::infallible_destructuring_match)]238 let id = match ¶m.0 {239 Destruct::Full(id) => id,240 #[cfg(feature = "exp-destruct")]241 _ => return false,242 };243 **desc.body == Expr::Var(id.clone())244 }245 _ => false,246 }247 }248 /// Identity function value.249 pub const fn identity() -> Self {250 Self::Id251 }252253 pub fn evaluate_trivial(&self) -> Option<Val> {254 match self {255 Self::Normal(n) => n.evaluate_trivial(),256 _ => None,257 }258 }259}260261impl<T> From<T> for FuncVal262where263 T: Builtin,264{265 fn from(value: T) -> Self {266 Self::builtin(value)267 }268}269impl From<&'static dyn StaticBuiltin> for FuncVal {270 fn from(value: &'static dyn StaticBuiltin) -> Self {271 Self::static_builtin(value)272 }273}1use std::{fmt::Debug, rc::Rc};23pub use arglike::{ArgLike, ArgsLike, TlaArg};4use educe::Educe;5use jrsonnet_gcmodule::{Cc, Trace};6use jrsonnet_interner::IStr;7pub use jrsonnet_macros::builtin;8use jrsonnet_parser::{Destruct, Expr, ParamsDesc, Span, Spanned};910use self::{11 arglike::OptionalContext,12 builtin::{Builtin, ParamParse, StaticBuiltin},13 native::NativeDesc,14 parse::{parse_default_function_call, parse_function_call},15};16use crate::{17 bail, error::ErrorKind::*, evaluate, evaluate_trivial, function::builtin::BuiltinFunc, Context,18 ContextBuilder, Result, Thunk, Val,19};2021pub mod arglike;22pub mod builtin;23pub mod native;24pub mod parse;2526/// Function callsite location.27/// Either from other jsonnet code, specified by expression location, or from native (without location).28#[derive(Clone, Copy)]29pub struct CallLocation<'l>(pub Option<&'l Span>);30impl<'l> CallLocation<'l> {31 /// Construct new location for calls coming from specified jsonnet expression location.32 pub const fn new(loc: &'l Span) -> Self {33 Self(Some(loc))34 }35}36impl CallLocation<'static> {37 /// Construct new location for calls coming from native code.38 pub const fn native() -> Self {39 Self(None)40 }41}4243/// Represents Jsonnet function defined in code.44#[derive(Trace, Educe)]45#[educe(Debug, PartialEq)]46pub struct FuncDesc {47 /// # Example48 ///49 /// In expressions like this, deducted to `a`, unspecified otherwise.50 /// ```jsonnet51 /// local a = function() ...52 /// local a() ...53 /// { a: function() ... }54 /// { a() = ... }55 /// ```56 pub name: IStr,57 /// Context, in which this function was evaluated.58 ///59 /// # Example60 /// In61 /// ```jsonnet62 /// local a = 2;63 /// function() ...64 /// ```65 /// context will contain `a`.66 pub ctx: Context,6768 /// Function parameter definition69 pub params: ParamsDesc,70 /// Function body71 pub body: Rc<Spanned<Expr>>,7273 #[educe(PartialEq = false, Debug = false)]74 pub(crate) params_parse: Vec<ParamParse>,75}76impl FuncDesc {77 /// Create body context, but fill arguments without defaults with lazy error78 pub fn default_body_context(&self) -> Result<Context> {79 parse_default_function_call(self.ctx.clone(), &self.params)80 }8182 /// Create context, with which body code will run83 pub fn call_body_context(84 &self,85 call_ctx: Context,86 args: &dyn ArgsLike,87 tailstrict: bool,88 ) -> Result<Context> {89 parse_function_call(call_ctx, self.ctx.clone(), &self.params, args, tailstrict)90 }9192 pub fn evaluate_trivial(&self) -> Option<Val> {93 evaluate_trivial(&self.body)94 }95}9697/// Represents a Jsonnet function value, including plain functions and user-provided builtins.98#[allow(clippy::module_name_repetitions)]99#[derive(Trace, Clone)]100pub enum FuncVal {101 /// Identity function, kept this way for comparsions.102 Id,103 /// Plain function implemented in jsonnet.104 Normal(Cc<FuncDesc>),105 /// Function without arguments works just as a fancy thunk value.106 Thunk(Thunk<Val>),107 /// Standard library function.108 StaticBuiltin(#[trace(skip)] &'static dyn StaticBuiltin),109 /// User-provided function.110 Builtin(BuiltinFunc),111}112113impl Debug for FuncVal {114 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {115 match self {116 Self::Id => f.debug_tuple("Id").finish(),117 Self::Thunk(arg0) => f.debug_tuple("Thunk").field(arg0).finish(),118 Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),119 Self::StaticBuiltin(arg0) => {120 f.debug_tuple("StaticBuiltin").field(&arg0.name()).finish()121 }122 Self::Builtin(arg0) => f.debug_tuple("Builtin").field(&arg0.name()).finish(),123 }124 }125}126127#[allow(clippy::unnecessary_wraps)]128#[builtin]129const fn builtin_id(x: Val) -> Val {130 x131}132static ID: &builtin_id = &builtin_id {};133134impl FuncVal {135 pub fn builtin(builtin: impl Builtin) -> Self {136 Self::Builtin(BuiltinFunc::new(builtin))137 }138 pub fn static_builtin(static_builtin: &'static dyn StaticBuiltin) -> Self {139 Self::StaticBuiltin(static_builtin)140 }141142 pub fn params(&self) -> &[ParamParse] {143 match self {144 Self::Id => ID.params(),145 Self::StaticBuiltin(i) => i.params(),146 Self::Builtin(i) => i.params(),147 Self::Normal(p) => &p.params_parse,148 Self::Thunk(_) => &[],149 }150 }151 /// Amount of non-default required arguments152 pub fn params_len(&self) -> usize {153 match self {154 Self::Id => 1,155 Self::Normal(n) => n.params.iter().filter(|p| p.1.is_none()).count(),156 Self::StaticBuiltin(i) => i.params().iter().filter(|p| !p.has_default()).count(),157 Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default()).count(),158 Self::Thunk(_) => 0,159 }160 }161 /// Function name, as defined in code.162 pub fn name(&self) -> IStr {163 match self {164 Self::Id => "id".into(),165 Self::Normal(normal) => normal.name.clone(),166 Self::StaticBuiltin(builtin) => builtin.name().into(),167 Self::Builtin(builtin) => builtin.name().into(),168 Self::Thunk(_) => "thunk".into(),169 }170 }171 /// Call function using arguments evaluated in specified `call_ctx` [`Context`].172 ///173 /// If `tailstrict` is specified - then arguments will be evaluated before being passed to function body.174 pub fn evaluate(175 &self,176 call_ctx: Context,177 loc: CallLocation<'_>,178 args: &dyn ArgsLike,179 tailstrict: bool,180 ) -> Result<Val> {181 match self {182 Self::Id => ID.call(call_ctx, loc, args),183 Self::Normal(func) => {184 let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?;185 evaluate(body_ctx, &func.body)186 }187 Self::Thunk(thunk) => {188 if args.is_empty() {189 bail!(TooManyArgsFunctionHas(0, vec![],))190 }191 thunk.evaluate()192 }193 Self::StaticBuiltin(b) => b.call(call_ctx, loc, args),194 Self::Builtin(b) => b.call(call_ctx, loc, args),195 }196 }197 pub fn evaluate_simple<A: ArgsLike + OptionalContext>(198 &self,199 args: &A,200 tailstrict: bool,201 ) -> Result<Val> {202 self.evaluate(203 ContextBuilder::new().build(),204 CallLocation::native(),205 args,206 tailstrict,207 )208 }209 /// Convert jsonnet function to plain `Fn` value.210 pub fn into_native<D: NativeDesc>(self) -> D::Value {211 D::into_native(self)212 }213214 /// Is this function an indentity function.215 ///216 /// Currently only works for builtin `std.id`, aka `Self::Id` value, and `function(x) x`.217 ///218 /// This function should only be used for optimization, not for the conditional logic, i.e code should work with syntetic identity function too219 pub fn is_identity(&self) -> bool {220 match self {221 Self::Id => true,222 Self::Normal(desc) => {223 if desc.params.len() != 1 {224 return false;225 }226 let param = &desc.params[0];227 if param.1.is_some() {228 return false;229 }230 #[allow(clippy::infallible_destructuring_match)]231 let id = match ¶m.0 {232 Destruct::Full(id) => id,233 #[cfg(feature = "exp-destruct")]234 _ => return false,235 };236 **desc.body == Expr::Var(id.clone())237 }238 _ => false,239 }240 }241 /// Identity function value.242 pub const fn identity() -> Self {243 Self::Id244 }245246 pub fn evaluate_trivial(&self) -> Option<Val> {247 match self {248 Self::Normal(n) => n.evaluate_trivial(),249 _ => None,250 }251 }252}253254impl<T> From<T> for FuncVal255where256 T: Builtin,257{258 fn from(value: T) -> Self {259 Self::builtin(value)260 }261}262impl From<&'static dyn StaticBuiltin> for FuncVal {263 fn from(value: &'static dyn StaticBuiltin) -> Self {264 Self::static_builtin(value)265 }266}crates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function/parse.rs
+++ b/crates/jrsonnet-evaluator/src/function/parse.rs
@@ -4,7 +4,7 @@
use jrsonnet_parser::ParamsDesc;
use rustc_hash::FxHashMap;
-use super::{arglike::ArgsLike, builtin::BuiltinParam};
+use super::{arglike::ArgsLike, builtin::ParamParse};
use crate::{
bail,
destructure::destruct,
@@ -147,7 +147,7 @@
/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily
pub fn parse_builtin_call(
ctx: Context,
- params: &[BuiltinParam],
+ params: &[ParamParse],
args: &dyn ArgsLike,
tailstrict: bool,
) -> Result<Vec<Option<Thunk<Val>>>> {
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)