Remove modules from Scope and use separate stack.
This commit is contained in:
208
src/module.rs
208
src/module.rs
@@ -2,7 +2,7 @@
|
||||
|
||||
use crate::any::{Dynamic, Variant};
|
||||
use crate::calc_fn_hash;
|
||||
use crate::engine::{make_getter, make_setter, Engine, FN_IDX_GET, FN_IDX_SET};
|
||||
use crate::engine::{make_getter, make_setter, Engine, Imports, FN_IDX_GET, FN_IDX_SET};
|
||||
use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn, SendSync};
|
||||
use crate::parser::{
|
||||
FnAccess,
|
||||
@@ -10,13 +10,14 @@ use crate::parser::{
|
||||
ScriptFnDef, AST,
|
||||
};
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
|
||||
use crate::scope::{Entry as ScopeEntry, Scope};
|
||||
use crate::token::{Position, Token};
|
||||
use crate::utils::{StaticVec, StraightHasherBuilder};
|
||||
|
||||
use crate::stdlib::{
|
||||
any::TypeId,
|
||||
boxed::Box,
|
||||
cell::RefCell,
|
||||
collections::HashMap,
|
||||
fmt,
|
||||
iter::empty,
|
||||
@@ -24,6 +25,7 @@ use crate::stdlib::{
|
||||
num::NonZeroUsize,
|
||||
ops::{Deref, DerefMut},
|
||||
string::{String, ToString},
|
||||
sync::RwLock,
|
||||
vec,
|
||||
vec::Vec,
|
||||
};
|
||||
@@ -35,7 +37,7 @@ pub type FuncReturn<T> = Result<T, Box<EvalAltResult>>;
|
||||
/// external Rust functions, and script-defined functions.
|
||||
///
|
||||
/// Not available under the `no_module` feature.
|
||||
#[derive(Clone, Default)]
|
||||
#[derive(Default)]
|
||||
pub struct Module {
|
||||
/// Sub-modules.
|
||||
modules: HashMap<String, Module>,
|
||||
@@ -59,19 +61,47 @@ pub struct Module {
|
||||
/// Flattened collection of all external Rust functions, native or scripted,
|
||||
/// including those in sub-modules.
|
||||
all_functions: HashMap<u64, CallableFunction, StraightHasherBuilder>,
|
||||
|
||||
/// Is the module indexed?
|
||||
indexed: bool,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Module {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"<module vars={:?}, functions={}>",
|
||||
self.variables,
|
||||
self.functions.len(),
|
||||
"Module(\n modules: {}\n vars: {}\n functions: {}\n)",
|
||||
self.modules
|
||||
.keys()
|
||||
.map(|k| k.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
self.variables
|
||||
.iter()
|
||||
.map(|(k, v)| format!("{}={:?}", k, v))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
self.functions
|
||||
.values()
|
||||
.map(|(_, _, _, f)| f.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Module {
|
||||
fn clone(&self) -> Self {
|
||||
// Only clone the index at the top level
|
||||
Self {
|
||||
all_variables: self.all_variables.clone(),
|
||||
all_functions: self.all_functions.clone(),
|
||||
indexed: self.indexed,
|
||||
..self.do_clone(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Module {
|
||||
/// Create a new module.
|
||||
///
|
||||
@@ -106,6 +136,24 @@ impl Module {
|
||||
}
|
||||
}
|
||||
|
||||
/// Clone the module, optionally skipping the index.
|
||||
fn do_clone(&self, clone_index: bool) -> Self {
|
||||
Self {
|
||||
modules: if clone_index {
|
||||
self.modules.clone()
|
||||
} else {
|
||||
self.modules
|
||||
.iter()
|
||||
.map(|(k, m)| (k.clone(), m.do_clone(clone_index)))
|
||||
.collect()
|
||||
},
|
||||
variables: self.variables.clone(),
|
||||
functions: self.functions.clone(),
|
||||
type_iterators: self.type_iterators.clone(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Does a variable exist in the module?
|
||||
///
|
||||
/// # Examples
|
||||
@@ -166,6 +214,7 @@ impl Module {
|
||||
/// ```
|
||||
pub fn set_var(&mut self, name: impl Into<String>, value: impl Variant + Clone) {
|
||||
self.variables.insert(name.into(), Dynamic::from(value));
|
||||
self.indexed = false;
|
||||
}
|
||||
|
||||
/// Get a mutable reference to a modules-qualified variable.
|
||||
@@ -197,6 +246,7 @@ impl Module {
|
||||
fn_def.into(),
|
||||
),
|
||||
);
|
||||
self.indexed = false;
|
||||
}
|
||||
|
||||
/// Does a sub-module exist in the module?
|
||||
@@ -263,6 +313,7 @@ impl Module {
|
||||
/// ```
|
||||
pub fn set_sub_module(&mut self, name: impl Into<String>, sub_module: Module) {
|
||||
self.modules.insert(name.into(), sub_module.into());
|
||||
self.indexed = false;
|
||||
}
|
||||
|
||||
/// Does the particular Rust function exist in the module?
|
||||
@@ -302,6 +353,8 @@ impl Module {
|
||||
self.functions
|
||||
.insert(hash_fn, (name, access, params, func.into()));
|
||||
|
||||
self.indexed = false;
|
||||
|
||||
hash_fn
|
||||
}
|
||||
|
||||
@@ -855,32 +908,27 @@ impl Module {
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub fn eval_ast_as_new(mut scope: Scope, ast: &AST, engine: &Engine) -> FuncReturn<Self> {
|
||||
let mut mods = Imports::new();
|
||||
|
||||
// Run the script
|
||||
engine.eval_ast_with_scope_raw(&mut scope, &ast)?;
|
||||
engine.eval_ast_with_scope_raw(&mut scope, &mut mods, &ast)?;
|
||||
|
||||
// Create new module
|
||||
let mut module = Module::new();
|
||||
|
||||
scope.into_iter().for_each(
|
||||
|ScopeEntry {
|
||||
typ, value, alias, ..
|
||||
}| {
|
||||
match typ {
|
||||
// Variables with an alias left in the scope become module variables
|
||||
ScopeEntryType::Normal | ScopeEntryType::Constant if alias.is_some() => {
|
||||
module.variables.insert(*alias.unwrap(), value);
|
||||
}
|
||||
// Modules left in the scope become sub-modules
|
||||
ScopeEntryType::Module if alias.is_some() => {
|
||||
module
|
||||
.modules
|
||||
.insert(*alias.unwrap(), value.cast::<Module>());
|
||||
}
|
||||
// Variables and modules with no alias are private and not exported
|
||||
_ => (),
|
||||
scope
|
||||
.into_iter()
|
||||
.for_each(|ScopeEntry { value, alias, .. }| {
|
||||
// Variables with an alias left in the scope become module variables
|
||||
if alias.is_some() {
|
||||
module.variables.insert(*alias.unwrap(), value);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
// Modules left in the scope become sub-modules
|
||||
mods.into_iter().for_each(|(alias, m)| {
|
||||
module.modules.insert(alias.to_string(), m);
|
||||
});
|
||||
|
||||
module.merge(ast.lib());
|
||||
|
||||
@@ -945,6 +993,10 @@ impl Module {
|
||||
}
|
||||
}
|
||||
|
||||
if self.indexed {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut variables = Vec::new();
|
||||
let mut functions = Vec::new();
|
||||
|
||||
@@ -952,6 +1004,7 @@ impl Module {
|
||||
|
||||
self.all_variables = variables.into_iter().collect();
|
||||
self.all_functions = functions.into_iter().collect();
|
||||
self.indexed = true;
|
||||
}
|
||||
|
||||
/// Does a type iterator exist in the module?
|
||||
@@ -962,6 +1015,7 @@ impl Module {
|
||||
/// Set a type iterator into the module.
|
||||
pub fn set_iter(&mut self, typ: TypeId, func: IteratorFn) {
|
||||
self.type_iterators.insert(typ, func);
|
||||
self.indexed = false;
|
||||
}
|
||||
|
||||
/// Get the specified type iterator.
|
||||
@@ -1055,6 +1109,8 @@ mod file {
|
||||
|
||||
/// Module resolution service that loads module script files from the file system.
|
||||
///
|
||||
/// Script files are cached so they are are not reloaded and recompiled in subsequent requests.
|
||||
///
|
||||
/// The `new_with_path` and `new_with_path_and_extension` constructor functions
|
||||
/// allow specification of a base directory with module path used as a relative path offset
|
||||
/// to the base directory. The script file is then forced to be in a specified extension
|
||||
@@ -1073,10 +1129,16 @@ mod file {
|
||||
/// let mut engine = Engine::new();
|
||||
/// engine.set_module_resolver(Some(resolver));
|
||||
/// ```
|
||||
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Clone, Hash)]
|
||||
#[derive(Debug)]
|
||||
pub struct FileModuleResolver {
|
||||
path: PathBuf,
|
||||
extension: String,
|
||||
|
||||
#[cfg(not(feature = "sync"))]
|
||||
cache: RefCell<HashMap<PathBuf, AST>>,
|
||||
|
||||
#[cfg(feature = "sync")]
|
||||
cache: RwLock<HashMap<PathBuf, AST>>,
|
||||
}
|
||||
|
||||
impl Default for FileModuleResolver {
|
||||
@@ -1129,6 +1191,7 @@ mod file {
|
||||
Self {
|
||||
path: path.into(),
|
||||
extension: extension.into(),
|
||||
cache: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1173,12 +1236,45 @@ mod file {
|
||||
file_path.push(path);
|
||||
file_path.set_extension(&self.extension); // Force extension
|
||||
|
||||
// Compile it
|
||||
let ast = engine
|
||||
.compile_file(file_path)
|
||||
.map_err(|err| err.new_position(pos))?;
|
||||
let scope = Default::default();
|
||||
|
||||
Module::eval_ast_as_new(Scope::new(), &ast, engine).map_err(|err| err.new_position(pos))
|
||||
// See if it is cached
|
||||
let (module, ast) = {
|
||||
#[cfg(not(feature = "sync"))]
|
||||
let c = self.cache.borrow();
|
||||
#[cfg(feature = "sync")]
|
||||
let c = self.cache.read().unwrap();
|
||||
|
||||
match c.get(&file_path) {
|
||||
Some(ast) => (
|
||||
Module::eval_ast_as_new(scope, ast, engine)
|
||||
.map_err(|err| err.new_position(pos))?,
|
||||
None,
|
||||
),
|
||||
None => {
|
||||
// Load the file and compile it if not found
|
||||
let ast = engine
|
||||
.compile_file(file_path.clone())
|
||||
.map_err(|err| err.new_position(pos))?;
|
||||
|
||||
(
|
||||
Module::eval_ast_as_new(scope, &ast, engine)
|
||||
.map_err(|err| err.new_position(pos))?,
|
||||
Some(ast),
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(ast) = ast {
|
||||
// Put it into the cache
|
||||
#[cfg(not(feature = "sync"))]
|
||||
self.cache.borrow_mut().insert(file_path, ast);
|
||||
#[cfg(feature = "sync")]
|
||||
self.cache.write().unwrap().insert(file_path, ast);
|
||||
}
|
||||
|
||||
Ok(module)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1222,7 +1318,7 @@ mod stat {
|
||||
/// let mut resolver = StaticModuleResolver::new();
|
||||
///
|
||||
/// let module = Module::new();
|
||||
/// resolver.insert("hello".to_string(), module);
|
||||
/// resolver.insert("hello", module);
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
/// engine.set_module_resolver(Some(resolver));
|
||||
@@ -1232,17 +1328,43 @@ mod stat {
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for StaticModuleResolver {
|
||||
type Target = HashMap<String, Module>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
impl StaticModuleResolver {
|
||||
/// Add a module keyed by its path.
|
||||
pub fn insert<S: Into<String>>(&mut self, path: S, mut module: Module) {
|
||||
module.index_all_sub_modules();
|
||||
self.0.insert(path.into(), module);
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for StaticModuleResolver {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
/// Remove a module given its path.
|
||||
pub fn remove(&mut self, path: &str) -> Option<Module> {
|
||||
self.0.remove(path)
|
||||
}
|
||||
/// Does the path exist?
|
||||
pub fn contains_path(&self, path: &str) -> bool {
|
||||
self.0.contains_key(path)
|
||||
}
|
||||
/// Get an iterator of all the modules.
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&str, &Module)> {
|
||||
self.0.iter().map(|(k, v)| (k.as_str(), v))
|
||||
}
|
||||
/// Get a mutable iterator of all the modules.
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut Module)> {
|
||||
self.0.iter_mut().map(|(k, v)| (k.as_str(), v))
|
||||
}
|
||||
/// Get an iterator of all the module paths.
|
||||
pub fn paths(&self) -> impl Iterator<Item = &str> {
|
||||
self.0.keys().map(String::as_str)
|
||||
}
|
||||
/// Get an iterator of all the modules.
|
||||
pub fn values(&self) -> impl Iterator<Item = &Module> {
|
||||
self.0.values()
|
||||
}
|
||||
/// Get a mutable iterator of all the modules.
|
||||
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut Module> {
|
||||
self.0.values_mut()
|
||||
}
|
||||
/// Remove all modules.
|
||||
pub fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user