diff --git a/src/api/eval.rs b/src/api/eval.rs index 82011f4c..6e2601e4 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -207,6 +207,8 @@ impl Engine { ast: &'a AST, ) -> RhaiResult { let orig_source = mem::replace(&mut global.source, ast.source_raw().cloned()); + + #[cfg(not(feature = "no_function"))] let orig_lib_len = global.lib.len(); #[cfg(not(feature = "no_function"))] @@ -242,7 +244,9 @@ impl Engine { global.embedded_module_resolver = orig_embedded_module_resolver; } + #[cfg(not(feature = "no_function"))] global.lib.truncate(orig_lib_len); + global.source = orig_source; result diff --git a/src/eval/cache.rs b/src/eval/cache.rs index ee4f07a3..96160d7e 100644 --- a/src/eval/cache.rs +++ b/src/eval/cache.rs @@ -44,44 +44,39 @@ impl FnResolutionCache { /// The following caches are contained inside this type: /// * A stack of [function resolution caches][FnResolutionCache] #[derive(Debug, Clone)] -pub struct Caches { - /// Stack of [function resolution caches][FnResolutionCache]. - stack: StaticVec, -} +pub struct Caches(StaticVec); impl Caches { /// Create an empty [`Caches`]. #[inline(always)] #[must_use] pub const fn new() -> Self { - Self { - stack: StaticVec::new_const(), - } + Self(StaticVec::new_const()) } /// Get the number of function resolution cache(s) in the stack. #[inline(always)] #[must_use] pub fn fn_resolution_caches_len(&self) -> usize { - self.stack.len() + self.0.len() } /// Get a mutable reference to the current function resolution cache. #[inline] #[must_use] 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 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. #[inline(always)] 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. #[inline(always)] pub fn rewind_fn_resolution_caches(&mut self, len: usize) { - self.stack.truncate(len); + self.0.truncate(len); } } diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index 339afd8d..5c3fa792 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -742,8 +742,9 @@ impl Engine { let fn_name = crate::engine::FN_IDX_GET; let pos = Position::NONE; + let orig_level = global.level; 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) .map(|(r, ..)| r) @@ -765,8 +766,9 @@ impl Engine { let fn_name = crate::engine::FN_IDX_SET; let pos = Position::NONE; + let orig_level = global.level; 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) } diff --git a/src/eval/data_check.rs b/src/eval/data_check.rs index abda0e45..bce0b87b 100644 --- a/src/eval/data_check.rs +++ b/src/eval/data_check.rs @@ -147,15 +147,11 @@ impl Engine { return Err(ERR::ErrorTooManyOperations(pos).into()); } - // Report progress - only in steps - if let Some(ref progress) = self.progress { - if let Some(token) = progress(num_operations) { - // Terminate script if progress returns a termination token - return Err(ERR::ErrorTerminated(token, pos).into()); - } - } - - Ok(()) + // Report progress + self.progress + .as_ref() + .and_then(|p| p(num_operations)) + .map_or(Ok(()), |token| Err(ERR::ErrorTerminated(token, pos).into())) } /// Check a result to ensure that it is valid. diff --git a/src/eval/eval_context.rs b/src/eval/eval_context.rs index d9d73c25..82b3750b 100644 --- a/src/eval/eval_context.rs +++ b/src/eval/eval_context.rs @@ -11,12 +11,12 @@ use std::prelude::v1::*; pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't> { /// The current [`Engine`]. engine: &'a Engine, - /// The current [`Scope`]. - scope: &'s mut Scope<'ps>, /// The current [`GlobalRuntimeState`]. global: &'g mut GlobalRuntimeState, /// The current [caches][Caches], if available. caches: &'c mut Caches, + /// The current [`Scope`]. + scope: &'s mut Scope<'ps>, /// The current bound `this` pointer, if any. 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 { engine, - scope, global, caches, + scope, this_ptr, } } @@ -100,12 +100,18 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { self.global } /// Get an iterator over the namespaces containing definition of all script-defined functions. + /// + /// Not available under `no_function`. + #[cfg(not(feature = "no_function"))] #[inline] pub fn iter_namespaces(&self) -> impl Iterator { self.global.lib.iter().map(|m| m.as_ref()) } /// _(internals)_ The current set of namespaces containing definitions of all script-defined functions. /// Exported under the `internals` feature only. + /// + /// Not available under `no_function`. + #[cfg(not(feature = "no_function"))] #[cfg(feature = "internals")] #[inline(always)] #[must_use] diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index 179812c0..703ae510 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -29,7 +29,8 @@ pub struct GlobalRuntimeState { /// Stack of imported [modules][crate::Module]. #[cfg(not(feature = "no_module"))] modules: StaticVec, - /// 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, /// Source of the current context. /// @@ -86,6 +87,7 @@ impl GlobalRuntimeState { imports: StaticVec::new_const(), #[cfg(not(feature = "no_module"))] modules: StaticVec::new_const(), + #[cfg(not(feature = "no_function"))] lib: StaticVec::new_const(), source: None, num_operations: 0, @@ -333,22 +335,31 @@ impl fmt::Debug for GlobalRuntimeState { let mut f = f.debug_struct("GlobalRuntimeState"); #[cfg(not(feature = "no_module"))] - f.field("imports", &self.scan_imports_raw().collect::>()); + f.field("imports", &self.scan_imports_raw().collect::>()) + .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) - .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")))] 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_function"))] f.field("constants", &self.constants); + f.field("tag", &self.tag); + + #[cfg(feature = "debugging")] + f.field("debugger", &self.debugger); + f.finish() } } diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index c6a497d6..ae5a7890 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -141,8 +141,9 @@ impl Engine { // Built-in found let op = op_assign_token.literal_syntax(); + let orig_level = global.level; 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(); return func(context, args).map(|_| ()); diff --git a/src/func/call.rs b/src/func/call.rs index 9de11db3..dff6e528 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -193,12 +193,21 @@ impl Engine { let mut bitmask = 1usize; // Bitmask of which parameter to replace with `Dynamic` loop { + #[cfg(not(feature = "no_function"))] let func = global .lib .iter() .rev() .chain(self.global_modules.iter()) .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"))] let func = if args.is_none() { @@ -230,12 +239,15 @@ impl Engine { // Check `Dynamic` parameters for functions with parameters if allow_dynamic && max_bitmask == 0 && num_args > 0 { - let is_dynamic = global - .lib + let is_dynamic = self + .global_modules .iter() - .any(|m| m.may_contain_dynamic_fn(hash_base)) - || self - .global_modules + .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)); @@ -560,8 +572,9 @@ impl Engine { #[cfg(not(feature = "no_closure"))] ensure_no_data_race(fn_name, _args, is_ref_mut)?; + let orig_level = global.level; 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. if hashes.is_native_only() { @@ -1093,8 +1106,8 @@ impl Engine { .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, pos))?; + let orig_level = global.level; 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); @@ -1108,6 +1121,7 @@ impl Engine { if scope_changed { global.always_search_scope = true; } + global.level = orig_level; return result.map_err(|err| { ERR::ErrorInFunctionCall( @@ -1346,8 +1360,9 @@ impl Engine { } } + let orig_level = global.level; 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 { #[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]) { // Built-in found + let orig_level = global.level; 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(); return func(context, operands); diff --git a/src/func/native.rs b/src/func/native.rs index ab56cb59..745388cd 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -273,12 +273,18 @@ impl<'a> NativeCallContext<'a> { } /// 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). + /// + /// Not available under `no_function`. + #[cfg(not(feature = "no_function"))] #[inline] pub fn iter_namespaces(&self) -> impl Iterator { self.global.lib.iter().map(|m| m.as_ref()) } /// _(internals)_ The current stack of namespaces containing definitions of all script-defined functions. /// Exported under the `internals` feature only. + /// + /// Not available under `no_function`. + #[cfg(not(feature = "no_function"))] #[cfg(feature = "internals")] #[inline(always)] #[must_use] diff --git a/src/module/mod.rs b/src/module/mod.rs index 78e7bc58..e468cfa0 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -1980,7 +1980,10 @@ impl Module { // Save global state let orig_imports_len = global.num_imports(); let orig_source = global.source.clone(); + + #[cfg(not(feature = "no_function"))] let orig_lib_len = global.lib.len(); + #[cfg(not(feature = "no_function"))] let orig_constants = std::mem::take(&mut global.constants); @@ -2008,8 +2011,12 @@ impl Module { // Restore global state #[cfg(not(feature = "no_function"))] let constants = std::mem::replace(&mut global.constants, orig_constants); + global.truncate_imports(orig_imports_len); + + #[cfg(not(feature = "no_function"))] global.lib.truncate(orig_lib_len); + global.source = orig_source; result?;