This commit is contained in:
Stephen Chung 2022-11-10 14:25:48 +08:00
parent 54353c6676
commit 013ee223ee
10 changed files with 87 additions and 43 deletions

View File

@ -207,6 +207,8 @@ impl Engine {
ast: &'a AST, ast: &'a AST,
) -> RhaiResult { ) -> RhaiResult {
let orig_source = mem::replace(&mut global.source, ast.source_raw().cloned()); let orig_source = mem::replace(&mut global.source, ast.source_raw().cloned());
#[cfg(not(feature = "no_function"))]
let orig_lib_len = global.lib.len(); let orig_lib_len = global.lib.len();
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
@ -242,7 +244,9 @@ impl Engine {
global.embedded_module_resolver = orig_embedded_module_resolver; global.embedded_module_resolver = orig_embedded_module_resolver;
} }
#[cfg(not(feature = "no_function"))]
global.lib.truncate(orig_lib_len); global.lib.truncate(orig_lib_len);
global.source = orig_source; global.source = orig_source;
result result

View File

@ -44,44 +44,39 @@ impl FnResolutionCache {
/// The following caches are contained inside this type: /// The following caches are contained inside this type:
/// * A stack of [function resolution caches][FnResolutionCache] /// * A stack of [function resolution caches][FnResolutionCache]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Caches { pub struct Caches(StaticVec<FnResolutionCache>);
/// Stack of [function resolution caches][FnResolutionCache].
stack: StaticVec<FnResolutionCache>,
}
impl Caches { impl Caches {
/// Create an empty [`Caches`]. /// Create an empty [`Caches`].
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub const fn new() -> Self { pub const fn new() -> Self {
Self { Self(StaticVec::new_const())
stack: StaticVec::new_const(),
}
} }
/// Get the number of function resolution cache(s) in the stack. /// Get the number of function resolution cache(s) in the stack.
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn fn_resolution_caches_len(&self) -> usize { pub fn fn_resolution_caches_len(&self) -> usize {
self.stack.len() self.0.len()
} }
/// Get a mutable reference to the current function resolution cache. /// Get a mutable reference to the current function resolution cache.
#[inline] #[inline]
#[must_use] #[must_use]
pub fn fn_resolution_cache_mut(&mut self) -> &mut FnResolutionCache { pub fn fn_resolution_cache_mut(&mut self) -> &mut FnResolutionCache {
if self.stack.is_empty() { if self.0.is_empty() {
// Push a new function resolution cache if the stack is empty // Push a new function resolution cache if the stack is empty
self.push_fn_resolution_cache(); self.push_fn_resolution_cache();
} }
self.stack.last_mut().unwrap() self.0.last_mut().unwrap()
} }
/// Push an empty function resolution cache onto the stack and make it current. /// Push an empty function resolution cache onto the stack and make it current.
#[inline(always)] #[inline(always)]
pub fn push_fn_resolution_cache(&mut self) { pub fn push_fn_resolution_cache(&mut self) {
self.stack.push(Default::default()); self.0.push(Default::default());
} }
/// Rewind the function resolution caches stack to a particular size. /// Rewind the function resolution caches stack to a particular size.
#[inline(always)] #[inline(always)]
pub fn rewind_fn_resolution_caches(&mut self, len: usize) { pub fn rewind_fn_resolution_caches(&mut self, len: usize) {
self.stack.truncate(len); self.0.truncate(len);
} }
} }

View File

