diff --git a/src/engine.rs b/src/engine.rs index 41fd4242..75f44be0 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -28,274 +28,6 @@ use std::{ pub type Precedence = NonZeroU8; -/// _(internals)_ A stack of imported [modules][Module] plus mutable global runtime states. -/// Exported under the `internals` feature only. -// -// # Implementation Notes -// -// This implementation splits the module names from the shared modules to improve data locality. -// Most usage will be looking up a particular key from the list and then getting the module that -// corresponds to that key. -#[derive(Clone)] -pub struct GlobalRuntimeState { - /// Stack of module names. - // - // # Implementation Notes - // - // We cannot use Cow here because `eval` may load a [module][Module] and - // the module name will live beyond the AST of the eval script text. - keys: StaticVec, - /// Stack of imported modules. - modules: StaticVec>, - - /// Source of the current context. - pub source: Option, - /// Number of operations performed. - pub num_operations: u64, - /// Number of modules loaded. - pub num_modules_loaded: usize, - - /// 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")))] - fn_hash_indexing: (u64, u64), - /// Embedded module resolver. - /// - /// Not available under `no_module`. - #[cfg(not(feature = "no_module"))] - pub embedded_module_resolver: Option>, - - /// Cache of globally-defined constants. - /// - /// Not available under `no_module` and `no_function`. - #[cfg(not(feature = "no_module"))] - #[cfg(not(feature = "no_function"))] - constants: Option>>>, -} - -impl Default for GlobalRuntimeState { - #[inline(always)] - fn default() -> Self { - Self::new() - } -} - -impl GlobalRuntimeState { - /// Create a new [`GlobalRuntimeState`]. - #[inline(always)] - #[must_use] - pub const fn new() -> Self { - Self { - keys: StaticVec::new_const(), - modules: StaticVec::new_const(), - source: None, - num_operations: 0, - num_modules_loaded: 0, - #[cfg(not(feature = "no_module"))] - embedded_module_resolver: None, - #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - fn_hash_indexing: (0, 0), - #[cfg(not(feature = "no_module"))] - #[cfg(not(feature = "no_function"))] - constants: None, - } - } - /// Get the length of the stack of globally-imported [modules][Module]. - #[inline(always)] - #[must_use] - pub fn num_imported_modules(&self) -> usize { - self.keys.len() - } - /// Get the globally-imported [module][Module] at a particular index. - #[inline(always)] - #[must_use] - pub fn get_shared_module(&self, index: usize) -> Option> { - self.modules.get(index).cloned() - } - /// Get a mutable reference to the globally-imported [module][Module] at a particular index. - #[allow(dead_code)] - #[inline(always)] - #[must_use] - pub(crate) fn get_shared_module_mut(&mut self, index: usize) -> Option<&mut Shared> { - self.modules.get_mut(index) - } - /// Get the index of a globally-imported [module][Module] by name. - #[inline] - #[must_use] - pub fn find_module(&self, name: impl AsRef) -> Option { - let name = name.as_ref(); - let len = self.keys.len(); - - self.keys.iter().rev().enumerate().find_map(|(i, key)| { - if key == name { - Some(len - 1 - i) - } else { - None - } - }) - } - /// Push an imported [module][Module] onto the stack. - #[inline(always)] - pub fn push_module(&mut self, name: impl Into, module: impl Into>) { - self.keys.push(name.into()); - self.modules.push(module.into()); - } - /// Truncate the stack of globally-imported [modules][Module] to a particular length. - #[inline(always)] - pub fn truncate_modules(&mut self, size: usize) { - self.keys.truncate(size); - self.modules.truncate(size); - } - /// Get an iterator to the stack of globally-imported [modules][Module] in reverse order. - #[allow(dead_code)] - #[inline] - pub fn iter_modules(&self) -> impl Iterator { - self.keys - .iter() - .rev() - .zip(self.modules.iter().rev()) - .map(|(name, module)| (name.as_str(), module.as_ref())) - } - /// Get an iterator to the stack of globally-imported [modules][Module] in reverse order. - #[allow(dead_code)] - #[inline] - pub(crate) fn iter_modules_raw(&self) -> impl Iterator)> { - self.keys.iter().rev().zip(self.modules.iter().rev()) - } - /// Get an iterator to the stack of globally-imported [modules][Module] in forward order. - #[allow(dead_code)] - #[inline] - pub(crate) fn scan_modules_raw(&self) -> impl Iterator)> { - self.keys.iter().zip(self.modules.iter()) - } - /// Does the specified function hash key exist in the stack of globally-imported [modules][Module]? - #[allow(dead_code)] - #[inline] - #[must_use] - pub fn contains_fn(&self, hash: u64) -> bool { - self.modules.iter().any(|m| m.contains_qualified_fn(hash)) - } - /// Get the specified function via its hash key from the stack of globally-imported [modules][Module]. - #[inline] - #[must_use] - pub fn get_fn(&self, hash: u64) -> Option<(&CallableFunction, Option<&Identifier>)> { - self.modules - .iter() - .rev() - .find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw()))) - } - /// Does the specified [`TypeId`][std::any::TypeId] iterator exist in the stack of - /// globally-imported [modules][Module]? - #[allow(dead_code)] - #[inline] - #[must_use] - pub fn contains_iter(&self, id: TypeId) -> bool { - self.modules.iter().any(|m| m.contains_qualified_iter(id)) - } - /// Get the specified [`TypeId`][std::any::TypeId] iterator from the stack of globally-imported - /// [modules][Module]. - #[inline] - #[must_use] - pub fn get_iter(&self, id: TypeId) -> Option { - self.modules - .iter() - .rev() - .find_map(|m| m.get_qualified_iter(id)) - } - /// Get a mutable reference to the cache of globally-defined constants. - #[cfg(not(feature = "no_module"))] - #[cfg(not(feature = "no_function"))] - #[must_use] - pub(crate) fn constants_mut<'a>( - &'a mut self, - ) -> Option> + 'a> { - if let Some(ref global_constants) = self.constants { - Some(crate::func::native::shared_write_lock(global_constants)) - } else { - None - } - } - /// Set a constant into the cache of globally-defined constants. - #[cfg(not(feature = "no_module"))] - #[cfg(not(feature = "no_function"))] - pub(crate) fn set_constant(&mut self, name: impl Into, value: Dynamic) { - if self.constants.is_none() { - let dict: crate::Locked<_> = BTreeMap::new().into(); - self.constants = Some(dict.into()); - } - - crate::func::native::shared_write_lock(self.constants.as_mut().expect("`Some`")) - .insert(name.into(), value); - } - /// Get the pre-calculated index getter hash. - #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - #[must_use] - pub(crate) fn hash_idx_get(&mut self) -> u64 { - if self.fn_hash_indexing != (0, 0) { - self.fn_hash_indexing.0 - } else { - let n1 = crate::calc_fn_hash(FN_IDX_GET, 2); - let n2 = crate::calc_fn_hash(FN_IDX_SET, 3); - self.fn_hash_indexing = (n1, n2); - n1 - } - } - /// Get the pre-calculated index setter hash. - #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - #[must_use] - pub(crate) fn hash_idx_set(&mut self) -> u64 { - if self.fn_hash_indexing != (0, 0) { - self.fn_hash_indexing.1 - } else { - let n1 = crate::calc_fn_hash(FN_IDX_GET, 2); - let n2 = crate::calc_fn_hash(FN_IDX_SET, 3); - self.fn_hash_indexing = (n1, n2); - n2 - } - } -} - -impl IntoIterator for GlobalRuntimeState { - type Item = (Identifier, Shared); - type IntoIter = - Zip>, Rev; 3]>>>; - - #[inline] - fn into_iter(self) -> Self::IntoIter { - self.keys - .into_iter() - .rev() - .zip(self.modules.into_iter().rev()) - } -} - -impl, M: Into>> FromIterator<(K, M)> for GlobalRuntimeState { - fn from_iter>(iter: T) -> Self { - let mut lib = Self::new(); - lib.extend(iter); - lib - } -} - -impl, M: Into>> Extend<(K, M)> for GlobalRuntimeState { - fn extend>(&mut self, iter: T) { - iter.into_iter().for_each(|(k, m)| { - self.keys.push(k.into()); - self.modules.push(m.into()); - }) - } -} - -impl fmt::Debug for GlobalRuntimeState { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Imports")?; - f.debug_map() - .entries(self.keys.iter().zip(self.modules.iter())) - .finish() - } -} - pub const KEYWORD_PRINT: &str = "print"; pub const KEYWORD_DEBUG: &str = "debug"; pub const KEYWORD_TYPE_OF: &str = "type_of"; @@ -757,6 +489,274 @@ impl> From for Target<'_> { } } +/// _(internals)_ A stack of imported [modules][Module] plus mutable global runtime states. +/// Exported under the `internals` feature only. +// +// # Implementation Notes +// +// This implementation splits the module names from the shared modules to improve data locality. +// Most usage will be looking up a particular key from the list and then getting the module that +// corresponds to that key. +#[derive(Clone)] +pub struct GlobalRuntimeState { + /// Stack of module names. + // + // # Implementation Notes + // + // We cannot use Cow here because `eval` may load a [module][Module] and + // the module name will live beyond the AST of the eval script text. + keys: StaticVec, + /// Stack of imported modules. + modules: StaticVec>, + + /// Source of the current context. + pub source: Option, + /// Number of operations performed. + pub num_operations: u64, + /// Number of modules loaded. + pub num_modules_loaded: usize, + + /// 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")))] + fn_hash_indexing: (u64, u64), + /// Embedded module resolver. + /// + /// Not available under `no_module`. + #[cfg(not(feature = "no_module"))] + pub embedded_module_resolver: Option>, + + /// Cache of globally-defined constants. + /// + /// Not available under `no_module` and `no_function`. + #[cfg(not(feature = "no_module"))] + #[cfg(not(feature = "no_function"))] + constants: Option>>>, +} + +impl Default for GlobalRuntimeState { + #[inline(always)] + fn default() -> Self { + Self::new() + } +} + +impl GlobalRuntimeState { + /// Create a new [`GlobalRuntimeState`]. + #[inline(always)] + #[must_use] + pub const fn new() -> Self { + Self { + keys: StaticVec::new_const(), + modules: StaticVec::new_const(), + source: None, + num_operations: 0, + num_modules_loaded: 0, + #[cfg(not(feature = "no_module"))] + embedded_module_resolver: None, + #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] + fn_hash_indexing: (0, 0), + #[cfg(not(feature = "no_module"))] + #[cfg(not(feature = "no_function"))] + constants: None, + } + } + /// Get the length of the stack of globally-imported [modules][Module]. + #[inline(always)] + #[must_use] + pub fn num_imported_modules(&self) -> usize { + self.keys.len() + } + /// Get the globally-imported [module][Module] at a particular index. + #[inline(always)] + #[must_use] + pub fn get_shared_module(&self, index: usize) -> Option> { + self.modules.get(index).cloned() + } + /// Get a mutable reference to the globally-imported [module][Module] at a particular index. + #[allow(dead_code)] + #[inline(always)] + #[must_use] + pub(crate) fn get_shared_module_mut(&mut self, index: usize) -> Option<&mut Shared> { + self.modules.get_mut(index) + } + /// Get the index of a globally-imported [module][Module] by name. + #[inline] + #[must_use] + pub fn find_module(&self, name: impl AsRef) -> Option { + let name = name.as_ref(); + let len = self.keys.len(); + + self.keys.iter().rev().enumerate().find_map(|(i, key)| { + if key == name { + Some(len - 1 - i) + } else { + None + } + }) + } + /// Push an imported [module][Module] onto the stack. + #[inline(always)] + pub fn push_module(&mut self, name: impl Into, module: impl Into>) { + self.keys.push(name.into()); + self.modules.push(module.into()); + } + /// Truncate the stack of globally-imported [modules][Module] to a particular length. + #[inline(always)] + pub fn truncate_modules(&mut self, size: usize) { + self.keys.truncate(size); + self.modules.truncate(size); + } + /// Get an iterator to the stack of globally-imported [modules][Module] in reverse order. + #[allow(dead_code)] + #[inline] + pub fn iter_modules(&self) -> impl Iterator { + self.keys + .iter() + .rev() + .zip(self.modules.iter().rev()) + .map(|(name, module)| (name.as_str(), module.as_ref())) + } + /// Get an iterator to the stack of globally-imported [modules][Module] in reverse order. + #[allow(dead_code)] + #[inline] + pub(crate) fn iter_modules_raw(&self) -> impl Iterator)> { + self.keys.iter().rev().zip(self.modules.iter().rev()) + } + /// Get an iterator to the stack of globally-imported [modules][Module] in forward order. + #[allow(dead_code)] + #[inline] + pub(crate) fn scan_modules_raw(&self) -> impl Iterator)> { + self.keys.iter().zip(self.modules.iter()) + } + /// Does the specified function hash key exist in the stack of globally-imported [modules][Module]? + #[allow(dead_code)] + #[inline] + #[must_use] + pub fn contains_fn(&self, hash: u64) -> bool { + self.modules.iter().any(|m| m.contains_qualified_fn(hash)) + } + /// Get the specified function via its hash key from the stack of globally-imported [modules][Module]. + #[inline] + #[must_use] + pub fn get_fn(&self, hash: u64) -> Option<(&CallableFunction, Option<&Identifier>)> { + self.modules + .iter() + .rev() + .find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw()))) + } + /// Does the specified [`TypeId`][std::any::TypeId] iterator exist in the stack of + /// globally-imported [modules][Module]? + #[allow(dead_code)] + #[inline] + #[must_use] + pub fn contains_iter(&self, id: TypeId) -> bool { + self.modules.iter().any(|m| m.contains_qualified_iter(id)) + } + /// Get the specified [`TypeId`][std::any::TypeId] iterator from the stack of globally-imported + /// [modules][Module]. + #[inline] + #[must_use] + pub fn get_iter(&self, id: TypeId) -> Option { + self.modules + .iter() + .rev() + .find_map(|m| m.get_qualified_iter(id)) + } + /// Get a mutable reference to the cache of globally-defined constants. + #[cfg(not(feature = "no_module"))] + #[cfg(not(feature = "no_function"))] + #[must_use] + pub(crate) fn constants_mut<'a>( + &'a mut self, + ) -> Option> + 'a> { + if let Some(ref global_constants) = self.constants { + Some(crate::func::native::shared_write_lock(global_constants)) + } else { + None + } + } + /// Set a constant into the cache of globally-defined constants. + #[cfg(not(feature = "no_module"))] + #[cfg(not(feature = "no_function"))] + pub(crate) fn set_constant(&mut self, name: impl Into, value: Dynamic) { + if self.constants.is_none() { + let dict: crate::Locked<_> = BTreeMap::new().into(); + self.constants = Some(dict.into()); + } + + crate::func::native::shared_write_lock(self.constants.as_mut().expect("`Some`")) + .insert(name.into(), value); + } + /// Get the pre-calculated index getter hash. + #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] + #[must_use] + pub(crate) fn hash_idx_get(&mut self) -> u64 { + if self.fn_hash_indexing != (0, 0) { + self.fn_hash_indexing.0 + } else { + let n1 = crate::calc_fn_hash(FN_IDX_GET, 2); + let n2 = crate::calc_fn_hash(FN_IDX_SET, 3); + self.fn_hash_indexing = (n1, n2); + n1 + } + } + /// Get the pre-calculated index setter hash. + #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] + #[must_use] + pub(crate) fn hash_idx_set(&mut self) -> u64 { + if self.fn_hash_indexing != (0, 0) { + self.fn_hash_indexing.1 + } else { + let n1 = crate::calc_fn_hash(FN_IDX_GET, 2); + let n2 = crate::calc_fn_hash(FN_IDX_SET, 3); + self.fn_hash_indexing = (n1, n2); + n2 + } + } +} + +impl IntoIterator for GlobalRuntimeState { + type Item = (Identifier, Shared); + type IntoIter = + Zip>, Rev; 3]>>>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.keys + .into_iter() + .rev() + .zip(self.modules.into_iter().rev()) + } +} + +impl, M: Into>> FromIterator<(K, M)> for GlobalRuntimeState { + fn from_iter>(iter: T) -> Self { + let mut lib = Self::new(); + lib.extend(iter); + lib + } +} + +impl, M: Into>> Extend<(K, M)> for GlobalRuntimeState { + fn extend>(&mut self, iter: T) { + iter.into_iter().for_each(|(k, m)| { + self.keys.push(k.into()); + self.modules.push(m.into()); + }) + } +} + +impl fmt::Debug for GlobalRuntimeState { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Imports")?; + f.debug_map() + .entries(self.keys.iter().zip(self.modules.iter())) + .finish() + } +} + /// _(internals)_ An entry in a function resolution cache. /// Exported under the `internals` feature only. #[derive(Debug, Clone)] @@ -764,7 +764,7 @@ pub struct FnResolutionCacheEntry { /// Function. pub func: CallableFunction, /// Optional source. - pub source: Option, + pub source: Option>, } /// _(internals)_ A function resolution cache. diff --git a/src/func/call.rs b/src/func/call.rs index 7b43937b..24dabc9d 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -206,14 +206,14 @@ impl Engine { .find_map(|m| { m.get_fn(hash).cloned().map(|func| FnResolutionCacheEntry { func, - source: m.id_raw().cloned(), + source: m.id_raw().map(|s| s.to_string().into_boxed_str()), }) }) .or_else(|| { self.global_modules.iter().find_map(|m| { m.get_fn(hash).cloned().map(|func| FnResolutionCacheEntry { func, - source: m.id_raw().cloned(), + source: m.id_raw().map(|s| s.to_string().into_boxed_str()), }) }) }) @@ -222,7 +222,7 @@ impl Engine { .get_fn(hash) .map(|(func, source)| FnResolutionCacheEntry { func: func.clone(), - source: source.cloned(), + source: source.map(|s| s.to_string().into_boxed_str()), }) }) .or_else(|| { @@ -230,7 +230,7 @@ impl Engine { m.get_qualified_fn(hash).cloned().map(|func| { FnResolutionCacheEntry { func, - source: m.id_raw().cloned(), + source: m.id_raw().map(|s| s.to_string().into_boxed_str()), } }) }) @@ -355,8 +355,8 @@ impl Engine { // Run external function let source = source .as_ref() - .or_else(|| parent_source.as_ref()) - .map(|s| s.as_str()); + .map(|s| &**s) + .or_else(|| parent_source.as_ref().map(|s| s.as_str())); let context = (self, name, source, &*global, lib, pos).into(); @@ -608,7 +608,7 @@ impl Engine { let (first_arg, rest_args) = args.split_first_mut().expect("not empty"); let orig_source = global.source.take(); - global.source = source; + global.source = source.map(Into::into); let level = _level + 1; @@ -642,7 +642,7 @@ impl Engine { } let orig_source = global.source.take(); - global.source = source; + global.source = source.map(Into::into); let level = _level + 1;