Expose methods for Engine::register_module.

This commit is contained in:
Stephen Chung
2020-11-16 14:07:48 +08:00
parent cd62104296
commit ef02150afd
13 changed files with 385 additions and 149 deletions

View File

@@ -3,7 +3,7 @@
use crate::ast::{BinaryExpr, Expr, FnCallExpr, Ident, IdentX, ReturnType, Stmt};
use crate::dynamic::{map_std_type_name, Dynamic, Union, Variant};
use crate::fn_call::run_builtin_op_assignment;
use crate::fn_native::{Callback, FnPtr, OnVarCallback, Shared};
use crate::fn_native::{CallableFunction, Callback, FnPtr, IteratorFn, OnVarCallback, Shared};
use crate::module::{Module, NamespaceRef};
use crate::optimize::OptimizationLevel;
use crate::packages::{Package, PackagesCollection, StandardPackage};
@@ -85,7 +85,7 @@ pub const TYPICAL_MAP_SIZE: usize = 8; // Small maps are typical
// We cannot use &str or Cow<str> here because `eval` may load a module and the module name will live beyond
// the AST of the eval script text. The best we can do is a shared reference.
#[derive(Debug, Clone, Default)]
pub struct Imports(StaticVec<(ImmutableString, Shared<Module>)>);
pub struct Imports(StaticVec<(ImmutableString, bool, Shared<Module>)>);
impl Imports {
/// Get the length of this stack of imported modules.
@@ -98,7 +98,7 @@ impl Imports {
}
/// Get the imported module at a particular index.
pub fn get(&self, index: usize) -> Option<Shared<Module>> {
self.0.get(index).map(|(_, m)| m).cloned()
self.0.get(index).map(|(_, _, m)| m).cloned()
}
/// Get the index of an imported module by name.
pub fn find(&self, name: &str) -> Option<usize> {
@@ -106,12 +106,21 @@ impl Imports {
.iter()
.enumerate()
.rev()
.find(|(_, (key, _))| key.as_str() == name)
.find(|(_, (key, _, _))| key.as_str() == name)
.map(|(index, _)| index)
}
/// Push an imported module onto the stack.
pub fn push(&mut self, name: impl Into<ImmutableString>, module: impl Into<Shared<Module>>) {
self.0.push((name.into(), module.into()));
self.0.push((name.into(), false, module.into()));
}
/// Push a fixed module onto the stack.
#[cfg(not(feature = "no_module"))]
pub(crate) fn push_fixed(
&mut self,
name: impl Into<ImmutableString>,
module: impl Into<Shared<Module>>,
) {
self.0.push((name.into(), true, module.into()));
}
/// Truncate the stack of imported modules to a particular length.
pub fn truncate(&mut self, size: usize) {
@@ -119,26 +128,59 @@ impl Imports {
}
/// Get an iterator to this stack of imported modules.
#[allow(dead_code)]
pub fn iter(&self) -> impl Iterator<Item = (&str, Shared<Module>)> {
pub fn iter(&self) -> impl Iterator<Item = (&str, bool, Shared<Module>)> {
self.0
.iter()
.map(|(name, module)| (name.as_str(), module.clone()))
.map(|(name, fixed, module)| (name.as_str(), *fixed, module.clone()))
}
/// Get an iterator to this stack of imported modules.
#[allow(dead_code)]
pub(crate) fn iter_raw<'a>(
&'a self,
) -> impl Iterator<Item = (ImmutableString, Shared<Module>)> + 'a {
) -> impl Iterator<Item = (ImmutableString, bool, Shared<Module>)> + 'a {
self.0.iter().cloned()
}
/// Get a consuming iterator to this stack of imported modules.
pub fn into_iter(self) -> impl Iterator<Item = (ImmutableString, Shared<Module>)> {
pub fn into_iter(self) -> impl Iterator<Item = (ImmutableString, bool, Shared<Module>)> {
self.0.into_iter()
}
/// Add a stream of imported modules.
pub fn extend(&mut self, stream: impl Iterator<Item = (ImmutableString, Shared<Module>)>) {
pub fn extend(
&mut self,
stream: impl Iterator<Item = (ImmutableString, bool, Shared<Module>)>,
) {
self.0.extend(stream)
}
/// Does the specified function hash key exist in this stack of imported modules?
#[allow(dead_code)]
pub fn contains_fn(&self, hash: u64) -> bool {
self.0
.iter()
.any(|(_, fixed, m)| *fixed && m.contains_qualified_fn(hash))
}
/// Get specified function via its hash key.
pub fn get_fn(&self, hash: u64) -> Option<&CallableFunction> {
self.0
.iter()
.rev()
.filter(|&&(_, fixed, _)| fixed)
.find_map(|(_, _, m)| m.get_qualified_fn(hash))
}
/// Does the specified TypeId iterator exist in this stack of imported modules?
#[allow(dead_code)]
pub fn contains_iter(&self, id: TypeId) -> bool {
self.0
.iter()
.any(|(_, fixed, m)| *fixed && m.contains_qualified_iter(id))
}
/// Get the specified TypeId iterator.
pub fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
self.0
.iter()
.rev()
.filter(|&&(_, fixed, _)| fixed)
.find_map(|(_, _, m)| m.get_qualified_iter(id))
}
}
#[cfg(not(feature = "unchecked"))]
@@ -1947,7 +1989,8 @@ impl Engine {
match self
.global_module
.get_fn(hash_fn, false)
.or_else(|| self.packages.get_fn(hash_fn, false))
.or_else(|| self.packages.get_fn(hash_fn))
.or_else(|| mods.get_fn(hash_fn))
{
// op= function registered as method
Some(func) if func.is_method() => {
@@ -1965,9 +2008,10 @@ impl Engine {
// Overriding exact implementation
if func.is_plugin_fn() {
func.get_plugin_fn().call((self, mods, lib).into(), args)?;
func.get_plugin_fn()
.call((self, &*mods, lib).into(), args)?;
} else {
func.get_native_fn()((self, mods, lib).into(), args)?;
func.get_native_fn()((self, &*mods, lib).into(), args)?;
}
}
// Built-in op-assignment function
@@ -2142,7 +2186,8 @@ impl Engine {
let func = self
.global_module
.get_iter(iter_type)
.or_else(|| self.packages.get_iter(iter_type));
.or_else(|| self.packages.get_iter(iter_type))
.or_else(|| mods.get_iter(iter_type));
if let Some(func) = func {
// Add the loop variable

View File

@@ -811,9 +811,9 @@ impl Engine {
// Index the module (making a clone copy if necessary) if it is not indexed
let mut module = shared_take_or_clone(module);
module.build_index();
self.global_sub_modules.push(name, module);
self.global_sub_modules.push_fixed(name, module);
} else {
self.global_sub_modules.push(name, module);
self.global_sub_modules.push_fixed(name, module);
}
self
}

View File

@@ -175,7 +175,7 @@ impl Engine {
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
pub(crate) fn call_native_fn(
&self,
mods: &mut Imports,
mods: &Imports,
state: &mut State,
lib: &[&Module],
fn_name: &str,
@@ -192,7 +192,8 @@ impl Engine {
// Then search packages
let func = //lib.get_fn(hash_fn, pub_only)
self.global_module.get_fn(hash_fn, pub_only)
.or_else(|| self.packages.get_fn(hash_fn, pub_only));
.or_else(|| self.packages.get_fn(hash_fn))
.or_else(|| mods.get_fn(hash_fn));
if let Some(func) = func {
assert!(func.is_native());
@@ -428,6 +429,7 @@ impl Engine {
#[inline]
pub(crate) fn has_override_by_name_and_arguments(
&self,
mods: &Imports,
lib: &[&Module],
fn_name: &str,
arg_types: impl AsRef<[TypeId]>,
@@ -437,13 +439,14 @@ impl Engine {
let hash_fn = calc_native_fn_hash(empty(), fn_name, arg_types.iter().cloned());
let hash_script = calc_script_fn_hash(empty(), fn_name, arg_types.len());
self.has_override(lib, hash_fn, hash_script, pub_only)
self.has_override(mods, lib, hash_fn, hash_script, pub_only)
}
// Has a system function an override?
#[inline(always)]
pub(crate) fn has_override(
&self,
mods: &Imports,
lib: &[&Module],
hash_fn: u64,
hash_script: u64,
@@ -456,10 +459,13 @@ impl Engine {
//|| lib.iter().any(|&m| m.contains_fn(hash_fn, pub_only))
// Then check registered functions
//|| self.global_module.contains_fn(hash_script, pub_only)
|| self.global_module.contains_fn(hash_fn, pub_only)
|| self.global_module.contains_fn(hash_fn, false)
// Then check packages
|| self.packages.contains_fn(hash_script, pub_only)
|| self.packages.contains_fn(hash_fn, pub_only)
|| self.packages.contains_fn(hash_script)
|| self.packages.contains_fn(hash_fn)
// Then check imported modules
|| mods.contains_fn(hash_script)
|| mods.contains_fn(hash_fn)
}
/// Perform an actual function call, native Rust or scripted, taking care of special functions.
@@ -497,7 +503,8 @@ impl Engine {
match fn_name {
// type_of
KEYWORD_TYPE_OF
if args.len() == 1 && !self.has_override(lib, hash_fn, hash_script, pub_only) =>
if args.len() == 1
&& !self.has_override(mods, lib, hash_fn, hash_script, pub_only) =>
{
Ok((
self.map_type_name(args[0].type_name()).to_string().into(),
@@ -508,7 +515,8 @@ impl Engine {
// Fn/eval - reaching this point it must be a method-style call, mostly like redirected
// by a function pointer so it isn't caught at parse time.
KEYWORD_FN_PTR | KEYWORD_EVAL
if args.len() == 1 && !self.has_override(lib, hash_fn, hash_script, pub_only) =>
if args.len() == 1
&& !self.has_override(mods, lib, hash_fn, hash_script, pub_only) =>
{
EvalAltResult::ErrorRuntime(
format!(
@@ -523,16 +531,14 @@ impl Engine {
// Script-like function found
#[cfg(not(feature = "no_function"))]
_ if lib.iter().any(|&m| m.contains_fn(hash_script, pub_only))
//|| self.global_module.contains_fn(hash_script, pub_only)
|| self.packages.contains_fn(hash_script, pub_only) =>
{
_ if self.has_override(mods, lib, 0, hash_script, pub_only) => {
// Get function
let func = lib
.iter()
.find_map(|&m| m.get_fn(hash_script, pub_only))
//.or_else(|| self.global_module.get_fn(hash_script, pub_only))
.or_else(|| self.packages.get_fn(hash_script, pub_only))
.or_else(|| self.packages.get_fn(hash_script))
//.or_else(|| mods.get_fn(hash_script))
.unwrap();
if func.is_script() {
@@ -860,7 +866,7 @@ impl Engine {
let hash_fn =
calc_native_fn_hash(empty(), fn_name, once(TypeId::of::<ImmutableString>()));
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
if !self.has_override(mods, lib, hash_fn, hash_script, pub_only) {
// Fn - only in function call style
return self
.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?
@@ -916,7 +922,7 @@ impl Engine {
if name == KEYWORD_FN_PTR_CALL
&& args_expr.len() >= 1
&& !self.has_override(lib, 0, hash_script, pub_only)
&& !self.has_override(mods, lib, 0, hash_script, pub_only)
{
let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
@@ -946,7 +952,7 @@ impl Engine {
if name == KEYWORD_IS_DEF_VAR && args_expr.len() == 1 {
let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::<ImmutableString>()));
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
if !self.has_override(mods, lib, hash_fn, hash_script, pub_only) {
let var_name =
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
let var_name = var_name.as_str().map_err(|err| {
@@ -966,7 +972,7 @@ impl Engine {
.cloned(),
);
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
if !self.has_override(mods, lib, hash_fn, hash_script, pub_only) {
let fn_name =
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
let num_params =
@@ -993,7 +999,7 @@ impl Engine {
if name == KEYWORD_EVAL && args_expr.len() == 1 {
let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::<ImmutableString>()));
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
if !self.has_override(mods, lib, hash_fn, hash_script, pub_only) {
// eval - only in function call style
let prev_len = scope.len();
let script =
@@ -1194,7 +1200,7 @@ impl Engine {
Some(f) if f.is_plugin_fn() => f
.get_plugin_fn()
.clone()
.call((self, mods, lib).into(), args.as_mut()),
.call((self, &*mods, lib).into(), args.as_mut()),
Some(f) if f.is_native() => {
if !f.is_method() {
// Clone first argument
@@ -1205,7 +1211,7 @@ impl Engine {
}
}
f.get_native_fn().clone()((self, mods, lib).into(), args.as_mut())
f.get_native_fn().clone()((self, &*mods, lib).into(), args.as_mut())
}
Some(_) => unreachable!(),
None if def_val.is_some() => Ok(def_val.unwrap().into()),

View File

@@ -56,10 +56,10 @@ pub struct NativeCallContext<'e, 'a, 'm, 'pm: 'm> {
lib: &'m [&'pm Module],
}
impl<'e, 'a, 'm, 'pm: 'm, M: AsRef<[&'pm Module]> + ?Sized>
From<(&'e Engine, &'a mut Imports, &'m M)> for NativeCallContext<'e, 'a, 'm, 'pm>
impl<'e, 'a, 'm, 'pm: 'm, M: AsRef<[&'pm Module]> + ?Sized> From<(&'e Engine, &'a Imports, &'m M)>
for NativeCallContext<'e, 'a, 'm, 'pm>
{
fn from(value: (&'e Engine, &'a mut Imports, &'m M)) -> Self {
fn from(value: (&'e Engine, &'a Imports, &'m M)) -> Self {
Self {
engine: value.0,
mods: Some(value.1),

View File

@@ -73,11 +73,13 @@ pub struct Module {
all_variables: HashMap<u64, Dynamic, StraightHasherBuilder>,
/// External Rust functions.
functions: HashMap<u64, FuncInfo, StraightHasherBuilder>,
/// Flattened collection of all external Rust functions, native or scripted,
/// Flattened collection of all external Rust functions, native or scripted.
/// including those in sub-modules.
all_functions: HashMap<u64, CallableFunction, StraightHasherBuilder>,
/// Iterator functions, keyed by the type producing the iterator.
type_iterators: HashMap<TypeId, IteratorFn>,
/// Flattened collection of iterator functions, including those in sub-modules.
all_type_iterators: HashMap<TypeId, IteratorFn>,
/// Is the module indexed?
indexed: bool,
}
@@ -91,6 +93,7 @@ impl Default for Module {
functions: HashMap::with_capacity_and_hasher(64, StraightHasherBuilder),
all_functions: HashMap::with_capacity_and_hasher(256, StraightHasherBuilder),
type_iterators: Default::default(),
all_type_iterators: Default::default(),
indexed: false,
}
}
@@ -181,6 +184,7 @@ impl Module {
&& self.all_variables.is_empty()
&& self.modules.is_empty()
&& self.type_iterators.is_empty()
&& self.all_type_iterators.is_empty()
}
/// Is the module indexed?
@@ -1123,6 +1127,15 @@ impl Module {
}
}
/// Does the particular namespace-qualified function exist in the module?
///
/// The `u64` hash is calculated by the function `crate::calc_native_fn_hash` and must match
/// the hash calculated by `build_index`.
#[inline]
pub fn contains_qualified_fn(&self, hash_fn: u64) -> bool {
self.all_functions.contains_key(&hash_fn)
}
/// Get a namespace-qualified function.
/// Name and Position in `EvalAltResult` are None and must be set afterwards.
///
@@ -1143,6 +1156,7 @@ impl Module {
self.type_iterators.extend(other.type_iterators.into_iter());
self.all_functions.clear();
self.all_variables.clear();
self.all_type_iterators.clear();
self.indexed = false;
self
}
@@ -1160,6 +1174,7 @@ impl Module {
self.type_iterators.extend(other.type_iterators.into_iter());
self.all_functions.clear();
self.all_variables.clear();
self.all_type_iterators.clear();
self.indexed = false;
self
}
@@ -1186,6 +1201,7 @@ impl Module {
});
self.all_functions.clear();
self.all_variables.clear();
self.all_type_iterators.clear();
self.indexed = false;
self
}
@@ -1231,6 +1247,7 @@ impl Module {
self.type_iterators.extend(other.type_iterators.iter());
self.all_functions.clear();
self.all_variables.clear();
self.all_type_iterators.clear();
self.indexed = false;
self
}
@@ -1250,6 +1267,7 @@ impl Module {
self.all_functions.clear();
self.all_variables.clear();
self.all_type_iterators.clear();
self.indexed = false;
self
}
@@ -1388,10 +1406,13 @@ impl Module {
// Extra modules left in the scope become sub-modules
let mut func_mods: Imports = Default::default();
mods.into_iter().skip(orig_mods_len).for_each(|(alias, m)| {
func_mods.push(alias.clone(), m.clone());
module.set_sub_module(alias, m);
});
mods.into_iter()
.skip(orig_mods_len)
.filter(|&(_, fixed, _)| !fixed)
.for_each(|(alias, _, m)| {
func_mods.push(alias.clone(), m.clone());
module.set_sub_module(alias, m);
});
// Non-private functions defined become module functions
#[cfg(not(feature = "no_function"))]
@@ -1422,13 +1443,14 @@ impl Module {
fn index_module<'a>(
module: &'a Module,
qualifiers: &mut Vec<&'a str>,
variables: &mut Vec<(u64, Dynamic)>,
functions: &mut Vec<(u64, CallableFunction)>,
variables: &mut HashMap<u64, Dynamic, StraightHasherBuilder>,
functions: &mut HashMap<u64, CallableFunction, StraightHasherBuilder>,
type_iterators: &mut HashMap<TypeId, IteratorFn>,
) {
module.modules.iter().for_each(|(name, m)| {
// Index all the sub-modules first.
qualifiers.push(name);
index_module(m, qualifiers, variables, functions);
index_module(m, qualifiers, variables, functions, type_iterators);
qualifiers.pop();
});
@@ -1436,8 +1458,14 @@ impl Module {
module.variables.iter().for_each(|(var_name, value)| {
// Qualifiers + variable name
let hash_var = calc_script_fn_hash(qualifiers.iter().map(|&v| v), var_name, 0);
variables.push((hash_var, value.clone()));
variables.insert(hash_var, value.clone());
});
// Index type iterators
module.type_iterators.iter().for_each(|(&type_id, func)| {
type_iterators.insert(type_id, func.clone());
});
// Index all Rust functions
module
.functions
@@ -1445,7 +1473,7 @@ impl Module {
.filter(|(_, FuncInfo { access, .. })| access.is_public())
.for_each(
|(
&_hash,
&hash,
FuncInfo {
name,
params,
@@ -1454,6 +1482,12 @@ impl Module {
..
},
)| {
// Flatten all methods so they can be available without namespace qualifiers
#[cfg(not(feature = "no_object"))]
if func.is_method() {
functions.insert(hash, func.clone());
}
if let Some(param_types) = types {
assert_eq!(*params, param_types.len());
@@ -1469,15 +1503,17 @@ impl Module {
// 3) The final hash is the XOR of the two hashes.
let hash_qualified_fn = hash_qualified_script ^ hash_fn_args;
functions.push((hash_qualified_fn, func.clone()));
functions.insert(hash_qualified_fn, func.clone());
} else if cfg!(not(feature = "no_function")) {
let hash_qualified_script = if qualifiers.is_empty() {
_hash
let hash_qualified_script = if cfg!(feature = "no_object")
&& qualifiers.is_empty()
{
hash
} else {
// Qualifiers + function name + number of arguments.
calc_script_fn_hash(qualifiers.iter().map(|&v| v), &name, *params)
};
functions.push((hash_qualified_script, func.clone()));
functions.insert(hash_qualified_script, func.clone());
}
},
);
@@ -1485,19 +1521,32 @@ impl Module {
if !self.indexed {
let mut qualifiers = Vec::with_capacity(4);
let mut variables = Vec::with_capacity(16);
let mut functions = Vec::with_capacity(256);
let mut variables = HashMap::with_capacity_and_hasher(16, StraightHasherBuilder);
let mut functions = HashMap::with_capacity_and_hasher(256, StraightHasherBuilder);
let mut type_iterators = HashMap::with_capacity(16);
qualifiers.push("root");
index_module(self, &mut qualifiers, &mut variables, &mut functions);
index_module(
self,
&mut qualifiers,
&mut variables,
&mut functions,
&mut type_iterators,
);
self.all_variables = variables.into_iter().collect();
self.all_functions = functions.into_iter().collect();
self.all_variables = variables;
self.all_functions = functions;
self.all_type_iterators = type_iterators;
self.indexed = true;
}
}
/// Does a type iterator exist in the entire module tree?
pub fn contains_qualified_iter(&self, id: TypeId) -> bool {
self.all_type_iterators.contains_key(&id)
}
/// Does a type iterator exist in the module?
pub fn contains_iter(&self, id: TypeId) -> bool {
self.type_iterators.contains_key(&id)
@@ -1532,6 +1581,11 @@ impl Module {
})
}
/// Get the specified type iterator.
pub(crate) fn get_qualified_iter(&self, id: TypeId) -> Option<IteratorFn> {
self.all_type_iterators.get(&id).cloned()
}
/// Get the specified type iterator.
pub(crate) fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
self.type_iterators.get(&id).cloned()

View File

@@ -673,7 +673,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
// Search for overloaded operators (can override built-in).
if !state.engine.has_override_by_name_and_arguments(state.lib, x.name.as_ref(), arg_types.as_ref(), false) {
if !state.engine.has_override_by_name_and_arguments(&state.engine.global_sub_modules, state.lib, x.name.as_ref(), arg_types.as_ref(), false) {
if let Some(result) = run_builtin_binary_op(x.name.as_ref(), &arg_values[0], &arg_values[1])
.ok().flatten()
.and_then(|result| map_dynamic_to_expr(result, *pos))

View File

@@ -64,16 +64,12 @@ impl PackagesCollection {
}
/// Does the specified function hash key exist in the `PackagesCollection`?
#[allow(dead_code)]
pub fn contains_fn(&self, hash: u64, public_only: bool) -> bool {
self.0.iter().any(|p| p.contains_fn(hash, public_only))
pub fn contains_fn(&self, hash: u64) -> bool {
self.0.iter().any(|p| p.contains_fn(hash, false))
}
/// Get specified function via its hash key.
pub fn get_fn(&self, hash: u64, public_only: bool) -> Option<&CallableFunction> {
self.0
.iter()
.map(|p| p.get_fn(hash, public_only))
.find(|f| f.is_some())
.flatten()
pub fn get_fn(&self, hash: u64) -> Option<&CallableFunction> {
self.0.iter().find_map(|p| p.get_fn(hash, false))
}
/// Does the specified TypeId iterator exist in the `PackagesCollection`?
#[allow(dead_code)]
@@ -82,11 +78,7 @@ impl PackagesCollection {
}
/// Get the specified TypeId iterator.
pub fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
self.0
.iter()
.map(|p| p.get_iter(id))
.find(|f| f.is_some())
.flatten()
self.0.iter().find_map(|p| p.get_iter(id))
}
}