diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index d02e4388..778a70fe 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -2,8 +2,8 @@ name = "rhai_codegen" version = "0.3.5" edition = "2018" -authors = ["jhwgh1968"] -description = "Procedural macro support package for Rhai, a scripting language for Rust" +authors = ["jhwgh1968", "Stephen Chung"] +description = "Procedural macros support package for Rhai, a scripting language and engine for Rust" homepage = "https://rhai.rs/book/plugins/index.html" repository = "https://github.com/rhaiscript/rhai" license = "MIT OR Apache-2.0" diff --git a/src/engine.rs b/src/engine.rs index f85d5b66..1c696e02 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -51,35 +51,42 @@ pub type Precedence = NonZeroU8; // 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. // The best we can do is a shared reference. +// +// 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, Default)] -pub struct Imports(StaticVec, StaticVec>); +pub struct Imports { + keys: StaticVec, + modules: StaticVec>, +} impl Imports { /// Get the length of this stack of imported [modules][Module]. #[inline(always)] pub fn len(&self) -> usize { - self.0.len() + self.keys.len() } /// Is this stack of imported [modules][Module] empty? #[inline(always)] pub fn is_empty(&self) -> bool { - self.0.is_empty() + self.keys.is_empty() } /// Get the imported [modules][Module] at a particular index. #[inline(always)] pub fn get(&self, index: usize) -> Option> { - self.1.get(index).cloned() + self.modules.get(index).cloned() } /// Get the imported [modules][Module] at a particular index. #[allow(dead_code)] #[inline(always)] pub(crate) fn get_mut(&mut self, index: usize) -> Option<&mut Shared> { - self.1.get_mut(index) + self.modules.get_mut(index) } /// Get the index of an imported [modules][Module] by name. #[inline(always)] pub fn find(&self, name: &str) -> Option { - self.0 + self.keys .iter() .enumerate() .rev() @@ -88,22 +95,22 @@ impl Imports { /// Push an imported [modules][Module] onto the stack. #[inline(always)] pub fn push(&mut self, name: impl Into, module: impl Into>) { - self.0.push(name.into()); - self.1.push(module.into()); + self.keys.push(name.into()); + self.modules.push(module.into()); } /// Truncate the stack of imported [modules][Module] to a particular length. #[inline(always)] pub fn truncate(&mut self, size: usize) { - self.0.truncate(size); - self.1.truncate(size); + self.keys.truncate(size); + self.modules.truncate(size); } /// Get an iterator to this stack of imported [modules][Module] in reverse order. #[allow(dead_code)] #[inline(always)] pub fn iter(&self) -> impl Iterator { - self.0 + self.keys .iter() - .zip(self.1.iter()) + .zip(self.modules.iter()) .rev() .map(|(name, module)| (name.as_str(), module.as_ref())) } @@ -111,29 +118,32 @@ impl Imports { #[allow(dead_code)] #[inline(always)] pub(crate) fn iter_raw(&self) -> impl Iterator)> { - self.0.iter().rev().zip(self.1.iter().rev()) + self.keys.iter().rev().zip(self.modules.iter().rev()) } /// Get an iterator to this stack of imported [modules][Module] in forward order. #[allow(dead_code)] #[inline(always)] pub(crate) fn scan_raw(&self) -> impl Iterator)> { - self.0.iter().zip(self.1.iter()) + self.keys.iter().zip(self.modules.iter()) } /// Get a consuming iterator to this stack of imported [modules][Module] in reverse order. #[inline(always)] pub fn into_iter(self) -> impl Iterator)> { - self.0.into_iter().rev().zip(self.1.into_iter().rev()) + self.keys + .into_iter() + .rev() + .zip(self.modules.into_iter().rev()) } /// Does the specified function hash key exist in this stack of imported [modules][Module]? #[allow(dead_code)] #[inline(always)] pub fn contains_fn(&self, hash: u64) -> bool { - self.1.iter().any(|m| m.contains_qualified_fn(hash)) + self.modules.iter().any(|m| m.contains_qualified_fn(hash)) } /// Get specified function via its hash key. #[inline(always)] pub fn get_fn(&self, hash: u64) -> Option<(&CallableFunction, Option<&Identifier>)> { - self.1 + self.modules .iter() .rev() .find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw()))) @@ -143,12 +153,15 @@ impl Imports { #[allow(dead_code)] #[inline(always)] pub fn contains_iter(&self, id: TypeId) -> bool { - self.1.iter().any(|m| m.contains_qualified_iter(id)) + self.modules.iter().any(|m| m.contains_qualified_iter(id)) } /// Get the specified [`TypeId`][std::any::TypeId] iterator. #[inline(always)] pub fn get_iter(&self, id: TypeId) -> Option { - self.1.iter().rev().find_map(|m| m.get_qualified_iter(id)) + self.modules + .iter() + .rev() + .find_map(|m| m.get_qualified_iter(id)) } } @@ -160,7 +173,7 @@ impl fmt::Debug for Imports { f.debug_map().finish() } else { f.debug_map() - .entries(self.0.iter().zip(self.1.iter())) + .entries(self.keys.iter().zip(self.modules.iter())) .finish() } } @@ -232,24 +245,27 @@ pub const TOKEN_OP_CONCAT: Token = Token::PlusAssign; /// Method of chaining. #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] -pub enum ChainType { - /// Not a chaining type. - NonChaining, +enum ChainType { /// Indexing. + #[cfg(not(feature = "no_index"))] Index, /// Dotting. + #[cfg(not(feature = "no_object"))] Dot, } /// Value of a chaining argument. #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[derive(Debug, Clone, Hash)] -pub enum ChainArgument { +enum ChainArgument { /// Dot-property access. + #[cfg(not(feature = "no_object"))] Property(Position), - /// Arguments to a dot-function call. - FnCallArgs(StaticVec, StaticVec), + /// Arguments to a dot method call. + #[cfg(not(feature = "no_object"))] + MethodCallArgs(StaticVec, StaticVec), /// Index value. + #[cfg(not(feature = "no_index"))] IndexValue(Dynamic, Position), } @@ -264,7 +280,8 @@ impl ChainArgument { #[cfg(not(feature = "no_index"))] pub fn as_index_value(self) -> Dynamic { match self { - Self::Property(_) | Self::FnCallArgs(_, _) => { + #[cfg(not(feature = "no_object"))] + Self::Property(_) | Self::MethodCallArgs(_, _) => { panic!("expecting ChainArgument::IndexValue") } Self::IndexValue(value, _) => value, @@ -274,28 +291,32 @@ impl ChainArgument { /// /// # Panics /// - /// Panics if not `ChainArgument::FnCallArgs`. + /// Panics if not `ChainArgument::MethodCallArgs`. #[inline(always)] #[cfg(not(feature = "no_object"))] pub fn as_fn_call_args(self) -> (StaticVec, StaticVec) { match self { - Self::Property(_) | Self::IndexValue(_, _) => { - panic!("expecting ChainArgument::FnCallArgs") + Self::Property(_) => { + panic!("expecting ChainArgument::MethodCallArgs") } - Self::FnCallArgs(values, positions) => (values, positions), + #[cfg(not(feature = "no_index"))] + Self::IndexValue(_, _) => { + panic!("expecting ChainArgument::MethodCallArgs") + } + Self::MethodCallArgs(values, positions) => (values, positions), } } } -#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] +#[cfg(not(feature = "no_object"))] impl From<(StaticVec, StaticVec)> for ChainArgument { #[inline(always)] fn from((values, positions): (StaticVec, StaticVec)) -> Self { - Self::FnCallArgs(values, positions) + Self::MethodCallArgs(values, positions) } } -#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] +#[cfg(not(feature = "no_index"))] impl From<(Dynamic, Position)> for ChainArgument { #[inline(always)] fn from((value, pos): (Dynamic, Position)) -> Self { @@ -1129,14 +1150,14 @@ impl Engine { level: usize, new_val: Option<((Dynamic, Position), (Option, Position))>, ) -> Result<(Dynamic, bool), Box> { - assert!(chain_type != ChainType::NonChaining); - let is_ref = target.is_ref(); - let next_chain = match rhs { - Expr::Index(_, _) => ChainType::Index, - Expr::Dot(_, _) => ChainType::Dot, - _ => ChainType::NonChaining, + let rhs_chain = match rhs { + #[cfg(not(feature = "no_index"))] + Expr::Index(_, _) => Some(ChainType::Index), + #[cfg(not(feature = "no_object"))] + Expr::Dot(_, _) => Some(ChainType::Dot), + _ => None, }; // Pop the last index value @@ -1155,9 +1176,10 @@ impl Engine { let obj_ptr = &mut self.get_indexed_mut( mods, state, lib, target, idx_val, idx_pos, false, is_ref, true, level, )?; + let rhs_chain = rhs_chain.unwrap(); self.eval_dot_index_chain_helper( - mods, state, lib, this_ptr, obj_ptr, &x.rhs, idx_values, next_chain, + mods, state, lib, this_ptr, obj_ptr, &x.rhs, idx_values, rhs_chain, level, new_val, ) .map_err(|err| err.fill_position(*x_pos)) @@ -1340,9 +1362,10 @@ impl Engine { // Others - syntax error expr => unreachable!("invalid dot expression: {:?}", expr), }; + let rhs_chain = rhs_chain.unwrap(); self.eval_dot_index_chain_helper( - mods, state, lib, this_ptr, &mut val, &x.rhs, idx_values, next_chain, + mods, state, lib, this_ptr, &mut val, &x.rhs, idx_values, rhs_chain, level, new_val, ) .map_err(|err| err.fill_position(*x_pos)) @@ -1354,6 +1377,7 @@ impl Engine { Expr::Property(p) => { let ((getter, hash_get), (setter, hash_set), Ident { pos, .. }) = p.as_ref(); + let rhs_chain = rhs_chain.unwrap(); let hash_get = FnCallHashes::from_native(*hash_get); let hash_set = FnCallHashes::from_native(*hash_set); let arg_values = &mut [target.as_mut(), &mut Default::default()]; @@ -1374,7 +1398,7 @@ impl Engine { &mut val.into(), &x.rhs, idx_values, - next_chain, + rhs_chain, level, new_val, ) @@ -1405,6 +1429,7 @@ impl Engine { // xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr Expr::FnCall(f, pos) if !f.is_qualified() => { let FnCallExpr { name, hashes, .. } = f.as_ref(); + let rhs_chain = rhs_chain.unwrap(); let mut args = idx_val.as_fn_call_args(); let (mut val, _) = self.make_method_call( mods, state, lib, name, *hashes, target, &mut args, *pos, level, @@ -1414,7 +1439,7 @@ impl Engine { self.eval_dot_index_chain_helper( mods, state, lib, this_ptr, target, &x.rhs, idx_values, - next_chain, level, new_val, + rhs_chain, level, new_val, ) .map_err(|err| err.fill_position(*pos)) } @@ -1430,8 +1455,6 @@ impl Engine { _ => EvalAltResult::ErrorDotExpr("".into(), rhs.position()).into(), } } - - chain_type => unreachable!("invalid ChainType: {:?}", chain_type), } } @@ -1449,7 +1472,9 @@ impl Engine { new_val: Option<((Dynamic, Position), (Option, Position))>, ) -> RhaiResult { let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, op_pos) = match expr { + #[cfg(not(feature = "no_index"))] Expr::Index(x, pos) => (x.as_ref(), ChainType::Index, *pos), + #[cfg(not(feature = "no_object"))] Expr::Dot(x, pos) => (x.as_ref(), ChainType::Dot, *pos), _ => unreachable!("index or dot chain expected, but gets {:?}", expr), }; @@ -1510,7 +1535,7 @@ impl Engine { lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, expr: &Expr, - parent_chain_type: ChainType, + _parent_chain_type: ChainType, idx_values: &mut StaticVec, size: usize, level: usize, @@ -1519,7 +1544,8 @@ impl Engine { self.inc_operations(state, expr.position())?; match expr { - Expr::FnCall(x, _) if parent_chain_type == ChainType::Dot && !x.is_qualified() => { + #[cfg(not(feature = "no_object"))] + Expr::FnCall(x, _) if _parent_chain_type == ChainType::Dot && !x.is_qualified() => { let mut arg_positions: StaticVec<_> = Default::default(); let mut arg_values = x @@ -1539,11 +1565,13 @@ impl Engine { idx_values.push((arg_values, arg_positions).into()); } - Expr::FnCall(_, _) if parent_chain_type == ChainType::Dot => { + #[cfg(not(feature = "no_object"))] + Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dot => { unreachable!("function call in dot chain should not be namespace-qualified") } - Expr::Property(x) if parent_chain_type == ChainType::Dot => { + #[cfg(not(feature = "no_object"))] + Expr::Property(x) if _parent_chain_type == ChainType::Dot => { idx_values.push(ChainArgument::Property(x.2.pos)) } Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"), @@ -1553,12 +1581,15 @@ impl Engine { // Evaluate in left-to-right order let lhs_val = match lhs { - Expr::Property(x) if parent_chain_type == ChainType::Dot => { + #[cfg(not(feature = "no_object"))] + Expr::Property(x) if _parent_chain_type == ChainType::Dot => { ChainArgument::Property(x.2.pos) } Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"), + + #[cfg(not(feature = "no_object"))] Expr::FnCall(x, _) - if parent_chain_type == ChainType::Dot && !x.is_qualified() => + if _parent_chain_type == ChainType::Dot && !x.is_qualified() => { let mut arg_positions: StaticVec<_> = Default::default(); @@ -1579,17 +1610,26 @@ impl Engine { (arg_values, arg_positions).into() } - Expr::FnCall(_, _) if parent_chain_type == ChainType::Dot => { + #[cfg(not(feature = "no_object"))] + Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dot => { unreachable!("function call in dot chain should not be namespace-qualified") } - _ => self + #[cfg(not(feature = "no_object"))] + expr if _parent_chain_type == ChainType::Dot => { + unreachable!("invalid dot expression: {:?}", expr); + } + #[cfg(not(feature = "no_index"))] + _ if _parent_chain_type == ChainType::Index => self .eval_expr(scope, mods, state, lib, this_ptr, lhs, level) .map(|v| (v.flatten(), lhs.position()).into())?, + expr => unreachable!("unknown chained expression: {:?}", expr), }; // Push in reverse order let chain_type = match expr { + #[cfg(not(feature = "no_index"))] Expr::Index(_, _) => ChainType::Index, + #[cfg(not(feature = "no_object"))] Expr::Dot(_, _) => ChainType::Dot, _ => unreachable!("index or dot chain expected, but gets {:?}", expr), }; @@ -1600,10 +1640,16 @@ impl Engine { idx_values.push(lhs_val); } - _ => idx_values.push( + #[cfg(not(feature = "no_object"))] + _ if _parent_chain_type == ChainType::Dot => { + unreachable!("invalid dot expression: {:?}", expr); + } + #[cfg(not(feature = "no_index"))] + _ if _parent_chain_type == ChainType::Index => idx_values.push( self.eval_expr(scope, mods, state, lib, this_ptr, expr, level) .map(|v| (v.flatten(), expr.position()).into())?, ), + _ => unreachable!("unknown chained expression: {:?}", expr), } Ok(()) diff --git a/src/token.rs b/src/token.rs index 490bc232..ad4d6a44 100644 --- a/src/token.rs +++ b/src/token.rs @@ -187,10 +187,10 @@ impl Position { } /// Print this [`Position`] for debug purposes. #[inline(always)] - pub(crate) fn debug_print(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + pub(crate) fn debug_print(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(not(feature = "no_position"))] if !self.is_none() { - write!(f, " @ {:?}", self)?; + write!(_f, " @ {:?}", self)?; } Ok(())