Optimize Scope.
This commit is contained in:
parent
3485f9b00b
commit
c55fc5a9a5
@ -2070,7 +2070,7 @@ impl Engine {
|
||||
} else {
|
||||
unsafe_cast_var_name_to_lifetime(&var_def.name).into()
|
||||
};
|
||||
scope.push_dynamic_value(var_name, entry_type, val, false);
|
||||
scope.push_dynamic_value(var_name, entry_type, val);
|
||||
Ok(Default::default())
|
||||
}
|
||||
|
||||
|
@ -36,10 +36,6 @@ use crate::engine::{Map, Target, FN_GET, FN_SET};
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
use crate::engine::KEYWORD_IS_SHARED;
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
use crate::scope::Entry as ScopeEntry;
|
||||
|
||||
use crate::stdlib::{
|
||||
any::{type_name, TypeId},
|
||||
boxed::Box,
|
||||
@ -553,22 +549,14 @@ impl Engine {
|
||||
if let Some(captured) = _capture_scope {
|
||||
captured
|
||||
.into_iter()
|
||||
.filter(|ScopeEntry { name, .. }| {
|
||||
func.externals.contains(name.as_ref())
|
||||
})
|
||||
.for_each(
|
||||
|ScopeEntry {
|
||||
name, typ, value, ..
|
||||
}| {
|
||||
// Consume the scope values.
|
||||
match typ {
|
||||
ScopeEntryType::Normal => scope.push(name, value),
|
||||
ScopeEntryType::Constant => {
|
||||
scope.push_constant(name, value)
|
||||
}
|
||||
};
|
||||
},
|
||||
);
|
||||
.filter(|(name, _, _, _)| func.externals.contains(name.as_ref()))
|
||||
.for_each(|(name, typ, value, _)| {
|
||||
// Consume the scope values.
|
||||
match typ {
|
||||
ScopeEntryType::Normal => scope.push(name, value),
|
||||
ScopeEntryType::Constant => scope.push_constant(name, value),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
let result = if _is_method {
|
||||
|
@ -16,7 +16,7 @@ use crate::{ast::ScriptFnDef, fn_native::Shared};
|
||||
use crate::{
|
||||
ast::AST,
|
||||
engine::{Engine, Imports},
|
||||
scope::{Entry as ScopeEntry, Scope},
|
||||
scope::Scope,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
@ -1348,14 +1348,12 @@ impl Module {
|
||||
// Create new module
|
||||
let mut module = Module::new();
|
||||
|
||||
scope
|
||||
.into_iter()
|
||||
.for_each(|ScopeEntry { value, alias, .. }| {
|
||||
// Variables with an alias left in the scope become module variables
|
||||
if let Some(alias) = alias {
|
||||
module.variables.insert(*alias, value);
|
||||
}
|
||||
});
|
||||
scope.into_iter().for_each(|(_, _, value, alias)| {
|
||||
// Variables with an alias left in the scope become module variables
|
||||
if let Some(alias) = alias {
|
||||
module.variables.insert(alias, value);
|
||||
}
|
||||
});
|
||||
|
||||
// Modules left in the scope become sub-modules
|
||||
mods.into_iter().for_each(|(alias, m)| {
|
||||
|
@ -9,7 +9,7 @@ use crate::engine::{
|
||||
use crate::fn_call::run_builtin_binary_op;
|
||||
use crate::module::Module;
|
||||
use crate::parser::map_dynamic_to_expr;
|
||||
use crate::scope::{Entry as ScopeEntry, Scope};
|
||||
use crate::scope::Scope;
|
||||
use crate::token::{is_valid_identifier, Position};
|
||||
use crate::{calc_native_fn_hash, StaticVec};
|
||||
|
||||
@ -721,24 +721,15 @@ fn optimize(
|
||||
// Set up the state
|
||||
let mut state = State::new(engine, lib, level);
|
||||
|
||||
// Add constants from the scope into the state
|
||||
// Add constants from the scope that can be made into a literal into the state
|
||||
scope
|
||||
.to_iter()
|
||||
// Get all the constants that can be made into a constant literal.
|
||||
.filter(|ScopeEntry { typ, .. }| typ.is_constant())
|
||||
.for_each(
|
||||
|ScopeEntry {
|
||||
name, expr, value, ..
|
||||
}| {
|
||||
if let Some(val) = expr
|
||||
.as_ref()
|
||||
.map(|expr| expr.as_ref().clone())
|
||||
.or_else(|| map_dynamic_to_expr(value.clone(), Position::none()))
|
||||
{
|
||||
state.push_constant(name.as_ref(), val);
|
||||
}
|
||||
},
|
||||
);
|
||||
.iter()
|
||||
.filter(|(_, typ, _)| *typ)
|
||||
.for_each(|(name, _, value)| {
|
||||
if let Some(val) = map_dynamic_to_expr(value.clone(), Position::none()) {
|
||||
state.push_constant(name, val);
|
||||
}
|
||||
});
|
||||
|
||||
let orig_constants_len = state.constants.len();
|
||||
|
||||
|
182
src/scope.rs
182
src/scope.rs
@ -1,11 +1,8 @@
|
||||
//! Module that defines the `Scope` type representing a function call-stack scope.
|
||||
|
||||
use crate::ast::Expr;
|
||||
use crate::dynamic::{Dynamic, Variant};
|
||||
use crate::parser::map_dynamic_to_expr;
|
||||
use crate::token::Position;
|
||||
|
||||
use crate::stdlib::{borrow::Cow, boxed::Box, iter, string::String, vec::Vec};
|
||||
use crate::stdlib::{borrow::Cow, iter, string::String, vec::Vec};
|
||||
|
||||
/// Type of an entry in the Scope.
|
||||
#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
|
||||
@ -27,21 +24,6 @@ impl EntryType {
|
||||
}
|
||||
}
|
||||
|
||||
/// An entry in the Scope.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Entry<'a> {
|
||||
/// Name of the entry.
|
||||
pub name: Cow<'a, str>,
|
||||
/// Type of the entry.
|
||||
pub typ: EntryType,
|
||||
/// Current value of the entry.
|
||||
pub value: Dynamic,
|
||||
/// Alias of the entry.
|
||||
pub alias: Option<Box<String>>,
|
||||
/// A constant expression if the initial value matches one of the recognized types.
|
||||
pub expr: Option<Box<Expr>>,
|
||||
}
|
||||
|
||||
/// Type containing information about the current scope.
|
||||
/// Useful for keeping state between `Engine` evaluation runs.
|
||||
///
|
||||
@ -72,8 +54,25 @@ pub struct Entry<'a> {
|
||||
///
|
||||
/// When searching for entries, newly-added entries are found before similarly-named but older entries,
|
||||
/// allowing for automatic _shadowing_.
|
||||
//
|
||||
// # Implementation Notes
|
||||
//
|
||||
// `Scope` is implemented as three `Vec`'s of exactly the same length. Variables data (name, type, etc.)
|
||||
// is manually split into three equal-length arrays. That's because variable names take up the most space,
|
||||
// with `Cow<str>` being four words long, but in the vast majority of cases the name is NOT used to look up
|
||||
// a variable's value. Variable lookup is usually via direct index, by-passing the name altogether.
|
||||
//
|
||||
// Since `Dynamic` is reasonably small, packing it tightly improves cache locality when variables are accessed.
|
||||
// The variable type is packed separately into another array because it is even smaller.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Scope<'a>(Vec<Entry<'a>>);
|
||||
pub struct Scope<'a> {
|
||||
/// (Name, alias) of the entry.
|
||||
names: Vec<(Cow<'a, str>, Option<String>)>,
|
||||
/// Type of the entry.
|
||||
types: Vec<EntryType>,
|
||||
/// Current value of the entry.
|
||||
values: Vec<Dynamic>,
|
||||
}
|
||||
|
||||
impl<'a> Scope<'a> {
|
||||
/// Create a new Scope.
|
||||
@ -114,7 +113,9 @@ impl<'a> Scope<'a> {
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn clear(&mut self) -> &mut Self {
|
||||
self.0.clear();
|
||||
self.names.clear();
|
||||
self.types.clear();
|
||||
self.values.clear();
|
||||
self
|
||||
}
|
||||
|
||||
@ -133,7 +134,7 @@ impl<'a> Scope<'a> {
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
self.values.len()
|
||||
}
|
||||
|
||||
/// Is the Scope empty?
|
||||
@ -151,7 +152,7 @@ impl<'a> Scope<'a> {
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.len() == 0
|
||||
self.values.len() == 0
|
||||
}
|
||||
|
||||
/// Add (push) a new entry to the Scope.
|
||||
@ -172,7 +173,7 @@ impl<'a> Scope<'a> {
|
||||
name: K,
|
||||
value: T,
|
||||
) -> &mut Self {
|
||||
self.push_dynamic_value(name, EntryType::Normal, Dynamic::from(value), false)
|
||||
self.push_dynamic_value(name, EntryType::Normal, Dynamic::from(value))
|
||||
}
|
||||
|
||||
/// Add (push) a new `Dynamic` entry to the Scope.
|
||||
@ -189,7 +190,7 @@ impl<'a> Scope<'a> {
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn push_dynamic<K: Into<Cow<'a, str>>>(&mut self, name: K, value: Dynamic) -> &mut Self {
|
||||
self.push_dynamic_value(name, EntryType::Normal, value, false)
|
||||
self.push_dynamic_value(name, EntryType::Normal, value)
|
||||
}
|
||||
|
||||
/// Add (push) a new constant to the Scope.
|
||||
@ -216,7 +217,7 @@ impl<'a> Scope<'a> {
|
||||
name: K,
|
||||
value: T,
|
||||
) -> &mut Self {
|
||||
self.push_dynamic_value(name, EntryType::Constant, Dynamic::from(value), true)
|
||||
self.push_dynamic_value(name, EntryType::Constant, Dynamic::from(value))
|
||||
}
|
||||
|
||||
/// Add (push) a new constant with a `Dynamic` value to the Scope.
|
||||
@ -244,7 +245,7 @@ impl<'a> Scope<'a> {
|
||||
name: K,
|
||||
value: Dynamic,
|
||||
) -> &mut Self {
|
||||
self.push_dynamic_value(name, EntryType::Constant, value, true)
|
||||
self.push_dynamic_value(name, EntryType::Constant, value)
|
||||
}
|
||||
|
||||
/// Add (push) a new entry with a `Dynamic` value to the Scope.
|
||||
@ -254,22 +255,10 @@ impl<'a> Scope<'a> {
|
||||
name: K,
|
||||
entry_type: EntryType,
|
||||
value: Dynamic,
|
||||
map_expr: bool,
|
||||
) -> &mut Self {
|
||||
let expr = if map_expr {
|
||||
map_dynamic_to_expr(value.clone(), Position::none()).map(Box::new)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
self.0.push(Entry {
|
||||
name: name.into(),
|
||||
typ: entry_type,
|
||||
alias: None,
|
||||
value: value.into(),
|
||||
expr,
|
||||
});
|
||||
|
||||
self.names.push((name.into(), None));
|
||||
self.types.push(entry_type);
|
||||
self.values.push(value.into());
|
||||
self
|
||||
}
|
||||
|
||||
@ -301,7 +290,9 @@ impl<'a> Scope<'a> {
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn rewind(&mut self, size: usize) -> &mut Self {
|
||||
self.0.truncate(size);
|
||||
self.names.truncate(size);
|
||||
self.types.truncate(size);
|
||||
self.values.truncate(size);
|
||||
self
|
||||
}
|
||||
|
||||
@ -320,37 +311,28 @@ impl<'a> Scope<'a> {
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn contains(&self, name: &str) -> bool {
|
||||
self.0
|
||||
self.names
|
||||
.iter()
|
||||
.rev() // Always search a Scope in reverse order
|
||||
.any(|Entry { name: key, .. }| name == key)
|
||||
.any(|(key, _)| name == key.as_ref())
|
||||
}
|
||||
|
||||
/// Find an entry in the Scope, starting from the last.
|
||||
#[inline(always)]
|
||||
pub(crate) fn get_index(&self, name: &str) -> Option<(usize, EntryType)> {
|
||||
self.0
|
||||
self.names
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rev() // Always search a Scope in reverse order
|
||||
.find_map(|(index, Entry { name: key, typ, .. })| {
|
||||
if name == key {
|
||||
Some((index, *typ))
|
||||
.find_map(|(index, (key, _))| {
|
||||
if name == key.as_ref() {
|
||||
Some((index, self.types[index]))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get an entry in the Scope, starting from the last.
|
||||
#[inline(always)]
|
||||
pub(crate) fn get_entry(&self, name: &str) -> Option<&Entry> {
|
||||
self.0
|
||||
.iter()
|
||||
.rev()
|
||||
.find(|Entry { name: key, .. }| name == key)
|
||||
}
|
||||
|
||||
/// Get the value of an entry in the Scope, starting from the last.
|
||||
///
|
||||
/// # Example
|
||||
@ -365,8 +347,12 @@ impl<'a> Scope<'a> {
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn get_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
|
||||
self.get_entry(name)
|
||||
.and_then(|Entry { value, .. }| value.flatten_clone().try_cast())
|
||||
self.names
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rev()
|
||||
.find(|(_, (key, _))| name == key.as_ref())
|
||||
.and_then(|(index, _)| self.values[index].flatten_clone().try_cast())
|
||||
}
|
||||
|
||||
/// Update the value of the named entry.
|
||||
@ -398,7 +384,7 @@ impl<'a> Scope<'a> {
|
||||
}
|
||||
Some((_, EntryType::Constant)) => panic!("variable {} is constant", name),
|
||||
Some((index, EntryType::Normal)) => {
|
||||
self.0.get_mut(index).unwrap().value = Dynamic::from(value);
|
||||
*self.values.get_mut(index).unwrap() = Dynamic::from(value);
|
||||
}
|
||||
}
|
||||
self
|
||||
@ -407,16 +393,18 @@ impl<'a> Scope<'a> {
|
||||
/// Get a mutable reference to an entry in the Scope.
|
||||
#[inline(always)]
|
||||
pub(crate) fn get_mut(&mut self, index: usize) -> (&mut Dynamic, EntryType) {
|
||||
let entry = self.0.get_mut(index).expect("invalid index in Scope");
|
||||
(&mut entry.value, entry.typ)
|
||||
(
|
||||
self.values.get_mut(index).expect("invalid index in Scope"),
|
||||
self.types[index],
|
||||
)
|
||||
}
|
||||
|
||||
/// Update the access type of an entry in the Scope.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline(always)]
|
||||
pub(crate) fn set_entry_alias(&mut self, index: usize, alias: String) -> &mut Self {
|
||||
let entry = self.0.get_mut(index).expect("invalid index in Scope");
|
||||
entry.alias = Some(Box::new(alias));
|
||||
let entry = self.names.get_mut(index).expect("invalid index in Scope");
|
||||
entry.1 = Some(alias);
|
||||
self
|
||||
}
|
||||
|
||||
@ -424,34 +412,36 @@ impl<'a> Scope<'a> {
|
||||
/// Shadowed variables are omitted in the copy.
|
||||
#[inline]
|
||||
pub(crate) fn clone_visible(&self) -> Self {
|
||||
let mut entries: Vec<Entry> = Default::default();
|
||||
let mut entries: Self = Default::default();
|
||||
|
||||
self.0.iter().rev().for_each(|entry| {
|
||||
if entries
|
||||
.iter()
|
||||
.find(|Entry { name, .. }| &entry.name == name)
|
||||
.is_none()
|
||||
{
|
||||
entries.push(entry.clone());
|
||||
}
|
||||
});
|
||||
self.names
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rev()
|
||||
.for_each(|(index, (name, alias))| {
|
||||
if !entries.names.iter().any(|(key, _)| key == name) {
|
||||
entries.names.push((name.clone(), alias.clone()));
|
||||
entries.types.push(self.types[index]);
|
||||
entries.values.push(self.values[index].clone());
|
||||
}
|
||||
});
|
||||
|
||||
Self(entries)
|
||||
entries
|
||||
}
|
||||
|
||||
/// Get an iterator to entries in the Scope.
|
||||
#[inline(always)]
|
||||
pub(crate) fn into_iter(self) -> impl Iterator<Item = Entry<'a>> {
|
||||
self.0.into_iter()
|
||||
}
|
||||
|
||||
/// Get an iterator to entries in the Scope in reverse order.
|
||||
#[inline(always)]
|
||||
pub(crate) fn to_iter(&self) -> impl Iterator<Item = &Entry> {
|
||||
self.0.iter().rev() // Always search a Scope in reverse order
|
||||
pub(crate) fn into_iter(
|
||||
self,
|
||||
) -> impl Iterator<Item = (Cow<'a, str>, EntryType, Dynamic, Option<String>)> {
|
||||
self.names
|
||||
.into_iter()
|
||||
.zip(self.types.into_iter().zip(self.values.into_iter()))
|
||||
.map(|((name, alias), (typ, value))| (name, typ, value, alias))
|
||||
}
|
||||
|
||||
/// Get an iterator to entries in the Scope.
|
||||
/// Shared values are flatten-cloned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -484,25 +474,21 @@ impl<'a> Scope<'a> {
|
||||
/// Get an iterator to entries in the Scope.
|
||||
/// Shared values are not expanded.
|
||||
#[inline(always)]
|
||||
pub fn iter_raw(&self) -> impl Iterator<Item = (&str, bool, &Dynamic)> {
|
||||
self.0.iter().map(
|
||||
|Entry {
|
||||
name, typ, value, ..
|
||||
}| { (name.as_ref(), typ.is_constant(), value) },
|
||||
)
|
||||
pub fn iter_raw<'x: 'a>(&'x self) -> impl Iterator<Item = (&'a str, bool, &'x Dynamic)> + 'x {
|
||||
self.names
|
||||
.iter()
|
||||
.zip(self.types.iter().zip(self.values.iter()))
|
||||
.map(|((name, _), (typ, value))| (name.as_ref(), typ.is_constant(), value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K: Into<Cow<'a, str>>> iter::Extend<(K, EntryType, Dynamic)> for Scope<'a> {
|
||||
#[inline(always)]
|
||||
fn extend<T: IntoIterator<Item = (K, EntryType, Dynamic)>>(&mut self, iter: T) {
|
||||
self.0
|
||||
.extend(iter.into_iter().map(|(name, typ, value)| Entry {
|
||||
name: name.into(),
|
||||
typ,
|
||||
alias: None,
|
||||
value: value.into(),
|
||||
expr: None,
|
||||
}));
|
||||
iter.into_iter().for_each(|(name, typ, value)| {
|
||||
self.names.push((name.into(), None));
|
||||
self.types.push(typ);
|
||||
self.values.push(value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user