From 07a8a43298647b4e498d0fa29b37eef2faa1afbf Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 6 Nov 2020 16:27:40 +0800 Subject: [PATCH] Optimize data structures. --- src/ast.rs | 63 ++++++++++++++++++++++++++++++------------------- src/dynamic.rs | 17 ------------- src/engine.rs | 4 ++-- src/fn_call.rs | 25 +++++++++++--------- src/optimize.rs | 21 ++++++++++------- src/parser.rs | 21 ++++++++++------- src/scope.rs | 27 ++++----------------- src/syntax.rs | 5 ++++ 8 files changed, 88 insertions(+), 95 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 4a31d1f9..25bdace5 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -4,7 +4,7 @@ use crate::dynamic::{Dynamic, Union}; use crate::fn_native::{FnPtr, Shared}; use crate::module::{Module, ModuleRef}; use crate::syntax::FnCustomSyntaxEval; -use crate::token::{Position, Token}; +use crate::token::{Position, Token, NO_POS}; use crate::utils::ImmutableString; use crate::StaticVec; use crate::INT; @@ -93,9 +93,9 @@ pub struct ScriptFnDef { pub access: FnAccess, /// Names of function parameters. pub params: StaticVec, - /// Access to external variables. + /// Access to external variables. Boxed because it occurs rarely. #[cfg(not(feature = "no_closure"))] - pub externals: HashSet, + pub externals: Option>>, } impl fmt::Display for ScriptFnDef { @@ -590,11 +590,12 @@ pub enum ReturnType { Exception, } -/// _[INTERNALS]_ A Rhai statement. +/// _[INTERNALS]_ A statement. /// Exported under the `internals` feature only. /// -/// Each variant is at most one pointer in size (for speed), -/// with everything being allocated together in one single tuple. +/// ## WARNING +/// +/// This type is volatile and may change. #[derive(Debug, Clone, Hash)] pub enum Stmt { /// No-op. @@ -616,7 +617,7 @@ pub enum Stmt { /// { stmt; ... } Block(Vec, Position), /// try { stmt; ... } catch ( var ) { stmt; ... } - TryCatch(Box<(Stmt, Option, Stmt, (Position, Position))>), + TryCatch(Box<(Stmt, Option, Stmt)>, Position, Position), /// expr Expr(Expr), /// continue @@ -639,7 +640,7 @@ pub enum Stmt { impl Default for Stmt { #[inline(always)] fn default() -> Self { - Self::Noop(Default::default()) + Self::Noop(NO_POS) } } @@ -667,7 +668,7 @@ impl Stmt { | Self::ReturnWithVal((_, pos), _, _) => *pos, Self::Let(x, _, _) | Self::Const(x, _, _) => x.pos, - Self::TryCatch(x) => (x.3).0, + Self::TryCatch(_, pos, _) => *pos, Self::Expr(x) => x.position(), @@ -696,7 +697,7 @@ impl Stmt { | Self::ReturnWithVal((_, pos), _, _) => *pos = new_pos, Self::Let(x, _, _) | Self::Const(x, _, _) => x.pos = new_pos, - Self::TryCatch(x) => (x.3).0 = new_pos, + Self::TryCatch(_, pos, _) => *pos = new_pos, Self::Expr(x) => { x.set_position(new_pos); @@ -722,7 +723,7 @@ impl Stmt { | Self::Loop(_, _) | Self::For(_, _, _) | Self::Block(_, _) - | Self::TryCatch(_) => true, + | Self::TryCatch(_, _, _) => true, // A No-op requires a semicolon in order to know it is an empty statement! Self::Noop(_) => false, @@ -758,7 +759,7 @@ impl Stmt { Self::Let(_, _, _) | Self::Const(_, _, _) | Self::Assignment(_, _) => false, Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()), Self::Continue(_) | Self::Break(_) | Self::ReturnWithVal(_, _, _) => false, - Self::TryCatch(x) => x.0.is_pure() && x.2.is_pure(), + Self::TryCatch(x, _, _) => x.0.is_pure() && x.2.is_pure(), #[cfg(not(feature = "no_module"))] Self::Import(_, _, _) => false, @@ -779,7 +780,9 @@ impl Stmt { /// This type is volatile and may change. #[derive(Clone)] pub struct CustomExpr { + /// List of keywords. pub(crate) keywords: StaticVec, + /// Implementation function. pub(crate) func: Shared, } @@ -848,6 +851,11 @@ impl From for FloatWrapper { } /// A binary expression structure. +/// Exported under the `internals` feature only. +/// +/// ## WARNING +/// +/// This type is volatile and may change. #[derive(Debug, Clone, Hash)] pub struct BinaryExpr { /// LHS expression. @@ -856,7 +864,12 @@ pub struct BinaryExpr { pub rhs: Expr, } -/// A function call. +/// _[INTERNALS]_ A function call. +/// Exported under the `internals` feature only. +/// +/// ## WARNING +/// +/// This type is volatile and may change. #[derive(Debug, Clone, Hash, Default)] pub struct FnCallInfo { /// Pre-calculated hash for a script-defined function of the same name and number of parameters. @@ -869,7 +882,7 @@ pub struct FnCallInfo { /// Default value when the function is not found, mostly used to provide a default for comparison functions. /// Type is `bool` in order for `FnCallInfo` to be `Hash` pub def_value: Option, - /// Namespace of the function, if any. + /// Namespace of the function, if any. Boxed because it occurs rarely. pub namespace: Option>, /// Function name. /// Use `Cow<'static, str>` because a lot of operators (e.g. `==`, `>=`) are implemented as function calls @@ -882,9 +895,6 @@ pub struct FnCallInfo { /// _[INTERNALS]_ An expression sub-tree. /// Exported under the `internals` feature only. /// -/// Each variant is at most one pointer in size (for speed), -/// with everything being allocated together in one single tuple. -/// /// ## WARNING /// /// This type is volatile and may change. @@ -938,7 +948,7 @@ pub enum Expr { impl Default for Expr { #[inline(always)] fn default() -> Self { - Self::Unit(Default::default()) + Self::Unit(NO_POS) } } @@ -1241,16 +1251,21 @@ impl Expr { #[cfg(test)] mod tests { - use super::*; - /// This test is to make sure no code changes increase the sizes of critical data structures. #[test] fn check_struct_sizes() { use std::mem::size_of; - assert_eq!(size_of::(), 16); - assert_eq!(size_of::>(), 16); - assert_eq!(size_of::(), 16); - assert_eq!(size_of::(), 32); + assert_eq!(size_of::(), 16); + assert_eq!(size_of::>(), 16); + assert_eq!(size_of::(), 4); + assert_eq!(size_of::(), 16); + assert_eq!(size_of::>(), 16); + assert_eq!(size_of::(), 32); + assert_eq!(size_of::>(), 32); + assert_eq!(size_of::(), 72); + assert_eq!(size_of::(), 32); + assert_eq!(size_of::(), 16); + assert_eq!(size_of::(), 64); } } diff --git a/src/dynamic.rs b/src/dynamic.rs index b405fd8a..6b3cc8b1 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -619,7 +619,6 @@ impl Dynamic { Self(Union::Variant(Box::new(boxed))) } - /// Turn the `Dynamic` value into a shared `Dynamic` value backed by an `Rc>` /// or `Arc>` depending on the `sync` feature. /// @@ -644,7 +643,6 @@ impl Dynamic { #[cfg(feature = "no_closure")] panic!("converting into a shared value is not supported under 'no_closure'"); } - /// Convert the `Dynamic` value into specific type. /// /// Casting to a `Dynamic` just returns as is, but if it contains a shared value, @@ -775,7 +773,6 @@ impl Dynamic { _ => None, } } - /// Convert the `Dynamic` value into a specific type. /// /// Casting to a `Dynamic` just returns as is, but if it contains a shared value, @@ -819,7 +816,6 @@ impl Dynamic { ) }) } - /// Flatten the `Dynamic` and clone it. /// /// If the `Dynamic` is not a shared value, it returns a cloned copy. @@ -839,7 +835,6 @@ impl Dynamic { _ => self.clone(), } } - /// Flatten the `Dynamic`. /// /// If the `Dynamic` is not a shared value, it returns itself. @@ -867,7 +862,6 @@ impl Dynamic { _ => self, } } - /// Is the `Dynamic` a shared value that is locked? /// /// ## Note @@ -889,7 +883,6 @@ impl Dynamic { _ => false, } } - /// Get a reference of a specific type to the `Dynamic`. /// Casting to `Dynamic` just returns a reference to it. /// @@ -922,7 +915,6 @@ impl Dynamic { .map(|r| DynamicReadLock(DynamicReadLockInner::Reference(r))), } } - /// Get a mutable reference of a specific type to the `Dynamic`. /// Casting to `Dynamic` just returns a mutable reference to it. /// @@ -955,7 +947,6 @@ impl Dynamic { .map(|r| DynamicWriteLock(DynamicWriteLockInner::Reference(r))), } } - /// Get a reference of a specific type to the `Dynamic`. /// Casting to `Dynamic` just returns a reference to it. /// @@ -1045,7 +1036,6 @@ impl Dynamic { _ => None, } } - /// Get a mutable reference of a specific type to the `Dynamic`. /// Casting to `Dynamic` just returns a mutable reference to it. /// @@ -1129,7 +1119,6 @@ impl Dynamic { _ => None, } } - /// Cast the `Dynamic` as the system integer type `INT` and return it. /// Returns the name of the actual type if the cast fails. #[inline(always)] @@ -1141,7 +1130,6 @@ impl Dynamic { _ => Err(self.type_name()), } } - /// Cast the `Dynamic` as the system floating-point type `FLOAT` and return it. /// Returns the name of the actual type if the cast fails. #[cfg(not(feature = "no_float"))] @@ -1154,7 +1142,6 @@ impl Dynamic { _ => Err(self.type_name()), } } - /// Cast the `Dynamic` as a `bool` and return it. /// Returns the name of the actual type if the cast fails. #[inline(always)] @@ -1166,7 +1153,6 @@ impl Dynamic { _ => Err(self.type_name()), } } - /// Cast the `Dynamic` as a `char` and return it. /// Returns the name of the actual type if the cast fails. #[inline(always)] @@ -1178,7 +1164,6 @@ impl Dynamic { _ => Err(self.type_name()), } } - /// Cast the `Dynamic` as a string and return the string slice. /// Returns the name of the actual type if the cast fails. /// @@ -1191,7 +1176,6 @@ impl Dynamic { _ => Err(self.type_name()), } } - /// Convert the `Dynamic` into `String` and return it. /// If there are other references to the same string, a cloned copy is returned. /// Returns the name of the actual type if the cast fails. @@ -1200,7 +1184,6 @@ impl Dynamic { self.take_immutable_string() .map(ImmutableString::into_owned) } - /// Convert the `Dynamic` into `ImmutableString` and return it. /// Returns the name of the actual type if the cast fails. #[inline] diff --git a/src/engine.rs b/src/engine.rs index bb1f0725..e88cee23 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2034,8 +2034,8 @@ impl Engine { Stmt::Break(pos) => EvalAltResult::LoopBreak(true, *pos).into(), // Try/Catch statement - Stmt::TryCatch(x) => { - let (try_body, var_def, catch_body, _) = x.as_ref(); + Stmt::TryCatch(x, _, _) => { + let (try_body, var_def, catch_body) = x.as_ref(); let result = self .eval_stmt(scope, mods, state, lib, this_ptr, try_body, level) diff --git a/src/fn_call.rs b/src/fn_call.rs index 2979c401..18932715 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -118,7 +118,6 @@ impl<'a> ArgBackup<'a> { mem::transmute(&mut self.value_copy) })); } - /// This function restores the first argument that was replaced by `change_first_arg_to_copy`. /// /// # Safety @@ -541,16 +540,20 @@ impl Engine { // Move captured variables into scope #[cfg(not(feature = "no_closure"))] if let Some(captured) = _capture_scope { - captured - .into_iter() - .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), - }; - }); + if let Some(ref externals) = func.externals { + captured + .into_iter() + .filter(|(name, _, _, _)| 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 { diff --git a/src/optimize.rs b/src/optimize.rs index 862aa855..4b023627 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -387,7 +387,7 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt { } } // try { block } catch ( var ) { block } - Stmt::TryCatch(x) if x.0.is_pure() => { + Stmt::TryCatch(x, _, _) if x.0.is_pure() => { // If try block is pure, there will never be any exceptions state.set_dirty(); let pos = x.0.position(); @@ -399,14 +399,17 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt { Stmt::Block(statements, pos) } // try { block } catch ( var ) { block } - Stmt::TryCatch(x) => { - let (try_block, var_name, catch_block, pos) = *x; - Stmt::TryCatch(Box::new(( - optimize_stmt(try_block, state, false), - var_name, - optimize_stmt(catch_block, state, false), - pos, - ))) + Stmt::TryCatch(x, try_pos, catch_pos) => { + let (try_block, var_name, catch_block) = *x; + Stmt::TryCatch( + Box::new(( + optimize_stmt(try_block, state, false), + var_name, + optimize_stmt(catch_block, state, false), + )), + try_pos, + catch_pos, + ) } // {} Stmt::Expr(Expr::Stmt(x, pos)) if x.is_empty() => { diff --git a/src/parser.rs b/src/parser.rs index 448df46f..f4f08b81 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -32,7 +32,7 @@ use crate::{ use crate::stdlib::{ borrow::Cow, boxed::Box, - collections::HashMap, + collections::{HashMap, HashSet}, format, hash::Hash, iter::empty, @@ -2376,12 +2376,11 @@ fn parse_try_catch( // try { body } catch ( var ) { catch_block } let catch_body = parse_block(input, state, lib, settings.level_up())?; - Ok(Stmt::TryCatch(Box::new(( - body, - var_def, - catch_body, - (token_pos, catch_pos), - )))) + Ok(Stmt::TryCatch( + Box::new((body, var_def, catch_body)), + token_pos, + catch_pos, + )) } /// Parse a function definition. @@ -2470,7 +2469,7 @@ fn parse_fn( let params: StaticVec<_> = params.into_iter().map(|(p, _)| p).collect(); #[cfg(not(feature = "no_closure"))] - let externals = state + let externals: HashSet<_> = state .externals .iter() .map(|(name, _)| name) @@ -2483,7 +2482,11 @@ fn parse_fn( access, params, #[cfg(not(feature = "no_closure"))] - externals, + externals: if externals.is_empty() { + None + } else { + Some(Box::new(externals)) + }, body, lib: None, }) diff --git a/src/scope.rs b/src/scope.rs index 1f2befb3..4282cfae 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -70,8 +70,8 @@ pub struct Scope<'a> { values: Vec, /// Type of the entry. types: Vec, - /// (Name, alias) of the entry. - names: Vec<(Cow<'a, str>, Option)>, + /// (Name, alias) of the entry. The alias is Boxed because it occurs rarely. + names: Vec<(Cow<'a, str>, Option>)>, } impl<'a> Scope<'a> { @@ -91,7 +91,6 @@ impl<'a> Scope<'a> { pub fn new() -> Self { Default::default() } - /// Empty the Scope. /// /// # Example @@ -118,7 +117,6 @@ impl<'a> Scope<'a> { self.values.clear(); self } - /// Get the number of entries inside the Scope. /// /// # Example @@ -136,7 +134,6 @@ impl<'a> Scope<'a> { pub fn len(&self) -> usize { self.values.len() } - /// Is the Scope empty? /// /// # Example @@ -154,7 +151,6 @@ impl<'a> Scope<'a> { pub fn is_empty(&self) -> bool { self.values.len() == 0 } - /// Add (push) a new entry to the Scope. /// /// # Example @@ -175,7 +171,6 @@ impl<'a> Scope<'a> { ) -> &mut Self { self.push_dynamic_value(name, EntryType::Normal, Dynamic::from(value)) } - /// Add (push) a new `Dynamic` entry to the Scope. /// /// # Example @@ -192,7 +187,6 @@ impl<'a> Scope<'a> { pub fn push_dynamic(&mut self, name: impl Into>, value: Dynamic) -> &mut Self { self.push_dynamic_value(name, EntryType::Normal, value) } - /// Add (push) a new constant to the Scope. /// /// Constants are immutable and cannot be assigned to. Their values never change. @@ -219,7 +213,6 @@ impl<'a> Scope<'a> { ) -> &mut Self { self.push_dynamic_value(name, EntryType::Constant, Dynamic::from(value)) } - /// Add (push) a new constant with a `Dynamic` value to the Scope. /// /// Constants are immutable and cannot be assigned to. Their values never change. @@ -247,7 +240,6 @@ impl<'a> Scope<'a> { ) -> &mut Self { self.push_dynamic_value(name, EntryType::Constant, value) } - /// Add (push) a new entry with a `Dynamic` value to the Scope. #[inline] pub(crate) fn push_dynamic_value( @@ -261,7 +253,6 @@ impl<'a> Scope<'a> { self.values.push(value.into()); self } - /// Truncate (rewind) the Scope to a previous size. /// /// # Example @@ -295,7 +286,6 @@ impl<'a> Scope<'a> { self.values.truncate(size); self } - /// Does the scope contain the entry? /// /// # Example @@ -316,7 +306,6 @@ impl<'a> Scope<'a> { .rev() // Always search a Scope in reverse order .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)> { @@ -332,7 +321,6 @@ impl<'a> Scope<'a> { } }) } - /// Get the value of an entry in the Scope, starting from the last. /// /// # Example @@ -354,7 +342,6 @@ impl<'a> Scope<'a> { .find(|(_, (key, _))| name == key.as_ref()) .and_then(|(index, _)| self.values[index].flatten_clone().try_cast()) } - /// Update the value of the named entry. /// Search starts backwards from the last, and only the first entry matching the specified name is updated. /// If no entry matching the specified name is found, a new one is added. @@ -389,7 +376,6 @@ impl<'a> Scope<'a> { } self } - /// Get a mutable reference to an entry in the Scope. #[inline(always)] pub(crate) fn get_mut(&mut self, index: usize) -> (&mut Dynamic, EntryType) { @@ -398,16 +384,14 @@ impl<'a> Scope<'a> { 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.names.get_mut(index).expect("invalid index in Scope"); - entry.1 = Some(alias); + entry.1 = Some(Box::new(alias)); self } - /// Clone the Scope, keeping only the last instances of each variable name. /// Shadowed variables are omitted in the copy. #[inline] @@ -428,7 +412,6 @@ impl<'a> Scope<'a> { entries } - /// Get an iterator to entries in the Scope. #[inline(always)] pub(crate) fn into_iter( @@ -437,9 +420,8 @@ impl<'a> Scope<'a> { self.names .into_iter() .zip(self.types.into_iter().zip(self.values.into_iter())) - .map(|((name, alias), (typ, value))| (name, typ, value, alias)) + .map(|((name, alias), (typ, value))| (name, typ, value, alias.map(|v| *v))) } - /// Get an iterator to entries in the Scope. /// Shared values are flatten-cloned. /// @@ -470,7 +452,6 @@ impl<'a> Scope<'a> { self.iter_raw() .map(|(name, constant, value)| (name, constant, value.flatten_clone())) } - /// Get an iterator to entries in the Scope. /// Shared values are not expanded. #[inline(always)] diff --git a/src/syntax.rs b/src/syntax.rs index e03c6a21..4baa5a7a 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -85,9 +85,14 @@ impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_, '_> { } } +/// Definition of a custom syntax definition. pub struct CustomSyntax { + /// A parsing function to return the next keyword in a custom syntax based on the + /// keywords parsed so far. pub parse: Box, + /// Custom syntax implementation function. pub func: Shared, + /// Delta number of variables in the scope. pub scope_delta: isize, }