--- a/bindings/jsonnet/src/native.rs +++ b/bindings/jsonnet/src/native.rs @@ -1,11 +1,11 @@ use gcmodule::Cc; use jrsonnet_evaluator::{ error::{Error, LocError}, + function::BuiltinParam, gc::TraceBox, native::{NativeCallback, NativeCallbackHandler}, EvaluationState, IStr, Val, }; -use jrsonnet_parser::{Param, ParamsDesc}; use std::{ convert::TryFrom, ffi::{c_void, CStr}, @@ -28,7 +28,7 @@ cb: JsonnetNativeCallback, } impl NativeCallbackHandler for JsonnetNativeCallbackHandler { - fn call(&self, _from: Rc, args: &[Val]) -> Result { + fn call(&self, _from: Option>, args: &[Val]) -> Result { let mut n_args = Vec::new(); for a in args { n_args.push(Some(Box::new(a.clone()))); @@ -68,16 +68,19 @@ break; } let param = CStr::from_ptr(*raw_params).to_str().expect("not utf8"); - params.push(Param(param.into(), None)); + params.push(BuiltinParam { + name: param.into(), + has_default: false, + }); raw_params = raw_params.offset(1); } - let params = ParamsDesc(Rc::new(params)); vm.add_native( name, - Cc::new(NativeCallback::new( + #[allow(deprecated)] + Cc::new(TraceBox(Box::new(NativeCallback::new( params, TraceBox(Box::new(JsonnetNativeCallbackHandler { ctx, cb })), - )), + )))), ) } --- a/crates/jrsonnet-evaluator/src/builtin/mod.rs +++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs @@ -11,7 +11,6 @@ }; use crate::{Either, ObjValue}; use format::{format_arr, format_obj}; -use gcmodule::Cc; use jrsonnet_interner::IStr; use jrsonnet_parser::ExprLocation; use serde::Deserialize; @@ -142,7 +141,7 @@ } #[jrsonnet_macros::builtin] -fn builtin_length(x: Either![IStr, VecVal, ObjValue, Cc]) -> Result { +fn builtin_length(x: Either![IStr, VecVal, ObjValue, FuncVal]) -> Result { use Either4::*; Ok(match x { A(x) => x.chars().count(), @@ -162,7 +161,7 @@ } #[jrsonnet_macros::builtin] -fn builtin_make_array(sz: usize, func: Cc) -> Result { +fn builtin_make_array(sz: usize, func: FuncVal) -> Result { let mut out = Vec::with_capacity(sz); for i in 0..sz { out.push(func.evaluate_simple(&[i as f64].as_slice())?) @@ -345,24 +344,24 @@ } #[jrsonnet_macros::builtin] -fn builtin_native(name: IStr) -> Result> { +fn builtin_native(name: IStr) -> Result { Ok(with_state(|s| s.settings().ext_natives.get(&name).cloned()) - .map(|v| Cc::new(FuncVal::NativeExt(name.clone(), v))) + .map(|v| FuncVal::Builtin(v.clone())) .ok_or(UndefinedExternalFunction(name))?) } #[jrsonnet_macros::builtin] -fn builtin_filter(func: Cc, arr: ArrValue) -> Result { +fn builtin_filter(func: FuncVal, arr: ArrValue) -> Result { arr.filter(|val| bool::try_from(func.evaluate_simple(&[Any(val.clone())].as_slice())?)) } #[jrsonnet_macros::builtin] -fn builtin_map(func: Cc, arr: ArrValue) -> Result { +fn builtin_map(func: FuncVal, arr: ArrValue) -> Result { arr.map(|val| func.evaluate_simple(&[Any(val)].as_slice())) } #[jrsonnet_macros::builtin] -fn builtin_flatmap(func: Cc, arr: IndexableVal) -> Result { +fn builtin_flatmap(func: FuncVal, arr: IndexableVal) -> Result { match arr { IndexableVal::Str(s) => { let mut out = String::new(); @@ -397,7 +396,7 @@ } #[jrsonnet_macros::builtin] -fn builtin_foldl(func: Cc, arr: ArrValue, init: Any) -> Result { +fn builtin_foldl(func: FuncVal, arr: ArrValue, init: Any) -> Result { let mut acc = init.0; for i in arr.iter() { acc = func.evaluate_simple(&[Any(acc), Any(i?)].as_slice())?; @@ -406,7 +405,7 @@ } #[jrsonnet_macros::builtin] -fn builtin_foldr(func: Cc, arr: ArrValue, init: Any) -> Result { +fn builtin_foldr(func: FuncVal, arr: ArrValue, init: Any) -> Result { let mut acc = init.0; for i in arr.iter().rev() { acc = func.evaluate_simple(&[Any(i?), Any(acc)].as_slice())?; @@ -416,13 +415,13 @@ #[jrsonnet_macros::builtin] #[allow(non_snake_case)] -fn builtin_sort(arr: ArrValue, keyF: Option>) -> Result { +fn builtin_sort(arr: ArrValue, keyF: Option) -> Result { if arr.len() <= 1 { return Ok(arr); } Ok(ArrValue::Eager(sort::sort( arr.evaluated()?, - keyF.as_deref(), + keyF.as_ref(), )?)) } --- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs +++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs @@ -177,7 +177,7 @@ } pub fn evaluate_method(ctx: Context, name: IStr, params: ParamsDesc, body: LocExpr) -> Val { - Val::Func(Cc::new(FuncVal::Normal(FuncDesc { + Val::Func(FuncVal::Normal(Cc::new(FuncDesc { name, ctx, params, @@ -630,11 +630,11 @@ Function(params, body) => { evaluate_method(context, "anonymous".into(), params.clone(), body.clone()) } - Intrinsic(name) => Val::Func(Cc::new(FuncVal::StaticBuiltin( + Intrinsic(name) => Val::Func(FuncVal::StaticBuiltin( BUILTINS .with(|b| b.get(name).copied()) .ok_or_else(|| IntrinsicNotFound(name.clone()))?, - ))), + )), AssertExpr(assert, returned) => { evaluate_assert(context.clone(), assert)?; evaluate(context, returned)? --- a/crates/jrsonnet-evaluator/src/function.rs +++ b/crates/jrsonnet-evaluator/src/function.rs @@ -371,7 +371,7 @@ type BuiltinParamName = Cow<'static, str>; -#[derive(Clone)] +#[derive(Clone, Trace)] pub struct BuiltinParam { pub name: BuiltinParamName, pub has_default: bool, --- a/crates/jrsonnet-evaluator/src/lib.rs +++ b/crates/jrsonnet-evaluator/src/lib.rs @@ -13,7 +13,7 @@ mod dynamic; pub mod error; mod evaluate; -mod function; +pub mod function; mod import; mod integrations; mod map; @@ -27,14 +27,12 @@ pub use dynamic::*; use error::{Error::*, LocError, Result, StackTraceElement}; pub use evaluate::*; -pub use function::parse_function_call; -use function::TlaArg; +use function::{Builtin, TlaArg}; use gc::{GcHashMap, TraceBox}; use gcmodule::{Cc, Trace}; pub use import::*; pub use jrsonnet_interner::IStr; use jrsonnet_parser::*; -use native::NativeCallback; pub use obj::*; use std::{ cell::{Ref, RefCell, RefMut}, @@ -79,7 +77,7 @@ /// Used for s`td.extVar` pub ext_vars: HashMap, /// Used for ext.native - pub ext_natives: HashMap>, + pub ext_natives: HashMap>>, /// TLA vars pub tla_vars: HashMap, /// Global variables are inserted in default context @@ -614,7 +612,7 @@ self.settings_mut().import_resolver = resolver; } - pub fn add_native(&self, name: IStr, cb: Cc) { + pub fn add_native(&self, name: IStr, cb: Cc>) { self.settings_mut().ext_natives.insert(name, cb); } @@ -657,8 +655,8 @@ pub mod tests { use super::Val; use crate::{ - error::Error::*, gc::TraceBox, native::NativeCallbackHandler, primitive_equals, - EvaluationState, + error::Error::*, function::BuiltinParam, gc::TraceBox, native::NativeCallbackHandler, + primitive_equals, EvaluationState, }; use gcmodule::{Cc, Trace}; use jrsonnet_interner::IStr; @@ -1096,8 +1094,11 @@ #[derive(Trace)] struct NativeAdd; impl NativeCallbackHandler for NativeAdd { - fn call(&self, from: Rc, args: &[Val]) -> crate::error::Result { - assert_eq!(&from as &Path, &PathBuf::from("native_caller.jsonnet")); + fn call(&self, from: Option>, args: &[Val]) -> crate::error::Result { + assert_eq!( + &from.unwrap() as &Path, + &PathBuf::from("native_caller.jsonnet") + ); match (&args[0], &args[1]) { (Val::Num(a), Val::Num(b)) => Ok(Val::Num(a + b)), (_, _) => unreachable!(), @@ -1106,13 +1107,20 @@ } evaluator.settings_mut().ext_natives.insert( "native_add".into(), - Cc::new(NativeCallback::new( - ParamsDesc(Rc::new(vec![ - Param("a".into(), None), - Param("b".into(), None), - ])), + #[allow(deprecated)] + Cc::new(TraceBox(Box::new(NativeCallback::new( + vec![ + BuiltinParam { + name: "a".into(), + has_default: false, + }, + BuiltinParam { + name: "b".into(), + has_default: false, + }, + ], TraceBox(Box::new(NativeAdd)), - )), + )))), ); evaluator.evaluate_snippet_raw( PathBuf::from("native_caller.jsonnet").into(), --- a/crates/jrsonnet-evaluator/src/native.rs +++ b/crates/jrsonnet-evaluator/src/native.rs @@ -1,33 +1,52 @@ #![allow(clippy::type_complexity)] +use crate::function::{parse_builtin_call, ArgsLike, Builtin, BuiltinParam}; use crate::gc::TraceBox; +use crate::Context; use crate::{error::Result, Val}; use gcmodule::Trace; -use jrsonnet_parser::ParamsDesc; -use std::fmt::Debug; +use jrsonnet_parser::ExprLocation; use std::path::Path; use std::rc::Rc; -#[deprecated(note = "Use builtins instead")] -pub trait NativeCallbackHandler: Trace { - fn call(&self, from: Rc, args: &[Val]) -> Result; -} - #[derive(Trace)] pub struct NativeCallback { - pub params: ParamsDesc, + pub(crate) params: Vec, handler: TraceBox, } impl NativeCallback { - pub fn new(params: ParamsDesc, handler: TraceBox) -> Self { + #[deprecated = "prefer using builtins directly, use this interface only for bindings"] + pub fn new(params: Vec, handler: TraceBox) -> Self { Self { params, handler } - } - pub fn call(&self, caller: Rc, args: &[Val]) -> Result { - self.handler.call(caller, args) } } -impl Debug for NativeCallback { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("NativeCallback").finish() + +impl Builtin for NativeCallback { + fn name(&self) -> &str { + // TODO: standard natives gets their names from definition + // But builitins should already have them + "" + } + + fn params(&self) -> &[BuiltinParam] { + &self.params + } + + fn call( + &self, + context: Context, + loc: Option<&ExprLocation>, + args: &dyn ArgsLike, + ) -> Result { + let args = parse_builtin_call(context, &self.params, args, true)?; + let mut out_args = Vec::with_capacity(self.params.len()); + for p in self.params.iter() { + out_args.push(args[&p.name].evaluate()?); + } + self.handler.call(loc.map(|l| l.0.clone()), &out_args) } } + +pub trait NativeCallbackHandler: Trace { + fn call(&self, from: Option>, args: &[Val]) -> Result; +} --- a/crates/jrsonnet-evaluator/src/typed/conversions.rs +++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs @@ -1,6 +1,5 @@ use std::convert::{TryFrom, TryInto}; -use gcmodule::Cc; use jrsonnet_interner::IStr; use jrsonnet_types::{ComplexValType, ValType}; @@ -400,10 +399,10 @@ } } -impl Typed for Cc { +impl Typed for FuncVal { const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func); } -impl TryFrom for Cc { +impl TryFrom for FuncVal { type Error = LocError; fn try_from(value: Val) -> Result { @@ -414,10 +413,10 @@ } } } -impl TryFrom> for Val { +impl TryFrom for Val { type Error = LocError; - fn try_from(value: Cc) -> Result { + fn try_from(value: FuncVal) -> Result { Ok(Self::Func(value)) } } --- a/crates/jrsonnet-evaluator/src/val.rs +++ b/crates/jrsonnet-evaluator/src/val.rs @@ -7,7 +7,6 @@ evaluate, function::{parse_function_call, ArgsLike, Builtin, StaticBuiltin}, gc::TraceBox, - native::NativeCallback, throw, Context, ObjValue, Result, }; use gcmodule::{Cc, Trace}; @@ -86,16 +85,14 @@ pub body: LocExpr, } -#[derive(Trace)] +#[derive(Trace, Clone)] pub enum FuncVal { /// Plain function implemented in jsonnet - Normal(FuncDesc), + Normal(Cc), /// Standard library function StaticBuiltin(#[skip_trace] &'static dyn StaticBuiltin), - Builtin(TraceBox), - /// Library functions implemented in native - NativeExt(IStr, Cc), + Builtin(Cc>), } impl Debug for FuncVal { @@ -104,9 +101,6 @@ Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(), Self::StaticBuiltin(arg0) => f.debug_tuple("Intrinsic").field(&arg0.name()).finish(), Self::Builtin(arg0) => f.debug_tuple("Intrinsic").field(&arg0.name()).finish(), - Self::NativeExt(arg0, arg1) => { - f.debug_tuple("NativeExt").field(arg0).field(arg1).finish() - } } } } @@ -116,7 +110,6 @@ match (self, other) { (Self::Normal(a), Self::Normal(b)) => a == b, (Self::StaticBuiltin(an), Self::StaticBuiltin(bn)) => std::ptr::eq(*an, *bn), - (Self::NativeExt(an, _), Self::NativeExt(bn, _)) => an == bn, (..) => false, } } @@ -127,7 +120,6 @@ 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::NativeExt(_, n) => n.params.iter().filter(|p| p.1.is_none()).count(), } } pub fn name(&self) -> IStr { @@ -135,7 +127,6 @@ Self::Normal(normal) => normal.name.clone(), Self::StaticBuiltin(builtin) => builtin.name().into(), Self::Builtin(builtin) => builtin.name().into(), - Self::NativeExt(n, _) => format!("native.{}", n).into(), } } pub fn evaluate( @@ -158,15 +149,6 @@ } Self::StaticBuiltin(name) => name.call(call_ctx, loc, args), Self::Builtin(b) => b.call(call_ctx, loc, args), - Self::NativeExt(_name, handler) => { - let args = - parse_function_call(call_ctx, Context::new(), &handler.params, args, true)?; - let mut out_args = Vec::with_capacity(handler.params.len()); - for p in handler.params.0.iter() { - out_args.push(args.binding(p.0.clone())?.evaluate()?); - } - Ok(handler.call(loc.expect("todo").0.clone(), &out_args)?) - } } } pub fn evaluate_simple(&self, args: &dyn ArgsLike) -> Result { @@ -352,7 +334,7 @@ Num(f64), Arr(ArrValue), Obj(ObjValue), - Func(Cc), + Func(FuncVal), } impl Val {