Minor code and docs refactor.

This commit is contained in:
Stephen Chung 2021-11-28 22:57:28 +08:00
parent dca47d5233
commit 9fa4d60336
13 changed files with 175 additions and 127 deletions

View File

@ -7,7 +7,7 @@ Version 1.3.0
Compiler requirement Compiler requirement
-------------------- --------------------
* Minimum compiler version is 1.51. * Minimum compiler version is bumped to 1.51.
Enhancements Enhancements
------------ ------------
@ -20,6 +20,7 @@ Deprecated API's
* The internal `no_smartstring` feature is removed since `SmartString` now supports `no-std`. * The internal `no_smartstring` feature is removed since `SmartString` now supports `no-std`.
* `NativeCallContext::new` is deprecated because it is simpler to call a function pointer via `FnPtr::call`. * `NativeCallContext::new` is deprecated because it is simpler to call a function pointer via `FnPtr::call`.
* `AST::shared_lib` is changed to return `&Shared<Module>` while `AST::lib` is deprecated.
Version 1.2.1 Version 1.2.1

View File

@ -156,7 +156,7 @@ impl Engine {
if eval_ast && !statements.is_empty() { if eval_ast && !statements.is_empty() {
// Make sure new variables introduced at global level do not _spill_ into the function call // Make sure new variables introduced at global level do not _spill_ into the function call
self.eval_global_statements(scope, mods, state, statements, &[ast.lib()], 0)?; self.eval_global_statements(scope, mods, state, statements, &[ast.as_ref()], 0)?;
if rewind_scope { if rewind_scope {
scope.rewind(orig_scope_len); scope.rewind(orig_scope_len);
@ -169,7 +169,7 @@ impl Engine {
let mut args: StaticVec<_> = arg_values.as_mut().iter_mut().collect(); let mut args: StaticVec<_> = arg_values.as_mut().iter_mut().collect();
let fn_def = ast let fn_def = ast
.lib() .shared_lib()
.get_script_fn(name, args.len()) .get_script_fn(name, args.len())
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::NONE))?; .ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::NONE))?;
@ -181,7 +181,7 @@ impl Engine {
scope, scope,
mods, mods,
state, state,
&[ast.lib()], &[ast.as_ref()],
&mut this_ptr, &mut this_ptr,
fn_def, fn_def,
&mut args, &mut args,

View File

@ -230,6 +230,12 @@ impl NativeCallContext<'_> {
/// ///
/// If `is_method` is [`true`], the first argument is assumed to be passed /// If `is_method` is [`true`], the first argument is assumed to be passed
/// by reference and is not consumed. /// by reference and is not consumed.
///
/// # Deprecated
///
/// This method is deprecated. Use [`call_fn_raw`][NativeCallContext::call_fn_raw] instead.
///
/// This method will be removed in the next major version.
#[deprecated(since = "1.2.0", note = "use `call_fn_raw` instead")] #[deprecated(since = "1.2.0", note = "use `call_fn_raw` instead")]
#[inline(always)] #[inline(always)]
pub fn call_fn_dynamic_raw( pub fn call_fn_dynamic_raw(
@ -250,3 +256,23 @@ impl<T> From<EvalAltResult> for Result<T, Box<EvalAltResult>> {
Err(err.into()) Err(err.into())
} }
} }
impl AST {
/// _(internals)_ Get the internal [`Module`] containing all script-defined functions.
/// Exported under the `internals` feature only.
///
/// Not available under `no_function`.
///
/// # Deprecated
///
/// This method is deprecated. Use [`shared_lib`][AST::shared_lib] instead.
///
/// This method will be removed in the next major version.
#[deprecated(since = "1.3.0", note = "use `shared_lib` instead")]
#[cfg(feature = "internals")]
#[inline(always)]
#[must_use]
pub fn lib(&self) -> &Module {
&self.functions
}
}

View File

@ -226,7 +226,7 @@ impl Engine {
return Ok(Dynamic::UNIT); return Ok(Dynamic::UNIT);
} }
let lib = &[ast.lib()]; let lib = &[ast.as_ref()];
self.eval_global_statements(scope, mods, &mut state, statements, lib, level) self.eval_global_statements(scope, mods, &mut state, statements, lib, level)
} }
} }

View File