@ -742,8 +742,9 @@ impl Engine {
let fn_name = crate::engine::FN_IDX_GET; let fn_name = crate::engine::FN_IDX_GET;
let pos = Position::NONE; let pos = Position::NONE;
let orig_level = global.level;
global.level += 1; global.level += 1;
let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1); let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level);
self.exec_native_fn_call(global, caches, fn_name, None, hash, args, true, pos) self.exec_native_fn_call(global, caches, fn_name, None, hash, args, true, pos)
.map(|(r, ..)| r) .map(|(r, ..)| r)
@ -765,8 +766,9 @@ impl Engine {
let fn_name = crate::engine::FN_IDX_SET; let fn_name = crate::engine::FN_IDX_SET;
let pos = Position::NONE; let pos = Position::NONE;
let orig_level = global.level;
global.level += 1; global.level += 1;
let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1); let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level);
self.exec_native_fn_call(global, caches, fn_name, None, hash, args, is_ref_mut, pos) self.exec_native_fn_call(global, caches, fn_name, None, hash, args, is_ref_mut, pos)
} }

View File

@ -147,15 +147,11 @@ impl Engine {
return Err(ERR::ErrorTooManyOperations(pos).into()); return Err(ERR::ErrorTooManyOperations(pos).into());
} }
// Report progress - only in steps // Report progress
if let Some(ref progress) = self.progress { self.progress
if let Some(token) = progress(num_operations) { .as_ref()
// Terminate script if progress returns a termination token .and_then(|p| p(num_operations))
return Err(ERR::ErrorTerminated(token, pos).into()); .map_or(Ok(()), |token| Err(ERR::ErrorTerminated(token, pos).into()))
}
}
Ok(())
} }
/// Check a result to ensure that it is valid. /// Check a result to ensure that it is valid.

View File

@ -11,12 +11,12 @@ use std::prelude::v1::*;
pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't> { pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't> {
/// The current [`Engine`]. /// The current [`Engine`].
engine: &'a Engine, engine: &'a Engine,
/// The current [`Scope`].
scope: &'s mut Scope<'ps>,
/// The current [`GlobalRuntimeState`]. /// The current [`GlobalRuntimeState`].
global: &'g mut GlobalRuntimeState, global: &'g mut GlobalRuntimeState,
/// The current [caches][Caches], if available. /// The current [caches][Caches], if available.
caches: &'c mut Caches, caches: &'c mut Caches,
/// The current [`Scope`].
scope: &'s mut Scope<'ps>,
/// The current bound `this` pointer, if any. /// The current bound `this` pointer, if any.
this_ptr: &'t mut Dynamic, this_ptr: &'t mut Dynamic,
} }
@ -34,9 +34,9 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> {
) -> Self { ) -> Self {
Self { Self {
engine, engine,
scope,
global, global,
caches, caches,
scope,
this_ptr, this_ptr,
} }
} }
@ -100,12 +100,18 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> {
self.global self.global
} }
/// Get an iterator over the namespaces containing definition of all script-defined functions. /// Get an iterator over the namespaces containing definition of all script-defined functions.
///
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))]
#[inline] #[inline]
pub fn iter_namespaces(&self) -> impl Iterator<Item = &Module> { pub fn iter_namespaces(&self) -> impl Iterator<Item = &Module> {
self.global.lib.iter().map(|m| m.as_ref()) self.global.lib.iter().map(|m| m.as_ref())
} }
/// _(internals)_ The current set of namespaces containing definitions of all script-defined functions. /// _(internals)_ The current set of namespaces containing definitions of all script-defined functions.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
///
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))]
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]

View File

