Rename Imports to GlobalRuntimeState.
This commit is contained in:
parent
9deddc679c
commit
807240e249
@ -15,11 +15,11 @@ pub struct ScriptFnDef {
|
||||
pub body: StmtBlock,
|
||||
/// Encapsulated running environment, if any.
|
||||
pub lib: Option<Shared<Module>>,
|
||||
/// Encapsulated [`GlobalRuntimeState`][crate::GlobalRuntimeState].
|
||||
/// Encapsulated stack of imported modules, if any.
|
||||
///
|
||||
/// Not available under `no_module`.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub global: crate::engine::GlobalRuntimeState,
|
||||
pub global: Option<Box<[(Identifier, Shared<Module>)]>>,
|
||||
/// Function name.
|
||||
pub name: Identifier,
|
||||
/// Function access mode.
|
||||
|
@ -771,10 +771,10 @@ pub struct FnResolutionCacheEntry {
|
||||
/// Exported under the `internals` feature only.
|
||||
pub type FnResolutionCache = BTreeMap<u64, Option<Box<FnResolutionCacheEntry>>>;
|
||||
|
||||
/// _(internals)_ A type that holds all the current states of the [`Engine`].
|
||||
/// _(internals)_ A type that holds all volatile evaluation states data.
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EvalState {
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default, Hash)]
|
||||
pub struct EvalStateData {
|
||||
/// Normally, access to variables are parsed with a relative offset into the [`Scope`] to avoid a lookup.
|
||||
/// In some situation, e.g. after running an `eval` statement, or after a custom syntax statement,
|
||||
/// subsequent offsets may become mis-aligned.
|
||||
@ -783,6 +783,24 @@ pub struct EvalState {
|
||||
/// Level of the current scope. The global (root) level is zero, a new block (or function call)
|
||||
/// is one level higher, and so on.
|
||||
pub scope_level: usize,
|
||||
}
|
||||
|
||||
impl EvalStateData {
|
||||
/// Create a new [`EvalStateData`].
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
always_search_scope: false,
|
||||
scope_level: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// _(internals)_ A type that holds all the current states of the [`Engine`].
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EvalState {
|
||||
/// Volatile states data.
|
||||
pub data: EvalStateData,
|
||||
/// Stack of function resolution caches.
|
||||
fn_resolution_caches: StaticVec<FnResolutionCache>,
|
||||
}
|
||||
@ -793,8 +811,7 @@ impl EvalState {
|
||||
#[must_use]
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
always_search_scope: false,
|
||||
scope_level: 0,
|
||||
data: EvalStateData::new(),
|
||||
fn_resolution_caches: StaticVec::new_const(),
|
||||
}
|
||||
}
|
||||
@ -802,7 +819,7 @@ impl EvalState {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn is_global(&self) -> bool {
|
||||
self.scope_level == 0
|
||||
self.data.scope_level == 0
|
||||
}
|
||||
/// Get the number of function resolution cache(s) in the stack.
|
||||
#[inline(always)]
|
||||
@ -1142,7 +1159,7 @@ impl Engine {
|
||||
let root = &namespace[0].name;
|
||||
|
||||
// Qualified - check if the root module is directly indexed
|
||||
let index = if state.always_search_scope {
|
||||
let index = if state.data.always_search_scope {
|
||||
None
|
||||
} else {
|
||||
namespace.index()
|
||||
@ -1267,7 +1284,7 @@ impl Engine {
|
||||
Err(ERR::ErrorUnboundThis(*pos).into())
|
||||
}
|
||||
}
|
||||
_ if state.always_search_scope => (0, expr.position()),
|
||||
_ if state.data.always_search_scope => (0, expr.position()),
|
||||
Expr::Variable(Some(i), pos, _) => (i.get() as usize, *pos),
|
||||
Expr::Variable(None, pos, v) => (v.0.map(NonZeroUsize::get).unwrap_or(0), *pos),
|
||||
_ => unreachable!("Expr::Variable expected, but gets {:?}", expr),
|
||||
@ -2454,13 +2471,13 @@ impl Engine {
|
||||
return Ok(Dynamic::UNIT);
|
||||
}
|
||||
|
||||
let orig_always_search_scope = state.always_search_scope;
|
||||
let orig_always_search_scope = state.data.always_search_scope;
|
||||
let orig_scope_len = scope.len();
|
||||
let orig_mods_len = global.num_imported_modules();
|
||||
let orig_fn_resolution_caches_len = state.fn_resolution_caches_len();
|
||||
|
||||
if rewind_scope {
|
||||
state.scope_level += 1;
|
||||
state.data.scope_level += 1;
|
||||
}
|
||||
|
||||
let result = statements.iter().try_fold(Dynamic::UNIT, |_, stmt| {
|
||||
@ -2500,14 +2517,14 @@ impl Engine {
|
||||
|
||||
if rewind_scope {
|
||||
scope.rewind(orig_scope_len);
|
||||
state.scope_level -= 1;
|
||||
state.data.scope_level -= 1;
|
||||
}
|
||||
if restore_orig_state {
|
||||
global.truncate_modules(orig_mods_len);
|
||||
|
||||
// The impact of new local variables goes away at the end of a block
|
||||
// because any new variables introduced will go out of scope
|
||||
state.always_search_scope = orig_always_search_scope;
|
||||
state.data.always_search_scope = orig_always_search_scope;
|
||||
}
|
||||
|
||||
result
|
||||
|
@ -695,48 +695,6 @@ impl Engine {
|
||||
})
|
||||
}
|
||||
|
||||
/// Evaluate a text script in place - used primarily for 'eval'.
|
||||
fn eval_script_expr_in_place(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
global: &mut GlobalRuntimeState,
|
||||
lib: &[&Module],
|
||||
script: impl AsRef<str>,
|
||||
_pos: Position,
|
||||
level: usize,
|
||||
) -> RhaiResult {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
self.inc_operations(&mut global.num_operations, _pos)?;
|
||||
|
||||
let script = script.as_ref().trim();
|
||||
if script.is_empty() {
|
||||
return Ok(Dynamic::UNIT);
|
||||
}
|
||||
|
||||
// Compile the script text
|
||||
// No optimizations because we only run it once
|
||||
let ast = self.compile_with_scope_and_optimization_level(
|
||||
&Scope::new(),
|
||||
&[script],
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
crate::OptimizationLevel::None,
|
||||
)?;
|
||||
|
||||
// If new functions are defined within the eval string, it is an error
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
if !ast.shared_lib().is_empty() {
|
||||
return Err(crate::PERR::WrongFnDefinition.into());
|
||||
}
|
||||
|
||||
let statements = ast.statements();
|
||||
if statements.is_empty() {
|
||||
return Ok(Dynamic::UNIT);
|
||||
}
|
||||
|
||||
// Evaluate the AST
|
||||
self.eval_global_statements(scope, global, &mut EvalState::new(), statements, lib, level)
|
||||
}
|
||||
|
||||
/// Call a dot method.
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
pub(crate) fn make_method_call(
|
||||
@ -1081,13 +1039,14 @@ impl Engine {
|
||||
let script = &value
|
||||
.into_immutable_string()
|
||||
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, pos))?;
|
||||
let level = level + 1;
|
||||
let result =
|
||||
self.eval_script_expr_in_place(scope, global, lib, script, pos, level + 1);
|
||||
self.eval_script_expr_in_place(scope, global, state, lib, script, pos, level);
|
||||
|
||||
// IMPORTANT! If the eval defines new variables in the current scope,
|
||||
// all variable offsets from this point on will be mis-aligned.
|
||||
if scope.len() != orig_scope_len {
|
||||
state.always_search_scope = true;
|
||||
state.data.always_search_scope = true;
|
||||
}
|
||||
|
||||
return result.map_err(|err| {
|
||||
|
@ -186,7 +186,8 @@ impl<'a> NativeCallContext<'a> {
|
||||
pub const fn source(&self) -> Option<&str> {
|
||||
self.source
|
||||
}
|
||||
/// Get an iterator over the current set of modules imported via `import` statements.
|
||||
/// Get an iterator over the current set of modules imported via `import` statements
|
||||
/// in reverse order.
|
||||
///
|
||||
/// Not available under `no_module`.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
@ -194,7 +195,7 @@ impl<'a> NativeCallContext<'a> {
|
||||
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &Module)> {
|
||||
self.global.iter().flat_map(|&m| m.iter_modules())
|
||||
}
|
||||
/// Get an iterator over the current set of modules imported via `import` statements.
|
||||
/// Get an iterator over the current set of modules imported via `import` statements in reverse order.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
@ -214,10 +215,11 @@ impl<'a> NativeCallContext<'a> {
|
||||
pub const fn global_runtime_state(&self) -> Option<&GlobalRuntimeState> {
|
||||
self.global
|
||||
}
|
||||
/// Get an iterator over the namespaces containing definitions of all script-defined functions.
|
||||
/// Get an iterator over the namespaces containing definitions of all script-defined functions
|
||||
/// in reverse order.
|
||||
#[inline]
|
||||
pub fn iter_namespaces(&self) -> impl Iterator<Item = &Module> {
|
||||
self.lib.iter().cloned()
|
||||
self.lib.iter().rev().cloned()
|
||||
}
|
||||
/// _(internals)_ The current set of namespaces containing definitions of all script-defined functions.
|
||||
/// Exported under the `internals` feature only.
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
use super::call::FnCallArgs;
|
||||
use crate::ast::ScriptFnDef;
|
||||
use crate::engine::{EvalState, GlobalRuntimeState};
|
||||
use crate::engine::{EvalState, EvalStateData, GlobalRuntimeState};
|
||||
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
|
||||
use crate::{Dynamic, Engine, Module, Position, RhaiError, RhaiResult, Scope, StaticVec, ERR};
|
||||
use std::mem;
|
||||
@ -91,6 +91,8 @@ impl Engine {
|
||||
// Merge in encapsulated environment, if any
|
||||
let mut lib_merged = StaticVec::with_capacity(lib.len() + 1);
|
||||
let orig_fn_resolution_caches_len = state.fn_resolution_caches_len();
|
||||
let orig_states_data = state.data;
|
||||
state.data = EvalStateData::new();
|
||||
|
||||
let lib = if let Some(ref fn_lib) = fn_def.lib {
|
||||
if fn_lib.is_empty() {
|
||||
@ -106,15 +108,14 @@ impl Engine {
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
if fn_def.global.num_imported_modules() > 0 {
|
||||
fn_def
|
||||
.global
|
||||
.iter_modules_raw()
|
||||
.for_each(|(n, m)| global.push_module(n.clone(), m.clone()));
|
||||
if let Some(ref modules) = fn_def.global {
|
||||
modules
|
||||
.iter()
|
||||
.cloned()
|
||||
.for_each(|(n, m)| global.push_module(n, m));
|
||||
}
|
||||
|
||||
// Evaluate the function
|
||||
let body = &fn_def.body;
|
||||
let result = self
|
||||
.eval_stmt_block(
|
||||
scope,
|
||||
@ -122,7 +123,7 @@ impl Engine {
|
||||
state,
|
||||
lib,
|
||||
this_ptr,
|
||||
body,
|
||||
&fn_def.body,
|
||||
true,
|
||||
rewind_scope,
|
||||
level,
|
||||
@ -149,15 +150,17 @@ impl Engine {
|
||||
_ => make_error(fn_def.name.to_string(), fn_def, global, err, pos),
|
||||
});
|
||||
|
||||
// Remove all local variables
|
||||
// Remove all local variables and imported modules
|
||||
if rewind_scope {
|
||||
scope.rewind(orig_scope_len);
|
||||
} else if !args.is_empty() {
|
||||
// Remove arguments only, leaving new variables in the scope
|
||||
scope.remove_range(orig_scope_len, args.len())
|
||||
}
|
||||
|
||||
global.truncate_modules(orig_mods_len);
|
||||
|
||||
// Restore state
|
||||
state.data = orig_states_data;
|
||||
state.rewind_fn_resolution_caches(orig_fn_resolution_caches_len);
|
||||
|
||||
result
|
||||
@ -193,4 +196,47 @@ impl Engine {
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Evaluate a text script in place - used primarily for 'eval'.
|
||||
pub(crate) fn eval_script_expr_in_place(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
global: &mut GlobalRuntimeState,
|
||||
state: &mut EvalState,
|
||||
lib: &[&Module],
|
||||
script: impl AsRef<str>,
|
||||
_pos: Position,
|
||||
level: usize,
|
||||
) -> RhaiResult {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
self.inc_operations(&mut global.num_operations, _pos)?;
|
||||
|
||||
let script = script.as_ref().trim();
|
||||
if script.is_empty() {
|
||||
return Ok(Dynamic::UNIT);
|
||||
}
|
||||
|
||||
// Compile the script text
|
||||
// No optimizations because we only run it once
|
||||
let ast = self.compile_with_scope_and_optimization_level(
|
||||
&Scope::new(),
|
||||
&[script],
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
crate::OptimizationLevel::None,
|
||||
)?;
|
||||
|
||||
// If new functions are defined within the eval string, it is an error
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
if !ast.shared_lib().is_empty() {
|
||||
return Err(crate::PERR::WrongFnDefinition.into());
|
||||
}
|
||||
|
||||
let statements = ast.statements();
|
||||
if statements.is_empty() {
|
||||
return Ok(Dynamic::UNIT);
|
||||
}
|
||||
|
||||
// Evaluate the AST
|
||||
self.eval_global_statements(scope, global, state, statements, lib, level)
|
||||
}
|
||||
}
|
||||
|
@ -241,7 +241,9 @@ pub use ast::{
|
||||
pub use ast::FloatWrapper;
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
pub use engine::{EvalState, FnResolutionCache, FnResolutionCacheEntry, GlobalRuntimeState};
|
||||
pub use engine::{
|
||||
EvalState, EvalStateData, FnResolutionCache, FnResolutionCacheEntry, GlobalRuntimeState,
|
||||
};
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
pub use module::Namespace;
|
||||
|
@ -1580,16 +1580,18 @@ impl Module {
|
||||
});
|
||||
|
||||
// Extra modules left in the scope become sub-modules
|
||||
let mut func_global = crate::engine::GlobalRuntimeState::new();
|
||||
let mut func_global = None;
|
||||
|
||||
global
|
||||
.into_iter()
|
||||
.skip(orig_mods_len)
|
||||
.for_each(|(alias, m)| {
|
||||
func_global.push_module(alias.clone(), m.clone());
|
||||
module.set_sub_module(alias, m);
|
||||
global.into_iter().skip(orig_mods_len).for_each(|kv| {
|
||||
if func_global.is_none() {
|
||||
func_global = Some(StaticVec::new());
|
||||
}
|
||||
func_global.as_mut().expect("`Some`").push(kv.clone());
|
||||
module.set_sub_module(kv.0, kv.1);
|
||||
});
|
||||
|
||||
let func_global = func_global.map(|v| v.into_boxed_slice());
|
||||
|
||||
// Non-private functions defined become module functions
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
if ast.has_functions() {
|
||||
|
@ -1155,7 +1155,7 @@ pub fn optimize_into_ast(
|
||||
params: fn_def.params.clone(),
|
||||
lib: None,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
global: GlobalRuntimeState::new(),
|
||||
global: None,
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[cfg(feature = "metadata")]
|
||||
comments: None,
|
||||
|
@ -3129,7 +3129,7 @@ fn parse_fn(
|
||||
body,
|
||||
lib: None,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
global: crate::engine::GlobalRuntimeState::new(),
|
||||
global: None,
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[cfg(feature = "metadata")]
|
||||
comments: if comments.is_empty() {
|
||||
@ -3280,7 +3280,7 @@ fn parse_anon_fn(
|
||||
body: body.into(),
|
||||
lib: None,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
global: crate::engine::GlobalRuntimeState::new(),
|
||||
global: None,
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[cfg(feature = "metadata")]
|
||||
comments: None,
|
||||
|
Loading…
Reference in New Issue
Block a user