@ -7,6 +7,80 @@ use std::prelude::v1::*;
use std::num::{NonZeroU64, NonZeroUsize}; use std::num::{NonZeroU64, NonZeroUsize};
/// _(internals)_ A type containing all the limits imposed by the [`Engine`].
/// Exported under the `internals` feature only.
///
/// Not available under `unchecked`.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Limits {
/// Maximum levels of call-stack to prevent infinite recursion.
///
/// Set to zero to effectively disable function calls.
///
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))]
pub max_call_stack_depth: usize,
/// Maximum depth of statements/expressions at global level.
pub max_expr_depth: Option<NonZeroUsize>,
/// Maximum depth of statements/expressions in functions.
///
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))]
pub max_function_expr_depth: Option<NonZeroUsize>,
/// Maximum number of operations allowed to run.
pub max_operations: Option<std::num::NonZeroU64>,
/// Maximum number of [modules][Module] allowed to load.
///
/// Set to zero to effectively disable loading any [module][Module].
///
/// Not available under `no_module`.
#[cfg(not(feature = "no_module"))]
pub max_modules: usize,
/// Maximum length of a [string][ImmutableString].
pub max_string_size: Option<NonZeroUsize>,
/// Maximum length of an [array][Array].
///
/// Not available under `no_index`.
#[cfg(not(feature = "no_index"))]
pub max_array_size: Option<NonZeroUsize>,
/// Maximum number of properties in an [object map][Map].
///
/// Not available under `no_object`.
#[cfg(not(feature = "no_object"))]
pub max_map_size: Option<NonZeroUsize>,
}
impl Limits {
/// Create a new [`Limits`] with default values.
///
/// Not available under `unchecked`.
#[inline]
pub const fn new() -> Self {
Self {
#[cfg(not(feature = "no_function"))]
max_call_stack_depth: crate::engine::MAX_CALL_STACK_DEPTH,
max_expr_depth: NonZeroUsize::new(crate::engine::MAX_EXPR_DEPTH),
#[cfg(not(feature = "no_function"))]
max_function_expr_depth: NonZeroUsize::new(crate::engine::MAX_FUNCTION_EXPR_DEPTH),
max_operations: None,
#[cfg(not(feature = "no_module"))]
max_modules: usize::MAX,
max_string_size: None,
#[cfg(not(feature = "no_index"))]
max_array_size: None,
#[cfg(not(feature = "no_object"))]
max_map_size: None,
}
}
}
impl Default for Limits {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
impl Engine { impl Engine {
/// Set the maximum levels of function calls allowed for a script in order to avoid /// Set the maximum levels of function calls allowed for a script in order to avoid
/// infinite recursion and stack overflows. /// infinite recursion and stack overflows.

View File

@ -75,7 +75,7 @@ impl Engine {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
let lib = ast let lib = ast
.lib() .shared_lib()
.iter_fn() .iter_fn()
.filter(|f| f.func.is_script()) .filter(|f| f.func.is_script())
.map(|f| { .map(|f| {

View File

@ -64,7 +64,7 @@ impl Engine {
let statements = ast.statements(); let statements = ast.statements();
if !statements.is_empty() { if !statements.is_empty() {
let lib = &[ast.lib()]; let lib = &[ast.as_ref()];
self.eval_global_statements(scope, mods, &mut state, statements, lib, 0)?; self.eval_global_statements(scope, mods, &mut state, statements, lib, 0)?;
} }
Ok(()) Ok(())

View File

@ -302,8 +302,8 @@ impl AST {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub(crate) fn shared_lib(&self) -> Shared<Module> { pub(crate) fn shared_lib(&self) -> &Shared<Module> {
self.functions.clone() &self.functions
} }
/// _(internals)_ Get the internal shared [`Module`] containing all script-defined functions. /// _(internals)_ Get the internal shared [`Module`] containing all script-defined functions.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
@ -314,24 +314,7 @@ impl AST {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn shared_lib(&self) -> Shared<Module> { pub fn shared_lib(&self) -> &Shared<Module> {
self.functions.clone()
}
/// Get the internal [`Module`] containing all script-defined functions.
#[cfg(not(feature = "internals"))]
#[inline(always)]
#[must_use]
pub(crate) fn lib(&self) -> &Module {
&self.functions
}
/// _(internals)_ Get the internal [`Module`] containing all script-defined functions.
/// Exported under the `internals` feature only.
///
/// Not available under `no_function`.
#[cfg(feature = "internals")]
#[inline(always)]
#[must_use]
pub fn lib(&self) -> &Module {
&self.functions &self.functions
} }
/// Get the embedded [module resolver][`ModuleResolver`]. /// Get the embedded [module resolver][`ModuleResolver`].
@ -895,7 +878,14 @@ impl AsRef<[Stmt]> for AST {
impl AsRef<Module> for AST { impl AsRef<Module> for AST {
#[inline(always)] #[inline(always)]
fn as_ref(&self) -> &Module { fn as_ref(&self) -> &Module {
self.lib() self.shared_lib().as_ref()
}
}
impl AsRef<Shared<Module>> for AST {
#[inline(always)]
fn as_ref(&self) -> &Shared<Module> {
self.shared_lib()
} }
} }

View File

@ -65,7 +65,9 @@ pub struct Imports {
pub num_operations: u64, pub num_operations: u64,
/// Number of modules loaded. /// Number of modules loaded.
pub num_modules: usize, pub num_modules: usize,
/// Function call hashes to FN_IDX_GET and FN_IDX_SET. /// Function call hashes to index getters and setters.
///
/// Not available under `no_index` and `no_object`.
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
fn_hash_indexing: (u64, u64), fn_hash_indexing: (u64, u64),
/// Embedded module resolver. /// Embedded module resolver.
@ -74,11 +76,20 @@ pub struct Imports {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
pub embedded_module_resolver: Option<Shared<crate::module::resolvers::StaticModuleResolver>>, pub embedded_module_resolver: Option<Shared<crate::module::resolvers::StaticModuleResolver>>,
/// Cache of globally-defined constants. /// Cache of globally-defined constants.
///
/// Not available under `no_module` and `no_function`.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
global_constants: Option<Shared<crate::Locked<BTreeMap<Identifier, Dynamic>>>>, global_constants: Option<Shared<crate::Locked<BTreeMap<Identifier, Dynamic>>>>,
} }
impl Default for Imports {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
impl Imports { impl Imports {
/// Create a new stack of imported [modules][Module]. /// Create a new stack of imported [modules][Module].
#[inline(always)] #[inline(always)]
@ -129,12 +140,15 @@ impl Imports {
#[must_use] #[must_use]
pub fn find(&self, name: impl AsRef<str>) -> Option<usize> { pub fn find(&self, name: impl AsRef<str>) -> Option<usize> {
let name = name.as_ref(); let name = name.as_ref();
let len = self.keys.len();
self.keys self.keys.iter().rev().enumerate().find_map(|(i, key)| {
.iter() if key == name {
.enumerate() Some(len - 1 - i)
.rev() } else {
.find_map(|(i, key)| if key == name { Some(i) } else { None }) None
}
})
} }
/// Push an imported [module][Module] onto the stack. /// Push an imported [module][Module] onto the stack.
#[inline(always)] #[inline(always)]
@ -154,8 +168,8 @@ impl Imports {
pub fn iter(&self) -> impl Iterator<Item = (&str, &Module)> { pub fn iter(&self) -> impl Iterator<Item = (&str, &Module)> {
self.keys self.keys
.iter() .iter()
.zip(self.modules.iter())
.rev() .rev()
.zip(self.modules.iter().rev())
.map(|(name, module)| (name.as_str(), module.as_ref())) .map(|(name, module)| (name.as_str(), module.as_ref()))
} }
/// Get an iterator to this stack of imported [modules][Module] in reverse order. /// Get an iterator to this stack of imported [modules][Module] in reverse order.
@ -812,69 +826,6 @@ impl EvalState {
} }
} }
/// _(internals)_ A type containing all the limits imposed by the [`Engine`].
/// Exported under the `internals` feature only.
#[cfg(not(feature = "unchecked"))]
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Limits {
/// Maximum levels of call-stack to prevent infinite recursion.
///
/// Set to zero to effectively disable function calls.
///
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))]
pub max_call_stack_depth: usize,
/// Maximum depth of statements/expressions at global level.
pub max_expr_depth: Option<NonZeroUsize>,
/// Maximum depth of statements/expressions in functions.
///
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))]
pub max_function_expr_depth: Option<NonZeroUsize>,
/// Maximum number of operations allowed to run.
pub max_operations: Option<std::num::NonZeroU64>,
/// Maximum number of [modules][Module] allowed to load.
///
/// Set to zero to effectively disable loading any [module][Module].
///
/// Not available under `no_module`.
#[cfg(not(feature = "no_module"))]
pub max_modules: usize,
/// Maximum length of a [string][ImmutableString].
pub max_string_size: Option<NonZeroUsize>,
/// Maximum length of an [array][Array].
///
/// Not available under `no_index`.
#[cfg(not(feature = "no_index"))]
pub max_array_size: Option<NonZeroUsize>,
/// Maximum number of properties in an [object map][Map].
///
/// Not available under `no_object`.
#[cfg(not(feature = "no_object"))]
pub max_map_size: Option<NonZeroUsize>,
}
#[cfg(not(feature = "unchecked"))]
impl Limits {
pub const fn new() -> Self {
Self {
#[cfg(not(feature = "no_function"))]
max_call_stack_depth: MAX_CALL_STACK_DEPTH,
max_expr_depth: NonZeroUsize::new(MAX_EXPR_DEPTH),
#[cfg(not(feature = "no_function"))]
max_function_expr_depth: NonZeroUsize::new(MAX_FUNCTION_EXPR_DEPTH),
max_operations: None,
#[cfg(not(feature = "no_module"))]
max_modules: usize::MAX,
max_string_size: None,
#[cfg(not(feature = "no_index"))]
max_array_size: None,
#[cfg(not(feature = "no_object"))]
max_map_size: None,
}
}
}
/// Context of a script evaluation process. /// Context of a script evaluation process.
#[derive(Debug)] #[derive(Debug)]
pub struct EvalContext<'a, 'x, 'px, 'm, 's, 'b, 't, 'pt> { pub struct EvalContext<'a, 'x, 'px, 'm, 's, 'b, 't, 'pt> {
@ -1024,7 +975,7 @@ pub struct Engine {
/// Max limits. /// Max limits.
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
pub(crate) limits: Limits, pub(crate) limits: crate::api::limits::Limits,
} }
impl fmt::Debug for Engine { impl fmt::Debug for Engine {
@ -1146,7 +1097,7 @@ impl Engine {
optimization_level: crate::OptimizationLevel::default(), optimization_level: crate::OptimizationLevel::default(),
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
limits: Limits::new(), limits: crate::api::limits::Limits::new(),
}; };
// Add the global namespace module // Add the global namespace module

View File

@ -886,7 +886,7 @@ impl Engine {
)?; )?;
// If new functions are defined within the eval string, it is an error // If new functions are defined within the eval string, it is an error
if !ast.lib().is_empty() { if !ast.shared_lib().is_empty() {
return Err(ParseErrorType::WrongFnDefinition.into()); return Err(ParseErrorType::WrongFnDefinition.into());
} }

View File

@ -190,13 +190,6 @@ impl fmt::Debug for Module {
} }
} }
impl AsRef<Module> for Module {
#[inline(always)]
fn as_ref(&self) -> &Module {
self
}
}
impl<M: AsRef<Module>> Add<M> for &Module { impl<M: AsRef<Module>> Add<M> for &Module {
type Output = Module; type Output = Module;
@ -1467,8 +1460,8 @@ impl Module {
// Non-private functions defined become module functions // Non-private functions defined become module functions
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
if !ast.lib().functions.is_empty() { if !ast.shared_lib().functions.is_empty() {
ast.lib() ast.shared_lib()
.functions .functions
.values() .values()
.filter(|f| match f.access { .filter(|f| match f.access {
@ -1484,7 +1477,7 @@ impl Module {
.expect("scripted function") .expect("scripted function")
.as_ref() .as_ref()
.clone(); .clone();
func.lib = Some(ast.shared_lib()); func.lib = Some(ast.shared_lib().clone());
func.mods = func_mods.clone(); func.mods = func_mods.clone();
module.set_script_fn(func); module.set_script_fn(func);
}); });
@ -1728,6 +1721,17 @@ impl DerefMut for NamespaceRef {
} }
} }
impl From<Vec<Ident>> for NamespaceRef {
#[inline(always)]
fn from(mut path: Vec<Ident>) -> Self {
path.shrink_to_fit();
Self {
index: None,
path: path.into(),
}
}
}
impl From<StaticVec<Ident>> for NamespaceRef { impl From<StaticVec<Ident>> for NamespaceRef {
#[inline(always)] #[inline(always)]
fn from(mut path: StaticVec<Ident>) -> Self { fn from(mut path: StaticVec<Ident>) -> Self {

View File

@ -728,8 +728,9 @@ mod array_functions {
} }
let mut result = initial; let mut result = initial;
let len = array.len();
for (i, item) in array.iter().enumerate().rev() { for (i, item) in array.iter().rev().enumerate() {
let item = item.clone(); let item = item.clone();
result = reducer result = reducer
@ -738,7 +739,11 @@ mod array_functions {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(reducer.fn_name()) => if fn_sig.starts_with(reducer.fn_name()) =>
{ {
reducer.call_dynamic(&ctx, None, [result, item, (i as INT).into()]) reducer.call_dynamic(
&ctx,
None,
[result, item, ((len - 1 - i) as INT).into()],
)
} }
_ => Err(err), _ => Err(err),
}) })

View File

@ -301,22 +301,21 @@ impl<'a> Scope<'a> {
#[inline] #[inline]
#[must_use] #[must_use]
pub fn contains(&self, name: &str) -> bool { pub fn contains(&self, name: &str) -> bool {
self.names self.names.iter().any(|(key, _)| name == key.as_ref())
.iter()
.rev() // Always search a Scope in reverse order
.any(|(key, _)| name == key.as_ref())
} }
/// Find an entry in the [`Scope`], starting from the last. /// Find an entry in the [`Scope`], starting from the last.
#[inline] #[inline]
#[must_use] #[must_use]
pub(crate) fn get_index(&self, name: &str) -> Option<(usize, AccessMode)> { pub(crate) fn get_index(&self, name: &str) -> Option<(usize, AccessMode)> {
let len = self.len();
self.names self.names
.iter() .iter()
.rev() // Always search a Scope in reverse order .rev() // Always search a Scope in reverse order
.enumerate() .enumerate()
.find_map(|(index, (key, _))| { .find_map(|(i, (key, _))| {
if name == key.as_ref() { if name == key.as_ref() {
let index = self.len() - 1 - index; let index = len - 1 - i;
Some((index, self.values[index].access_mode())) Some((index, self.values[index].access_mode()))
} else { } else {
None None
@ -338,16 +337,14 @@ impl<'a> Scope<'a> {
#[inline] #[inline]
#[must_use] #[must_use]
pub fn get_value<T: Variant + Clone>(&self, name: &str) -> Option<T> { pub fn get_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
let len = self.len();
self.names self.names
.iter() .iter()
.rev() .rev()
.enumerate() .enumerate()
.find(|(_, (key, _))| name == key.as_ref()) .find(|(_, (key, _))| name == key.as_ref())
.and_then(|(index, _)| { .and_then(|(index, _)| self.values[len - 1 - index].flatten_clone().try_cast())
self.values[self.len() - 1 - index]
.flatten_clone()
.try_cast()
})
} }
/// Check if the named entry in the [`Scope`] is constant. /// Check if the named entry in the [`Scope`] is constant.
/// ///
@ -522,14 +519,14 @@ impl<'a> Scope<'a> {
#[inline] #[inline]
#[must_use] #[must_use]
pub fn clone_visible(&self) -> Self { pub fn clone_visible(&self) -> Self {
let len = self.len();
self.names.iter().rev().enumerate().fold( self.names.iter().rev().enumerate().fold(
Self::new(), Self::new(),
|mut entries, (index, (name, alias))| { |mut entries, (index, (name, alias))| {
if !entries.names.iter().any(|(key, _)| key == name) { if !entries.names.iter().any(|(key, _)| key == name) {
entries.names.push((name.clone(), alias.clone())); entries.names.push((name.clone(), alias.clone()));
entries entries.values.push(self.values[len - 1 - index].clone());
.values
.push(self.values[self.len() - 1 - index].clone());
} }
entries entries
}, },