Create NativeCallable trait.

This commit is contained in:
Stephen Chung 2020-05-11 13:36:50 +08:00
parent 314ec5e4d2
commit 4a8710a4a9
9 changed files with 115 additions and 80 deletions

View File

@ -3,11 +3,12 @@
use crate::any::{Dynamic, Union};
use crate::calc_fn_hash;
use crate::error::ParseErrorType;
use crate::fn_native::{FnCallArgs, NativeFunction, SharedNativeFunction};
use crate::optimize::OptimizationLevel;
use crate::packages::{
CorePackage, Package, PackageLibrary, PackageStore, PackagesCollection, StandardPackage,
};
use crate::parser::{Expr, FnAccess, FnDef, ReturnType, Stmt, AST};
use crate::parser::{Expr, FnAccess, FnDef, ReturnType, SharedFnDef, Stmt, AST};
use crate::result::EvalAltResult;
use crate::scope::{EntryType as ScopeEntryType, Scope};
use crate::token::Position;
@ -46,19 +47,6 @@ pub type Array = Vec<Dynamic>;
#[cfg(not(feature = "no_object"))]
pub type Map = HashMap<String, Dynamic>;
pub type FnCallArgs<'a> = [&'a mut Dynamic];
#[cfg(feature = "sync")]
pub type FnAny =
dyn Fn(&mut FnCallArgs, Position) -> Result<Dynamic, Box<EvalAltResult>> + Send + Sync;
#[cfg(not(feature = "sync"))]
pub type FnAny = dyn Fn(&mut FnCallArgs, Position) -> Result<Dynamic, Box<EvalAltResult>>;
#[cfg(feature = "sync")]
pub type IteratorFn = dyn Fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + Send + Sync;
#[cfg(not(feature = "sync"))]
pub type IteratorFn = dyn Fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;
#[cfg(debug_assertions)]
pub const MAX_CALL_STACK_DEPTH: usize = 28;
@ -168,20 +156,6 @@ impl<'a> State<'a> {
}
}
/// An external native Rust function.
#[cfg(not(feature = "sync"))]
pub type NativeFunction = Rc<Box<FnAny>>;
/// An external native Rust function.
#[cfg(feature = "sync")]
pub type NativeFunction = Arc<Box<FnAny>>;
/// A sharable script-defined function.
#[cfg(feature = "sync")]
pub type ScriptedFunction = Arc<FnDef>;
/// A sharable script-defined function.
#[cfg(not(feature = "sync"))]
pub type ScriptedFunction = Rc<FnDef>;
/// A type that holds a library (`HashMap`) of script-defined functions.
///
/// Since script-defined functions have `Dynamic` parameters, functions with the same name
@ -190,7 +164,7 @@ pub type ScriptedFunction = Rc<FnDef>;
/// The key of the `HashMap` is a `u64` hash calculated by the function `calc_fn_hash`
/// with dummy parameter types `EMPTY_TYPE_ID()` repeated the correct number of times.
#[derive(Debug, Clone, Default)]
pub struct FunctionsLib(HashMap<u64, ScriptedFunction>);
pub struct FunctionsLib(HashMap<u64, SharedFnDef>);
impl FunctionsLib {
/// Create a new `FunctionsLib` from a collection of `FnDef`.
@ -261,8 +235,8 @@ impl FunctionsLib {
}
}
impl From<Vec<(u64, ScriptedFunction)>> for FunctionsLib {
fn from(values: Vec<(u64, ScriptedFunction)>) -> Self {
impl From<Vec<(u64, SharedFnDef)>> for FunctionsLib {
fn from(values: Vec<(u64, SharedFnDef)>) -> Self {
FunctionsLib(values.into_iter().collect())
}
}
@ -596,7 +570,7 @@ impl Engine {
.or_else(|| self.packages.get_function(hash_fn_spec))
{
// Run external function
let result = func(args, pos)?;
let result = func.call(args, pos)?;
// See if the function match print/debug (which requires special processing)
return Ok(match fn_name {
@ -1476,7 +1450,7 @@ impl Engine {
let hash = *hash_fn_def ^ hash_fn_args;
match module.get_qualified_fn(name, hash, *pos) {
Ok(func) => func(args.as_mut(), *pos),
Ok(func) => func.call(args.as_mut(), *pos),
Err(_) if def_val.is_some() => Ok(def_val.clone().unwrap()),
Err(err) => Err(err),
}

52
src/fn_native.rs Normal file
View File

@ -0,0 +1,52 @@
use crate::any::Dynamic;
use crate::result::EvalAltResult;
use crate::token::Position;
use crate::stdlib::{boxed::Box, rc::Rc, sync::Arc};
pub type FnCallArgs<'a> = [&'a mut Dynamic];
#[cfg(feature = "sync")]
pub type FnAny =
dyn Fn(&mut FnCallArgs, Position) -> Result<Dynamic, Box<EvalAltResult>> + Send + Sync;
#[cfg(not(feature = "sync"))]
pub type FnAny = dyn Fn(&mut FnCallArgs, Position) -> Result<Dynamic, Box<EvalAltResult>>;
#[cfg(feature = "sync")]
pub type IteratorFn = dyn Fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + Send + Sync;
#[cfg(not(feature = "sync"))]
pub type IteratorFn = dyn Fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;
/// A trait implemented by all native Rust functions that are callable by Rhai.
pub trait NativeCallable {
/// Call a native Rust function.
fn call(&self, args: &mut FnCallArgs, pos: Position) -> Result<Dynamic, Box<EvalAltResult>>;
}
/// A type encapsulating a native Rust function callable by Rhai.
pub struct NativeFunction(Box<FnAny>);
impl NativeCallable for NativeFunction {
fn call(&self, args: &mut FnCallArgs, pos: Position) -> Result<Dynamic, Box<EvalAltResult>> {
(self.0)(args, pos)
}
}
impl From<Box<FnAny>> for NativeFunction {
fn from(func: Box<FnAny>) -> Self {
Self::new(func)
}
}
impl NativeFunction {
/// Create a new `NativeFunction`.
pub fn new(func: Box<FnAny>) -> Self {
Self(func)
}
}
/// An external native Rust function.
#[cfg(not(feature = "sync"))]
pub type SharedNativeFunction = Rc<Box<dyn NativeCallable>>;
/// An external native Rust function.
#[cfg(feature = "sync")]
pub type SharedNativeFunction = Arc<Box<dyn NativeCallable>>;

View File

@ -3,7 +3,8 @@
#![allow(non_snake_case)]
use crate::any::{Dynamic, Variant};
use crate::engine::{Engine, FnCallArgs};
use crate::engine::Engine;
use crate::fn_native::{FnCallArgs, NativeFunction};
use crate::result::EvalAltResult;
use crate::token::Position;
use crate::utils::calc_fn_spec;
@ -117,14 +118,14 @@ pub struct Mut<T>(T);
//pub struct Ref<T>(T);
/// Dereference into &mut.
#[inline]
#[inline(always)]
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()
}
/// Dereference into value.
#[inline]
#[inline(always)]
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.
@ -133,14 +134,13 @@ pub fn by_value<T: Clone + 'static>(data: &mut Dynamic) -> T {
/// This macro creates a closure wrapping a registered function.
macro_rules! make_func {
($fn_name:ident : $fn:ident : $map:expr ; $($par:ident => $convert:expr),*) => {
// ^ function name
($fn:ident : $map:expr ; $($par:ident => $convert:expr),*) => {
// ^ function pointer
// ^ result mapping function
// ^ function parameter generic type name (A, B, C etc.)
// ^ dereferencing function
move |args: &mut FnCallArgs, pos: Position| {
NativeFunction::new(Box::new(move |args: &mut FnCallArgs, pos: Position| {
// The arguments are assumed to be of the correct number and types!
#[allow(unused_variables, unused_mut)]
@ -156,12 +156,12 @@ macro_rules! make_func {
// Map the result
$map(r, pos)
};
}));
};
}
/// To Dynamic mapping function.
#[inline]
#[inline(always)]
pub fn map_dynamic<T: Variant + Clone>(
data: T,
_pos: Position,
@ -170,13 +170,13 @@ pub fn map_dynamic<T: Variant + Clone>(
}
/// To Dynamic mapping function.
#[inline]
#[inline(always)]
pub fn map_identity(data: Dynamic, _pos: Position) -> Result<Dynamic, Box<EvalAltResult>> {
Ok(data)
}
/// To `Result<Dynamic, Box<EvalAltResult>>` mapping function.
#[inline]
#[inline(always)]
pub fn map_result<T: Variant + Clone>(
data: Result<T, Box<EvalAltResult>>,
pos: Position,
@ -207,8 +207,7 @@ macro_rules! def_register {
> RegisterFn<FN, ($($mark,)*), RET> for Engine
{
fn register_fn(&mut self, name: &str, f: FN) {
let fn_name = name.to_string();
let func = make_func!(fn_name : f : map_dynamic ; $($par => $clone),*);
let func = make_func!(f : map_dynamic ; $($par => $clone),*);
let hash = calc_fn_spec(empty(), name, [$(TypeId::of::<$par>()),*].iter().cloned());
self.base_package.functions.insert(hash, Box::new(func));
}
@ -225,8 +224,7 @@ macro_rules! def_register {
> RegisterDynamicFn<FN, ($($mark,)*)> for Engine
{
fn register_dynamic_fn(&mut self, name: &str, f: FN) {
let fn_name = name.to_string();
let func = make_func!(fn_name : f : map_identity ; $($par => $clone),*);
let func = make_func!(f : map_identity ; $($par => $clone),*);
let hash = calc_fn_spec(empty(), name, [$(TypeId::of::<$par>()),*].iter().cloned());
self.base_package.functions.insert(hash, Box::new(func));
}
@ -244,8 +242,7 @@ macro_rules! def_register {
> RegisterResultFn<FN, ($($mark,)*), RET> for Engine
{
fn register_result_fn(&mut self, name: &str, f: FN) {
let fn_name = name.to_string();
let func = make_func!(fn_name : f : map_result ; $($par => $clone),*);
let func = make_func!(f : map_result ; $($par => $clone),*);
let hash = calc_fn_spec(empty(), name, [$(TypeId::of::<$par>()),*].iter().cloned());
self.base_package.functions.insert(hash, Box::new(func));
}

View File

@ -75,6 +75,7 @@ mod engine;
mod error;
mod fn_call;
mod fn_func;
mod fn_native;
mod fn_register;
mod module;
mod optimize;
@ -90,6 +91,7 @@ pub use any::Dynamic;
pub use engine::Engine;
pub use error::{ParseError, ParseErrorType};
pub use fn_call::FuncArgs;
pub use fn_native::NativeCallable;
pub use fn_register::{RegisterDynamicFn, RegisterFn, RegisterResultFn};
pub use parser::{AST, INT};
pub use result::EvalAltResult;

View File

@ -3,8 +3,9 @@
use crate::any::{Dynamic, Variant};
use crate::calc_fn_hash;
use crate::engine::{Engine, FnAny, FnCallArgs, FunctionsLib, NativeFunction, ScriptedFunction};
use crate::parser::{FnAccess, FnDef, AST};
use crate::engine::{Engine, FunctionsLib};
use crate::fn_native::{FnAny, FnCallArgs, NativeCallable, NativeFunction, SharedNativeFunction};
use crate::parser::{FnAccess, FnDef, SharedFnDef, AST};
use crate::result::EvalAltResult;
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
use crate::token::{Position, Token};
@ -57,10 +58,10 @@ pub struct Module {
all_variables: HashMap<u64, Dynamic>,
/// External Rust functions.
functions: HashMap<u64, (String, FnAccess, Vec<TypeId>, NativeFunction)>,
functions: HashMap<u64, (String, FnAccess, Vec<TypeId>, SharedNativeFunction)>,
/// Flattened collection of all external Rust functions, including those in sub-modules.
all_functions: HashMap<u64, NativeFunction>,
all_functions: HashMap<u64, SharedNativeFunction>,
/// Script-defined functions.
fn_lib: FunctionsLib,
@ -269,12 +270,14 @@ impl Module {
) -> u64 {
let hash = calc_fn_hash(empty(), &fn_name, params.iter().cloned());
let f = Box::new(NativeFunction::from(func)) as Box<dyn NativeCallable>;
#[cfg(not(feature = "sync"))]
self.functions
.insert(hash, (fn_name, access, params, Rc::new(func)));
let func = Rc::new(f);
#[cfg(feature = "sync")]
self.functions
.insert(hash, (fn_name, access, params, Arc::new(func)));
let func = Arc::new(f);
self.functions.insert(hash, (fn_name, access, params, func));
hash
}
@ -528,7 +531,7 @@ impl Module {
/// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1));
/// assert!(module.get_fn(hash).is_some());
/// ```
pub fn get_fn(&self, hash: u64) -> Option<&Box<FnAny>> {
pub fn get_fn(&self, hash: u64) -> Option<&Box<dyn NativeCallable>> {
self.functions.get(&hash).map(|(_, _, _, v)| v.as_ref())
}
@ -541,7 +544,7 @@ impl Module {
name: &str,
hash: u64,
pos: Position,
) -> Result<&Box<FnAny>, Box<EvalAltResult>> {
) -> Result<&Box<dyn NativeCallable>, Box<EvalAltResult>> {
self.all_functions
.get(&hash)
.map(|f| f.as_ref())
@ -626,8 +629,8 @@ impl Module {
module: &'a mut Module,
qualifiers: &mut Vec<&'a str>,
variables: &mut Vec<(u64, Dynamic)>,
functions: &mut Vec<(u64, NativeFunction)>,
fn_lib: &mut Vec<(u64, ScriptedFunction)>,
functions: &mut Vec<(u64, SharedNativeFunction)>,
fn_lib: &mut Vec<(u64, SharedFnDef)>,
) {
for (name, m) in module.modules.iter_mut() {
// Index all the sub-modules first.

View File

@ -1,9 +1,9 @@
use crate::any::Dynamic;
use crate::calc_fn_hash;
use crate::engine::{
Engine, FnAny, FnCallArgs, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT,
KEYWORD_TYPE_OF,
Engine, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF,
};
use crate::fn_native::FnCallArgs;
use crate::packages::{PackageStore, PackagesCollection};
use crate::parser::{map_dynamic_to_expr, Expr, FnDef, ReturnType, Stmt, AST};
use crate::result::EvalAltResult;
@ -123,7 +123,7 @@ fn call_fn(
base_package
.get_function(hash)
.or_else(|| packages.get_function(hash))
.map(|func| func(args, pos))
.map(|func| func.call(args, pos))
.transpose()
}

View File

@ -1,6 +1,6 @@
//! This module contains all built-in _packages_ available to Rhai, plus facilities to define custom packages.
use crate::engine::{FnAny, IteratorFn};
use crate::fn_native::{IteratorFn, NativeCallable};
use crate::stdlib::{any::TypeId, boxed::Box, collections::HashMap, rc::Rc, sync::Arc, vec::Vec};
@ -48,7 +48,7 @@ pub trait Package {
/// Type to store all functions in the package.
pub struct PackageStore {
/// All functions, keyed by a hash created from the function name and parameter types.
pub functions: HashMap<u64, Box<FnAny>>,
pub functions: HashMap<u64, Box<dyn NativeCallable>>,
/// All iterator functions, keyed by the type producing the iterator.
pub type_iterators: HashMap<TypeId, Box<IteratorFn>>,
@ -64,7 +64,7 @@ impl PackageStore {
self.functions.contains_key(&hash)
}
/// Get specified function via its hash key.
pub fn get_function(&self, hash: u64) -> Option<&Box<FnAny>> {
pub fn get_function(&self, hash: u64) -> Option<&Box<dyn NativeCallable>> {
self.functions.get(&hash)
}
/// Does the specified TypeId iterator exist in the `PackageStore`?
@ -113,7 +113,7 @@ impl PackagesCollection {
self.packages.iter().any(|p| p.contains_function(hash))
}
/// Get specified function via its hash key.
pub fn get_function(&self, hash: u64) -> Option<&Box<FnAny>> {
pub fn get_function(&self, hash: u64) -> Option<&Box<dyn NativeCallable>> {
self.packages
.iter()
.map(|p| p.get_function(hash))

View File

@ -2,7 +2,7 @@ use super::PackageStore;
use crate::any::{Dynamic, Variant};
use crate::calc_fn_hash;
use crate::engine::FnCallArgs;
use crate::fn_native::{FnCallArgs, NativeFunction};
use crate::result::EvalAltResult;
use crate::token::Position;
@ -106,7 +106,7 @@ pub fn reg_none<R>(
map_result(r, pos)
});
lib.functions.insert(hash, f);
lib.functions.insert(hash, Box::new(NativeFunction::new(f)));
}
/// Add a function with one parameter to the package.
@ -157,7 +157,7 @@ pub fn reg_unary<T: Variant + Clone, R>(
map_result(r, pos)
});
lib.functions.insert(hash, f);
lib.functions.insert(hash, Box::new(NativeFunction::new(f)));
}
/// Add a function with one mutable reference parameter to the package.
@ -215,7 +215,7 @@ pub fn reg_unary_mut<T: Variant + Clone, R>(
map_result(r, pos)
});
lib.functions.insert(hash, f);
lib.functions.insert(hash, Box::new(NativeFunction::new(f)));
}
/// Add a function with two parameters to the package.
@ -271,7 +271,7 @@ pub fn reg_binary<A: Variant + Clone, B: Variant + Clone, R>(
map_result(r, pos)
});
lib.functions.insert(hash, f);
lib.functions.insert(hash, Box::new(NativeFunction::new(f)));
}
/// Add a function with two parameters (the first one being a mutable reference) to the package.
@ -334,7 +334,7 @@ pub fn reg_binary_mut<A: Variant + Clone, B: Variant + Clone, R>(
map_result(r, pos)
});
lib.functions.insert(hash, f);
lib.functions.insert(hash, Box::new(NativeFunction::new(f)));
}
/// Add a function with three parameters to the package.
@ -374,7 +374,7 @@ pub fn reg_trinary<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone, R
map_result(r, pos)
});
lib.functions.insert(hash, f);
lib.functions.insert(hash, Box::new(NativeFunction::new(f)));
}
/// Add a function with three parameters (the first one is a mutable reference) to the package.
@ -414,5 +414,5 @@ pub fn reg_trinary_mut<A: Variant + Clone, B: Variant + Clone, C: Variant + Clon
map_result(r, pos)
});
lib.functions.insert(hash, f);
lib.functions.insert(hash, Box::new(NativeFunction::new(f)));
}

View File

@ -202,6 +202,13 @@ pub struct FnDef {
pub pos: Position,
}
/// A sharable script-defined function.
#[cfg(feature = "sync")]
pub type SharedFnDef = Arc<FnDef>;
/// A sharable script-defined function.
#[cfg(not(feature = "sync"))]
pub type SharedFnDef = Rc<FnDef>;
/// `return`/`throw` statement.
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum ReturnType {