Refactor and add state to debugger.

This commit is contained in:
Stephen Chung
2022-01-28 18:59:18 +08:00
parent 20baae71d4
commit 66af69aaff
30 changed files with 693 additions and 624 deletions

View File

@@ -1401,9 +1401,9 @@ impl Module {
/// Sub-modules are flattened onto the root [`Module`], with higher level overriding lower level.
#[inline]
pub fn combine_flatten(&mut self, other: Self) -> &mut Self {
other.modules.into_iter().for_each(|(_, m)| {
for (_, m) in other.modules.into_iter() {
self.combine_flatten(shared_take_or_clone(m));
});
}
self.variables.extend(other.variables.into_iter());
self.functions.extend(other.functions.into_iter());
self.type_iterators.extend(other.type_iterators.into_iter());
@@ -1419,22 +1419,22 @@ impl Module {
/// Only items not existing in this [`Module`] are added.
#[inline]
pub fn fill_with(&mut self, other: &Self) -> &mut Self {
other.modules.iter().for_each(|(k, v)| {
for (k, v) in &other.modules {
if !self.modules.contains_key(k) {
self.modules.insert(k.clone(), v.clone());
}
});
other.variables.iter().for_each(|(k, v)| {
}
for (k, v) in &other.variables {
if !self.variables.contains_key(k) {
self.variables.insert(k.clone(), v.clone());
}
});
other.functions.iter().for_each(|(&k, v)| {
}
for (&k, v) in &other.functions {
self.functions.entry(k).or_insert_with(|| v.clone());
});
other.type_iterators.iter().for_each(|(&k, v)| {
}
for (&k, v) in &other.type_iterators {
self.type_iterators.entry(k).or_insert(v.clone());
});
}
self.all_functions.clear();
self.all_variables.clear();
self.all_type_iterators.clear();
@@ -1455,12 +1455,11 @@ impl Module {
other: &Self,
_filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool + Copy,
) -> &mut Self {
#[cfg(not(feature = "no_function"))]
other.modules.iter().for_each(|(k, v)| {
for (k, v) in &other.modules {
let mut m = Self::new();
m.merge_filtered(v, _filter);
self.set_sub_module(k.clone(), m);
});
}
#[cfg(feature = "no_function")]
self.modules
.extend(other.modules.iter().map(|(k, v)| (k.clone(), v.clone())));
@@ -1658,60 +1657,90 @@ impl Module {
/// # }
/// ```
#[cfg(not(feature = "no_module"))]
#[inline(always)]
pub fn eval_ast_as_new(
scope: crate::Scope,
ast: &crate::AST,
engine: &crate::Engine,
) -> RhaiResultOf<Self> {
let global = &mut crate::eval::GlobalRuntimeState::new(engine);
Self::eval_ast_as_new_raw(engine, scope, global, ast)
}
/// Create a new [`Module`] by evaluating an [`AST`][crate::AST].
///
/// The entire [`AST`][crate::AST] is encapsulated into each function, allowing functions
/// to cross-call each other. Functions in the global namespace, plus all functions
/// defined in the [`Module`], are _merged_ into a _unified_ namespace before each call.
/// Therefore, all functions will be found.
#[cfg(not(feature = "no_module"))]
pub(crate) fn eval_ast_as_new_raw(
engine: &crate::Engine,
scope: crate::Scope,
global: &mut crate::eval::GlobalRuntimeState,
ast: &crate::AST,
) -> RhaiResultOf<Self> {
let mut scope = scope;
let mut global = crate::eval::GlobalRuntimeState::new();
#[cfg(feature = "debugging")]
global.debugger.activate(engine.debugger.is_some());
// Save global state
let orig_imports_len = global.num_imports();
let orig_source = global.source.clone();
let orig_constants = std::mem::take(&mut global.constants);
// Run the script
engine.eval_ast_with_scope_raw(&mut scope, &mut global, &ast, 0)?;
let result = engine.eval_ast_with_scope_raw(&mut scope, global, &ast, 0);
// Create new module
let mut module =
scope
.into_iter()
.fold(Module::new(), |mut module, (_, value, mut aliases)| {
// Variables with an alias left in the scope become module variables
match aliases.len() {
0 => (),
1 => {
let alias = aliases.pop().unwrap();
module.set_var(alias, value);
}
_ => {
let last_alias = aliases.pop().unwrap();
aliases.into_iter().for_each(|alias| {
module.set_var(alias, value.clone());
});
// Avoid cloning the last value
module.set_var(last_alias, value);
}
}
module
});
let mut module = Module::new();
// Extra modules left in the scope become sub-modules
// Extra modules left become sub-modules
#[cfg(not(feature = "no_function"))]
let mut func_global = None;
global.into_iter().skip(orig_imports_len).for_each(|kv| {
#[cfg(not(feature = "no_function"))]
if func_global.is_none() {
func_global = Some(StaticVec::new());
}
#[cfg(not(feature = "no_function"))]
func_global.as_mut().expect("`Some`").push(kv.clone());
if result.is_ok() {
global
.scan_imports_raw()
.skip(orig_imports_len)
.for_each(|(k, m)| {
#[cfg(not(feature = "no_function"))]
if func_global.is_none() {
func_global = Some(StaticVec::new());
}
#[cfg(not(feature = "no_function"))]
func_global
.as_mut()
.expect("`Some`")
.push((k.clone(), m.clone()));
module.set_sub_module(kv.0, kv.1);
});
module.set_sub_module(k.clone(), m.clone());
});
}
// Restore global state
global.constants = orig_constants;
global.truncate_imports(orig_imports_len);
global.source = orig_source;
result?;
// Variables with an alias left in the scope become module variables
for (_, value, mut aliases) in scope {
match aliases.len() {
0 => (),
1 => {
let alias = aliases.pop().unwrap();
module.set_var(alias, value);
}
_ => {
let last_alias = aliases.pop().unwrap();
for alias in aliases {
module.set_var(alias, value.clone());
}
// Avoid cloning the last value
module.set_var(last_alias, value);
}
}
}
#[cfg(not(feature = "no_function"))]
let func_global = func_global.map(|v| v.into_boxed_slice());
@@ -1773,29 +1802,29 @@ impl Module {
) -> bool {
let mut contains_indexed_global_functions = false;
module.modules.iter().for_each(|(name, m)| {
for (name, m) in &module.modules {
// Index all the sub-modules first.
path.push(name);
if index_module(m, path, variables, functions, type_iterators) {
contains_indexed_global_functions = true;
}
path.pop();
});
}
// Index all variables
module.variables.iter().for_each(|(var_name, value)| {
for (var_name, value) in &module.variables {
let hash_var = crate::calc_qualified_var_hash(path.iter().copied(), var_name);
variables.insert(hash_var, value.clone());
});
}
// Index type iterators
module.type_iterators.iter().for_each(|(&type_id, func)| {
for (&type_id, func) in &module.type_iterators {
type_iterators.insert(type_id, func.clone());
contains_indexed_global_functions = true;
});
}
// Index all Rust functions
module.functions.iter().for_each(|(&hash, f)| {
for (&hash, f) in &module.functions {
match f.metadata.namespace {
FnNamespace::Global => {
// Flatten all functions with global namespace
@@ -1806,7 +1835,7 @@ impl Module {
}
match f.metadata.access {
FnAccess::Public => (),
FnAccess::Private => return, // Do not index private functions
FnAccess::Private => continue, // Do not index private functions
}
if !f.func.is_script() {
@@ -1824,7 +1853,7 @@ impl Module {
);
functions.insert(hash_qualified_script, f.func.clone());
}
});
}
contains_indexed_global_functions
}

View File

@@ -124,7 +124,7 @@ impl ModuleResolver for ModuleResolversCollection {
path: &str,
pos: Position,
) -> RhaiResultOf<Shared<Module>> {
for resolver in self.0.iter() {
for resolver in &self.0 {
match resolver.resolve(engine, source_path, path, pos) {
Ok(module) => return Ok(module),
Err(err) => match *err {

View File

@@ -1,9 +1,10 @@
#![cfg(not(feature = "no_std"))]
#![cfg(not(target_family = "wasm"))]
use crate::func::native::shared_write_lock;
use crate::func::native::locked_write;
use crate::{
Engine, Identifier, Module, ModuleResolver, Position, RhaiResultOf, Scope, Shared, ERR,
Engine, GlobalRuntimeState, Identifier, Module, ModuleResolver, Position, RhaiResultOf, Scope,
Shared, ERR,
};
use std::{
@@ -207,12 +208,12 @@ impl FileModuleResolver {
let file_path = self.get_file_path(path.as_ref(), source_path);
shared_write_lock(&self.cache).contains_key(&file_path)
locked_write(&self.cache).contains_key(&file_path)
}
/// Empty the internal cache.
#[inline]
pub fn clear_cache(&mut self) -> &mut Self {
shared_write_lock(&self.cache).clear();
locked_write(&self.cache).clear();
self
}
/// Remove the specified path from internal cache.
@@ -227,7 +228,7 @@ impl FileModuleResolver {
) -> Option<Shared<Module>> {
let file_path = self.get_file_path(path.as_ref(), source_path.as_ref().map(<_>::as_ref));
shared_write_lock(&self.cache)
locked_write(&self.cache)
.remove_entry(&file_path)
.map(|(_, v)| v)
}
@@ -252,24 +253,25 @@ impl FileModuleResolver {
file_path.set_extension(self.extension.as_str()); // Force extension
file_path
}
}
impl ModuleResolver for FileModuleResolver {
fn resolve(
/// Resolve a module based on a path.
fn impl_resolve(
&self,
engine: &Engine,
source_path: Option<&str>,
global: Option<&mut GlobalRuntimeState>,
source: Option<&str>,
path: &str,
pos: Position,
) -> RhaiResultOf<Shared<Module>> {
) -> Result<Shared<Module>, Box<crate::EvalAltResult>> {
// Load relative paths from source if there is no base path specified
let source_path =
source_path.and_then(|p| Path::new(p).parent().map(|p| p.to_string_lossy()));
let source_path = global
.as_ref()
.and_then(|g| g.source())
.or(source)
.and_then(|p| Path::new(p).parent().map(|p| p.to_string_lossy()));
// Construct the script file path
let file_path = self.get_file_path(path, source_path.as_ref().map(|p| p.as_ref()));
// See if it is cached
if self.is_cache_enabled() {
#[cfg(not(feature = "sync"))]
let c = self.cache.borrow();
@@ -281,7 +283,6 @@ impl ModuleResolver for FileModuleResolver {
}
}
// Load the script file and compile it
let scope = Scope::new();
let mut ast = engine
@@ -295,18 +296,43 @@ impl ModuleResolver for FileModuleResolver {
ast.set_source(path);
// Make a module from the AST
let m: Shared<Module> = Module::eval_ast_as_new(scope, &ast, engine)
.map_err(|err| Box::new(ERR::ErrorInModule(path.to_string(), err, pos)))?
.into();
let m: Shared<Module> = if let Some(global) = global {
Module::eval_ast_as_new_raw(engine, scope, global, &ast)
} else {
Module::eval_ast_as_new(scope, &ast, engine)
}
.map_err(|err| Box::new(ERR::ErrorInModule(path.to_string(), err, pos)))?
.into();
// Put it into the cache
if self.is_cache_enabled() {
shared_write_lock(&self.cache).insert(file_path, m.clone());
locked_write(&self.cache).insert(file_path, m.clone());
}
Ok(m)
}
}
impl ModuleResolver for FileModuleResolver {
fn resolve_raw(
&self,
engine: &Engine,
global: &mut GlobalRuntimeState,
path: &str,
pos: Position,
) -> RhaiResultOf<Shared<Module>> {
self.impl_resolve(engine, Some(global), None, path, pos)
}
#[inline(always)]
fn resolve(
&self,
engine: &Engine,
source: Option<&str>,
path: &str,
pos: Position,
) -> RhaiResultOf<Shared<Module>> {
self.impl_resolve(engine, None, source, path, pos)
}
/// Resolve an `AST` based on a path string.
///

View File

@@ -1,5 +1,5 @@
use crate::func::native::SendSync;
use crate::{Engine, Module, Position, RhaiResultOf, Shared, AST};
use crate::{Engine, GlobalRuntimeState, Module, Position, RhaiResultOf, Shared, AST};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
@@ -21,11 +21,26 @@ pub trait ModuleResolver: SendSync {
fn resolve(
&self,
engine: &Engine,
source_path: Option<&str>,
source: Option<&str>,
path: &str,
pos: Position,
) -> RhaiResultOf<Shared<Module>>;
/// Resolve a module based on a path string, given a [`GlobalRuntimeState`].
///
/// # WARNING - Low Level API
///
/// This function is very low level.
fn resolve_raw(
&self,
engine: &Engine,
global: &mut GlobalRuntimeState,
path: &str,
pos: Position,
) -> RhaiResultOf<Shared<Module>> {
self.resolve(engine, global.source(), path, pos)
}
/// Resolve an `AST` based on a path string.
///
/// Returns [`None`] (default) if such resolution is not supported
@@ -40,7 +55,7 @@ pub trait ModuleResolver: SendSync {
fn resolve_ast(
&self,
engine: &Engine,
source_path: Option<&str>,
source: Option<&str>,
path: &str,
pos: Position,
) -> Option<RhaiResultOf<AST>> {