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.rsdiffbeforeafterboth1use std::{any::Any, borrow::Cow};23use jrsonnet_gcmodule::{cc_dyn, Trace, TraceBox};4use jrsonnet_interner::IStr;56use super::{arglike::ArgsLike, parse::parse_builtin_call, CallLocation};7use crate::{Context, Result, Val};89/// Can't have `str` | `IStr`, because constant `BuiltinParam` causes10/// `E0492: constant functions cannot refer to interior mutable data`11#[derive(Clone, Trace)]12pub struct ParamName(Option<Cow<'static, str>>);13impl ParamName {14 pub const ANONYMOUS: Self = Self(None);15 pub const fn new_static(name: &'static str) -> Self {16 Self(Some(Cow::Borrowed(name)))17 }18 pub fn new_dynamic(name: String) -> Self {19 Self(Some(Cow::Owned(name)))20 }21 pub fn as_str(&self) -> Option<&str> {22 self.0.as_deref()23 }24 pub fn is_anonymous(&self) -> bool {25 self.0.is_none()26 }27}28impl PartialEq<IStr> for ParamName {29 fn eq(&self, other: &IStr) -> bool {30 self.031 .as_ref()32 .map_or(false, |s| s.as_bytes() == other.as_bytes())33 }34}3536#[derive(Clone, Copy, Debug, Trace)]37pub enum ParamDefault {38 None,39 Exists,40 Literal(&'static str),41}42impl ParamDefault {43 pub const fn exists(is_exists: bool) -> Self {44 if is_exists {45 Self::Exists46 } else {47 Self::None48 }49 }50}5152#[derive(Clone, Trace)]53pub struct BuiltinParam {54 name: ParamName,55 default: ParamDefault,56}57impl BuiltinParam {58 pub const fn new(name: ParamName, default: ParamDefault) -> Self {59 Self { name, default }60 }61 /// Parameter name for named call parsing62 pub fn name(&self) -> &ParamName {63 &self.name64 }65 pub fn default(&self) -> ParamDefault {66 self.default67 }68 pub fn has_default(&self) -> bool {69 !matches!(self.default, ParamDefault::None)70 }71}7273cc_dyn!(74 #[derive(Clone)]75 BuiltinFunc,76 Builtin,77 pub(crate) fn new() {...}78);79impl Builtin for BuiltinFunc {80 fn name(&self) -> &str {81 self.0.name()82 }8384 fn params(&self) -> &[BuiltinParam] {85 self.0.params()86 }8788 fn call(&self, ctx: Context, loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val> {89 self.0.call(ctx, loc, args)90 }9192 fn as_any(&self) -> &dyn Any {93 self.0.as_any()94 }95}9697/// Description of function defined by native code98///99/// Prefer to use #[builtin] macro, instead of manual implementation of this trait100pub trait Builtin: Trace {101 /// Function name to be used in stack traces102 fn name(&self) -> &str;103 /// Parameter names for named calls104 fn params(&self) -> &[BuiltinParam];105 /// Call the builtin106 fn call(&self, ctx: Context, loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val>;107108 fn as_any(&self) -> &dyn Any;109}110111pub trait StaticBuiltin: Builtin + Send + Sync112where113 Self: 'static,114{115 // In impl, to make it object safe:116 // const INST: &'static Self;117}118119#[derive(Trace)]120pub struct NativeCallback {121 pub(crate) params: Vec<BuiltinParam>,122 handler: TraceBox<dyn NativeCallbackHandler>,123}124impl NativeCallback {125 #[deprecated = "prefer using builtins directly, use this interface only for bindings"]126 pub fn new(params: Vec<String>, handler: impl NativeCallbackHandler) -> Self {127 Self {128 params: params129 .into_iter()130 .map(|n| BuiltinParam {131 name: ParamName::new_dynamic(n),132 default: ParamDefault::None,133 })134 .collect(),135 handler: TraceBox(Box::new(handler)),136 }137 }138}139140impl Builtin for NativeCallback {141 fn name(&self) -> &str {142 // TODO: standard natives gets their names from definition143 // But builitins should already have them144 "<native>"145 }146147 fn params(&self) -> &[BuiltinParam] {148 &self.params149 }150151 fn call(&self, ctx: Context, _loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val> {152 let args = parse_builtin_call(ctx, &self.params, args, true)?;153 let args = args154 .into_iter()155 .map(|a| a.expect("legacy natives have no default params"))156 .map(|a| a.evaluate())157 .collect::<Result<Vec<Val>>>()?;158 self.handler.call(&args)159 }160161 fn as_any(&self) -> &dyn Any {162 self163 }164}165166pub trait NativeCallbackHandler: Trace {167 fn call(&self, args: &[Val]) -> Result<Val>;168}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.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)