diff --git a/src/api/eval.rs b/src/api/eval.rs index 6808e2e6..3f37264d 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -217,7 +217,7 @@ impl Engine { level: usize, ) -> RhaiResult { let mut caches = Caches::new(); - global.source = ast.source_raw().clone(); + global.source = ast.source_raw().cloned(); #[cfg(not(feature = "no_module"))] let orig_embedded_module_resolver = std::mem::replace( diff --git a/src/api/run.rs b/src/api/run.rs index b361200a..e32136e6 100644 --- a/src/api/run.rs +++ b/src/api/run.rs @@ -113,7 +113,7 @@ impl Engine { pub fn run_ast_with_scope(&self, scope: &mut Scope, ast: &AST) -> RhaiResultOf<()> { let caches = &mut Caches::new(); let global = &mut GlobalRuntimeState::new(self); - global.source = ast.source_raw().clone(); + global.source = ast.source_raw().cloned(); #[cfg(not(feature = "no_module"))] { diff --git a/src/ast/ast.rs b/src/ast/ast.rs index 98ed86b3..aa18b3ac 100644 --- a/src/ast/ast.rs +++ b/src/ast/ast.rs @@ -1,7 +1,7 @@ //! Module defining the AST (abstract syntax tree). use super::{ASTFlags, Expr, FnAccess, Stmt, StmtBlock, StmtBlockContainer}; -use crate::{Dynamic, FnNamespace, Identifier, Position}; +use crate::{Dynamic, FnNamespace, ImmutableString, Position}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ @@ -20,8 +20,7 @@ use std::{ #[derive(Clone)] pub struct AST { /// Source of the [`AST`]. - /// No source if string is empty. - source: Identifier, + source: Option, /// [`AST`] documentation. #[cfg(feature = "metadata")] doc: crate::SmartString, @@ -98,7 +97,7 @@ impl AST { #[cfg(not(feature = "no_function"))] functions: impl Into>, ) -> Self { Self { - source: Identifier::new_const(), + source: None, #[cfg(feature = "metadata")] doc: crate::SmartString::new_const(), body: StmtBlock::new(statements, Position::NONE, Position::NONE), @@ -133,7 +132,7 @@ impl AST { pub fn new_with_source( statements: impl IntoIterator, #[cfg(not(feature = "no_function"))] functions: impl Into>, - source: impl Into, + source: impl Into, ) -> Self { let mut ast = Self::new( statements, @@ -148,7 +147,7 @@ impl AST { #[must_use] pub fn empty() -> Self { Self { - source: Identifier::new_const(), + source: None, #[cfg(feature = "metadata")] doc: crate::SmartString::new_const(), body: StmtBlock::NONE, @@ -159,36 +158,39 @@ impl AST { } } /// Get the source, if any. - #[inline] + #[inline(always)] #[must_use] pub fn source(&self) -> Option<&str> { - if self.source.is_empty() { - None - } else { - Some(self.source.as_str()) - } + self.source.as_ref().map(|s| s.as_str()) } /// Get a reference to the source. #[inline(always)] #[must_use] - pub(crate) const fn source_raw(&self) -> &Identifier { - &self.source + pub(crate) const fn source_raw(&self) -> Option<&ImmutableString> { + self.source.as_ref() } /// Set the source. #[inline] - pub fn set_source(&mut self, source: impl Into) -> &mut Self { + pub fn set_source(&mut self, source: impl Into) -> &mut Self { let source = source.into(); + #[cfg(not(feature = "no_function"))] crate::Shared::get_mut(&mut self.lib) .as_mut() .map(|m| m.set_id(source.clone())); - self.source = source; + + if source.is_empty() { + self.source = None; + } else { + self.source = Some(source); + } + self } /// Clear the source. #[inline(always)] pub fn clear_source(&mut self) -> &mut Self { - self.source.clear(); + self.source = None; self } /// Get the documentation (if any). @@ -559,18 +561,18 @@ impl AST { lib }; - let mut _ast = if other.source.is_empty() { - Self::new( - merged, - #[cfg(not(feature = "no_function"))] - lib, - ) - } else { + let mut _ast = if let Some(ref source) = other.source { Self::new_with_source( merged, #[cfg(not(feature = "no_function"))] lib, - other.source.clone(), + source.clone(), + ) + } else { + Self::new( + merged, + #[cfg(not(feature = "no_function"))] + lib, ) }; diff --git a/src/eval/cache.rs b/src/eval/cache.rs index cf96dc24..b71eb8cf 100644 --- a/src/eval/cache.rs +++ b/src/eval/cache.rs @@ -2,7 +2,7 @@ use crate::func::{CallableFunction, StraightHashMap}; use crate::types::BloomFilterU64; -use crate::{Identifier, StaticVec}; +use crate::{ImmutableString, StaticVec}; use std::marker::PhantomData; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -14,7 +14,7 @@ pub struct FnResolutionCacheEntry { /// Function. pub func: CallableFunction, /// Optional source. - pub source: Option>, + pub source: Option, } /// _(internals)_ A function resolution cache with a bloom filter. diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs index 63bb3d22..913f66a5 100644 --- a/src/eval/debugger.rs +++ b/src/eval/debugger.rs @@ -3,7 +3,10 @@ use super::{EvalContext, GlobalRuntimeState}; use crate::ast::{ASTNode, Expr, Stmt}; -use crate::{Dynamic, Engine, EvalAltResult, Identifier, Module, Position, RhaiResultOf, Scope}; +use crate::{ + Dynamic, Engine, EvalAltResult, Identifier, ImmutableString, Module, Position, RhaiResultOf, + Scope, +}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{fmt, iter::repeat, mem}; @@ -226,8 +229,8 @@ pub struct CallStackFrame { pub fn_name: Identifier, /// Copies of function call arguments, if any. pub args: crate::StaticVec, - /// Source of the function, empty if none. - pub source: Identifier, + /// Source of the function. + pub source: Option, /// [Position][`Position`] of the function call. pub pos: Position, } @@ -243,8 +246,8 @@ impl fmt::Display for CallStackFrame { fp.finish()?; if !self.pos.is_none() { - if !self.source.is_empty() { - write!(f, ": {}", self.source)?; + if let Some(ref source) = self.source { + write!(f, ": {source}")?; } write!(f, " @ {:?}", self.pos)?; } @@ -295,13 +298,13 @@ impl Debugger { &mut self, fn_name: impl Into, args: crate::StaticVec, - source: impl Into, + source: Option, pos: Position, ) { self.call_stack.push(CallStackFrame { fn_name: fn_name.into(), args, - source: source.into(), + source, pos, }); } @@ -487,7 +490,10 @@ impl Engine { let event = match event { Some(e) => e, - None => match global.debugger.is_break_point(&global.source, node) { + None => match global + .debugger + .is_break_point(global.source().unwrap_or(""), node) + { Some(bp) => DebuggerEvent::BreakPoint(bp), None => return Ok(None), }, @@ -512,17 +518,12 @@ impl Engine { event: DebuggerEvent, level: usize, ) -> Result, Box> { - let source = global.source.clone(); - let source = if source.is_empty() { - None - } else { - Some(source.as_str()) - }; - + let src = global.source_raw().cloned(); + let src = src.as_ref().map(|s| s.as_str()); let context = crate::EvalContext::new(self, scope, global, None, lib, this_ptr, level); if let Some((.., ref on_debugger)) = self.debugger { - let command = on_debugger(context, event, node, source, node.position())?; + let command = on_debugger(context, event, node, src, node.position())?; match command { DebuggerCommand::Continue => { diff --git a/src/eval/eval_context.rs b/src/eval/eval_context.rs index 5ecad699..b1185a39 100644 --- a/src/eval/eval_context.rs +++ b/src/eval/eval_context.rs @@ -58,11 +58,7 @@ impl<'a, 's, 'ps, 'g, 'pg, 'c, 'pc, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'pg, ' #[inline(always)] #[must_use] pub fn source(&self) -> Option<&str> { - if self.global.source.is_empty() { - None - } else { - Some(self.global.source.as_str()) - } + self.global.source() } /// The current [`Scope`]. #[inline(always)] diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index a22a45a2..bf4489f9 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -1,6 +1,6 @@ //! Global runtime state. -use crate::{Dynamic, Engine, Identifier}; +use crate::{Dynamic, Engine, ImmutableString}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{fmt, marker::PhantomData}; @@ -9,7 +9,7 @@ use std::{fmt, marker::PhantomData}; #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_function"))] pub type GlobalConstants = - crate::Shared>>; + crate::Shared>>; /// _(internals)_ Global runtime states. /// Exported under the `internals` feature only. @@ -25,14 +25,14 @@ pub type GlobalConstants = pub struct GlobalRuntimeState<'a> { /// Names of imported [modules][crate::Module]. #[cfg(not(feature = "no_module"))] - imports: crate::StaticVec, + imports: crate::StaticVec, /// Stack of imported [modules][crate::Module]. #[cfg(not(feature = "no_module"))] modules: crate::StaticVec>, /// Source of the current context. /// /// No source if the string is empty. - pub source: Identifier, + pub source: Option, /// Number of operations performed. pub num_operations: u64, /// Number of modules loaded. @@ -84,7 +84,7 @@ impl GlobalRuntimeState<'_> { imports: crate::StaticVec::new_const(), #[cfg(not(feature = "no_module"))] modules: crate::StaticVec::new_const(), - source: Identifier::new_const(), + source: None, num_operations: 0, #[cfg(not(feature = "no_module"))] num_modules_loaded: 0, @@ -168,7 +168,7 @@ impl GlobalRuntimeState<'_> { #[inline(always)] pub fn push_import( &mut self, - name: impl Into, + name: impl Into, module: impl Into>, ) { self.imports.push(name.into()); @@ -202,7 +202,7 @@ impl GlobalRuntimeState<'_> { #[inline] pub(crate) fn iter_imports_raw( &self, - ) -> impl Iterator)> { + ) -> impl Iterator)> { self.imports.iter().zip(self.modules.iter()).rev() } /// Get an iterator to the stack of globally-imported [modules][crate::Module] in forward order. @@ -212,7 +212,7 @@ impl GlobalRuntimeState<'_> { #[inline] pub fn scan_imports_raw( &self, - ) -> impl Iterator)> { + ) -> impl Iterator)> { self.imports.iter().zip(self.modules.iter()) } /// Can the particular function with [`Dynamic`] parameter(s) exist in the stack of @@ -247,11 +247,11 @@ impl GlobalRuntimeState<'_> { pub fn get_qualified_fn( &self, hash: u64, - ) -> Option<(&crate::func::CallableFunction, Option<&str>)> { + ) -> Option<(&crate::func::CallableFunction, Option<&ImmutableString>)> { self.modules .iter() .rev() - .find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id()))) + .find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw()))) } /// Does the specified [`TypeId`][std::any::TypeId] iterator exist in the stack of /// globally-imported [modules][crate::Module]? @@ -278,14 +278,16 @@ impl GlobalRuntimeState<'_> { .find_map(|m| m.get_qualified_iter(id)) } /// Get the current source. - #[inline] + #[inline(always)] #[must_use] pub fn source(&self) -> Option<&str> { - if self.source.is_empty() { - None - } else { - Some(self.source.as_str()) - } + self.source.as_ref().map(|s| s.as_str()) + } + /// Get the current source. + #[inline(always)] + #[must_use] + pub(crate) const fn source_raw(&self) -> Option<&ImmutableString> { + self.source.as_ref() } /// Get the pre-calculated index getter hash. #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] @@ -317,10 +319,10 @@ impl GlobalRuntimeState<'_> { #[cfg(not(feature = "no_module"))] impl IntoIterator for GlobalRuntimeState<'_> { - type Item = (crate::ImmutableString, crate::Shared); + type Item = (ImmutableString, crate::Shared); type IntoIter = std::iter::Rev< std::iter::Zip< - smallvec::IntoIter<[crate::ImmutableString; crate::STATIC_VEC_INLINE_SIZE]>, + smallvec::IntoIter<[ImmutableString; crate::STATIC_VEC_INLINE_SIZE]>, smallvec::IntoIter<[crate::Shared; crate::STATIC_VEC_INLINE_SIZE]>, >, >; @@ -332,10 +334,10 @@ impl IntoIterator for GlobalRuntimeState<'_> { #[cfg(not(feature = "no_module"))] impl<'a> IntoIterator for &'a GlobalRuntimeState<'_> { - type Item = (&'a crate::ImmutableString, &'a crate::Shared); + type Item = (&'a ImmutableString, &'a crate::Shared); type IntoIter = std::iter::Rev< std::iter::Zip< - std::slice::Iter<'a, crate::ImmutableString>, + std::slice::Iter<'a, ImmutableString>, std::slice::Iter<'a, crate::Shared>, >, >; @@ -346,7 +348,7 @@ impl<'a> IntoIterator for &'a GlobalRuntimeState<'_> { } #[cfg(not(feature = "no_module"))] -impl, M: Into>> Extend<(K, M)> +impl, M: Into>> Extend<(K, M)> for GlobalRuntimeState<'_> { #[inline] diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index ae933564..4eecbf88 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -719,8 +719,8 @@ impl Engine { err_map.insert("message".into(), err.to_string().into()); - if !global.source.is_empty() { - err_map.insert("source".into(), global.source.clone().into()); + if let Some(ref source) = global.source { + err_map.insert("source".into(), source.into()); } if !err_pos.is_none() { diff --git a/src/func/call.rs b/src/func/call.rs index ae4298fe..5c3fbf43 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -218,7 +218,7 @@ impl Engine { .iter() .copied() .chain(self.global_modules.iter().map(|m| m.as_ref())) - .find_map(|m| m.get_fn(hash).map(|f| (f, m.id()))); + .find_map(|m| m.get_fn(hash).map(|f| (f, m.id_raw()))); #[cfg(not(feature = "no_module"))] let func = if args.is_none() { @@ -228,7 +228,7 @@ impl Engine { func.or_else(|| _global.get_qualified_fn(hash)).or_else(|| { self.global_sub_modules .values() - .find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id()))) + .find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw()))) }) }; @@ -236,7 +236,7 @@ impl Engine { // Specific version found let new_entry = Some(FnResolutionCacheEntry { func: f.clone(), - source: s.map(|s| Box::new(s.into())), + source: s.cloned(), }); return if cache.filter.is_absent_and_set(hash) { // Do not cache "one-hit wonders" @@ -358,7 +358,6 @@ impl Engine { ) -> RhaiResultOf<(Dynamic, bool)> { self.track_operation(global, pos)?; - let parent_source = global.source.clone(); let op_assign = if is_op_assign { Token::lookup_from_syntax(name) } else { @@ -398,24 +397,19 @@ impl Engine { backup.change_first_arg_to_copy(args); } - let source = match (source, parent_source.as_str()) { - (None, "") => None, - (None, s) => Some(s), - (Some(s), ..) => Some(s.as_str()), - }; - #[cfg(feature = "debugging")] if self.debugger.is_some() { global.debugger.push_call_stack_frame( name, args.iter().map(|v| (*v).clone()).collect(), - source.unwrap_or(""), + source.clone().or_else(|| global.source.clone()), pos, ); } // Run external function - let context = (self, name, source, &*global, lib, pos, level).into(); + let src = source.as_ref().map(|s| s.as_str()); + let context = (self, name, src, &*global, lib, pos, level).into(); let result = if func.is_plugin_fn() { let f = func.get_plugin_fn().unwrap(); @@ -484,12 +478,7 @@ impl Engine { let t = self.map_type_name(type_name::()).into(); ERR::ErrorMismatchOutputType(t, typ.into(), pos) })?; - let source = if global.source.is_empty() { - None - } else { - Some(global.source.as_str()) - }; - ((*self.debug)(&text, source, pos).into(), false) + ((*self.debug)(&text, global.source(), pos).into(), false) } _ => (result, is_method), }); @@ -685,12 +674,7 @@ impl Engine { } }; - let orig_source = mem::replace( - &mut global.source, - source - .as_ref() - .map_or(crate::Identifier::new_const(), |s| (**s).clone()), - ); + let orig_source = mem::replace(&mut global.source, source.clone()); let result = if _is_method_call { // Method call of script function - map first argument to `this` @@ -1172,7 +1156,7 @@ impl Engine { return result.map_err(|err| { ERR::ErrorInFunctionCall( KEYWORD_EVAL.to_string(), - global.source.to_string(), + global.source().unwrap_or("").to_string(), err, pos, ) @@ -1416,14 +1400,13 @@ impl Engine { Some(f) if f.is_script() => { let fn_def = f.get_script_fn_def().expect("script-defined function"); let new_scope = &mut Scope::new(); - let mut source = module.id_raw().clone(); - mem::swap(&mut global.source, &mut source); + let orig_source = mem::replace(&mut global.source, module.id_raw().cloned()); let result = self.call_script_fn( new_scope, global, caches, lib, &mut None, fn_def, &mut args, true, pos, level, ); - global.source = source; + global.source = orig_source; result } diff --git a/src/func/script.rs b/src/func/script.rs index c387b3dc..476343af 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -54,7 +54,7 @@ impl Engine { Err(ERR::ErrorInFunctionCall( name, - source.unwrap_or_else(|| global.source.to_string()), + source.unwrap_or_else(|| global.source().unwrap_or("").to_string()), err, pos, ) diff --git a/src/module/mod.rs b/src/module/mod.rs index fe48d77f..37a0e167 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -160,8 +160,7 @@ pub fn calc_native_fn_hash<'a>( #[derive(Clone)] pub struct Module { /// ID identifying the module. - /// No ID if string is empty. - id: Identifier, + id: Option, /// Module documentation. #[cfg(feature = "metadata")] doc: crate::SmartString, @@ -292,7 +291,7 @@ impl Module { #[must_use] pub fn with_capacity(capacity: usize) -> Self { Self { - id: Identifier::new_const(), + id: None, #[cfg(feature = "metadata")] doc: crate::SmartString::new_const(), internal: false, @@ -324,18 +323,14 @@ impl Module { #[inline] #[must_use] pub fn id(&self) -> Option<&str> { - if self.id_raw().is_empty() { - None - } else { - Some(self.id_raw()) - } + self.id.as_ref().map(|s| s.as_str()) } /// Get the ID of the [`Module`] as an [`Identifier`], if any. #[inline(always)] #[must_use] - pub(crate) const fn id_raw(&self) -> &Identifier { - &self.id + pub(crate) const fn id_raw(&self) -> Option<&ImmutableString> { + self.id.as_ref() } /// Set the ID of the [`Module`]. @@ -351,8 +346,15 @@ impl Module { /// assert_eq!(module.id(), Some("hello")); /// ``` #[inline(always)] - pub fn set_id(&mut self, id: impl Into) -> &mut Self { - self.id = id.into(); + pub fn set_id(&mut self, id: impl Into) -> &mut Self { + let id = id.into(); + + if id.is_empty() { + self.id = None; + } else { + self.id = Some(id); + } + self } @@ -370,7 +372,7 @@ impl Module { /// ``` #[inline(always)] pub fn clear_id(&mut self) -> &mut Self { - self.id.clear(); + self.id = None; self } @@ -434,7 +436,7 @@ impl Module { /// Clear the [`Module`]. #[inline(always)] pub fn clear(&mut self) { - self.id.clear(); + self.id = None; #[cfg(feature = "metadata")] self.doc.clear(); self.internal = false; @@ -2078,7 +2080,7 @@ impl Module { }); } - module.set_id(ast.source_raw().clone()); + module.id = ast.source_raw().cloned(); #[cfg(feature = "metadata")] module.set_doc(ast.doc()); diff --git a/src/packages/debugging.rs b/src/packages/debugging.rs index e48571a8..eba64f5e 100644 --- a/src/packages/debugging.rs +++ b/src/packages/debugging.rs @@ -62,8 +62,8 @@ mod debugging_functions { Dynamic::from_array(_args.clone().to_vec()), ); } - if !_source.is_empty() { - map.insert("source".into(), _source.into()); + if let Some(source) = _source { + map.insert("source".into(), source.into()); } if !_pos.is_none() { map.insert( diff --git a/tests/modules.rs b/tests/modules.rs index 77e60b2b..f18d9aa7 100644 --- a/tests/modules.rs +++ b/tests/modules.rs @@ -578,7 +578,7 @@ fn test_module_context() -> Result<(), Box> { let new_context = NativeCallContext::new_with_all_fields( engine, &fn_name, - source.as_ref().map(|s| s.as_str()), + source.as_ref().map(String::as_str), &global, &lib, pos, diff --git a/tests/optimizer.rs b/tests/optimizer.rs index 248a15f0..b72cab02 100644 --- a/tests/optimizer.rs +++ b/tests/optimizer.rs @@ -89,21 +89,21 @@ fn test_optimizer_parse() -> Result<(), Box> { assert_eq!( format!("{ast:?}"), - r#"AST { source: "", doc: "", resolver: None, body: [Expr(123 @ 1:53)] }"# + r#"AST { source: None, doc: "", resolver: None, body: [Expr(123 @ 1:53)] }"# ); let ast = engine.compile("const DECISION = false; if DECISION { 42 } else { 123 }")?; assert_eq!( format!("{ast:?}"), - r#"AST { source: "", doc: "", resolver: None, body: [Var(("DECISION" @ 1:7, false @ 1:18, None), CONSTANT, 1:1), Expr(123 @ 1:51)] }"# + r#"AST { source: None, doc: "", resolver: None, body: [Var(("DECISION" @ 1:7, false @ 1:18, None), CONSTANT, 1:1), Expr(123 @ 1:51)] }"# ); let ast = engine.compile("if 1 == 2 { 42 }")?; assert_eq!( format!("{ast:?}"), - r#"AST { source: "", doc: "", resolver: None, body: [] }"# + r#"AST { source: None, doc: "", resolver: None, body: [] }"# ); engine.set_optimization_level(OptimizationLevel::Full); @@ -112,14 +112,14 @@ fn test_optimizer_parse() -> Result<(), Box> { assert_eq!( format!("{ast:?}"), - r#"AST { source: "", doc: "", resolver: None, body: [Expr(42 @ 1:1)] }"# + r#"AST { source: None, doc: "", resolver: None, body: [Expr(42 @ 1:1)] }"# ); let ast = engine.compile("NUMBER")?; assert_eq!( format!("{ast:?}"), - r#"AST { source: "", doc: "", resolver: None, body: [Expr(Variable(NUMBER) @ 1:1)] }"# + r#"AST { source: None, doc: "", resolver: None, body: [Expr(Variable(NUMBER) @ 1:1)] }"# ); let mut module = Module::new(); @@ -131,7 +131,7 @@ fn test_optimizer_parse() -> Result<(), Box> { assert_eq!( format!("{ast:?}"), - r#"AST { source: "", doc: "", resolver: None, body: [Expr(42 @ 1:1)] }"# + r#"AST { source: None, doc: "", resolver: None, body: [Expr(42 @ 1:1)] }"# ); Ok(())