Pre-calculate index for module-qualified calls.

This commit is contained in:
Stephen Chung 2020-05-08 22:38:56 +08:00
parent eb52bfa28a
commit e50fcc385f
5 changed files with 63 additions and 42 deletions

View File

@ -2158,7 +2158,7 @@ let ast = engine.compile(r#"
"#)?; "#)?;
// Convert the 'AST' into a module, using the 'Engine' to evaluate it first // Convert the 'AST' into a module, using the 'Engine' to evaluate it first
let module = Module::eval_ast_as_new(&ast, &engine)?; let module = Module::eval_ast_as_new(Scope::new(), &ast, &engine)?;
// 'module' now can be loaded into a custom 'Scope' for future use. It contains: // 'module' now can be loaded into a custom 'Scope' for future use. It contains:
// - sub-module: 'extra' // - sub-module: 'extra'

View File

@ -434,7 +434,7 @@ fn default_print(s: &str) {
fn search_scope<'a>( fn search_scope<'a>(
scope: &'a mut Scope, scope: &'a mut Scope,
name: &str, name: &str,
#[cfg(not(feature = "no_module"))] mut modules: &Option<Box<ModuleRef>>, #[cfg(not(feature = "no_module"))] modules: &Option<Box<ModuleRef>>,
#[cfg(feature = "no_module")] _: &Option<ModuleRef>, #[cfg(feature = "no_module")] _: &Option<ModuleRef>,
index: Option<NonZeroUsize>, index: Option<NonZeroUsize>,
pos: Position, pos: Position,
@ -444,7 +444,7 @@ fn search_scope<'a>(
if let Some(modules) = modules { if let Some(modules) = modules {
let (id, root_pos) = modules.get(0); let (id, root_pos) = modules.get(0);
let module = if let Some(index) = index { let module = if let Some(index) = modules.index() {
scope scope
.get_mut(scope.len() - index.get()) .get_mut(scope.len() - index.get())
.0 .0
@ -1369,9 +1369,17 @@ impl Engine {
let (id, root_pos) = modules.get(0); // First module let (id, root_pos) = modules.get(0); // First module
let module = scope.find_module(id).ok_or_else(|| { let module = if let Some(index) = modules.index() {
Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos)) scope
})?; .get_mut(scope.len() - index.get())
.0
.downcast_mut::<Module>()
.unwrap()
} else {
scope.find_module(id).ok_or_else(|| {
Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos))
})?
};
// First search in script-defined functions (can override built-in) // First search in script-defined functions (can override built-in)
if let Some(fn_def) = module.get_qualified_scripted_fn(modules.key()) { if let Some(fn_def) = module.get_qualified_scripted_fn(modules.key()) {
@ -1623,9 +1631,8 @@ impl Engine {
{ {
if let Some(resolver) = self.module_resolver.as_ref() { if let Some(resolver) = self.module_resolver.as_ref() {
// Use an empty scope to create a module // Use an empty scope to create a module
let mut mod_scope = Scope::new();
let module = let module =
resolver.resolve(self, mod_scope, &path, expr.position())?; resolver.resolve(self, Scope::new(), &path, expr.position())?;
// TODO - avoid copying module name in inner block? // TODO - avoid copying module name in inner block?
let mod_name = name.as_ref().clone(); let mod_name = name.as_ref().clone();
@ -1649,18 +1656,18 @@ impl Engine {
let mut found = false; let mut found = false;
// Mark scope variables as public // Mark scope variables as public
match scope.get_index(id) { if let Some(index) = scope
Some((index, ScopeEntryType::Normal)) .get_index(id)
| Some((index, ScopeEntryType::Constant)) => { .map(|(i, _)| i)
let alias = rename .or_else(|| scope.get_module_index(id))
.as_ref() {
.map(|(n, _)| n.clone()) let alias = rename
.unwrap_or_else(|| id.clone()); .as_ref()
scope.set_entry_alias(index, alias); .map(|(n, _)| n.clone())
found = true; .unwrap_or_else(|| id.clone());
}
Some((_, ScopeEntryType::Module)) => unreachable!(), scope.set_entry_alias(index, alias);
_ => (), found = true;
} }
if !found { if !found {

View File

@ -17,6 +17,7 @@ use crate::stdlib::{
fmt, fmt,
iter::{empty, repeat}, iter::{empty, repeat},
mem, mem,
num::NonZeroUsize,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
rc::Rc, rc::Rc,
string::{String, ToString}, string::{String, ToString},
@ -568,12 +569,11 @@ impl Module {
/// ///
/// ``` /// ```
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> { /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
/// use rhai::{Engine, Module}; /// use rhai::{Engine, Module, Scope};
/// ///
/// let engine = Engine::new(); /// let engine = Engine::new();
/// let mut scope = Scope::new(); /// let ast = engine.compile("let answer = 42; export answer;")?;
/// let ast = engine.compile("let answer = 42;")?; /// let module = Module::eval_ast_as_new(Scope::new(), &ast, &engine)?;
/// let module = Module::eval_ast_as_new(scope, &ast, &engine)?;
/// assert!(module.contains_var("answer")); /// assert!(module.contains_var("answer"));
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42); /// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
/// # Ok(()) /// # Ok(())
@ -599,14 +599,14 @@ impl Module {
ScopeEntryType::Normal | ScopeEntryType::Constant if alias.is_some() => { ScopeEntryType::Normal | ScopeEntryType::Constant if alias.is_some() => {
module.variables.insert(*alias.unwrap(), value); module.variables.insert(*alias.unwrap(), value);
} }
// Variables with no alias are private and not exported
ScopeEntryType::Normal | ScopeEntryType::Constant => (),
// Modules left in the scope become sub-modules // Modules left in the scope become sub-modules
ScopeEntryType::Module => { ScopeEntryType::Module if alias.is_some() => {
module module
.modules .modules
.insert(name.into_owned(), value.cast::<Module>()); .insert(*alias.unwrap(), value.cast::<Module>());
} }
// Variables and modules with no alias are private and not exported
_ => (),
} }
}, },
); );
@ -830,14 +830,18 @@ mod file {
/// A `StaticVec` is used because most module-level access contains only one level, /// A `StaticVec` is used because most module-level access contains only one level,
/// and it is wasteful to always allocate a `Vec` with one element. /// and it is wasteful to always allocate a `Vec` with one element.
#[derive(Clone, Hash, Default)] #[derive(Clone, Hash, Default)]
pub struct ModuleRef(StaticVec<(String, Position)>, u64); pub struct ModuleRef(StaticVec<(String, Position)>, Option<NonZeroUsize>, u64);
impl fmt::Debug for ModuleRef { impl fmt::Debug for ModuleRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)?; fmt::Debug::fmt(&self.0, f)?;
if self.1 > 0 { if self.2 > 0 {
write!(f, " -> {:0>16x}", self.1) if let Some(index) = self.1 {
write!(f, " -> {},{:0>16x}", index, self.2)
} else {
write!(f, " -> {:0>16x}", self.2)
}
} else { } else {
Ok(()) Ok(())
} }
@ -869,16 +873,22 @@ impl fmt::Display for ModuleRef {
impl From<StaticVec<(String, Position)>> for ModuleRef { impl From<StaticVec<(String, Position)>> for ModuleRef {
fn from(modules: StaticVec<(String, Position)>) -> Self { fn from(modules: StaticVec<(String, Position)>) -> Self {
Self(modules, 0) Self(modules, None, 0)
} }
} }
impl ModuleRef { impl ModuleRef {
pub fn key(&self) -> u64 { pub(crate) fn key(&self) -> u64 {
self.2
}
pub(crate) fn set_key(&mut self, key: u64) {
self.2 = key
}
pub(crate) fn index(&self) -> Option<NonZeroUsize> {
self.1 self.1
} }
pub fn set_key(&mut self, key: u64) { pub(crate) fn set_index(&mut self, index: Option<NonZeroUsize>) {
self.1 = key self.1 = index
} }
} }

View File

@ -719,6 +719,7 @@ fn parse_call_expr<'a>(
// Calculate hash // Calculate hash
let hash = calc_fn_hash(modules.iter().map(|(m, _)| m.as_str()), &id, empty()); let hash = calc_fn_hash(modules.iter().map(|(m, _)| m.as_str()), &id, empty());
modules.set_key(hash); modules.set_key(hash);
modules.set_index(stack.find_module(&modules.get(0).0));
} }
} }
return Ok(Expr::FnCall( return Ok(Expr::FnCall(
@ -751,6 +752,7 @@ fn parse_call_expr<'a>(
repeat(EMPTY_TYPE_ID()).take(args.len()), repeat(EMPTY_TYPE_ID()).take(args.len()),
); );
modules.set_key(hash); modules.set_key(hash);
modules.set_index(stack.find_module(&modules.get(0).0));
} }
} }
return Ok(Expr::FnCall( return Ok(Expr::FnCall(
@ -1147,14 +1149,12 @@ fn parse_primary<'a>(
} }
// module access // module access
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
(Expr::Variable(id, mut modules, mut index, pos), Token::DoubleColon) => { (Expr::Variable(id, mut modules, index, pos), Token::DoubleColon) => {
match input.next().unwrap() { match input.next().unwrap() {
(Token::Identifier(id2), pos2) => { (Token::Identifier(id2), pos2) => {
if let Some(ref mut modules) = modules { if let Some(ref mut modules) = modules {
modules.push((*id, pos)); modules.push((*id, pos));
} else { } else {
index = stack.find_module(id.as_ref());
let mut m: ModuleRef = Default::default(); let mut m: ModuleRef = Default::default();
m.push((*id, pos)); m.push((*id, pos));
modules = Some(Box::new(m)); modules = Some(Box::new(m));
@ -1181,6 +1181,7 @@ fn parse_primary<'a>(
Expr::Variable(id, Some(modules), _, _) => { Expr::Variable(id, Some(modules), _, _) => {
let hash = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), id, empty()); let hash = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), id, empty());
modules.set_key(hash); modules.set_key(hash);
modules.set_index(stack.find_module(&modules.get(0).0));
} }
_ => (), _ => (),
} }
@ -2131,7 +2132,8 @@ fn parse_stmt<'a>(
Token::LeftBrace => parse_block(input, stack, breakable, allow_stmt_expr), Token::LeftBrace => parse_block(input, stack, breakable, allow_stmt_expr),
// fn ... // fn ...
Token::Fn => Err(PERR::WrongFnDefinition.into_err(*pos)), Token::Fn if !is_global => Err(PERR::WrongFnDefinition.into_err(*pos)),
Token::Fn => unreachable!(),
Token::If => parse_if(input, stack, breakable, allow_stmt_expr), Token::If => parse_if(input, stack, breakable, allow_stmt_expr),
Token::While => parse_while(input, stack, allow_stmt_expr), Token::While => parse_while(input, stack, allow_stmt_expr),

View File

@ -114,23 +114,25 @@ fn test_module_from_ast() -> Result<(), Box<EvalAltResult>> {
// Final variable values become constant module variable values // Final variable values become constant module variable values
foo = calc(foo); foo = calc(foo);
hello = "hello, " + foo + " worlds!"; hello = "hello, " + foo + " worlds!";
export x as abc, foo, hello, extra as foobar;
"#, "#,
)?; )?;
let module = Module::eval_ast_as_new(&ast, &engine)?; let module = Module::eval_ast_as_new(Scope::new(), &ast, &engine)?;
let mut scope = Scope::new(); let mut scope = Scope::new();
scope.push_module("testing", module); scope.push_module("testing", module);
assert_eq!( assert_eq!(
engine.eval_expression_with_scope::<INT>(&mut scope, "testing::x")?, engine.eval_expression_with_scope::<INT>(&mut scope, "testing::abc")?,
123 123
); );
assert_eq!( assert_eq!(
engine.eval_expression_with_scope::<INT>(&mut scope, "testing::foo")?, engine.eval_expression_with_scope::<INT>(&mut scope, "testing::foo")?,
42 42
); );
assert!(engine.eval_expression_with_scope::<bool>(&mut scope, "testing::extra::foo")?); assert!(engine.eval_expression_with_scope::<bool>(&mut scope, "testing::foobar::foo")?);
assert_eq!( assert_eq!(
engine.eval_expression_with_scope::<String>(&mut scope, "testing::hello")?, engine.eval_expression_with_scope::<String>(&mut scope, "testing::hello")?,
"hello, 42 worlds!" "hello, 42 worlds!"