From cec6748ac6271cdb6469c7f93765cd31bdd2d067 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 1 Jun 2020 10:58:14 +0800 Subject: [PATCH] Remove no_module gates to prepare for plugins. --- README.md | 2 +- src/any.rs | 14 +------- src/engine.rs | 89 +++++++++++++++++++++--------------------------- src/module.rs | 22 +++--------- src/parser.rs | 85 +++++++++++++++++---------------------------- src/scope.rs | 20 +++++++++-- src/token.rs | 8 ++--- tests/modules.rs | 1 + 8 files changed, 99 insertions(+), 142 deletions(-) diff --git a/README.md b/README.md index f894ea7b..bd0cc029 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ Optional features | `no_index` | Disable [arrays] and indexing features. | | `no_object` | Disable support for custom types and [object maps]. | | `no_function` | Disable script-defined functions. | -| `no_module` | Disable loading modules. | +| `no_module` | Disable loading external modules. | | `no_std` | Build for `no-std`. Notice that additional dependencies will be pulled in to replace `std` features. | By default, Rhai includes all the standard functionalities in a small, tight package. diff --git a/src/any.rs b/src/any.rs index f685c303..0b6d81d2 100644 --- a/src/any.rs +++ b/src/any.rs @@ -1,11 +1,9 @@ //! Helper module which defines the `Any` trait to to allow dynamic value handling. +use crate::module::Module; use crate::parser::{ImmutableString, INT}; use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast}; -#[cfg(not(feature = "no_module"))] -use crate::module::Module; - #[cfg(not(feature = "no_float"))] use crate::parser::FLOAT; @@ -160,7 +158,6 @@ pub enum Union { Array(Box), #[cfg(not(feature = "no_object"))] Map(Box), - #[cfg(not(feature = "no_module"))] Module(Box), Variant(Box>), } @@ -198,7 +195,6 @@ impl Dynamic { Union::Array(_) => TypeId::of::(), #[cfg(not(feature = "no_object"))] Union::Map(_) => TypeId::of::(), - #[cfg(not(feature = "no_module"))] Union::Module(_) => TypeId::of::(), Union::Variant(value) => (***value).type_id(), } @@ -218,7 +214,6 @@ impl Dynamic { Union::Array(_) => "array", #[cfg(not(feature = "no_object"))] Union::Map(_) => "map", - #[cfg(not(feature = "no_module"))] Union::Module(_) => "sub-scope", #[cfg(not(feature = "no_std"))] @@ -242,7 +237,6 @@ impl fmt::Display for Dynamic { Union::Array(value) => write!(f, "{:?}", value), #[cfg(not(feature = "no_object"))] Union::Map(value) => write!(f, "#{:?}", value), - #[cfg(not(feature = "no_module"))] Union::Module(value) => write!(f, "{:?}", value), #[cfg(not(feature = "no_std"))] @@ -266,7 +260,6 @@ impl fmt::Debug for Dynamic { Union::Array(value) => write!(f, "{:?}", value), #[cfg(not(feature = "no_object"))] Union::Map(value) => write!(f, "#{:?}", value), - #[cfg(not(feature = "no_module"))] Union::Module(value) => write!(f, "{:?}", value), #[cfg(not(feature = "no_std"))] @@ -290,7 +283,6 @@ impl Clone for Dynamic { Union::Array(ref value) => Self(Union::Array(value.clone())), #[cfg(not(feature = "no_object"))] Union::Map(ref value) => Self(Union::Map(value.clone())), - #[cfg(not(feature = "no_module"))] Union::Module(ref value) => Self(Union::Module(value.clone())), Union::Variant(ref value) => (***value).clone_into_dynamic(), } @@ -426,7 +418,6 @@ impl Dynamic { Union::Array(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v), #[cfg(not(feature = "no_object"))] Union::Map(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v), - #[cfg(not(feature = "no_module"))] Union::Module(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v), Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).ok(), } @@ -470,7 +461,6 @@ impl Dynamic { Union::Array(value) => *unsafe_cast_box::<_, T>(value).unwrap(), #[cfg(not(feature = "no_object"))] Union::Map(value) => *unsafe_cast_box::<_, T>(value).unwrap(), - #[cfg(not(feature = "no_module"))] Union::Module(value) => *unsafe_cast_box::<_, T>(value).unwrap(), Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).unwrap(), } @@ -498,7 +488,6 @@ impl Dynamic { Union::Array(value) => (value.as_ref() as &dyn Any).downcast_ref::(), #[cfg(not(feature = "no_object"))] Union::Map(value) => (value.as_ref() as &dyn Any).downcast_ref::(), - #[cfg(not(feature = "no_module"))] Union::Module(value) => (value.as_ref() as &dyn Any).downcast_ref::(), Union::Variant(value) => value.as_ref().as_ref().as_any().downcast_ref::(), } @@ -524,7 +513,6 @@ impl Dynamic { Union::Array(value) => (value.as_mut() as &mut dyn Any).downcast_mut::(), #[cfg(not(feature = "no_object"))] Union::Map(value) => (value.as_mut() as &mut dyn Any).downcast_mut::(), - #[cfg(not(feature = "no_module"))] Union::Module(value) => (value.as_mut() as &mut dyn Any).downcast_mut::(), Union::Variant(value) => value.as_mut().as_mut_any().downcast_mut::(), } diff --git a/src/engine.rs b/src/engine.rs index 03521f4f..453ee184 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -4,7 +4,7 @@ use crate::any::{Dynamic, Union}; use crate::calc_fn_hash; use crate::error::ParseErrorType; use crate::fn_native::{CallableFunction, FnCallArgs, Shared}; -use crate::module::Module; +use crate::module::{resolvers, Module, ModuleResolver}; use crate::optimize::OptimizationLevel; use crate::packages::{CorePackage, Package, PackageLibrary, PackagesCollection, StandardPackage}; use crate::parser::{Expr, FnAccess, FnDef, ImmutableString, ReturnType, Stmt, AST, INT}; @@ -17,9 +17,6 @@ use crate::utils::{StaticVec, StraightHasherBuilder}; #[cfg(not(feature = "no_float"))] use crate::parser::FLOAT; -#[cfg(not(feature = "no_module"))] -use crate::module::{resolvers, ModuleResolver}; - use crate::stdlib::{ any::TypeId, boxed::Box, @@ -297,7 +294,6 @@ pub struct Engine { pub(crate) packages: PackagesCollection, /// A module resolution service. - #[cfg(not(feature = "no_module"))] pub(crate) module_resolver: Option>, /// A hashmap mapping type names to pretty-print names. @@ -350,8 +346,7 @@ impl Default for Engine { #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_std"))] module_resolver: Some(Box::new(resolvers::FileModuleResolver::new())), - #[cfg(not(feature = "no_module"))] - #[cfg(feature = "no_std")] + #[cfg(any(feature = "no_module", feature = "no_std"))] module_resolver: None, type_names: HashMap::new(), @@ -442,31 +437,28 @@ fn search_scope<'s, 'a>( _ => unreachable!(), }; - #[cfg(not(feature = "no_module"))] - { - if let Some(modules) = modules.as_ref() { - let module = if let Some(index) = modules.index() { - scope - .get_mut(scope.len() - index.get()) - .0 - .downcast_mut::() - .unwrap() - } else { - let (id, root_pos) = modules.get(0); + if let Some(modules) = modules.as_ref() { + let module = if let Some(index) = modules.index() { + scope + .get_mut(scope.len() - index.get()) + .0 + .downcast_mut::() + .unwrap() + } else { + let (id, root_pos) = modules.get(0); - scope.find_module(id).ok_or_else(|| { - Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos)) - })? - }; + scope + .find_module_internal(id) + .ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos)))? + }; - return Ok(( - module.get_qualified_var_mut(name, *hash_var, *pos)?, - name, - // Module variables are constant - ScopeEntryType::Constant, - *pos, - )); - } + return Ok(( + module.get_qualified_var_mut(name, *hash_var, *pos)?, + name, + // Module variables are constant + ScopeEntryType::Constant, + *pos, + )); } let index = if state.always_search { None } else { *index }; @@ -496,8 +488,6 @@ impl Engine { Self { packages: Default::default(), global_module: Default::default(), - - #[cfg(not(feature = "no_module"))] module_resolver: None, type_names: HashMap::new(), @@ -1609,7 +1599,6 @@ impl Engine { } // Module-qualified function call - #[cfg(not(feature = "no_module"))] Expr::FnCall(x) if x.1.is_some() => { let ((name, _, pos), modules, hash_fn_def, args_expr, def_val) = x.as_ref(); let modules = modules.as_ref().unwrap(); @@ -1630,7 +1619,7 @@ impl Engine { .downcast_mut::() .unwrap() } else { - scope.find_module(id).ok_or_else(|| { + scope.find_module_internal(id).ok_or_else(|| { Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos)) })? }; @@ -1919,21 +1908,18 @@ impl Engine { // Import statement Stmt::Import(x) => { - #[cfg(feature = "no_module")] - unreachable!(); + let (expr, (name, pos)) = x.as_ref(); - #[cfg(not(feature = "no_module"))] + // Guard against too many modules + if state.modules >= self.max_modules { + return Err(Box::new(EvalAltResult::ErrorTooManyModules(*pos))); + } + + if let Some(path) = self + .eval_expr(scope, state, lib, &expr, level)? + .try_cast::() { - let (expr, (name, pos)) = x.as_ref(); - - // Guard against too many modules - if state.modules >= self.max_modules { - return Err(Box::new(EvalAltResult::ErrorTooManyModules(*pos))); - } - - if let Some(path) = self - .eval_expr(scope, state, lib, &expr, level)? - .try_cast::() + #[cfg(not(feature = "no_module"))] { if let Some(resolver) = &self.module_resolver { // Use an empty scope to create a module @@ -1941,7 +1927,7 @@ impl Engine { resolver.resolve(self, Scope::new(), &path, expr.position())?; let mod_name = unsafe_cast_var_name_to_lifetime(name, &state); - scope.push_module(mod_name, module); + scope.push_module_internal(mod_name, module); state.modules += 1; @@ -1952,9 +1938,12 @@ impl Engine { expr.position(), ))) } - } else { - Err(Box::new(EvalAltResult::ErrorImportExpr(expr.position()))) } + + #[cfg(feature = "no_module")] + Ok(Default::default()) + } else { + Err(Box::new(EvalAltResult::ErrorImportExpr(expr.position()))) } } diff --git a/src/module.rs b/src/module.rs index 9e16e276..cd567a57 100644 --- a/src/module.rs +++ b/src/module.rs @@ -3,7 +3,7 @@ use crate::any::{Dynamic, Variant}; use crate::calc_fn_hash; use crate::engine::{make_getter, make_setter, Engine, FunctionsLib, FUNC_INDEXER}; -use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn}; +use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn, SendSync}; use crate::parser::{ FnAccess, FnAccess::{Private, Public}, @@ -945,23 +945,7 @@ impl ModuleRef { } /// Trait that encapsulates a module resolution service. -#[cfg(not(feature = "no_module"))] -#[cfg(not(feature = "sync"))] -pub trait ModuleResolver { - /// Resolve a module based on a path string. - fn resolve( - &self, - engine: &Engine, - scope: Scope, - path: &str, - pos: Position, - ) -> Result>; -} - -/// Trait that encapsulates a module resolution service. -#[cfg(not(feature = "no_module"))] -#[cfg(feature = "sync")] -pub trait ModuleResolver: Send + Sync { +pub trait ModuleResolver: SendSync { /// Resolve a module based on a path string. fn resolve( &self, @@ -979,6 +963,8 @@ pub mod resolvers { pub use super::file::FileModuleResolver; pub use super::stat::StaticModuleResolver; } +#[cfg(feature = "no_module")] +pub mod resolvers {} /// Script file-based module resolver. #[cfg(not(feature = "no_module"))] diff --git a/src/parser.rs b/src/parser.rs index 2a20422e..8005acbc 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -4,18 +4,12 @@ use crate::any::{Dynamic, Union}; use crate::calc_fn_hash; use crate::engine::{make_getter, make_setter, Engine, FunctionsLib}; use crate::error::{LexError, ParseError, ParseErrorType}; +use crate::module::ModuleRef; use crate::optimize::{optimize_into_ast, OptimizationLevel}; use crate::scope::{EntryType as ScopeEntryType, Scope}; use crate::token::{Position, Token, TokenIterator}; use crate::utils::{StaticVec, StraightHasherBuilder}; -#[cfg(not(feature = "no_module"))] -use crate::module::ModuleRef; - -#[cfg(feature = "no_module")] -#[derive(Debug, Eq, PartialEq, Clone, Hash, Copy, Default)] -pub struct ModuleRef; - use crate::stdlib::{ borrow::Cow, boxed::Box, @@ -644,7 +638,6 @@ impl Expr { Self::Variable(_) => match token { Token::LeftBracket | Token::LeftParen => true, - #[cfg(not(feature = "no_module"))] Token::DoubleColon => true, _ => false, }, @@ -761,27 +754,21 @@ fn parse_call_expr<'a>( Token::RightParen => { eat_token(input, Token::RightParen); - #[cfg(not(feature = "no_module"))] - let hash_fn_def = { - if let Some(modules) = modules.as_mut() { - modules.set_index(state.find_module(&modules.get(0).0)); + let hash_fn_def = if let Some(modules) = modules.as_mut() { + modules.set_index(state.find_module(&modules.get(0).0)); - // Rust functions are indexed in two steps: - // 1) Calculate a hash in a similar manner to script-defined functions, - // i.e. qualifiers + function name + number of arguments. - // 2) Calculate a second hash with no qualifiers, empty function name, - // zero number of arguments, and the actual list of argument `TypeId`'s. - // 3) The final hash is the XOR of the two hashes. - let qualifiers = modules.iter().map(|(m, _)| m.as_str()); - calc_fn_hash(qualifiers, &id, 0, empty()) - } else { - // Qualifiers (none) + function name + no parameters. - calc_fn_hash(empty(), &id, 0, empty()) - } + // Rust functions are indexed in two steps: + // 1) Calculate a hash in a similar manner to script-defined functions, + // i.e. qualifiers + function name + number of arguments. + // 2) Calculate a second hash with no qualifiers, empty function name, + // zero number of arguments, and the actual list of argument `TypeId`'s. + // 3) The final hash is the XOR of the two hashes. + let qualifiers = modules.iter().map(|(m, _)| m.as_str()); + calc_fn_hash(qualifiers, &id, 0, empty()) + } else { + // Qualifiers (none) + function name + no parameters. + calc_fn_hash(empty(), &id, 0, empty()) }; - // Qualifiers (none) + function name + no parameters. - #[cfg(feature = "no_module")] - let hash_fn_def = calc_fn_hash(empty(), &id, 0, empty()); return Ok(Expr::FnCall(Box::new(( (id.into(), false, begin), @@ -803,27 +790,21 @@ fn parse_call_expr<'a>( (Token::RightParen, _) => { eat_token(input, Token::RightParen); - #[cfg(not(feature = "no_module"))] - let hash_fn_def = { - if let Some(modules) = modules.as_mut() { - modules.set_index(state.find_module(&modules.get(0).0)); + let hash_fn_def = if let Some(modules) = modules.as_mut() { + modules.set_index(state.find_module(&modules.get(0).0)); - // Rust functions are indexed in two steps: - // 1) Calculate a hash in a similar manner to script-defined functions, - // i.e. qualifiers + function name + number of arguments. - // 2) Calculate a second hash with no qualifiers, empty function name, - // zero number of arguments, and the actual list of argument `TypeId`'s. - // 3) The final hash is the XOR of the two hashes. - let qualifiers = modules.iter().map(|(m, _)| m.as_str()); - calc_fn_hash(qualifiers, &id, args.len(), empty()) - } else { - // Qualifiers (none) + function name + number of arguments. - calc_fn_hash(empty(), &id, args.len(), empty()) - } + // Rust functions are indexed in two steps: + // 1) Calculate a hash in a similar manner to script-defined functions, + // i.e. qualifiers + function name + number of arguments. + // 2) Calculate a second hash with no qualifiers, empty function name, + // zero number of arguments, and the actual list of argument `TypeId`'s. + // 3) The final hash is the XOR of the two hashes. + let qualifiers = modules.iter().map(|(m, _)| m.as_str()); + calc_fn_hash(qualifiers, &id, args.len(), empty()) + } else { + // Qualifiers (none) + function name + number of arguments. + calc_fn_hash(empty(), &id, args.len(), empty()) }; - // Qualifiers (none) + function name + number of arguments. - #[cfg(feature = "no_module")] - let hash_fn_def = calc_fn_hash(empty(), &id, args.len(), empty()); return Ok(Expr::FnCall(Box::new(( (id.into(), false, begin), @@ -1265,7 +1246,6 @@ fn parse_primary<'a>( } (Expr::Property(_), _) => unreachable!(), // module access - #[cfg(not(feature = "no_module"))] (Expr::Variable(x), Token::DoubleColon) => match input.next().unwrap() { (Token::Identifier(id2), pos2) => { let ((name, pos), mut modules, _, index) = *x; @@ -1293,7 +1273,6 @@ fn parse_primary<'a>( match &mut root_expr { // Cache the hash key for module-qualified variables - #[cfg(not(feature = "no_module"))] Expr::Variable(x) if x.1.is_some() => { let ((name, _), modules, hash, _) = x.as_mut(); let modules = modules.as_mut().unwrap(); @@ -1520,9 +1499,6 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result { - #[cfg(feature = "no_module")] - unreachable!(); - #[cfg(not(feature = "no_module"))] return Err(PERR::PropertyExpected.into_err(x.1.unwrap().get(0).1)); } // lhs.prop @@ -2113,6 +2089,7 @@ fn parse_import<'a>( } /// Parse an export statement. +#[cfg(not(feature = "no_module"))] fn parse_export<'a>( input: &mut Peekable>, state: &mut ParseState, @@ -2294,7 +2271,9 @@ fn parse_stmt<'a>( Token::LeftBrace => parse_block(input, state, breakable, level + 1, allow_stmt_expr), // fn ... + #[cfg(not(feature = "no_function"))] Token::Fn if !is_global => Err(PERR::WrongFnDefinition.into_err(*pos)), + #[cfg(not(feature = "no_function"))] Token::Fn => unreachable!(), Token::If => parse_if(input, state, breakable, level + 1, allow_stmt_expr), @@ -2343,8 +2322,6 @@ fn parse_stmt<'a>( Token::Let => parse_let(input, state, Normal, level + 1, allow_stmt_expr), Token::Const => parse_let(input, state, Constant, level + 1, allow_stmt_expr), - - #[cfg(not(feature = "no_module"))] Token::Import => parse_import(input, state, level + 1, allow_stmt_expr), #[cfg(not(feature = "no_module"))] @@ -2358,6 +2335,7 @@ fn parse_stmt<'a>( } /// Parse a function definition. +#[cfg(not(feature = "no_function"))] fn parse_fn<'a>( input: &mut Peekable>, state: &mut ParseState, @@ -2499,6 +2477,7 @@ fn parse_global_level<'a>( }; match input.peek().unwrap() { + #[cfg(not(feature = "no_function"))] (Token::Fn, _) => { let mut state = ParseState::new(max_expr_depth.1); let func = parse_fn(input, &mut state, access, 0, true)?; diff --git a/src/scope.rs b/src/scope.rs index 1b9c8b1d..50a5694f 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -1,12 +1,10 @@ //! Module that defines the `Scope` type representing a function call-stack scope. use crate::any::{Dynamic, Union, Variant}; +use crate::module::Module; use crate::parser::{map_dynamic_to_expr, Expr}; use crate::token::Position; -#[cfg(not(feature = "no_module"))] -use crate::module::Module; - use crate::stdlib::{borrow::Cow, boxed::Box, iter, string::String, vec::Vec}; /// Type of an entry in the Scope. @@ -178,6 +176,17 @@ impl<'a> Scope<'a> { /// Modules are used for accessing member variables, functions and plugins under a namespace. #[cfg(not(feature = "no_module"))] pub fn push_module>>(&mut self, name: K, mut value: Module) { + self.push_module_internal(name, value); + } + + /// Add (push) a new module to the Scope. + /// + /// Modules are used for accessing member variables, functions and plugins under a namespace. + pub(crate) fn push_module_internal>>( + &mut self, + name: K, + mut value: Module, + ) { value.index_all_sub_modules(); self.push_dynamic_value( @@ -350,6 +359,11 @@ impl<'a> Scope<'a> { /// Find a module in the Scope, starting from the last entry. #[cfg(not(feature = "no_module"))] pub fn find_module(&mut self, name: &str) -> Option<&mut Module> { + self.find_module_internal(name) + } + + /// Find a module in the Scope, starting from the last entry. + pub(crate) fn find_module_internal(&mut self, name: &str) -> Option<&mut Module> { let index = self.get_module_index(name)?; self.get_mut(index).0.downcast_mut::() } diff --git a/src/token.rs b/src/token.rs index dda005a9..9e6613b0 100644 --- a/src/token.rs +++ b/src/token.rs @@ -181,6 +181,7 @@ pub enum Token { XOr, Ampersand, And, + #[cfg(not(feature = "no_function"))] Fn, Continue, Break, @@ -199,6 +200,7 @@ pub enum Token { PowerOfAssign, Private, Import, + #[cfg(not(feature = "no_module"))] Export, As, LexError(Box), @@ -260,6 +262,7 @@ impl Token { Or => "||", Ampersand => "&", And => "&&", + #[cfg(not(feature = "no_function"))] Fn => "fn", Continue => "continue", Break => "break", @@ -283,6 +286,7 @@ impl Token { PowerOfAssign => "~=", Private => "private", Import => "import", + #[cfg(not(feature = "no_module"))] Export => "export", As => "as", EOF => "{EOF}", @@ -754,12 +758,9 @@ impl<'a> TokenIterator<'a> { "for" => Token::For, "in" => Token::In, "private" => Token::Private, - - #[cfg(not(feature = "no_module"))] "import" => Token::Import, #[cfg(not(feature = "no_module"))] "export" => Token::Export, - #[cfg(not(feature = "no_module"))] "as" => Token::As, #[cfg(not(feature = "no_function"))] @@ -916,7 +917,6 @@ impl<'a> TokenIterator<'a> { } ('=', _) => return Some((Token::Equals, pos)), - #[cfg(not(feature = "no_module"))] (':', ':') => { self.eat_next(); return Some((Token::DoubleColon, pos)); diff --git a/tests/modules.rs b/tests/modules.rs index 61273f53..a063e1bd 100644 --- a/tests/modules.rs +++ b/tests/modules.rs @@ -195,6 +195,7 @@ fn test_module_from_ast() -> Result<(), Box> { let module = Module::eval_ast_as_new(Scope::new(), &ast, &engine)?; let mut scope = Scope::new(); + scope.push_module("testing", module); assert_eq!(