Re-index imported modules if they are not yet indexed.

This commit is contained in:
Stephen Chung 2020-11-08 14:29:54 +08:00
parent 9a669ffe29
commit 1e07e4356e
6 changed files with 55 additions and 20 deletions

View File

@ -4,6 +4,14 @@ Rhai Release Notes
Version 0.19.5 Version 0.19.5
============== ==============
This version fixes a bug that prevents compilation with the `internals` feature.
It also speeds up importing modules.
Bug fixes
---------
* Fixes compilation error when using the `internals` feature. Bug introduced in `0.19.4`.
Breaking changes Breaking changes
---------------- ----------------

View File

@ -14,6 +14,10 @@ When Rhai prepares to load a module, `ModuleResolver::resolve` is called with th
of the _module path_ (i.e. the path specified in the [`import`] statement). of the _module path_ (i.e. the path specified in the [`import`] statement).
* Upon success, it should return an [`Rc<Module>`][module] (or `Arc<Module>` under [`sync`]). * Upon success, it should return an [`Rc<Module>`][module] (or `Arc<Module>` under [`sync`]).
The module should call `Module::build_index` on the target module before returning.
This method flattens the entire module tree and _indexes_ it for fast function name resolution.
If the module is already indexed, calling this method has no effect.
* If the path does not resolve to a valid module, return `EvalAltResult::ErrorModuleNotFound`. * If the path does not resolve to a valid module, return `EvalAltResult::ErrorModuleNotFound`.
@ -40,12 +44,14 @@ impl ModuleResolver for MyModuleResolver {
) -> Result<Rc<Module>, Box<EvalAltResult>> { ) -> Result<Rc<Module>, Box<EvalAltResult>> {
// Check module path. // Check module path.
if is_valid_module_path(path) { if is_valid_module_path(path) {
load_secret_module(path) // load the custom module let mut my_module =
.map(Rc::new) // share it load_secret_module(path) // load the custom module
.map_err(|err| .map_err(|err|
// Return EvalAltResult::ErrorInModule upon loading error // Return EvalAltResult::ErrorInModule upon loading error
EvalAltResult::ErrorInModule(err.to_string(), pos).into() EvalAltResult::ErrorInModule(path.into(), Box::new(err), pos).into()
) )?;
my_module.build_index(); // index it
Rc::new(my_module) // make it shared
} else { } else {
// Return EvalAltResult::ErrorModuleNotFound if the path is invalid // Return EvalAltResult::ErrorModuleNotFound if the path is invalid
Err(EvalAltResult::ErrorModuleNotFound(path.into(), pos).into()) Err(EvalAltResult::ErrorModuleNotFound(path.into(), pos).into())

View File

@ -3,7 +3,7 @@
use crate::ast::{BinaryExpr, Expr, FnCallInfo, Ident, IdentX, ReturnType, Stmt}; use crate::ast::{BinaryExpr, Expr, FnCallInfo, Ident, IdentX, ReturnType, Stmt};
use crate::dynamic::{map_std_type_name, Dynamic, Union, Variant}; use crate::dynamic::{map_std_type_name, Dynamic, Union, Variant};
use crate::fn_call::run_builtin_op_assignment; use crate::fn_call::run_builtin_op_assignment;
use crate::fn_native::{Callback, FnPtr, OnVarCallback, Shared}; use crate::fn_native::{shared_try_take, Callback, FnPtr, OnVarCallback, Shared};
use crate::module::{Module, ModuleRef}; use crate::module::{Module, ModuleRef};
use crate::optimize::OptimizationLevel; use crate::optimize::OptimizationLevel;
use crate::packages::{Package, PackagesCollection, StandardPackage}; use crate::packages::{Package, PackagesCollection, StandardPackage};
@ -2132,7 +2132,15 @@ impl Engine {
let module = resolver.resolve(self, &path, expr.position())?; let module = resolver.resolve(self, &path, expr.position())?;
if let Some(name_def) = alias { if let Some(name_def) = alias {
mods.push(name_def.name.clone(), module); if !module.is_indexed() {
// Index the module (making a clone copy if necessary) if it is not indexed
let mut module =
shared_try_take(module).unwrap_or_else(|m| m.as_ref().clone());
module.build_index();
mods.push(name_def.name.clone(), module);
} else {
mods.push(name_def.name.clone(), module);
}
} }
state.modules += 1; state.modules += 1;

View File

@ -60,23 +60,17 @@ pub struct FuncInfo {
pub struct Module { pub struct Module {
/// Sub-modules. /// Sub-modules.
modules: HashMap<String, Module>, modules: HashMap<String, Module>,
/// Module variables. /// Module variables.
variables: HashMap<String, Dynamic>, variables: HashMap<String, Dynamic>,
/// Flattened collection of all module variables, including those in sub-modules. /// Flattened collection of all module variables, including those in sub-modules.
all_variables: HashMap<u64, Dynamic, StraightHasherBuilder>, all_variables: HashMap<u64, Dynamic, StraightHasherBuilder>,
/// External Rust functions. /// External Rust functions.
functions: HashMap<u64, FuncInfo, StraightHasherBuilder>, functions: HashMap<u64, FuncInfo, StraightHasherBuilder>,
/// Iterator functions, keyed by the type producing the iterator. /// Iterator functions, keyed by the type producing the iterator.
type_iterators: HashMap<TypeId, IteratorFn>, type_iterators: HashMap<TypeId, IteratorFn>,
/// 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. /// including those in sub-modules.
all_functions: HashMap<u64, CallableFunction, StraightHasherBuilder>, all_functions: HashMap<u64, CallableFunction, StraightHasherBuilder>,
/// Is the module indexed? /// Is the module indexed?
indexed: bool, indexed: bool,
} }
@ -181,6 +175,23 @@ impl Module {
&& self.type_iterators.is_empty() && self.type_iterators.is_empty()
} }
/// Is the module indexed?
///
/// # Example
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// assert!(!module.is_indexed());
///
/// module.build_index();
/// assert!(module.is_indexed());
/// ```
pub fn is_indexed(&self) -> bool {
self.indexed
}
/// Clone the module, optionally skipping the index. /// Clone the module, optionally skipping the index.
#[inline(always)] #[inline(always)]
fn do_clone(&self, clone_index: bool) -> Self { fn do_clone(&self, clone_index: bool) -> Self {
@ -1120,7 +1131,7 @@ impl Module {
/// Name and Position in `EvalAltResult` are None and must be set afterwards. /// Name and Position in `EvalAltResult` are None and must be set afterwards.
/// ///
/// The `u64` hash is calculated by the function `crate::calc_native_fn_hash` and must match /// The `u64` hash is calculated by the function `crate::calc_native_fn_hash` and must match
/// the hash calculated by `index_all_sub_modules`. /// the hash calculated by `build_index`.
#[inline(always)] #[inline(always)]
pub(crate) fn get_qualified_fn(&self, hash_qualified_fn: u64) -> Option<&CallableFunction> { pub(crate) fn get_qualified_fn(&self, hash_qualified_fn: u64) -> Option<&CallableFunction> {
self.all_functions.get(&hash_qualified_fn) self.all_functions.get(&hash_qualified_fn)
@ -1396,10 +1407,12 @@ impl Module {
Ok(module) Ok(module)
} }
/// Scan through all the sub-modules in the module build an index of all /// Scan through all the sub-modules in the module and build a hash index of all
/// variables and external Rust functions via hashing. /// variables and functions as one flattened namespace.
///
/// If the module is already indexed, this method has no effect.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
pub(crate) fn index_all_sub_modules(&mut self) { pub fn build_index(&mut self) {
// Collect a particular module. // Collect a particular module.
fn index_module<'a>( fn index_module<'a>(
module: &'a Module, module: &'a Module,

View File

@ -155,7 +155,7 @@ impl ModuleResolver for FileModuleResolver {
Box::new(EvalAltResult::ErrorInModule(path.to_string(), err, pos)) Box::new(EvalAltResult::ErrorInModule(path.to_string(), err, pos))
})?; })?;
m.index_all_sub_modules(); m.build_index();
let m: Shared<Module> = m.into(); let m: Shared<Module> = m.into();
module = Some(m.clone()); module = Some(m.clone());

View File

@ -50,7 +50,7 @@ impl StaticModuleResolver {
/// Add a module keyed by its path. /// Add a module keyed by its path.
#[inline(always)] #[inline(always)]
pub fn insert(&mut self, path: impl Into<String>, mut module: Module) { pub fn insert(&mut self, path: impl Into<String>, mut module: Module) {
module.index_all_sub_modules(); module.build_index();
self.0.insert(path.into(), module.into()); self.0.insert(path.into(), module.into());
} }
/// Remove a module given its path. /// Remove a module given its path.