From 679bb1610c83c07b57442081643f44c907f462d5 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 3 Sep 2022 20:40:05 +0200 Subject: [PATCH 1/3] Experimental new function traits --- examples/dynamic/src/main.rs | 4 ++-- minijinja/src/filters.rs | 12 ++++++------ minijinja/src/functions.rs | 6 +++--- minijinja/src/tests.rs | 6 +++--- minijinja/src/value.rs | 25 ++++++------------------- minijinja/src/vm.rs | 30 ++++++++++++++++++------------ 6 files changed, 38 insertions(+), 45 deletions(-) diff --git a/examples/dynamic/src/main.rs b/examples/dynamic/src/main.rs index aba2afec..0c1b348a 100644 --- a/examples/dynamic/src/main.rs +++ b/examples/dynamic/src/main.rs @@ -18,7 +18,7 @@ impl fmt::Display for Cycler { } impl Object for Cycler { - fn call(&self, _state: &State, args: Vec) -> Result { + fn call(&self, _state: &State, args: &[Value]) -> Result { let _: () = FunctionArgs::from_values(args)?; let idx = self.idx.fetch_add(1, Ordering::Relaxed); Ok(self.values[idx % self.values.len()].clone()) @@ -42,7 +42,7 @@ impl fmt::Display for Magic { } impl Object for Magic { - fn call_method(&self, _state: &State, name: &str, args: Vec) -> Result { + fn call_method(&self, _state: &State, name: &str, args: &[Value]) -> Result { if name == "make_class" { let (tag,): (String,) = FunctionArgs::from_values(args)?; Ok(Value::from(format!("magic-{}", tag))) diff --git a/minijinja/src/filters.rs b/minijinja/src/filters.rs index 48d121fe..2a819dde 100644 --- a/minijinja/src/filters.rs +++ b/minijinja/src/filters.rs @@ -51,7 +51,7 @@ use crate::value::{ArgType, FunctionArgs, RcType, Value}; use crate::vm::State; use crate::AutoEscape; -type FilterFunc = dyn Fn(&State, Value, Vec) -> Result + Sync + Send + 'static; +type FilterFunc = dyn Fn(&State, Value, &[Value]) -> Result + Sync + Send + 'static; #[derive(Clone)] pub(crate) struct BoxedFilter(RcType); @@ -105,7 +105,7 @@ impl BoxedFilter { } /// Applies the filter to a value and argument. - pub fn apply_to(&self, state: &State, value: Value, args: Vec) -> Result { + pub fn apply_to(&self, state: &State, value: Value, args: &[Value]) -> Result { (self.0)(state, value, args) } } @@ -758,7 +758,7 @@ mod builtins { }; let bx = BoxedFilter::new(test); assert_eq!( - bx.apply_to(&state, Value::from(23), vec![Value::from(42)]) + bx.apply_to(&state, Value::from(23), &[Value::from(42)][..]) .unwrap(), Value::from(65) ); @@ -784,7 +784,7 @@ mod builtins { }; let bx = BoxedFilter::new(add); assert_eq!( - bx.apply_to(&state, Value::from(23), vec![Value::from(42)]) + bx.apply_to(&state, Value::from(23), &[Value::from(42)][..]) .unwrap(), Value::from(65) ); @@ -792,7 +792,7 @@ mod builtins { bx.apply_to( &state, Value::from(23), - vec![Value::from(42), Value::UNDEFINED] + &[Value::from(42), Value::UNDEFINED][..] ) .unwrap(), Value::from(65) @@ -801,7 +801,7 @@ mod builtins { bx.apply_to( &state, Value::from(23), - vec![Value::from(42), Value::from(1)] + &[Value::from(42), Value::from(1)][..] ) .unwrap(), Value::from(66) diff --git a/minijinja/src/functions.rs b/minijinja/src/functions.rs index b4c56374..6a33adbe 100644 --- a/minijinja/src/functions.rs +++ b/minijinja/src/functions.rs @@ -46,7 +46,7 @@ use crate::error::Error; use crate::value::{FunctionArgs, Object, Value}; use crate::vm::State; -type FuncFunc = dyn Fn(&State, Vec) -> Result + Sync + Send + 'static; +type FuncFunc = dyn Fn(&State, &[Value]) -> Result + Sync + Send + 'static; /// A boxed function. #[derive(Clone)] @@ -97,7 +97,7 @@ impl BoxedFunction { } /// Invokes the function. - pub fn invoke(&self, state: &State, args: Vec) -> Result { + pub fn invoke(&self, state: &State, args: &[Value]) -> Result { (self.0)(state, args) } @@ -128,7 +128,7 @@ impl fmt::Display for BoxedFunction { } impl Object for BoxedFunction { - fn call(&self, state: &State, args: Vec) -> Result { + fn call(&self, state: &State, args: &[Value]) -> Result { self.invoke(state, args) } } diff --git a/minijinja/src/tests.rs b/minijinja/src/tests.rs index 47ad5001..e2940836 100644 --- a/minijinja/src/tests.rs +++ b/minijinja/src/tests.rs @@ -51,7 +51,7 @@ use crate::error::Error; use crate::value::{ArgType, FunctionArgs, RcType, Value}; use crate::vm::State; -type TestFunc = dyn Fn(&State, Value, Vec) -> Result + Sync + Send + 'static; +type TestFunc = dyn Fn(&State, Value, &[Value]) -> Result + Sync + Send + 'static; #[derive(Clone)] pub(crate) struct BoxedTest(RcType); @@ -103,7 +103,7 @@ impl BoxedTest { } /// Applies the filter to a value and argument. - pub fn perform(&self, state: &State, value: Value, args: Vec) -> Result { + pub fn perform(&self, state: &State, value: Value, args: &[Value]) -> Result { (self.0)(state, value, args) } } @@ -212,7 +212,7 @@ mod builtins { }; let bx = BoxedTest::new(test); assert!(bx - .perform(&state, Value::from(23), vec![Value::from(23)]) + .perform(&state, Value::from(23), &[Value::from(23)][..]) .unwrap()); } } diff --git a/minijinja/src/value.rs b/minijinja/src/value.rs index 53368331..c30ca919 100644 --- a/minijinja/src/value.rs +++ b/minijinja/src/value.rs @@ -168,7 +168,7 @@ fn with_internal_serialization R>(f: F) -> R { /// as these types wrapped in [`Option`]. pub trait FunctionArgs: Sized { /// Converts to function arguments from a slice of values. - fn from_values(values: Vec) -> Result; + fn from_values(values: &[Value]) -> Result; } /// A trait implemented by all filter/test argument types. @@ -184,7 +184,7 @@ pub trait ArgType: Sized { macro_rules! tuple_impls { ( $( $name:ident )* ) => { impl<$($name: ArgType,)*> FunctionArgs for ($($name,)*) { - fn from_values(values: Vec) -> Result { + fn from_values(values: &[Value]) -> Result { #![allow(non_snake_case, unused)] let arg_count = 0 $( + { let $name = (); 1 } @@ -1039,7 +1039,7 @@ impl Value { } /// Calls the value directly. - pub(crate) fn call(&self, state: &State, args: Vec) -> Result { + pub(crate) fn call(&self, state: &State, args: &[Value]) -> Result { if let ValueRepr::Dynamic(ref dy) = self.0 { dy.call(state, args) } else { @@ -1055,7 +1055,7 @@ impl Value { &self, state: &State, name: &str, - args: Vec, + args: &[Value], ) -> Result { if let ValueRepr::Dynamic(ref dy) = self.0 { dy.call_method(state, name, args) @@ -1096,19 +1096,6 @@ impl Value { } } - pub(crate) fn try_into_vec(self) -> Result, Error> { - match self.0 { - ValueRepr::Seq(v) => Ok(match RcType::try_unwrap(v) { - Ok(v) => v, - Err(rc) => (*rc).clone(), - }), - _ => Err(Error::new( - ErrorKind::ImpossibleOperation, - "cannot convert value into list", - )), - } - } - pub(crate) fn iter_as_str_map(&self) -> impl Iterator { match self.0 { ValueRepr::Map(ref m) => Box::new( @@ -1711,7 +1698,7 @@ pub trait Object: fmt::Display + fmt::Debug + Any + Sync + Send { /// /// It's the responsibility of the implementer to ensure that an /// error is generated if an invalid method is invoked. - fn call_method(&self, state: &State, name: &str, args: Vec) -> Result { + fn call_method(&self, state: &State, name: &str, args: &[Value]) -> Result { let _state = state; let _args = args; Err(Error::new( @@ -1724,7 +1711,7 @@ pub trait Object: fmt::Display + fmt::Debug + Any + Sync + Send { /// /// The default implementation just generates an error that the object /// cannot be invoked. - fn call(&self, state: &State, args: Vec) -> Result { + fn call(&self, state: &State, args: &[Value]) -> Result { let _state = state; let _args = args; Err(Error::new( diff --git a/minijinja/src/vm.rs b/minijinja/src/vm.rs index 98bb5aa8..220b4ed6 100644 --- a/minijinja/src/vm.rs +++ b/minijinja/src/vm.rs @@ -64,21 +64,22 @@ impl Object for LoopState { } } - fn call(&self, _state: &State, _args: Vec) -> Result { + fn call(&self, _state: &State, _args: &[Value]) -> Result { Err(Error::new( ErrorKind::ImpossibleOperation, "loop cannot be called if reassigned to different variable", )) } - fn call_method(&self, _state: &State, name: &str, args: Vec) -> Result { + fn call_method(&self, _state: &State, name: &str, args: &[Value]) -> Result { #[cfg(feature = "sync")] { if name == "changed" { let mut last_changed_value = self.last_changed_value.lock().unwrap(); - let changed = last_changed_value.as_ref() != Some(&args); + let value = args.to_owned(); + let changed = last_changed_value.as_ref() != Some(&value); if changed { - *last_changed_value = Some(args); + *last_changed_value = Some(value); return Ok(Value::from(true)); } return Ok(Value::from(false)); @@ -404,7 +405,7 @@ impl<'vm, 'env> State<'vm, 'env> { &self, name: &str, value: Value, - args: Vec, + args: &[Value], ) -> Result { if let Some(filter) = self.env().get_filter(name) { filter.apply_to(self, value, args) @@ -420,7 +421,7 @@ impl<'vm, 'env> State<'vm, 'env> { &self, name: &str, value: Value, - args: Vec, + args: &[Value], ) -> Result { if let Some(test) = self.env().get_test(name) { test.perform(self, value, args) @@ -972,17 +973,20 @@ impl<'env> Vm<'env> { end_capture!(); } Instruction::ApplyFilter(name) => { - let args = try_ctx!(stack.pop().try_into_vec()); + let top = stack.pop(); + let args = try_ctx!(top.as_slice()); let value = stack.pop(); stack.push(try_ctx!(state.apply_filter(name, value, args))); } Instruction::PerformTest(name) => { - let args = try_ctx!(stack.pop().try_into_vec()); + let top = stack.pop(); + let args = try_ctx!(top.as_slice()); let value = stack.pop(); stack.push(Value::from(try_ctx!(state.perform_test(name, value, args)))); } Instruction::CallFunction(function_name) => { - let args = try_ctx!(stack.pop().try_into_vec()); + let top = stack.pop(); + let args = try_ctx!(top.as_slice()); // super is a special function reserved for super-ing into blocks. if *function_name == "super" { if !args.is_empty() { @@ -1000,7 +1004,7 @@ impl<'env> Vm<'env> { format!("loop() takes one argument, got {}", args.len()) )); } - stack.push(args.into_iter().next().unwrap()); + stack.push(args[0].clone()); recurse_loop!(true); } else if let Some(func) = state.ctx.load(self.env, function_name) { stack.push(try_ctx!(func.call(state, args))); @@ -1012,12 +1016,14 @@ impl<'env> Vm<'env> { } } Instruction::CallMethod(name) => { - let args = try_ctx!(stack.pop().try_into_vec()); + let top = stack.pop(); + let args = try_ctx!(top.as_slice()); let obj = stack.pop(); stack.push(try_ctx!(obj.call_method(state, name, args))); } Instruction::CallObject => { - let args = try_ctx!(stack.pop().try_into_vec()); + let top = stack.pop(); + let args = try_ctx!(top.as_slice()); let obj = stack.pop(); stack.push(try_ctx!(obj.call(state, args))); } From 886803cddbd74c539fb53408f93d85a9aa75b9bd Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 3 Sep 2022 23:35:56 +0200 Subject: [PATCH 2/3] Borrow first argument in filters and tests --- minijinja/src/environment.rs | 10 ++++----- minijinja/src/filters.rs | 20 +++++++++--------- minijinja/src/functions.rs | 2 +- minijinja/src/tests.rs | 13 ++++++------ minijinja/src/value.rs | 40 ++++++++++++++++++------------------ minijinja/src/vm.rs | 10 +++++---- 6 files changed, 49 insertions(+), 46 deletions(-) diff --git a/minijinja/src/environment.rs b/minijinja/src/environment.rs index ae49da8f..0d3419f5 100644 --- a/minijinja/src/environment.rs +++ b/minijinja/src/environment.rs @@ -526,10 +526,10 @@ impl<'source> Environment<'source> { /// For details about filters have a look at [`filters`]. pub fn add_filter(&mut self, name: &'source str, f: F) where - V: ArgType, + V: for<'a> ArgType<'a>, Rv: Into, F: filters::Filter, - Args: FunctionArgs, + Args: for<'a> FunctionArgs<'a>, { self.filters.insert(name, filters::BoxedFilter::new(f)); } @@ -544,9 +544,9 @@ impl<'source> Environment<'source> { /// For details about tests have a look at [`tests`]. pub fn add_test(&mut self, name: &'source str, f: F) where - V: ArgType, + V: for<'a> ArgType<'a>, F: tests::Test, - Args: FunctionArgs, + Args: for<'a> FunctionArgs<'a>, { self.tests.insert(name, tests::BoxedTest::new(f)); } @@ -564,7 +564,7 @@ impl<'source> Environment<'source> { where Rv: Into, F: functions::Function, - Args: FunctionArgs, + Args: for<'a> FunctionArgs<'a>, { self.add_global(name, functions::BoxedFunction::new(f).to_value()); } diff --git a/minijinja/src/filters.rs b/minijinja/src/filters.rs index 2a819dde..e5a57a72 100644 --- a/minijinja/src/filters.rs +++ b/minijinja/src/filters.rs @@ -51,7 +51,7 @@ use crate::value::{ArgType, FunctionArgs, RcType, Value}; use crate::vm::State; use crate::AutoEscape; -type FilterFunc = dyn Fn(&State, Value, &[Value]) -> Result + Sync + Send + 'static; +type FilterFunc = dyn Fn(&State, &Value, &[Value]) -> Result + Sync + Send + 'static; #[derive(Clone)] pub(crate) struct BoxedFilter(RcType); @@ -88,16 +88,16 @@ impl BoxedFilter { pub fn new(f: F) -> BoxedFilter where F: Filter, - V: ArgType, + V: for<'a> ArgType<'a>, Rv: Into, - Args: FunctionArgs, + Args: for<'a> FunctionArgs<'a>, { BoxedFilter(RcType::new( move |state, value, args| -> Result { f.apply_to( state, - ArgType::from_value(Some(value))?, - FunctionArgs::from_values(args)?, + ArgType::from_value(Some(unsafe { std::mem::transmute::<_, _>(value) }))?, + FunctionArgs::from_values(unsafe { std::mem::transmute::<_, _>(args) })?, ) .map(Into::into) }, @@ -105,7 +105,7 @@ impl BoxedFilter { } /// Applies the filter to a value and argument. - pub fn apply_to(&self, state: &State, value: Value, args: &[Value]) -> Result { + pub fn apply_to(&self, state: &State, value: &Value, args: &[Value]) -> Result { (self.0)(state, value, args) } } @@ -758,7 +758,7 @@ mod builtins { }; let bx = BoxedFilter::new(test); assert_eq!( - bx.apply_to(&state, Value::from(23), &[Value::from(42)][..]) + bx.apply_to(&state, &Value::from(23), &[Value::from(42)][..]) .unwrap(), Value::from(65) ); @@ -784,14 +784,14 @@ mod builtins { }; let bx = BoxedFilter::new(add); assert_eq!( - bx.apply_to(&state, Value::from(23), &[Value::from(42)][..]) + bx.apply_to(&state, &Value::from(23), &[Value::from(42)][..]) .unwrap(), Value::from(65) ); assert_eq!( bx.apply_to( &state, - Value::from(23), + &Value::from(23), &[Value::from(42), Value::UNDEFINED][..] ) .unwrap(), @@ -800,7 +800,7 @@ mod builtins { assert_eq!( bx.apply_to( &state, - Value::from(23), + &Value::from(23), &[Value::from(42), Value::from(1)][..] ) .unwrap(), diff --git a/minijinja/src/functions.rs b/minijinja/src/functions.rs index 6a33adbe..51490ac9 100644 --- a/minijinja/src/functions.rs +++ b/minijinja/src/functions.rs @@ -85,7 +85,7 @@ impl BoxedFunction { where F: Function, Rv: Into, - Args: FunctionArgs, + Args: for<'a> FunctionArgs<'a>, { BoxedFunction( Arc::new(move |env, args| -> Result { diff --git a/minijinja/src/tests.rs b/minijinja/src/tests.rs index e2940836..5082dffe 100644 --- a/minijinja/src/tests.rs +++ b/minijinja/src/tests.rs @@ -51,7 +51,7 @@ use crate::error::Error; use crate::value::{ArgType, FunctionArgs, RcType, Value}; use crate::vm::State; -type TestFunc = dyn Fn(&State, Value, &[Value]) -> Result + Sync + Send + 'static; +type TestFunc = dyn Fn(&State, &Value, &[Value]) -> Result + Sync + Send + 'static; #[derive(Clone)] pub(crate) struct BoxedTest(RcType); @@ -88,14 +88,15 @@ impl BoxedTest { pub fn new(f: F) -> BoxedTest where F: Test, - V: ArgType, - Args: FunctionArgs, + V: for<'a> ArgType<'a>, + Args: for<'a> FunctionArgs<'a>, { BoxedTest(RcType::new( move |state, value, args| -> Result { + let value = Some(value); f.perform( state, - ArgType::from_value(Some(value))?, + ArgType::from_value(value)?, FunctionArgs::from_values(args)?, ) }, @@ -103,7 +104,7 @@ impl BoxedTest { } /// Applies the filter to a value and argument. - pub fn perform(&self, state: &State, value: Value, args: &[Value]) -> Result { + pub fn perform(&self, state: &State, value: &Value, args: &[Value]) -> Result { (self.0)(state, value, args) } } @@ -212,7 +213,7 @@ mod builtins { }; let bx = BoxedTest::new(test); assert!(bx - .perform(&state, Value::from(23), &[Value::from(23)][..]) + .perform(&state, &Value::from(23), &[Value::from(23)][..]) .unwrap()); } } diff --git a/minijinja/src/value.rs b/minijinja/src/value.rs index c30ca919..8df7f917 100644 --- a/minijinja/src/value.rs +++ b/minijinja/src/value.rs @@ -166,9 +166,9 @@ fn with_internal_serialization R>(f: F) -> R { /// For each argument the conversion is performed via the [`ArgType`] /// trait which is implemented for some primitive concrete types as well /// as these types wrapped in [`Option`]. -pub trait FunctionArgs: Sized { +pub trait FunctionArgs<'a>: Sized { /// Converts to function arguments from a slice of values. - fn from_values(values: &[Value]) -> Result; + fn from_values(values: &'a [Value]) -> Result; } /// A trait implemented by all filter/test argument types. @@ -177,14 +177,14 @@ pub trait FunctionArgs: Sized { /// `Option` where `Some` means the argument was provided or /// `None` if it was not. This is used to implement optional arguments /// to functions. -pub trait ArgType: Sized { - fn from_value(value: Option) -> Result; +pub trait ArgType<'a>: Sized { + fn from_value(value: Option<&'a Value>) -> Result; } macro_rules! tuple_impls { ( $( $name:ident )* ) => { - impl<$($name: ArgType,)*> FunctionArgs for ($($name,)*) { - fn from_values(values: &[Value]) -> Result { + impl<'a, $($name: ArgType<'a>,)*> FunctionArgs<'a> for ($($name,)*) { + fn from_values(values: &'a [Value]) -> Result { #![allow(non_snake_case, unused)] let arg_count = 0 $( + { let $name = (); 1 } @@ -198,7 +198,7 @@ macro_rules! tuple_impls { { let mut idx = 0; $( - let $name = ArgType::from_value(values.get(idx).cloned())?; + let $name = ArgType::from_value(values.get(idx))?; idx += 1; )* Ok(( $($name,)* )) @@ -705,23 +705,23 @@ macro_rules! primitive_try_from { } } - impl ArgType for $ty { - fn from_value(value: Option) -> Result { + impl<'a> ArgType<'a> for $ty { + fn from_value(value: Option<&Value>) -> Result { match value { - Some(value) => TryFrom::try_from(value), + Some(value) => TryFrom::try_from(value.clone()), None => Err(Error::new(ErrorKind::UndefinedError, concat!("missing argument"))) } } } - impl ArgType for Option<$ty> { - fn from_value(value: Option) -> Result { + impl<'a> ArgType<'a> for Option<$ty> { + fn from_value(value: Option<&Value>) -> Result { match value { Some(value) => { if value.is_undefined() || value.is_none() { Ok(None) } else { - TryFrom::try_from(value).map(Some) + TryFrom::try_from(value.clone()).map(Some) } } None => Ok(None), @@ -767,8 +767,8 @@ primitive_try_from!(f64, { macro_rules! infallible_conversion { ($ty:ty) => { - impl ArgType for $ty { - fn from_value(value: Option) -> Result { + impl<'a> ArgType<'a> for $ty { + fn from_value(value: Option<&'a Value>) -> Result { match value { Some(value) => Ok(value.into()), None => Err(Error::new( @@ -779,8 +779,8 @@ macro_rules! infallible_conversion { } } - impl ArgType for Option<$ty> { - fn from_value(value: Option) -> Result { + impl<'a> ArgType<'a> for Option<$ty> { + fn from_value(value: Option<&'a Value>) -> Result { match value { Some(value) => { if value.is_undefined() || value.is_none() { @@ -811,15 +811,15 @@ impl From for Value { } } -impl ArgType for Vec { - fn from_value(value: Option) -> Result { +impl<'a, T: ArgType<'a>> ArgType<'a> for Vec { + fn from_value(value: Option<&'a Value>) -> Result { match value { None => Ok(Vec::new()), Some(values) => { let values = values.as_slice()?; let mut rv = Vec::new(); for value in values { - rv.push(ArgType::from_value(Some(value.clone()))?); + rv.push(ArgType::from_value(Some(value))?); } Ok(rv) } diff --git a/minijinja/src/vm.rs b/minijinja/src/vm.rs index 220b4ed6..0bfdfbef 100644 --- a/minijinja/src/vm.rs +++ b/minijinja/src/vm.rs @@ -404,7 +404,7 @@ impl<'vm, 'env> State<'vm, 'env> { pub(crate) fn apply_filter( &self, name: &str, - value: Value, + value: &Value, args: &[Value], ) -> Result { if let Some(filter) = self.env().get_filter(name) { @@ -420,7 +420,7 @@ impl<'vm, 'env> State<'vm, 'env> { pub(crate) fn perform_test( &self, name: &str, - value: Value, + value: &Value, args: &[Value], ) -> Result { if let Some(test) = self.env().get_test(name) { @@ -976,13 +976,15 @@ impl<'env> Vm<'env> { let top = stack.pop(); let args = try_ctx!(top.as_slice()); let value = stack.pop(); - stack.push(try_ctx!(state.apply_filter(name, value, args))); + stack.push(try_ctx!(state.apply_filter(name, &value, args))); } Instruction::PerformTest(name) => { let top = stack.pop(); let args = try_ctx!(top.as_slice()); let value = stack.pop(); - stack.push(Value::from(try_ctx!(state.perform_test(name, value, args)))); + stack.push(Value::from( + try_ctx!(state.perform_test(name, &value, args)), + )); } Instruction::CallFunction(function_name) => { let top = stack.pop(); From 94be375a43d5b2cd153c637e766492f01e80ce82 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sun, 4 Sep 2022 00:47:00 +0200 Subject: [PATCH 3/3] Minor changes to the VM and remove some leftover unsafe code --- minijinja/src/filters.rs | 4 +-- minijinja/src/value.rs | 70 ++++++++++++++++++++++++++-------------- minijinja/src/vm.rs | 4 +-- 3 files changed, 49 insertions(+), 29 deletions(-) diff --git a/minijinja/src/filters.rs b/minijinja/src/filters.rs index e5a57a72..fa1fc48d 100644 --- a/minijinja/src/filters.rs +++ b/minijinja/src/filters.rs @@ -96,8 +96,8 @@ impl BoxedFilter { move |state, value, args| -> Result { f.apply_to( state, - ArgType::from_value(Some(unsafe { std::mem::transmute::<_, _>(value) }))?, - FunctionArgs::from_values(unsafe { std::mem::transmute::<_, _>(args) })?, + ArgType::from_value(Some(value))?, + FunctionArgs::from_values(args)?, ) .map(Into::into) }, diff --git a/minijinja/src/value.rs b/minijinja/src/value.rs index 8df7f917..0c77ea29 100644 --- a/minijinja/src/value.rs +++ b/minijinja/src/value.rs @@ -765,40 +765,60 @@ primitive_try_from!(f64, { ValueRepr::F64(val) => val, }); -macro_rules! infallible_conversion { - ($ty:ty) => { - impl<'a> ArgType<'a> for $ty { - fn from_value(value: Option<&'a Value>) -> Result { - match value { - Some(value) => Ok(value.into()), - None => Err(Error::new( - ErrorKind::UndefinedError, - concat!("missing argument"), - )), +impl<'a> ArgType<'a> for Value { + fn from_value(value: Option<&'a Value>) -> Result { + match value { + Some(value) => Ok(value.clone()), + None => Err(Error::new( + ErrorKind::UndefinedError, + concat!("missing argument"), + )), + } + } +} + +impl<'a> ArgType<'a> for Option { + fn from_value(value: Option<&'a Value>) -> Result { + match value { + Some(value) => { + if value.is_undefined() || value.is_none() { + Ok(None) + } else { + Ok(Some(value.clone())) } } + None => Ok(None), } + } +} - impl<'a> ArgType<'a> for Option<$ty> { - fn from_value(value: Option<&'a Value>) -> Result { - match value { - Some(value) => { - if value.is_undefined() || value.is_none() { - Ok(None) - } else { - Ok(Some(value.clone().into())) - } - } - None => Ok(None), +impl<'a> ArgType<'a> for String { + fn from_value(value: Option<&'a Value>) -> Result { + match value { + Some(value) => Ok(value.to_string()), + None => Err(Error::new( + ErrorKind::UndefinedError, + concat!("missing argument"), + )), + } + } +} + +impl<'a> ArgType<'a> for Option { + fn from_value(value: Option<&'a Value>) -> Result { + match value { + Some(value) => { + if value.is_undefined() || value.is_none() { + Ok(None) + } else { + Ok(Some(value.to_string())) } } + None => Ok(None), } - }; + } } -infallible_conversion!(String); -infallible_conversion!(Value); - impl From for String { fn from(val: Value) -> Self { val.to_string() diff --git a/minijinja/src/vm.rs b/minijinja/src/vm.rs index 0bfdfbef..9101b203 100644 --- a/minijinja/src/vm.rs +++ b/minijinja/src/vm.rs @@ -684,12 +684,12 @@ impl<'env> Vm<'env> { stack.push(Value::from(map)); } Instruction::BuildList(count) => { - let mut v = Vec::new(); + let mut v = Vec::with_capacity(*count); for _ in 0..*count { v.push(stack.pop()); } v.reverse(); - stack.push(v.into()); + stack.push(Value(ValueRepr::Seq(RcType::new(v)))); } Instruction::UnpackList(count) => { let top = stack.pop();