Avoid copying arguments for function calls.

This commit is contained in:
Stephen Chung 2020-05-03 16:54:24 +08:00
parent fc66a7ecef
commit d83b829810
8 changed files with 81 additions and 64 deletions

View File

@ -374,19 +374,19 @@ engine.load_package(package.get()); // load the package manually
The follow packages are available:
| Package | Description | In `CorePackage` | In `StandardPackage` |
| ------------------------ | ----------------------------------------------- | :--------------: | :------------------: |
| `ArithmeticPackage` | Arithmetic operators (e.g. `+`, `-`, `*`, `/`) | Yes | Yes |
| `BasicIteratorPackage` | Numeric ranges (e.g. `range(1, 10)`) | Yes | Yes |
| `LogicPackage` | Logic and comparison operators (e.g. `==`, `>`) | Yes | Yes |
| `BasicStringPackage` | Basic string functions | Yes | Yes |
| `BasicTimePackage` | Basic time functions (e.g. [timestamps]) | Yes | Yes |
| `MoreStringPackage` | Additional string functions | No | Yes |
| `BasicMathPackage` | Basic math functions (e.g. `sin`, `sqrt`) | No | Yes |
| `BasicArrayPackage` | Basic [array] functions | No | Yes |
| `BasicMapPackage` | Basic [object map] functions | No | Yes |
| `CorePackage` | Basic essentials | | |
| `StandardPackage` | Standard library | | |
| Package | Description | In `CorePackage` | In `StandardPackage` |
| ---------------------- | ----------------------------------------------- | :--------------: | :------------------: |
| `ArithmeticPackage` | Arithmetic operators (e.g. `+`, `-`, `*`, `/`) | Yes | Yes |
| `BasicIteratorPackage` | Numeric ranges (e.g. `range(1, 10)`) | Yes | Yes |
| `LogicPackage` | Logic and comparison operators (e.g. `==`, `>`) | Yes | Yes |
| `BasicStringPackage` | Basic string functions | Yes | Yes |
| `BasicTimePackage` | Basic time functions (e.g. [timestamps]) | Yes | Yes |
| `MoreStringPackage` | Additional string functions | No | Yes |
| `BasicMathPackage` | Basic math functions (e.g. `sin`, `sqrt`) | No | Yes |
| `BasicArrayPackage` | Basic [array] functions | No | Yes |
| `BasicMapPackage` | Basic [object map] functions | No | Yes |
| `CorePackage` | Basic essentials | | |
| `StandardPackage` | Standard library | | |
Evaluate expressions only
-------------------------
@ -427,7 +427,7 @@ The following primitive types are supported natively:
| **Boolean value** | `bool` | `"bool"` | `"true"` or `"false"` |
| **Unicode character** | `char` | `"char"` | `"A"`, `"x"` etc. |
| **Unicode string** | `String` (_not_ `&str`) | `"string"` | `"hello"` etc. |
| **Array** (disabled with [`no_index`]) | `rhai::Array` | `"array"` | `"[ ? ? ? ]"` |
| **Array** (disabled with [`no_index`]) | `rhai::Array` | `"array"` | `"[ ?, ?, ? ]"` |
| **Object map** (disabled with [`no_object`]) | `rhai::Map` | `"map"` | `#{ "a": 1, "b": 2 }` |
| **Timestamp** (implemented in the [`BasicTimePackage`](#packages)) | `std::time::Instant` | `"timestamp"` | _not supported_ |
| **Dynamic value** (i.e. can be anything) | `rhai::Dynamic` | _the actual type_ | _actual value_ |
@ -1372,7 +1372,8 @@ y[2] == 3;
y[3] == 4;
(1 in y) == true; // use 'in' to test if an item exists in the array
(42 in y) == false;
(42 in y) == false; // 'in' uses the '==' operator (which users can override)
// to check if the target item exists in the array
y[1] = 42; // array elements can be reassigned
@ -1494,7 +1495,7 @@ y.a == 42;
y["baz!$@"] == 123.456; // access via index notation
"baz!$@" in y == true; // use 'in' to test if a property exists in the object map, prints true
"baz!$@" in y == true; // use 'in' to test if a property exists in the object map
("z" in y) == false;
ts.obj = y; // object maps can be assigned completely (by value copy)

View File

@ -396,7 +396,24 @@ impl Dynamic {
/// assert_eq!(x.cast::<u32>(), 42);
/// ```
pub fn cast<T: Variant + Clone>(self) -> T {
self.try_cast::<T>().unwrap()
//self.try_cast::<T>().unwrap()
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
return cast_box::<_, T>(Box::new(self)).unwrap();
}
match self.0 {
Union::Unit(ref value) => (value as &dyn Variant).downcast_ref::<T>().unwrap().clone(),
Union::Bool(ref value) => (value as &dyn Variant).downcast_ref::<T>().unwrap().clone(),
Union::Str(value) => cast_box::<_, T>(value).unwrap(),
Union::Char(ref value) => (value as &dyn Variant).downcast_ref::<T>().unwrap().clone(),
Union::Int(ref value) => (value as &dyn Variant).downcast_ref::<T>().unwrap().clone(),
#[cfg(not(feature = "no_float"))]
Union::Float(ref value) => (value as &dyn Variant).downcast_ref::<T>().unwrap().clone(),
Union::Array(value) => cast_box::<_, T>(value).unwrap(),
Union::Map(value) => cast_box::<_, T>(value).unwrap(),
Union::Variant(value) => value.as_ref().as_ref().downcast_ref::<T>().unwrap().clone(),
}
}
/// Get a reference of a specific type to the `Dynamic`.

View File

@ -692,7 +692,7 @@ impl Engine {
&self,
fn_lib: &FunctionsLib,
fn_name: &str,
args: &mut [&mut Dynamic],
args: &mut FnCallArgs,
def_val: Option<&Dynamic>,
pos: Position,
level: usize,
@ -1227,7 +1227,7 @@ impl Engine {
#[cfg(not(feature = "no_index"))]
Expr::Array(contents, _) => Ok(Dynamic(Union::Array(Box::new(
contents
.into_iter()
.iter()
.map(|item| self.eval_expr(scope, state, fn_lib, item, level))
.collect::<Result<Vec<_>, _>>()?,
)))),
@ -1235,9 +1235,9 @@ impl Engine {
#[cfg(not(feature = "no_object"))]
Expr::Map(contents, _) => Ok(Dynamic(Union::Map(Box::new(
contents
.into_iter()
.iter()
.map(|(key, expr, _)| {
self.eval_expr(scope, state, fn_lib, &expr, level)
self.eval_expr(scope, state, fn_lib, expr, level)
.map(|val| (key.clone(), val))
})
.collect::<Result<HashMap<_, _>, _>>()?,

View File

@ -22,7 +22,7 @@ macro_rules! impl_args {
fn into_vec(self) -> Vec<Dynamic> {
let ($($p,)*) = self;
#[allow(unused_variables, unused_mut)]
#[allow(unused_mut)]
let mut v = Vec::new();
$(v.push($p.into_dynamic());)*
@ -42,5 +42,4 @@ macro_rules! impl_args {
};
}
#[rustfmt::skip]
impl_args!(A, B, C, D, E, F, G, H, J, K, L, M, N, P, Q, R, S, T, U, V);

View File

@ -116,5 +116,4 @@ macro_rules! def_anonymous_fn {
};
}
#[rustfmt::skip]
def_anonymous_fn!(A, B, C, D, E, F, G, H, J, K, L, M, N, P, Q, R, S, T, U, V);

View File

@ -7,7 +7,7 @@ use crate::engine::{calc_fn_spec, Engine, FnCallArgs};
use crate::result::EvalAltResult;
use crate::token::Position;
use crate::stdlib::{any::TypeId, boxed::Box, string::ToString};
use crate::stdlib::{any::TypeId, boxed::Box, mem, string::ToString};
/// A trait to register custom functions with the `Engine`.
pub trait RegisterFn<FN, ARGS, RET> {
@ -115,16 +115,19 @@ pub trait RegisterResultFn<FN, ARGS, RET> {
pub struct Mut<T>(T);
//pub struct Ref<T>(T);
/// Identity dereferencing function.
/// Dereference into &mut.
#[inline]
pub fn identity<T>(data: &mut T) -> &mut T {
data
pub fn by_ref<T: Clone + 'static>(data: &mut Dynamic) -> &mut T {
// Directly cast the &mut Dynamic into &mut T to access the underlying data.
data.downcast_mut::<T>().unwrap()
}
/// Clone dereferencing function.
/// Dereference into value.
#[inline]
pub fn cloned<T: Clone>(data: &mut T) -> T {
data.clone()
pub fn by_value<T: Clone + 'static>(data: &mut Dynamic) -> T {
// We consume the argument and then replace it with () - the argument is not supposed to be used again.
// This way, we avoid having to clone the argument again, because it is already a clone when passed here.
mem::replace(data, Default::default()).cast::<T>()
}
/// This macro counts the number of arguments via recursion.
@ -135,7 +138,7 @@ macro_rules! count_args {
/// This macro creates a closure wrapping a registered function.
macro_rules! make_func {
($fn_name:ident : $fn:ident : $map:expr ; $($par:ident => $clone:expr),*) => {
($fn_name:ident : $fn:ident : $map:expr ; $($par:ident => $convert:expr),*) => {
// ^ function name
// ^ function pointer
// ^ result mapping function
@ -153,14 +156,16 @@ macro_rules! make_func {
#[allow(unused_variables, unused_mut)]
let mut drain = args.iter_mut();
$(
// Downcast every element, return in case of a type mismatch
let $par: &mut $par = drain.next().unwrap().downcast_mut().unwrap();
// Downcast every element, panic in case of a type mismatch (which shouldn't happen).
// Call the user-supplied function using ($convert) to access it either by value or by reference.
let $par = ($convert)(drain.next().unwrap());
)*
// Call the user-supplied function using ($clone) to
// potentially clone the value, otherwise pass the reference.
let r = $fn($(($clone)($par)),*);
$map(r, pos)
// Call the function with each parameter value
let r = $fn($($par),*);
// Map the result
$map(r, pos)
};
};
}
@ -259,18 +264,17 @@ macro_rules! def_register {
//def_register!(imp_pop $($par => $mark => $param),*);
};
($p0:ident $(, $p:ident)*) => {
def_register!(imp $p0 => $p0 => $p0 => cloned $(, $p => $p => $p => cloned)*);
def_register!(imp $p0 => Mut<$p0> => &mut $p0 => identity $(, $p => $p => $p => cloned)*);
def_register!(imp $p0 => $p0 => $p0 => by_value $(, $p => $p => $p => by_value)*);
def_register!(imp $p0 => Mut<$p0> => &mut $p0 => by_ref $(, $p => $p => $p => by_value)*);
// handle the first parameter ^ first parameter passed through
// ^ others passed by value (cloned)
// ^ others passed by value (by_value)
// Currently does not support first argument which is a reference, as there will be
// conflicting implementations since &T: Any and T: Any cannot be distinguished
//def_register!(imp $p0 => Ref<$p0> => &$p0 => identity $(, $p => $p => $p => Clone::clone)*);
//def_register!(imp $p0 => Ref<$p0> => &$p0 => by_ref $(, $p => $p => $p => by_value)*);
def_register!($($p),*);
};
}
#[rustfmt::skip]
def_register!(A, B, C, D, E, F, G, H, J, K, L, M, N, P, Q, R, S, T, U, V);

View File

@ -371,12 +371,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
(Expr::Variable(var, sp, _), Expr::Variable(var2, sp2, _)) if var == var2 && sp == sp2 => {
// Assignment to the same variable - fold
state.set_dirty();
Expr::Assignment(
Box::new(Expr::Variable(var, sp, pos)),
Box::new(optimize_expr(*expr2, state)),
pos,
)
Expr::Assignment(Box::new(Expr::Variable(var, sp, pos)), Box::new(optimize_expr(*expr2, state)), pos)
}
// id1 = id2 = expr2
(id1, id2) => Expr::Assignment(

View File

@ -15,7 +15,7 @@ use crate::stdlib::{
format,
iter::Peekable,
num::NonZeroUsize,
ops::Add,
ops::{Add, Deref, DerefMut},
rc::Rc,
string::{String, ToString},
sync::Arc,
@ -193,18 +193,6 @@ impl Stack {
pub fn new() -> Self {
Self(Vec::new())
}
/// Get the number of variables in the `Stack`.
pub fn len(&self) -> usize {
self.0.len()
}
/// Push (add) a new variable onto the `Stack`.
pub fn push(&mut self, name: String) {
self.0.push(name);
}
/// Rewind the stack to a previous size.
pub fn rewind(&mut self, len: usize) {
self.0.truncate(len);
}
/// Find a variable by name in the `Stack`, searching in reverse.
/// The return value is the offset to be deducted from `Stack::len`,
/// i.e. the top element of the `Stack` is offset 1.
@ -219,6 +207,20 @@ impl Stack {
}
}
impl Deref for Stack {
type Target = Vec<String>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Stack {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
/// A statement.
#[derive(Debug, Clone)]
pub enum Stmt {
@ -1639,7 +1641,7 @@ fn parse_for<'a>(
let body = parse_block(input, stack, true, allow_stmt_expr)?;
stack.rewind(prev_len);
stack.truncate(prev_len);
Ok(Stmt::For(Box::new(name), Box::new(expr), Box::new(body)))
}
@ -1747,7 +1749,7 @@ fn parse_block<'a>(
}
}
stack.rewind(prev_len);
stack.truncate(prev_len);
Ok(Stmt::Block(statements, pos))
}