@ -29,7 +29,8 @@ pub struct GlobalRuntimeState {
/// Stack of imported [modules][crate::Module]. /// Stack of imported [modules][crate::Module].
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
modules: StaticVec<SharedModule>, modules: StaticVec<SharedModule>,
/// The current stack of loaded [modules][Module]. /// The current stack of loaded [modules][crate::Module] containing script-defined functions.
#[cfg(not(feature = "no_function"))]
pub lib: StaticVec<SharedModule>, pub lib: StaticVec<SharedModule>,
/// Source of the current context. /// Source of the current context.
/// ///
@ -86,6 +87,7 @@ impl GlobalRuntimeState {
imports: StaticVec::new_const(), imports: StaticVec::new_const(),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
modules: StaticVec::new_const(), modules: StaticVec::new_const(),
#[cfg(not(feature = "no_function"))]
lib: StaticVec::new_const(), lib: StaticVec::new_const(),
source: None, source: None,
num_operations: 0, num_operations: 0,
@ -333,22 +335,31 @@ impl fmt::Debug for GlobalRuntimeState {
let mut f = f.debug_struct("GlobalRuntimeState"); let mut f = f.debug_struct("GlobalRuntimeState");
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
f.field("imports", &self.scan_imports_raw().collect::<Vec<_>>()); f.field("imports", &self.scan_imports_raw().collect::<Vec<_>>())
.field("num_modules_loaded", &self.num_modules_loaded)
.field("embedded_module_resolver", &self.embedded_module_resolver);
#[cfg(not(feature = "no_function"))]
f.field("lib", &self.lib);
f.field("source", &self.source) f.field("source", &self.source)
.field("num_operations", &self.num_operations); .field("num_operations", &self.num_operations)
.field("level", &self.level)
.field("scope_level", &self.scope_level)
.field("always_search_scope", &self.always_search_scope);
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
f.field("fn_hash_indexing", &self.fn_hash_indexing); f.field("fn_hash_indexing", &self.fn_hash_indexing);
#[cfg(not(feature = "no_module"))]
f.field("num_modules_loaded", &self.num_modules_loaded)
.field("embedded_module_resolver", &self.embedded_module_resolver);
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
f.field("constants", &self.constants); f.field("constants", &self.constants);
f.field("tag", &self.tag);
#[cfg(feature = "debugging")]
f.field("debugger", &self.debugger);
f.finish() f.finish()
} }
} }

View File

