Remove no_smartstring feature.

This commit is contained in:
Stephen Chung 2021-11-27 14:24:36 +08:00
parent abe6b4a29b
commit d56585c877
11 changed files with 105 additions and 124 deletions

View File

@ -14,6 +14,11 @@ Enhancements
* Added `into_array` and `into_typed_array` for `Dynamic`. * Added `into_array` and `into_typed_array` for `Dynamic`.
Deprecated API's
----------------
* The internal `no_smartstring` feature is removed since `SmartString` now supports `no-std`.
Version 1.2.1 Version 1.2.1
============= =============

View File

@ -48,9 +48,6 @@ no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "ahash/compi
wasm-bindgen = ["instant/wasm-bindgen"] wasm-bindgen = ["instant/wasm-bindgen"]
stdweb = ["instant/stdweb"] stdweb = ["instant/stdweb"]
# internal feature flags - volatile
no_smartstring = [] # do not use SmartString
[profile.release] [profile.release]
lto = "fat" lto = "fat"
codegen-units = 1 codegen-units = 1

View File

@ -228,7 +228,7 @@ impl AST {
pub fn empty() -> Self { pub fn empty() -> Self {
Self { Self {
source: None, source: None,
body: StmtBlock::empty(Position::NONE), body: StmtBlock::NONE,
functions: Module::new().into(), functions: Module::new().into(),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
resolver: None, resolver: None,
@ -401,7 +401,7 @@ impl AST {
functions.merge_filtered(&self.functions, &filter); functions.merge_filtered(&self.functions, &filter);
Self { Self {
source: self.source.clone(), source: self.source.clone(),
body: StmtBlock::empty(Position::NONE), body: StmtBlock::NONE,
functions: functions.into(), functions: functions.into(),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
resolver: self.resolver.clone(), resolver: self.resolver.clone(),
@ -597,7 +597,7 @@ impl AST {
} }
(false, true) => body.clone(), (false, true) => body.clone(),
(true, false) => other.body.clone(), (true, false) => other.body.clone(),
(true, true) => StmtBlock::empty(Position::NONE), (true, true) => StmtBlock::NONE,
}; };
let source = other.source.clone().or_else(|| self.source.clone()); let source = other.source.clone().or_else(|| self.source.clone());
@ -744,7 +744,7 @@ impl AST {
/// Clear all statements in the [`AST`], leaving only function definitions. /// Clear all statements in the [`AST`], leaving only function definitions.
#[inline(always)] #[inline(always)]
pub fn clear_statements(&mut self) -> &mut Self { pub fn clear_statements(&mut self) -> &mut Self {
self.body = StmtBlock::empty(Position::NONE); self.body = StmtBlock::NONE;
self self
} }
/// Extract all top-level literal constant and/or variable definitions. /// Extract all top-level literal constant and/or variable definitions.
@ -987,6 +987,9 @@ impl ASTNode<'_> {
pub struct StmtBlock(StaticVec<Stmt>, Position); pub struct StmtBlock(StaticVec<Stmt>, Position);
impl StmtBlock { impl StmtBlock {
/// A [`StmtBlock`] that does not exist.
pub const NONE: Self = Self::empty(Position::NONE);
/// Create a new [`StmtBlock`]. /// Create a new [`StmtBlock`].
#[must_use] #[must_use]
pub fn new(statements: impl IntoIterator<Item = Stmt>, pos: Position) -> Self { pub fn new(statements: impl IntoIterator<Item = Stmt>, pos: Position) -> Self {

View File

@ -797,6 +797,12 @@ impl EvalState {
pub const fn is_global(&self) -> bool { pub const fn is_global(&self) -> bool {
self.scope_level == 0 self.scope_level == 0
} }
/// Get the number of function resolution cache(s) in the stack.
#[inline(always)]
#[must_use]
pub fn fn_resolution_caches_len(&self) -> usize {
self.fn_resolution_caches.len()
}
/// Get a mutable reference to the current function resolution cache. /// Get a mutable reference to the current function resolution cache.
#[inline] #[inline]
#[must_use] #[must_use]
@ -813,14 +819,10 @@ impl EvalState {
pub fn push_fn_resolution_cache(&mut self) { pub fn push_fn_resolution_cache(&mut self) {
self.fn_resolution_caches.push(BTreeMap::new()); self.fn_resolution_caches.push(BTreeMap::new());
} }
/// Remove the current function resolution cache from the stack and make the last one current. /// Rewind the function resolution caches stack to a particular size.
///
/// # Panics
///
/// Panics if there is no more function resolution cache in the stack.
#[inline(always)] #[inline(always)]
pub fn pop_fn_resolution_cache(&mut self) { pub fn rewind_fn_resolution_caches(&mut self, len: usize) {
self.fn_resolution_caches.pop().expect("not empty"); self.fn_resolution_caches.truncate(len);
} }
} }
@ -2444,7 +2446,7 @@ impl Engine {
lib: &[&Module], lib: &[&Module],
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Option<&mut Dynamic>,
statements: &[Stmt], statements: &[Stmt],
restore_prev_state: bool, restore_orig_state: bool,
rewind_scope: bool, rewind_scope: bool,
level: usize, level: usize,
) -> RhaiResult { ) -> RhaiResult {
@ -2452,10 +2454,10 @@ impl Engine {
return Ok(Dynamic::UNIT); return Ok(Dynamic::UNIT);
} }
let mut _extra_fn_resolution_cache = false; let orig_always_search_scope = state.always_search_scope;
let prev_always_search_scope = state.always_search_scope; let orig_scope_len = scope.len();
let prev_scope_len = scope.len(); let orig_mods_len = mods.len();
let prev_mods_len = mods.len(); let orig_fn_resolution_caches_len = state.fn_resolution_caches_len();
if rewind_scope { if rewind_scope {
state.scope_level += 1; state.scope_level += 1;
@ -2475,15 +2477,14 @@ impl Engine {
.skip(_mods_len) .skip(_mods_len)
.any(|(_, m)| m.contains_indexed_global_functions()) .any(|(_, m)| m.contains_indexed_global_functions())
{ {
if _extra_fn_resolution_cache { if state.fn_resolution_caches_len() > orig_fn_resolution_caches_len {
// When new module is imported with global functions and there is already // When new module is imported with global functions and there is already
// a new cache, clear it - notice that this is expensive as all function // a new cache, clear it - notice that this is expensive as all function
// resolutions must start again // resolutions must start again
state.fn_resolution_cache_mut().clear(); state.fn_resolution_cache_mut().clear();
} else if restore_prev_state { } else if restore_orig_state {
// When new module is imported with global functions, push a new cache // When new module is imported with global functions, push a new cache
state.push_fn_resolution_cache(); state.push_fn_resolution_cache();
_extra_fn_resolution_cache = true;
} else { } else {
// When the block is to be evaluated in-place, just clear the current cache // When the block is to be evaluated in-place, just clear the current cache
state.fn_resolution_cache_mut().clear(); state.fn_resolution_cache_mut().clear();
@ -2494,21 +2495,19 @@ impl Engine {
Ok(r) Ok(r)
}); });
if _extra_fn_resolution_cache {
// If imports list is modified, pop the functions lookup cache // If imports list is modified, pop the functions lookup cache
state.pop_fn_resolution_cache(); state.rewind_fn_resolution_caches(orig_fn_resolution_caches_len);
}
if rewind_scope { if rewind_scope {
scope.rewind(prev_scope_len); scope.rewind(orig_scope_len);
state.scope_level -= 1; state.scope_level -= 1;
} }
if restore_prev_state { if restore_orig_state {
mods.truncate(prev_mods_len); mods.truncate(orig_mods_len);
// The impact of new local variables goes away at the end of a block // The impact of new local variables goes away at the end of a block
// because any new variables introduced will go out of scope // because any new variables introduced will go out of scope
state.always_search_scope = prev_always_search_scope; state.always_search_scope = orig_always_search_scope;
} }
result result

View File

@ -530,8 +530,8 @@ impl Engine {
return Err(EvalAltResult::ErrorStackOverflow(pos).into()); return Err(EvalAltResult::ErrorStackOverflow(pos).into());
} }
let prev_scope_len = scope.len(); let orig_scope_len = scope.len();
let prev_mods_len = mods.len(); let orig_mods_len = mods.len();
// Put arguments into scope as variables // Put arguments into scope as variables
// Actually consume the arguments instead of cloning them // Actually consume the arguments instead of cloning them
@ -549,14 +549,19 @@ impl Engine {
// Merge in encapsulated environment, if any // Merge in encapsulated environment, if any
let mut lib_merged = StaticVec::with_capacity(lib.len() + 1); let mut lib_merged = StaticVec::with_capacity(lib.len() + 1);
let orig_fn_resolution_caches_len = state.fn_resolution_caches_len();
let (unified, is_unified) = if let Some(ref env_lib) = fn_def.lib { let lib = if let Some(ref env_lib) = fn_def.lib {
if env_lib.is_empty() {
lib
} else {
state.push_fn_resolution_cache(); state.push_fn_resolution_cache();
lib_merged.push(env_lib.as_ref()); lib_merged.push(env_lib.as_ref());
lib_merged.extend(lib.iter().cloned()); lib_merged.extend(lib.iter().cloned());
(lib_merged.as_ref(), true) lib_merged.as_ref()
}
} else { } else {
(lib, false) lib
}; };
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
@ -574,7 +579,7 @@ impl Engine {
scope, scope,
mods, mods,
state, state,
unified, lib,
this_ptr, this_ptr,
body, body,
true, true,
@ -605,17 +610,14 @@ impl Engine {
// Remove all local variables // Remove all local variables
if rewind_scope { if rewind_scope {
scope.rewind(prev_scope_len); scope.rewind(orig_scope_len);
} else if !args.is_empty() { } else if !args.is_empty() {
// Remove arguments only, leaving new variables in the scope // Remove arguments only, leaving new variables in the scope
scope.remove_range(prev_scope_len, args.len()) scope.remove_range(orig_scope_len, args.len())
} }
mods.truncate(prev_mods_len); mods.truncate(orig_mods_len);
state.rewind_fn_resolution_caches(orig_fn_resolution_caches_len);
if is_unified {
state.pop_fn_resolution_cache();
}
result result
} }
@ -879,7 +881,7 @@ impl Engine {
)?; )?;
// If new functions are defined within the eval string, it is an error // If new functions are defined within the eval string, it is an error
if ast.lib().count().0 != 0 { if !ast.lib().is_empty() {
return Err(ParseErrorType::WrongFnDefinition.into()); return Err(ParseErrorType::WrongFnDefinition.into());
} }
@ -1225,7 +1227,7 @@ impl Engine {
// Handle eval() // Handle eval()
KEYWORD_EVAL if total_args == 1 => { KEYWORD_EVAL if total_args == 1 => {
// eval - only in function call style // eval - only in function call style
let prev_len = scope.len(); let orig_scope_len = scope.len();
let (value, pos) = self.get_arg_value( let (value, pos) = self.get_arg_value(
scope, mods, state, lib, this_ptr, level, &a_expr[0], constants, scope, mods, state, lib, this_ptr, level, &a_expr[0], constants,
)?; )?;
@ -1237,7 +1239,7 @@ impl Engine {
// IMPORTANT! If the eval defines new variables in the current scope, // IMPORTANT! If the eval defines new variables in the current scope,
// all variable offsets from this point on will be mis-aligned. // all variable offsets from this point on will be mis-aligned.
if scope.len() != prev_len { if scope.len() != orig_scope_len {
state.always_search_scope = true; state.always_search_scope = true;
} }

View File

@ -129,27 +129,14 @@ pub use types::{
/// An identifier in Rhai. [`SmartString`](https://crates.io/crates/smartstring) is used because most /// An identifier in Rhai. [`SmartString`](https://crates.io/crates/smartstring) is used because most
/// identifiers are ASCII and short, fewer than 23 characters, so they can be stored inline. /// identifiers are ASCII and short, fewer than 23 characters, so they can be stored inline.
#[cfg(not(feature = "internals"))] #[cfg(not(feature = "internals"))]
#[cfg(not(feature = "no_smartstring"))]
pub(crate) type Identifier = SmartString; pub(crate) type Identifier = SmartString;
/// An identifier in Rhai.
#[cfg(not(feature = "internals"))]
#[cfg(feature = "no_smartstring")]
pub(crate) type Identifier = ImmutableString;
/// An identifier in Rhai. [`SmartString`](https://crates.io/crates/smartstring) is used because most /// An identifier in Rhai. [`SmartString`](https://crates.io/crates/smartstring) is used because most
/// identifiers are ASCII and short, fewer than 23 characters, so they can be stored inline. /// identifiers are ASCII and short, fewer than 23 characters, so they can be stored inline.
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[cfg(not(feature = "no_smartstring"))]
#[deprecated = "this type is volatile and may change"] #[deprecated = "this type is volatile and may change"]
pub type Identifier = SmartString; pub type Identifier = SmartString;
/// An identifier in Rhai.
#[cfg(feature = "internals")]
#[cfg(feature = "no_smartstring")]
#[deprecated = "this type is volatile and may change"]
pub type Identifier = ImmutableString;
/// Alias to [`Rc`][std::rc::Rc] or [`Arc`][std::sync::Arc] depending on the `sync` feature flag. /// Alias to [`Rc`][std::rc::Rc] or [`Arc`][std::sync::Arc] depending on the `sync` feature flag.
pub use func::Shared; pub use func::Shared;
@ -314,12 +301,8 @@ type StaticVec<T> = smallvec::SmallVec<[T; 3]>;
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
pub type StaticVec<T> = smallvec::SmallVec<[T; 3]>; pub type StaticVec<T> = smallvec::SmallVec<[T; 3]>;
#[cfg(not(feature = "no_smartstring"))]
pub(crate) type SmartString = smartstring::SmartString<smartstring::LazyCompact>; pub(crate) type SmartString = smartstring::SmartString<smartstring::LazyCompact>;
#[cfg(feature = "no_smartstring")]
pub(crate) type SmartString = String;
// Compiler guards against mutually-exclusive feature flags // Compiler guards against mutually-exclusive feature flags
#[cfg(feature = "no_float")] #[cfg(feature = "no_float")]

View File

@ -324,7 +324,9 @@ impl Module {
#[inline] #[inline]
#[must_use] #[must_use]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.functions.is_empty() self.indexed
&& !self.contains_indexed_global_functions
&& self.functions.is_empty()
&& self.all_functions.is_empty() && self.all_functions.is_empty()
&& self.variables.is_empty() && self.variables.is_empty()
&& self.all_variables.is_empty() && self.all_variables.is_empty()
@ -502,11 +504,15 @@ impl Module {
name: &str, name: &str,
num_params: usize, num_params: usize,
) -> Option<&Shared<crate::ast::ScriptFnDef>> { ) -> Option<&Shared<crate::ast::ScriptFnDef>> {
if self.functions.is_empty() {
None
} else {
self.functions self.functions
.values() .values()
.find(|f| f.params == num_params && f.name == name) .find(|f| f.params == num_params && f.name == name)
.and_then(|f| f.func.get_script_fn_def()) .and_then(|f| f.func.get_script_fn_def())
} }
}
/// Get a mutable reference to the underlying [`BTreeMap`] of sub-modules. /// Get a mutable reference to the underlying [`BTreeMap`] of sub-modules.
/// ///
@ -1150,6 +1156,7 @@ impl Module {
self.all_type_iterators.clear(); self.all_type_iterators.clear();
self.indexed = false; self.indexed = false;
self.contains_indexed_global_functions = false; self.contains_indexed_global_functions = false;
self.identifiers += other.identifiers;
self self
} }
@ -1169,6 +1176,7 @@ impl Module {
self.all_type_iterators.clear(); self.all_type_iterators.clear();
self.indexed = false; self.indexed = false;
self.contains_indexed_global_functions = false; self.contains_indexed_global_functions = false;
self.identifiers += other.identifiers;
self self
} }
@ -1197,6 +1205,7 @@ impl Module {
self.all_type_iterators.clear(); self.all_type_iterators.clear();
self.indexed = false; self.indexed = false;
self.contains_indexed_global_functions = false; self.contains_indexed_global_functions = false;
self.identifiers.merge(&other.identifiers);
self self
} }
@ -1246,6 +1255,7 @@ impl Module {
self.all_type_iterators.clear(); self.all_type_iterators.clear();
self.indexed = false; self.indexed = false;
self.contains_indexed_global_functions = false; self.contains_indexed_global_functions = false;
self.identifiers.merge(&other.identifiers);
self self
} }
@ -1454,6 +1464,7 @@ impl Module {
// Non-private functions defined become module functions // Non-private functions defined become module functions
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
if !ast.lib().functions.is_empty() {
ast.lib() ast.lib()
.functions .functions
.values() .values()
@ -1474,6 +1485,7 @@ impl Module {
func.mods = func_mods.clone(); func.mods = func_mods.clone();
module.set_script_fn(func); module.set_script_fn(func);
}); });
}
if let Some(s) = ast.source_raw() { if let Some(s) = ast.source_raw() {
module.set_id(s.clone()); module.set_id(s.clone());

View File

@ -23,6 +23,7 @@ use std::{
collections::BTreeMap, collections::BTreeMap,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
num::{NonZeroU8, NonZeroUsize}, num::{NonZeroU8, NonZeroUsize},
ops::AddAssign,
}; };
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
@ -51,39 +52,29 @@ const NEVER_ENDS: &str = "`TokenStream` never ends";
/// collection of strings and returns shared instances, only creating a new string when it is not /// collection of strings and returns shared instances, only creating a new string when it is not
/// yet interned. /// yet interned.
#[derive(Debug, Clone, Hash)] #[derive(Debug, Clone, Hash)]
pub struct IdentifierBuilder( pub struct IdentifierBuilder();
#[cfg(feature = "no_smartstring")] std::collections::BTreeSet<Identifier>,
);
impl IdentifierBuilder { impl IdentifierBuilder {
/// Create a new IdentifierBuilder. /// Create a new [`IdentifierBuilder`].
#[cfg(not(feature = "no_smartstring"))]
#[inline] #[inline]
#[must_use] #[must_use]
pub const fn new() -> Self { pub const fn new() -> Self {
Self() Self()
} }
/// Create a new IdentifierBuilder.
#[cfg(feature = "no_smartstring")]
#[inline]
#[must_use]
pub fn new() -> Self {
Self(std::collections::BTreeSet::new())
}
/// Get an identifier from a text string. /// Get an identifier from a text string.
#[inline] #[inline]
#[must_use] #[must_use]
pub fn get(&mut self, text: impl AsRef<str> + Into<Identifier>) -> Identifier { pub fn get(&mut self, text: impl AsRef<str> + Into<Identifier>) -> Identifier {
#[cfg(not(feature = "no_smartstring"))] text.into()
return text.into();
#[cfg(feature = "no_smartstring")]
return self.0.get(text.as_ref()).cloned().unwrap_or_else(|| {
let s: Identifier = text.into();
self.0.insert(s.clone());
s
});
} }
/// Merge another [`IdentifierBuilder`] into this.
#[inline(always)]
pub fn merge(&mut self, _other: &Self) {}
}
impl AddAssign for IdentifierBuilder {
#[inline(always)]
fn add_assign(&mut self, _rhs: Self) {}
} }
/// _(internals)_ A type that encapsulates the current state of the parser. /// _(internals)_ A type that encapsulates the current state of the parser.

View File

@ -21,14 +21,7 @@ fn check_struct_sizes() {
assert_eq!(size_of::<Option<ast::Expr>>(), 16); assert_eq!(size_of::<Option<ast::Expr>>(), 16);
assert_eq!(size_of::<ast::Stmt>(), 32); assert_eq!(size_of::<ast::Stmt>(), 32);
assert_eq!(size_of::<Option<ast::Stmt>>(), 32); assert_eq!(size_of::<Option<ast::Stmt>>(), 32);
assert_eq!( assert_eq!(size_of::<FnPtr>(), 80);
size_of::<FnPtr>(),
if cfg!(feature = "no_smartstring") {
64
} else {
80
}
);
assert_eq!(size_of::<Scope>(), 464); assert_eq!(size_of::<Scope>(), 464);
assert_eq!(size_of::<LexError>(), 56); assert_eq!(size_of::<LexError>(), 56);
assert_eq!( assert_eq!(

View File

@ -153,7 +153,6 @@ impl TryFrom<Identifier> for FnPtr {
} }
} }
#[cfg(not(feature = "no_smartstring"))]
impl TryFrom<crate::ImmutableString> for FnPtr { impl TryFrom<crate::ImmutableString> for FnPtr {
type Error = Box<EvalAltResult>; type Error = Box<EvalAltResult>;

View File

@ -115,14 +115,12 @@ impl From<String> for ImmutableString {
Self(value.into()) Self(value.into())
} }
} }
#[cfg(not(feature = "no_smartstring"))]
impl From<&SmartString> for ImmutableString { impl From<&SmartString> for ImmutableString {
#[inline(always)] #[inline(always)]
fn from(value: &SmartString) -> Self { fn from(value: &SmartString) -> Self {
Self(value.clone().into()) Self(value.clone().into())
} }
} }
#[cfg(not(feature = "no_smartstring"))]
impl From<SmartString> for ImmutableString { impl From<SmartString> for ImmutableString {
#[inline(always)] #[inline(always)]
fn from(value: SmartString) -> Self { fn from(value: SmartString) -> Self {
@ -180,7 +178,6 @@ impl<'a> FromIterator<String> for ImmutableString {
} }
} }
#[cfg(not(feature = "no_smartstring"))]
impl<'a> FromIterator<SmartString> for ImmutableString { impl<'a> FromIterator<SmartString> for ImmutableString {
#[inline] #[inline]
fn from_iter<T: IntoIterator<Item = SmartString>>(iter: T) -> Self { fn from_iter<T: IntoIterator<Item = SmartString>>(iter: T) -> Self {