From 51581cdef53ca950efc38790a1a1eeef4db57d19 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 25 Feb 2023 19:57:19 +0800 Subject: [PATCH] Make Engine faster to create. --- src/api/compile.rs | 29 +++++++-- src/api/eval.rs | 11 +++- src/api/events.rs | 4 +- src/api/json.rs | 12 +++- src/api/mod.rs | 7 ++- src/api/options.rs | 38 ++++++------ src/api/register.rs | 26 ++++---- src/api/run.rs | 13 +++- src/engine.rs | 125 +++++++++++++++++++------------------- src/eval/stmt.rs | 2 +- src/func/call.rs | 28 ++++++--- src/packages/lang_core.rs | 1 + src/parser.rs | 3 +- 13 files changed, 184 insertions(+), 115 deletions(-) diff --git a/src/api/compile.rs b/src/api/compile.rs index e222e403..6d506b41 100644 --- a/src/api/compile.rs +++ b/src/api/compile.rs @@ -2,6 +2,7 @@ use crate::func::native::locked_write; use crate::parser::{ParseResult, ParseState}; +use crate::types::StringsInterner; use crate::{Engine, OptimizationLevel, Scope, AST}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -126,7 +127,7 @@ impl Engine { let path = path.clone(); match self - .module_resolver + .module_resolver() .resolve_ast(self, None, &path, crate::Position::NONE) { Some(Ok(module_ast)) => collect_imports(&module_ast, &resolver, &mut imports), @@ -135,7 +136,7 @@ impl Engine { } let module = - self.module_resolver + self.module_resolver() .resolve(self, None, &path, crate::Position::NONE)?; let module = shared_take_or_clone(module); @@ -223,7 +224,17 @@ impl Engine { optimization_level: OptimizationLevel, ) -> ParseResult { let (stream, tc) = self.lex_raw(scripts.as_ref(), self.token_mapper.as_deref()); - let interned_strings = &mut *locked_write(&self.interned_strings); + + let mut interner; + let mut guard; + let interned_strings = if let Some(ref interner) = self.interned_strings { + guard = locked_write(interner); + &mut *guard + } else { + interner = StringsInterner::new(); + &mut interner + }; + let state = &mut ParseState::new(scope, interned_strings, tc); let mut _ast = self.parse(stream.peekable(), state, optimization_level)?; #[cfg(feature = "metadata")] @@ -294,7 +305,17 @@ impl Engine { ) -> ParseResult { let scripts = [script]; let (stream, t) = self.lex_raw(&scripts, self.token_mapper.as_deref()); - let interned_strings = &mut *locked_write(&self.interned_strings); + + let mut interner; + let mut guard; + let interned_strings = if let Some(ref interner) = self.interned_strings { + guard = locked_write(interner); + &mut *guard + } else { + interner = StringsInterner::new(); + &mut interner + }; + let state = &mut ParseState::new(Some(scope), interned_strings, t); self.parse_global_expr(stream.peekable(), state, |_| {}, self.optimization_level) } diff --git a/src/api/eval.rs b/src/api/eval.rs index 7c00e01e..3882fae5 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -4,6 +4,7 @@ use crate::eval::{Caches, GlobalRuntimeState}; use crate::func::native::locked_write; use crate::parser::ParseState; use crate::types::dynamic::Variant; +use crate::types::StringsInterner; use crate::{ Dynamic, Engine, OptimizationLevel, Position, RhaiResult, RhaiResultOf, Scope, AST, ERR, }; @@ -119,7 +120,15 @@ impl Engine { ) -> RhaiResultOf { let scripts = [script]; let ast = { - let interned_strings = &mut *locked_write(&self.interned_strings); + let mut interner; + let mut guard; + let interned_strings = if let Some(ref interner) = self.interned_strings { + guard = locked_write(interner); + &mut *guard + } else { + interner = StringsInterner::new(); + &mut interner + }; let (stream, tc) = self.lex_raw(&scripts, self.token_mapper.as_deref()); diff --git a/src/api/events.rs b/src/api/events.rs index 09b9ce64..1b7d0749 100644 --- a/src/api/events.rs +++ b/src/api/events.rs @@ -286,7 +286,7 @@ impl Engine { /// ``` #[inline(always)] pub fn on_print(&mut self, callback: impl Fn(&str) + SendSync + 'static) -> &mut Self { - self.print = Box::new(callback); + self.print = Some(Box::new(callback)); self } /// Override default action of `debug` (print to stdout using [`println!`]) @@ -336,7 +336,7 @@ impl Engine { &mut self, callback: impl Fn(&str, Option<&str>, Position) + SendSync + 'static, ) -> &mut Self { - self.debug = Box::new(callback); + self.debug = Some(Box::new(callback)); self } /// _(debugging)_ Register a callback for debugging. diff --git a/src/api/json.rs b/src/api/json.rs index 24ad805d..ee266d24 100644 --- a/src/api/json.rs +++ b/src/api/json.rs @@ -4,6 +4,7 @@ use crate::func::native::locked_write; use crate::parser::{ParseSettingFlags, ParseState}; use crate::tokenizer::Token; +use crate::types::StringsInterner; use crate::{Engine, LexError, Map, OptimizationLevel, RhaiResultOf}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -115,7 +116,16 @@ impl Engine { ); let ast = { - let interned_strings = &mut *locked_write(&self.interned_strings); + let mut interner; + let mut guard; + let interned_strings = if let Some(ref interner) = self.interned_strings { + guard = locked_write(interner); + &mut *guard + } else { + interner = StringsInterner::new(); + &mut interner + }; + let state = &mut ParseState::new(None, interned_strings, tokenizer_control); self.parse_global_expr( diff --git a/src/api/mod.rs b/src/api/mod.rs index e9cc9c8a..50fbdaae 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -54,7 +54,10 @@ impl Engine { #[inline(always)] #[must_use] pub fn module_resolver(&self) -> &dyn crate::ModuleResolver { - &*self.module_resolver + const DUMMY_RESOLVER: crate::module::resolvers::DummyModuleResolver = + crate::module::resolvers::DummyModuleResolver; + + self.module_resolver.as_deref().unwrap_or(&DUMMY_RESOLVER) } /// Set the module resolution service used by the [`Engine`]. @@ -66,7 +69,7 @@ impl Engine { &mut self, resolver: impl crate::ModuleResolver + 'static, ) -> &mut Self { - self.module_resolver = Box::new(resolver); + self.module_resolver = Some(Box::new(resolver)); self } diff --git a/src/api/options.rs b/src/api/options.rs index 057a9ac3..4c23f4c9 100644 --- a/src/api/options.rs +++ b/src/api/options.rs @@ -38,24 +38,26 @@ impl LangOptions { /// Create a new [`LangOptions`] with default values. #[inline(always)] #[must_use] - pub fn new() -> Self { - Self::IF_EXPR - | Self::SWITCH_EXPR - | Self::LOOP_EXPR - | Self::STMT_EXPR - | Self::LOOPING - | Self::SHADOWING - | Self::FAST_OPS - | { - #[cfg(not(feature = "no_function"))] - { - Self::ANON_FN - } - #[cfg(feature = "no_function")] - { - Self::empty() - } - } + pub const fn new() -> Self { + Self::from_bits_truncate( + Self::IF_EXPR.bits() + | Self::SWITCH_EXPR.bits() + | Self::LOOP_EXPR.bits() + | Self::STMT_EXPR.bits() + | Self::LOOPING.bits() + | Self::SHADOWING.bits() + | Self::FAST_OPS.bits() + | { + #[cfg(not(feature = "no_function"))] + { + Self::ANON_FN.bits() + } + #[cfg(feature = "no_function")] + { + Self::empty().bits() + } + }, + ) } } diff --git a/src/api/register.rs b/src/api/register.rs index a2d26944..43c43a72 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -1,6 +1,7 @@ //! Module that defines the public function/module registration API of [`Engine`]. use crate::func::{FnCallArgs, RegisterNativeFunction, SendSync}; +use crate::module::ModuleFlags; use crate::types::dynamic::Variant; use crate::{ Engine, FnAccess, FnNamespace, Identifier, Module, NativeCallContext, RhaiResultOf, Shared, @@ -14,20 +15,18 @@ use std::prelude::v1::*; use crate::func::register::Mut; impl Engine { - /// Get the global namespace module (which is the fist module in `global_modules`). - #[inline(always)] - #[allow(dead_code)] - #[must_use] - pub(crate) fn global_namespace(&self) -> &Module { - self.global_modules.first().unwrap() - } /// Get a mutable reference to the global namespace module /// (which is the first module in `global_modules`). #[inline(always)] #[must_use] - pub(crate) fn global_namespace_mut(&mut self) -> &mut Module { - let module = self.global_modules.first_mut().unwrap(); - Shared::get_mut(module).expect("not shared") + fn global_namespace_mut(&mut self) -> &mut Module { + if self.global_modules.is_empty() { + let mut global_namespace = Module::new(); + global_namespace.flags |= ModuleFlags::INTERNAL; + self.global_modules.push(global_namespace.into()); + } + + Shared::get_mut(self.global_modules.first_mut().unwrap()).expect("not shared") } /// Register a custom function with the [`Engine`]. /// @@ -677,6 +676,9 @@ impl Engine { /// modules are searched in reverse order. #[inline(always)] pub fn register_global_module(&mut self, module: SharedModule) -> &mut Self { + // Make sure the global namespace is created. + let _ = self.global_namespace_mut(); + // Insert the module into the front. // The first module is always the global namespace. self.global_modules.insert(1, module); @@ -779,7 +781,9 @@ impl Engine { pub fn gen_fn_signatures(&self, include_packages: bool) -> Vec { let mut signatures = Vec::with_capacity(64); - signatures.extend(self.global_namespace().gen_fn_signatures()); + if let Some(global_namespace) = self.global_modules.first() { + signatures.extend(global_namespace.gen_fn_signatures()); + } #[cfg(not(feature = "no_module"))] for (name, m) in self.global_sub_modules.as_deref().into_iter().flatten() { diff --git a/src/api/run.rs b/src/api/run.rs index c6e14f13..1fe1e476 100644 --- a/src/api/run.rs +++ b/src/api/run.rs @@ -3,6 +3,7 @@ use crate::eval::{Caches, GlobalRuntimeState}; use crate::func::native::locked_write; use crate::parser::ParseState; +use crate::types::StringsInterner; use crate::{Engine, RhaiResultOf, Scope, AST}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -59,7 +60,17 @@ impl Engine { let scripts = [script]; let ast = { let (stream, tc) = self.lex_raw(&scripts, self.token_mapper.as_deref()); - let interned_strings = &mut *locked_write(&self.interned_strings); + + let mut interner; + let mut guard; + let interned_strings = if let Some(ref interner) = self.interned_strings { + guard = locked_write(interner); + &mut *guard + } else { + interner = StringsInterner::new(); + &mut interner + }; + let state = &mut ParseState::new(Some(scope), interned_strings, tc); self.parse(stream.peekable(), state, self.optimization_level)? }; diff --git a/src/engine.rs b/src/engine.rs index 750b35f0..3175c611 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -5,13 +5,11 @@ use crate::func::native::{ locked_write, OnDebugCallback, OnDefVarCallback, OnParseTokenCallback, OnPrintCallback, OnVarCallback, }; -use crate::module::ModuleFlags; use crate::packages::{Package, StandardPackage}; use crate::tokenizer::Token; use crate::types::StringsInterner; use crate::{ - Dynamic, Identifier, ImmutableString, Locked, Module, OptimizationLevel, SharedModule, - StaticVec, + Dynamic, Identifier, ImmutableString, Locked, OptimizationLevel, SharedModule, StaticVec, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -103,10 +101,10 @@ pub struct Engine { /// A module resolution service. #[cfg(not(feature = "no_module"))] - pub(crate) module_resolver: Box, + pub(crate) module_resolver: Option>, /// Strings interner. - pub(crate) interned_strings: Locked, + pub(crate) interned_strings: Option>>, /// A set of symbols to disable. pub(crate) disabled_symbols: Option>>, @@ -127,9 +125,9 @@ pub struct Engine { pub(crate) token_mapper: Option>, /// Callback closure for implementing the `print` command. - pub(crate) print: Box, + pub(crate) print: Option>, /// Callback closure for implementing the `debug` command. - pub(crate) debug: Box, + pub(crate) debug: Option>, /// Callback closure for progress reporting. #[cfg(not(feature = "unchecked"))] pub(crate) progress: Option>, @@ -231,6 +229,49 @@ pub fn make_setter(id: &str) -> Identifier { } impl Engine { + /// An empty [`Engine`]. + pub const EMPTY: Self = Self { + global_modules: StaticVec::new_const(), + + #[cfg(not(feature = "no_module"))] + global_sub_modules: None, + + #[cfg(not(feature = "no_module"))] + module_resolver: None, + + interned_strings: None, + disabled_symbols: None, + #[cfg(not(feature = "no_custom_syntax"))] + custom_keywords: None, + #[cfg(not(feature = "no_custom_syntax"))] + custom_syntax: None, + + def_var_filter: None, + resolve_var: None, + token_mapper: None, + + print: None, + debug: None, + + #[cfg(not(feature = "unchecked"))] + progress: None, + + options: LangOptions::new(), + + def_tag: Dynamic::UNIT, + + #[cfg(not(feature = "no_optimize"))] + optimization_level: OptimizationLevel::Simple, + #[cfg(feature = "no_optimize")] + optimization_level: (), + + #[cfg(not(feature = "unchecked"))] + limits: crate::api::limits::Limits::new(), + + #[cfg(feature = "debugging")] + debugger_interface: None, + }; + /// Create a new [`Engine`]. #[inline] #[must_use] @@ -242,22 +283,25 @@ impl Engine { #[cfg(not(feature = "no_std"))] #[cfg(not(target_family = "wasm"))] { - engine.module_resolver = Box::new(crate::module::resolvers::FileModuleResolver::new()); + engine.module_resolver = + Some(Box::new(crate::module::resolvers::FileModuleResolver::new())); } + engine.interned_strings = Some(Locked::new(StringsInterner::new()).into()); + // default print/debug implementations #[cfg(not(feature = "no_std"))] #[cfg(not(target_family = "wasm"))] { - engine.print = Box::new(|s| println!("{s}")); - engine.debug = Box::new(|s, source, pos| match (source, pos) { + engine.print = Some(Box::new(|s| println!("{s}"))); + engine.debug = Some(Box::new(|s, source, pos| match (source, pos) { (Some(source), crate::Position::NONE) => println!("{source} | {s}"), #[cfg(not(feature = "no_position"))] (Some(source), pos) => println!("{source} @ {pos:?} | {s}"), (None, crate::Position::NONE) => println!("{s}"), #[cfg(not(feature = "no_position"))] (None, pos) => println!("{pos:?} | {s}"), - }); + })); } engine.register_global_module(StandardPackage::new().as_shared_module()); @@ -270,55 +314,8 @@ impl Engine { /// Use [`register_global_module`][Engine::register_global_module] to add packages of functions. #[inline] #[must_use] - pub fn new_raw() -> Self { - let mut engine = Self { - global_modules: StaticVec::new_const(), - - #[cfg(not(feature = "no_module"))] - global_sub_modules: None, - - #[cfg(not(feature = "no_module"))] - module_resolver: Box::new(crate::module::resolvers::DummyModuleResolver::new()), - - interned_strings: StringsInterner::new().into(), - disabled_symbols: None, - #[cfg(not(feature = "no_custom_syntax"))] - custom_keywords: None, - #[cfg(not(feature = "no_custom_syntax"))] - custom_syntax: None, - - def_var_filter: None, - resolve_var: None, - token_mapper: None, - - print: Box::new(|_| {}), - debug: Box::new(|_, _, _| {}), - - #[cfg(not(feature = "unchecked"))] - progress: None, - - options: LangOptions::new(), - - def_tag: Dynamic::UNIT, - - #[cfg(not(feature = "no_optimize"))] - optimization_level: OptimizationLevel::Simple, - #[cfg(feature = "no_optimize")] - optimization_level: (), - - #[cfg(not(feature = "unchecked"))] - limits: crate::api::limits::Limits::new(), - - #[cfg(feature = "debugging")] - debugger_interface: None, - }; - - // Add the global namespace module - let mut global_namespace = Module::new(); - global_namespace.flags |= ModuleFlags::INTERNAL; - engine.global_modules.push(global_namespace.into()); - - engine + pub const fn new_raw() -> Self { + Self::EMPTY } /// Get an interned [string][ImmutableString]. @@ -338,13 +335,17 @@ impl Engine { /// [`Engine`] keeps a cache of [`ImmutableString`] instances and tries to avoid new allocations /// when an existing instance is found. #[cfg(feature = "internals")] - #[inline(always)] + #[inline] #[must_use] pub fn get_interned_string( &self, string: impl AsRef + Into, ) -> ImmutableString { - locked_write(&self.interned_strings).get(string) + if let Some(ref interner) = self.interned_strings { + locked_write(interner).get(string) + } else { + string.into() + } } /// Get an empty [`ImmutableString`] which refers to a shared instance. diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 99958724..0ed2bf37 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -806,7 +806,7 @@ impl Engine { ) .or_else(|| { Some( - self.module_resolver + self.module_resolver() .resolve_raw(self, global, scope, &path, path_pos), ) }) diff --git a/src/func/call.rs b/src/func/call.rs index 490ce278..131089fa 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -459,18 +459,26 @@ impl Engine { // See if the function match print/debug (which requires special processing) return Ok(match name { KEYWORD_PRINT => { - let text = result.into_immutable_string().map_err(|typ| { - let t = self.map_type_name(type_name::()).into(); - ERR::ErrorMismatchOutputType(t, typ.into(), pos) - })?; - ((*self.print)(&text).into(), false) + if let Some(ref print) = self.print { + let text = result.into_immutable_string().map_err(|typ| { + let t = self.map_type_name(type_name::()).into(); + ERR::ErrorMismatchOutputType(t, typ.into(), pos) + })?; + ((print)(&text).into(), false) + } else { + (Dynamic::UNIT, false) + } } KEYWORD_DEBUG => { - let text = result.into_immutable_string().map_err(|typ| { - let t = self.map_type_name(type_name::()).into(); - ERR::ErrorMismatchOutputType(t, typ.into(), pos) - })?; - ((*self.debug)(&text, global.source(), pos).into(), false) + if let Some(ref debug) = self.debug { + let text = result.into_immutable_string().map_err(|typ| { + let t = self.map_type_name(type_name::()).into(); + ERR::ErrorMismatchOutputType(t, typ.into(), pos) + })?; + ((debug)(&text, global.source(), pos).into(), false) + } else { + (Dynamic::UNIT, false) + } } _ => (result, is_method), }); diff --git a/src/packages/lang_core.rs b/src/packages/lang_core.rs index 229113d3..1e5a736b 100644 --- a/src/packages/lang_core.rs +++ b/src/packages/lang_core.rs @@ -7,6 +7,7 @@ use crate::{Dynamic, RhaiResultOf, ERR, INT}; use std::prelude::v1::*; #[cfg(not(feature = "no_float"))] +#[cfg(not(feature = "no_std"))] use crate::FLOAT; def_package! { diff --git a/src/parser.rs b/src/parser.rs index 6d4fd19b..300d44f0 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2354,7 +2354,6 @@ impl Engine { let op = op_token.to_string(); let hash = calc_fn_hash(None, &op, 2); let native_only = !is_valid_function_name(&op); - let operator_token = native_only.then(|| op_token.clone()); let mut args = FnArgsVec::new_const(); args.push(root); @@ -2366,7 +2365,7 @@ impl Engine { name: state.get_interned_string(&op), hashes: FnCallHashes::from_native_only(hash), args, - op_token: operator_token, + op_token: native_only.then(|| op_token.clone()), capture_parent_scope: false, };