@ -141,8 +141,9 @@ impl Engine {
// Built-in found // Built-in found
let op = op_assign_token.literal_syntax(); let op = op_assign_token.literal_syntax();
let orig_level = global.level;
global.level += 1; global.level += 1;
let global = &*RestoreOnDrop::lock(global, move |g| g.level -= 1); let global = &*RestoreOnDrop::lock(global, move |g| g.level = orig_level);
let context = (self, op, None, global, *op_pos).into(); let context = (self, op, None, global, *op_pos).into();
return func(context, args).map(|_| ()); return func(context, args).map(|_| ());

View File

@ -193,12 +193,21 @@ impl Engine {
let mut bitmask = 1usize; // Bitmask of which parameter to replace with `Dynamic` let mut bitmask = 1usize; // Bitmask of which parameter to replace with `Dynamic`
loop { loop {
#[cfg(not(feature = "no_function"))]
let func = global let func = global
.lib .lib
.iter() .iter()
.rev() .rev()
.chain(self.global_modules.iter()) .chain(self.global_modules.iter())
.find_map(|m| m.get_fn(hash).map(|f| (f, m.id_raw()))); .find_map(|m| m.get_fn(hash).map(|f| (f, m.id_raw())));
#[cfg(feature = "no_function")]
let func = None;
let func = func.or_else(|| {
self.global_modules
.iter()
.find_map(|m| m.get_fn(hash).map(|f| (f, m.id_raw())))
});
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
let func = if args.is_none() { let func = if args.is_none() {
@ -230,15 +239,18 @@ impl Engine {
// Check `Dynamic` parameters for functions with parameters // Check `Dynamic` parameters for functions with parameters
if allow_dynamic && max_bitmask == 0 && num_args > 0 { if allow_dynamic && max_bitmask == 0 && num_args > 0 {
let is_dynamic = global let is_dynamic = self
.lib
.iter()
.any(|m| m.may_contain_dynamic_fn(hash_base))
|| self
.global_modules .global_modules
.iter() .iter()
.any(|m| m.may_contain_dynamic_fn(hash_base)); .any(|m| m.may_contain_dynamic_fn(hash_base));
#[cfg(not(feature = "no_function"))]
let is_dynamic = is_dynamic
|| global
.lib
.iter()
.any(|m| m.may_contain_dynamic_fn(hash_base));
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
let is_dynamic = is_dynamic let is_dynamic = is_dynamic
|| global.may_contain_dynamic_fn(hash_base) || global.may_contain_dynamic_fn(hash_base)
@ -560,8 +572,9 @@ impl Engine {
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
ensure_no_data_race(fn_name, _args, is_ref_mut)?; ensure_no_data_race(fn_name, _args, is_ref_mut)?;
let orig_level = global.level;
global.level += 1; global.level += 1;
let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1); let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level);
// These may be redirected from method style calls. // These may be redirected from method style calls.
if hashes.is_native_only() { if hashes.is_native_only() {
@ -1093,8 +1106,8 @@ impl Engine {
.into_immutable_string() .into_immutable_string()
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, pos))?; .map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, pos))?;
let orig_level = global.level;
global.level += 1; global.level += 1;
let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1);
let result = self.eval_script_expr_in_place(global, caches, scope, s, pos); let result = self.eval_script_expr_in_place(global, caches, scope, s, pos);
@ -1108,6 +1121,7 @@ impl Engine {
if scope_changed { if scope_changed {
global.always_search_scope = true; global.always_search_scope = true;
} }
global.level = orig_level;
return result.map_err(|err| { return result.map_err(|err| {
ERR::ErrorInFunctionCall( ERR::ErrorInFunctionCall(
@ -1346,8 +1360,9 @@ impl Engine {
} }
} }
let orig_level = global.level;
global.level += 1; global.level += 1;
let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1); let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level);
match func { match func {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
@ -1484,8 +1499,9 @@ impl Engine {
get_builtin_binary_op_fn(op_token.as_ref().unwrap(), operands[0], operands[1]) get_builtin_binary_op_fn(op_token.as_ref().unwrap(), operands[0], operands[1])
{ {
// Built-in found // Built-in found
let orig_level = global.level;
global.level += 1; global.level += 1;
let global = &*RestoreOnDrop::lock(global, move |g| g.level -= 1); let global = &*RestoreOnDrop::lock(global, move |g| g.level = orig_level);
let context = (self, name.as_str(), None, global, pos).into(); let context = (self, name.as_str(), None, global, pos).into();
return func(context, operands); return func(context, operands);

View File

@ -273,12 +273,18 @@ impl<'a> NativeCallContext<'a> {
} }
/// 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 (i.e. parent namespaces are iterated after child namespaces). /// in reverse order (i.e. parent namespaces are iterated after child namespaces).
///
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))]
#[inline] #[inline]
pub fn iter_namespaces(&self) -> impl Iterator<Item = &Module> { pub fn iter_namespaces(&self) -> impl Iterator<Item = &Module> {
self.global.lib.iter().map(|m| m.as_ref()) self.global.lib.iter().map(|m| m.as_ref())
} }
/// _(internals)_ The current stack of namespaces containing definitions of all script-defined functions. /// _(internals)_ The current stack of namespaces containing definitions of all script-defined functions.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
///
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))]
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]

View File

@ -1980,7 +1980,10 @@ impl Module {
// Save global state // Save global state
let orig_imports_len = global.num_imports(); let orig_imports_len = global.num_imports();
let orig_source = global.source.clone(); let orig_source = global.source.clone();
#[cfg(not(feature = "no_function"))]
let orig_lib_len = global.lib.len(); let orig_lib_len = global.lib.len();
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
let orig_constants = std::mem::take(&mut global.constants); let orig_constants = std::mem::take(&mut global.constants);
@ -2008,8 +2011,12 @@ impl Module {
// Restore global state // Restore global state
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
let constants = std::mem::replace(&mut global.constants, orig_constants); let constants = std::mem::replace(&mut global.constants, orig_constants);
global.truncate_imports(orig_imports_len); global.truncate_imports(orig_imports_len);
#[cfg(not(feature = "no_function"))]
global.lib.truncate(orig_lib_len); global.lib.truncate(orig_lib_len);
global.source = orig_source; global.source = orig_source;
result?; result?;