From c41391dc98715c68f6823e0b8f1b460e6b8a5e0f Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sun, 06 Aug 2023 16:57:14 +0000 Subject: [PATCH] refactor: fancier builtin param type --- --- a/bindings/jsonnet/src/native.rs +++ b/bindings/jsonnet/src/native.rs @@ -1,5 +1,4 @@ use std::{ - borrow::Cow, ffi::{c_void, CStr}, os::raw::{c_char, c_int}, }; @@ -82,7 +81,7 @@ let param = CStr::from_ptr(*raw_params) .to_str() .expect("param name is not utf-8"); - params.push(Cow::Owned(param.into())); + params.push(param.into()); raw_params = raw_params.offset(1); } --- a/crates/jrsonnet-evaluator/src/function/builtin.rs +++ b/crates/jrsonnet-evaluator/src/function/builtin.rs @@ -1,18 +1,56 @@ use std::{any::Any, borrow::Cow}; use jrsonnet_gcmodule::Trace; +use jrsonnet_interner::IStr; use super::{arglike::ArgsLike, parse::parse_builtin_call, CallLocation}; use crate::{error::Result, gc::TraceBox, tb, Context, Val}; -pub type BuiltinParamName = Cow<'static, str>; +/// 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>); +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 as_str(&self) -> Option<&str> { + self.0.as_deref() + } + pub fn is_anonymous(&self) -> bool { + self.0.is_none() + } +} +impl PartialEq for ParamName { + fn eq(&self, other: &IStr) -> bool { + match &self.0 { + Some(s) => s.as_bytes() == other.as_bytes(), + None => false, + } + } +} #[derive(Clone, Trace)] pub struct BuiltinParam { + name: ParamName, + has_default: bool, +} +impl BuiltinParam { + pub const fn new(name: ParamName, has_default: bool) -> Self { + Self { name, has_default } + } /// Parameter name for named call parsing - pub name: Option, + pub fn name(&self) -> &ParamName { + &self.name + } /// Is implementation allowed to return empty value - pub has_default: bool, + pub fn has_default(&self) -> bool { + self.has_default + } } /// Description of function defined by native code @@ -44,12 +82,12 @@ } impl NativeCallback { #[deprecated = "prefer using builtins directly, use this interface only for bindings"] - pub fn new(params: Vec>, handler: impl NativeCallbackHandler) -> Self { + pub fn new(params: Vec, handler: impl NativeCallbackHandler) -> Self { Self { params: params .into_iter() .map(|n| BuiltinParam { - name: Some(n), + name: ParamName::new_dynamic(n.to_string()), has_default: false, }) .collect(), --- a/crates/jrsonnet-evaluator/src/function/mod.rs +++ b/crates/jrsonnet-evaluator/src/function/mod.rs @@ -8,7 +8,7 @@ use self::{ arglike::OptionalContext, - builtin::{Builtin, StaticBuiltin}, + builtin::{Builtin, BuiltinParam, ParamName, StaticBuiltin}, native::NativeDesc, parse::{parse_default_function_call, parse_function_call}, }; @@ -113,17 +113,45 @@ } } +#[allow(clippy::unnecessary_wraps)] +#[builtin] +const fn builtin_id(x: Val) -> Val { + x +} +static ID: &builtin_id = &builtin_id {}; + impl FuncVal { pub fn builtin(builtin: impl Builtin) -> Self { Self::Builtin(Cc::new(tb!(builtin))) } + + pub fn params(&self) -> Vec { + 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), + p.1.is_some(), + ) + }) + .collect(), + } + } /// Amount of non-default required arguments pub fn params_len(&self) -> usize { match self { Self::Id => 1, Self::Normal(n) => n.params.iter().filter(|p| p.1.is_none()).count(), - Self::StaticBuiltin(i) => i.params().iter().filter(|p| !p.has_default).count(), - Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default).count(), + Self::StaticBuiltin(i) => i.params().iter().filter(|p| !p.has_default()).count(), + Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default()).count(), } } /// Function name, as defined in code. @@ -146,16 +174,7 @@ tailstrict: bool, ) -> Result { match self { - Self::Id => { - #[allow(clippy::unnecessary_wraps)] - #[builtin] - const fn builtin_id(x: Val) -> Val { - x - } - static ID: &builtin_id = &builtin_id {}; - - ID.call(call_ctx, loc, args) - } + Self::Id => ID.call(call_ctx, loc, args), Self::Normal(func) => { let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?; evaluate(body_ctx, &func.body) --- a/crates/jrsonnet-evaluator/src/function/parse.rs +++ b/crates/jrsonnet-evaluator/src/function/parse.rs @@ -163,7 +163,7 @@ params.len(), params .iter() - .map(|p| (p.name.as_ref().map(|v| v.as_ref().into()), p.has_default)) + .map(|p| (p.name().as_str().map(IStr::from), p.has_default())) .collect() )) } @@ -180,7 +180,7 @@ // FIXME: O(n) for arg existence check let id = params .iter() - .position(|p| p.name.as_ref().map_or(false, |v| v as &str == name as &str)) + .position(|p| p.name() == name) .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?; if replace(&mut passed_args[id], Some(arg)).is_some() { throw!(BindingParameterASecondTime(name.clone())); @@ -190,7 +190,7 @@ })?; if filled_args < params.len() { - for (id, _) in params.iter().enumerate().filter(|(_, p)| p.has_default) { + for (id, _) in params.iter().enumerate().filter(|(_, p)| p.has_default()) { if passed_args[id].is_some() { continue; } @@ -202,20 +202,16 @@ for param in params.iter().skip(args.unnamed_len()) { let mut found = false; args.named_names(&mut |name| { - if param - .name - .as_ref() - .map_or(false, |v| v as &str == name as &str) - { + if param.name() == name { found = true; } }); if !found { throw!(FunctionParameterNotBoundInCall( - param.name.as_ref().map(|v| v.as_ref().into()), + param.name().as_str().map(IStr::from), params .iter() - .map(|p| (p.name.as_ref().map(|p| p.as_ref().into()), p.has_default)) + .map(|p| (p.name().as_str().map(IStr::from), p.has_default())) .collect() )); } --- a/tests/tests/common.rs +++ b/tests/tests/common.rs @@ -1,5 +1,3 @@ -use std::borrow::Cow; - use jrsonnet_evaluator::{ error::Result, function::{builtin, FuncVal}, @@ -66,22 +64,12 @@ FuncVal::StaticBuiltin(b) => b .params() .iter() - .map(|p| { - p.name - .as_ref() - .unwrap_or(&Cow::Borrowed("")) - .to_string() - }) + .map(|p| p.name().as_str().unwrap_or(&"").to_string()) .collect(), FuncVal::Builtin(b) => b .params() .iter() - .map(|p| { - p.name - .as_ref() - .unwrap_or(&Cow::Borrowed("")) - .to_string() - }) + .map(|p| p.name().as_str().unwrap_or(&"").to_string()) .collect(), } } -- gitstuff